summaryrefslogtreecommitdiff
path: root/chromium/components/mus
diff options
context:
space:
mode:
authorMichal Klocek <michal.klocek@qt.io>2016-12-02 12:10:47 +0100
committerMichal Klocek <michal.klocek@qt.io>2016-12-07 15:36:22 +0000
commitf0e73b6da23ab32db1edfbc912bc3ce989bf9a06 (patch)
treedb26d95d495b8a471f9ddc4e4fe25dae6ed4d700 /chromium/components/mus
parent777da810b25f517d54dc4b7771e42a4ea38c355b (diff)
downloadqtwebengine-chromium-f0e73b6da23ab32db1edfbc912bc3ce989bf9a06.tar.gz
Add mus and catapult project files
These files are required for //content/public/browser dependency. Change-Id: I7fc774416006edc12cd43198af4a9fda0ce7b0d8 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/components/mus')
-rw-r--r--chromium/components/mus/DEPS12
-rw-r--r--chromium/components/mus/OWNERS4
-rw-r--r--chromium/components/mus/clipboard/clipboard_impl.cc108
-rw-r--r--chromium/components/mus/clipboard/clipboard_impl.h65
-rw-r--r--chromium/components/mus/clipboard/clipboard_unittest.cc145
-rw-r--r--chromium/components/mus/clipboard/test_manifest.json10
-rw-r--r--chromium/components/mus/common/DEPS12
-rw-r--r--chromium/components/mus/common/event_matcher_util.cc28
-rw-r--r--chromium/components/mus/common/event_matcher_util.h22
-rw-r--r--chromium/components/mus/common/generic_shared_memory_id_generator.cc21
-rw-r--r--chromium/components/mus/common/generic_shared_memory_id_generator.h19
-rw-r--r--chromium/components/mus/common/gpu_memory_buffer_impl.cc46
-rw-r--r--chromium/components/mus/common/gpu_memory_buffer_impl.h61
-rw-r--r--chromium/components/mus/common/gpu_service.cc256
-rw-r--r--chromium/components/mus/common/gpu_service.h86
-rw-r--r--chromium/components/mus/common/gpu_type_converters.cc164
-rw-r--r--chromium/components/mus/common/gpu_type_converters.h95
-rw-r--r--chromium/components/mus/common/gpu_type_converters_unittest.cc94
-rw-r--r--chromium/components/mus/common/mojo_buffer_backing.cc34
-rw-r--r--chromium/components/mus/common/mojo_buffer_backing.h40
-rw-r--r--chromium/components/mus/common/mojo_gpu_memory_buffer.cc107
-rw-r--r--chromium/components/mus/common/mojo_gpu_memory_buffer.h54
-rw-r--r--chromium/components/mus/common/mojo_gpu_memory_buffer_manager.cc46
-rw-r--r--chromium/components/mus/common/mojo_gpu_memory_buffer_manager.h43
-rw-r--r--chromium/components/mus/common/mus_common_export.h29
-rw-r--r--chromium/components/mus/common/mus_common_unittests_app_manifest.json10
-rw-r--r--chromium/components/mus/common/run_all_shelltests.cc34
-rw-r--r--chromium/components/mus/common/switches.cc20
-rw-r--r--chromium/components/mus/common/switches.h21
-rw-r--r--chromium/components/mus/common/transient_window_utils.h126
-rw-r--r--chromium/components/mus/common/types.h25
-rw-r--r--chromium/components/mus/common/util.h32
-rw-r--r--chromium/components/mus/demo/DEPS4
-rw-r--r--chromium/components/mus/demo/OWNERS2
-rw-r--r--chromium/components/mus/demo/main.cc13
-rw-r--r--chromium/components/mus/demo/manifest.json14
-rw-r--r--chromium/components/mus/demo/mus_demo.cc179
-rw-r--r--chromium/components/mus/demo/mus_demo.h92
-rw-r--r--chromium/components/mus/gles2/DEPS8
-rw-r--r--chromium/components/mus/gles2/OWNERS2
-rw-r--r--chromium/components/mus/gles2/command_buffer_driver.cc568
-rw-r--r--chromium/components/mus/gles2/command_buffer_driver.h173
-rw-r--r--chromium/components/mus/gles2/command_buffer_driver_manager.cc50
-rw-r--r--chromium/components/mus/gles2/command_buffer_driver_manager.h46
-rw-r--r--chromium/components/mus/gles2/command_buffer_impl.cc301
-rw-r--r--chromium/components/mus/gles2/command_buffer_impl.h126
-rw-r--r--chromium/components/mus/gles2/command_buffer_local.cc576
-rw-r--r--chromium/components/mus/gles2/command_buffer_local.h185
-rw-r--r--chromium/components/mus/gles2/command_buffer_local_client.h31
-rw-r--r--chromium/components/mus/gles2/command_buffer_task_runner.cc77
-rw-r--r--chromium/components/mus/gles2/command_buffer_task_runner.h78
-rw-r--r--chromium/components/mus/gles2/gl_surface_adapter.cc57
-rw-r--r--chromium/components/mus/gles2/gl_surface_adapter.h71
-rw-r--r--chromium/components/mus/gles2/gpu_impl.cc27
-rw-r--r--chromium/components/mus/gles2/gpu_impl.h39
-rw-r--r--chromium/components/mus/gles2/gpu_memory_tracker.cc36
-rw-r--r--chromium/components/mus/gles2/gpu_memory_tracker.h39
-rw-r--r--chromium/components/mus/gles2/gpu_state.cc82
-rw-r--r--chromium/components/mus/gles2/gpu_state.h107
-rw-r--r--chromium/components/mus/gles2/ozone_gpu_memory_buffer.cc115
-rw-r--r--chromium/components/mus/gles2/ozone_gpu_memory_buffer.h71
-rw-r--r--chromium/components/mus/gles2/raster_thread_helper.cc27
-rw-r--r--chromium/components/mus/gles2/raster_thread_helper.h34
-rw-r--r--chromium/components/mus/gpu/DEPS21
-rw-r--r--chromium/components/mus/gpu/OWNERS2
-rw-r--r--chromium/components/mus/gpu/display_compositor/compositor_frame_sink_delegate.h27
-rw-r--r--chromium/components/mus/gpu/display_compositor/compositor_frame_sink_factory_impl.cc46
-rw-r--r--chromium/components/mus/gpu/display_compositor/compositor_frame_sink_factory_impl.h55
-rw-r--r--chromium/components/mus/gpu/display_compositor/compositor_frame_sink_impl.cc112
-rw-r--r--chromium/components/mus/gpu/display_compositor/compositor_frame_sink_impl.h74
-rw-r--r--chromium/components/mus/gpu/display_compositor/display_compositor_impl.cc28
-rw-r--r--chromium/components/mus/gpu/display_compositor/display_compositor_impl.h37
-rw-r--r--chromium/components/mus/gpu/display_compositor/display_impl.cc43
-rw-r--r--chromium/components/mus/gpu/display_compositor/display_impl.h44
-rw-r--r--chromium/components/mus/gpu/gpu_service_impl.cc63
-rw-r--r--chromium/components/mus/gpu/gpu_service_impl.h49
-rw-r--r--chromium/components/mus/gpu/gpu_service_mus.cc273
-rw-r--r--chromium/components/mus/gpu/gpu_service_mus.h173
-rw-r--r--chromium/components/mus/gpu/mus_gpu_memory_buffer_manager.cc115
-rw-r--r--chromium/components/mus/gpu/mus_gpu_memory_buffer_manager.h57
-rw-r--r--chromium/components/mus/gpu/mus_gpu_unittests_app_manifest.json10
-rw-r--r--chromium/components/mus/input_devices/input_device_server.cc110
-rw-r--r--chromium/components/mus/input_devices/input_device_server.h69
-rw-r--r--chromium/components/mus/main.cc13
-rw-r--r--chromium/components/mus/manifest.json26
-rw-r--r--chromium/components/mus/mus_app.cc388
-rw-r--r--chromium/components/mus/mus_app.h182
-rw-r--r--chromium/components/mus/public/cpp/DEPS3
-rw-r--r--chromium/components/mus/public/cpp/context_provider.h54
-rw-r--r--chromium/components/mus/public/cpp/gles2_context.h61
-rw-r--r--chromium/components/mus/public/cpp/input_devices/input_device_client.cc112
-rw-r--r--chromium/components/mus/public/cpp/input_devices/input_device_client.h84
-rw-r--r--chromium/components/mus/public/cpp/input_event_handler.h43
-rw-r--r--chromium/components/mus/public/cpp/lib/DEPS3
-rw-r--r--chromium/components/mus/public/cpp/lib/command_buffer_client_impl.cc370
-rw-r--r--chromium/components/mus/public/cpp/lib/command_buffer_client_impl.h117
-rw-r--r--chromium/components/mus/public/cpp/lib/context_provider.cc61
-rw-r--r--chromium/components/mus/public/cpp/lib/gles2_context.cc109
-rw-r--r--chromium/components/mus/public/cpp/lib/in_flight_change.cc220
-rw-r--r--chromium/components/mus/public/cpp/lib/in_flight_change.h298
-rw-r--r--chromium/components/mus/public/cpp/lib/output_surface.cc70
-rw-r--r--chromium/components/mus/public/cpp/lib/property_type_converters.cc207
-rw-r--r--chromium/components/mus/public/cpp/lib/scoped_window_ptr.cc44
-rw-r--r--chromium/components/mus/public/cpp/lib/window.cc891
-rw-r--r--chromium/components/mus/public/cpp/lib/window_observer.cc18
-rw-r--r--chromium/components/mus/public/cpp/lib/window_private.cc31
-rw-r--r--chromium/components/mus/public/cpp/lib/window_private.h100
-rw-r--r--chromium/components/mus/public/cpp/lib/window_surface.cc69
-rw-r--r--chromium/components/mus/public/cpp/lib/window_tree_client.cc1181
-rw-r--r--chromium/components/mus/public/cpp/lib/window_tree_client_delegate.cc11
-rw-r--r--chromium/components/mus/public/cpp/lib/window_tree_host_factory.cc32
-rw-r--r--chromium/components/mus/public/cpp/output_surface.h45
-rw-r--r--chromium/components/mus/public/cpp/property_type_converters.h96
-rw-r--r--chromium/components/mus/public/cpp/scoped_window_ptr.h42
-rw-r--r--chromium/components/mus/public/cpp/window.h356
-rw-r--r--chromium/components/mus/public/cpp/window_manager_delegate.h116
-rw-r--r--chromium/components/mus/public/cpp/window_observer.h110
-rw-r--r--chromium/components/mus/public/cpp/window_property.h156
-rw-r--r--chromium/components/mus/public/cpp/window_surface.h85
-rw-r--r--chromium/components/mus/public/cpp/window_surface_client.h24
-rw-r--r--chromium/components/mus/public/cpp/window_tracker.h21
-rw-r--r--chromium/components/mus/public/cpp/window_tree_client.h407
-rw-r--r--chromium/components/mus/public/cpp/window_tree_client_delegate.h53
-rw-r--r--chromium/components/mus/public/cpp/window_tree_client_observer.h27
-rw-r--r--chromium/components/mus/public/cpp/window_tree_host_factory.h37
-rw-r--r--chromium/components/mus/public/interfaces/OWNERS2
-rw-r--r--chromium/components/mus/public/interfaces/gpu/OWNERS2
-rw-r--r--chromium/components/mus/surfaces/DEPS10
-rw-r--r--chromium/components/mus/surfaces/OWNERS2
-rw-r--r--chromium/components/mus/surfaces/direct_output_surface.cc81
-rw-r--r--chromium/components/mus/surfaces/direct_output_surface.h47
-rw-r--r--chromium/components/mus/surfaces/direct_output_surface_ozone.cc166
-rw-r--r--chromium/components/mus/surfaces/direct_output_surface_ozone.h84
-rw-r--r--chromium/components/mus/surfaces/display_compositor.cc124
-rw-r--r--chromium/components/mus/surfaces/display_compositor.h80
-rw-r--r--chromium/components/mus/surfaces/ozone_gpu_memory_buffer_manager.cc49
-rw-r--r--chromium/components/mus/surfaces/ozone_gpu_memory_buffer_manager.h39
-rw-r--r--chromium/components/mus/surfaces/surfaces_context_provider.cc206
-rw-r--r--chromium/components/mus/surfaces/surfaces_context_provider.h109
-rw-r--r--chromium/components/mus/surfaces/surfaces_context_provider_delegate.h28
-rw-r--r--chromium/components/mus/surfaces/surfaces_state.cc13
-rw-r--r--chromium/components/mus/surfaces/surfaces_state.h49
-rw-r--r--chromium/components/mus/test_wm/manifest.json10
-rw-r--r--chromium/components/mus/test_wm/test_wm.cc107
-rw-r--r--chromium/components/mus/ws/accelerator.cc33
-rw-r--r--chromium/components/mus/ws/accelerator.h57
-rw-r--r--chromium/components/mus/ws/access_policy.h84
-rw-r--r--chromium/components/mus/ws/access_policy_delegate.h41
-rw-r--r--chromium/components/mus/ws/animation_runner.cc163
-rw-r--r--chromium/components/mus/ws/animation_runner.h119
-rw-r--r--chromium/components/mus/ws/animation_runner_observer.h25
-rw-r--r--chromium/components/mus/ws/animation_runner_unittest.cc639
-rw-r--r--chromium/components/mus/ws/cursor_unittest.cc198
-rw-r--r--chromium/components/mus/ws/default_access_policy.cc203
-rw-r--r--chromium/components/mus/ws/default_access_policy.h76
-rw-r--r--chromium/components/mus/ws/display.cc419
-rw-r--r--chromium/components/mus/ws/display.h224
-rw-r--r--chromium/components/mus/ws/display_binding.cc39
-rw-r--r--chromium/components/mus/ws/display_binding.h60
-rw-r--r--chromium/components/mus/ws/display_manager.cc167
-rw-r--r--chromium/components/mus/ws/display_manager.h102
-rw-r--r--chromium/components/mus/ws/display_manager_delegate.h38
-rw-r--r--chromium/components/mus/ws/display_unittest.cc308
-rw-r--r--chromium/components/mus/ws/event_dispatcher.cc559
-rw-r--r--chromium/components/mus/ws/event_dispatcher.h236
-rw-r--r--chromium/components/mus/ws/event_dispatcher_delegate.h73
-rw-r--r--chromium/components/mus/ws/event_dispatcher_unittest.cc1614
-rw-r--r--chromium/components/mus/ws/event_matcher.cc113
-rw-r--r--chromium/components/mus/ws/event_matcher.h56
-rw-r--r--chromium/components/mus/ws/event_matcher_unittest.cc71
-rw-r--r--chromium/components/mus/ws/focus_controller.cc311
-rw-r--r--chromium/components/mus/ws/focus_controller.h104
-rw-r--r--chromium/components/mus/ws/focus_controller_delegate.h26
-rw-r--r--chromium/components/mus/ws/focus_controller_observer.h29
-rw-r--r--chromium/components/mus/ws/focus_controller_unittest.cc312
-rw-r--r--chromium/components/mus/ws/ids.h113
-rw-r--r--chromium/components/mus/ws/modal_window_controller.cc121
-rw-r--r--chromium/components/mus/ws/modal_window_controller.h87
-rw-r--r--chromium/components/mus/ws/mus_ws_unittests_app_manifest.json16
-rw-r--r--chromium/components/mus/ws/operation.cc30
-rw-r--r--chromium/components/mus/ws/operation.h77
-rw-r--r--chromium/components/mus/ws/platform_display.cc414
-rw-r--r--chromium/components/mus/ws/platform_display.h195
-rw-r--r--chromium/components/mus/ws/platform_display_delegate.h53
-rw-r--r--chromium/components/mus/ws/platform_display_factory.h23
-rw-r--r--chromium/components/mus/ws/platform_display_init_params.cc21
-rw-r--r--chromium/components/mus/ws/platform_display_init_params.h39
-rw-r--r--chromium/components/mus/ws/platform_screen.h43
-rw-r--r--chromium/components/mus/ws/platform_screen_impl.cc43
-rw-r--r--chromium/components/mus/ws/platform_screen_impl.h39
-rw-r--r--chromium/components/mus/ws/platform_screen_impl_ozone.cc126
-rw-r--r--chromium/components/mus/ws/platform_screen_impl_ozone.h72
-rw-r--r--chromium/components/mus/ws/scheduled_animation_group.cc356
-rw-r--r--chromium/components/mus/ws/scheduled_animation_group.h116
-rw-r--r--chromium/components/mus/ws/scheduled_animation_group_unittest.cc92
-rw-r--r--chromium/components/mus/ws/server_window.cc470
-rw-r--r--chromium/components/mus/ws/server_window.h249
-rw-r--r--chromium/components/mus/ws/server_window_delegate.h46
-rw-r--r--chromium/components/mus/ws/server_window_drawn_tracker.cc136
-rw-r--r--chromium/components/mus/ws/server_window_drawn_tracker.h65
-rw-r--r--chromium/components/mus/ws/server_window_drawn_tracker_observer.h41
-rw-r--r--chromium/components/mus/ws/server_window_drawn_tracker_unittest.cc235
-rw-r--r--chromium/components/mus/ws/server_window_observer.h97
-rw-r--r--chromium/components/mus/ws/server_window_surface.cc110
-rw-r--r--chromium/components/mus/ws/server_window_surface.h86
-rw-r--r--chromium/components/mus/ws/server_window_surface_manager.cc102
-rw-r--r--chromium/components/mus/ws/server_window_surface_manager.h84
-rw-r--r--chromium/components/mus/ws/server_window_surface_manager_test_api.cc39
-rw-r--r--chromium/components/mus/ws/server_window_surface_manager_test_api.h39
-rw-r--r--chromium/components/mus/ws/server_window_tracker.h25
-rw-r--r--chromium/components/mus/ws/test_change_tracker.cc448
-rw-r--r--chromium/components/mus/ws/test_change_tracker.h185
-rw-r--r--chromium/components/mus/ws/test_server_window_delegate.cc34
-rw-r--r--chromium/components/mus/ws/test_server_window_delegate.h41
-rw-r--r--chromium/components/mus/ws/test_utils.cc496
-rw-r--r--chromium/components/mus/ws/test_utils.h537
-rw-r--r--chromium/components/mus/ws/touch_controller.cc105
-rw-r--r--chromium/components/mus/ws/touch_controller.h37
-rw-r--r--chromium/components/mus/ws/transient_windows_unittest.cc342
-rw-r--r--chromium/components/mus/ws/user_activity_monitor.cc134
-rw-r--r--chromium/components/mus/ws/user_activity_monitor.h82
-rw-r--r--chromium/components/mus/ws/user_activity_monitor_unittest.cc218
-rw-r--r--chromium/components/mus/ws/user_display_manager.cc151
-rw-r--r--chromium/components/mus/ws/user_display_manager.h118
-rw-r--r--chromium/components/mus/ws/user_display_manager_unittest.cc244
-rw-r--r--chromium/components/mus/ws/user_id.h22
-rw-r--r--chromium/components/mus/ws/user_id_tracker.cc68
-rw-r--r--chromium/components/mus/ws/user_id_tracker.h62
-rw-r--r--chromium/components/mus/ws/user_id_tracker_observer.h29
-rw-r--r--chromium/components/mus/ws/window_coordinate_conversions.cc74
-rw-r--r--chromium/components/mus/ws/window_coordinate_conversions.h39
-rw-r--r--chromium/components/mus/ws/window_coordinate_conversions_unittest.cc62
-rw-r--r--chromium/components/mus/ws/window_finder.cc68
-rw-r--r--chromium/components/mus/ws/window_finder.h33
-rw-r--r--chromium/components/mus/ws/window_finder_unittest.cc79
-rw-r--r--chromium/components/mus/ws/window_manager_access_policy.cc184
-rw-r--r--chromium/components/mus/ws/window_manager_access_policy.h76
-rw-r--r--chromium/components/mus/ws/window_manager_client_unittest.cc1171
-rw-r--r--chromium/components/mus/ws/window_manager_display_root.cc34
-rw-r--r--chromium/components/mus/ws/window_manager_display_root.h62
-rw-r--r--chromium/components/mus/ws/window_manager_state.cc485
-rw-r--r--chromium/components/mus/ws/window_manager_state.h183
-rw-r--r--chromium/components/mus/ws/window_manager_state_unittest.cc419
-rw-r--r--chromium/components/mus/ws/window_manager_window_tree_factory.cc64
-rw-r--r--chromium/components/mus/ws/window_manager_window_tree_factory.h68
-rw-r--r--chromium/components/mus/ws/window_manager_window_tree_factory_set.cc99
-rw-r--r--chromium/components/mus/ws/window_manager_window_tree_factory_set.h93
-rw-r--r--chromium/components/mus/ws/window_manager_window_tree_factory_set_observer.h26
-rw-r--r--chromium/components/mus/ws/window_server.cc707
-rw-r--r--chromium/components/mus/ws/window_server.h342
-rw-r--r--chromium/components/mus/ws/window_server_delegate.cc25
-rw-r--r--chromium/components/mus/ws/window_server_delegate.h67
-rw-r--r--chromium/components/mus/ws/window_server_test_impl.cc66
-rw-r--r--chromium/components/mus/ws/window_server_test_impl.h45
-rw-r--r--chromium/components/mus/ws/window_tree.cc1539
-rw-r--r--chromium/components/mus/ws/window_tree.h497
-rw-r--r--chromium/components/mus/ws/window_tree_binding.cc62
-rw-r--r--chromium/components/mus/ws/window_tree_binding.h72
-rw-r--r--chromium/components/mus/ws/window_tree_client_unittest.cc2042
-rw-r--r--chromium/components/mus/ws/window_tree_factory.cc42
-rw-r--r--chromium/components/mus/ws/window_tree_factory.h42
-rw-r--r--chromium/components/mus/ws/window_tree_host_factory.cc42
-rw-r--r--chromium/components/mus/ws/window_tree_host_factory.h46
-rw-r--r--chromium/components/mus/ws/window_tree_unittest.cc1017
264 files changed, 37812 insertions, 0 deletions
diff --git a/chromium/components/mus/DEPS b/chromium/components/mus/DEPS
new file mode 100644
index 00000000000..f3a2b3a7d97
--- /dev/null
+++ b/chromium/components/mus/DEPS
@@ -0,0 +1,12 @@
+include_rules = [
+ "+cc",
+ "+ipc",
+ "+mojo/common",
+ "+mojo/converters",
+ "+mojo/public",
+ "+services/catalog/public",
+ "+services/shell",
+ "+services/tracing/public",
+ "+third_party/skia/include",
+ "+ui",
+]
diff --git a/chromium/components/mus/OWNERS b/chromium/components/mus/OWNERS
new file mode 100644
index 00000000000..e6a7642f300
--- /dev/null
+++ b/chromium/components/mus/OWNERS
@@ -0,0 +1,4 @@
+ben@chromium.org
+erg@chromium.org
+msw@chromium.org
+sky@chromium.org
diff --git a/chromium/components/mus/clipboard/clipboard_impl.cc b/chromium/components/mus/clipboard/clipboard_impl.cc
new file mode 100644
index 00000000000..a82d95e8f03
--- /dev/null
+++ b/chromium/components/mus/clipboard/clipboard_impl.cc
@@ -0,0 +1,108 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/clipboard/clipboard_impl.h"
+
+#include <string.h>
+#include <utility>
+
+#include "base/macros.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/string.h"
+
+using mojo::Array;
+using mojo::Map;
+using mojo::String;
+
+namespace mus {
+namespace clipboard {
+
+// ClipboardData contains data copied to the Clipboard for a variety of formats.
+// It mostly just provides APIs to cleanly access and manipulate this data.
+class ClipboardImpl::ClipboardData {
+ public:
+ ClipboardData() : sequence_number_(0) {}
+ ~ClipboardData() {}
+
+ uint64_t sequence_number() const {
+ return sequence_number_;
+ }
+
+ Array<String> GetMimeTypes() const {
+ Array<String> types(data_types_.size());
+ int i = 0;
+ for (auto it = data_types_.begin(); it != data_types_.end(); ++it, ++i)
+ types[i] = it->first;
+
+ return types;
+ }
+
+ void SetData(Map<String, Array<uint8_t>> data) {
+ sequence_number_++;
+ data_types_ = std::move(data);
+ }
+
+ void GetData(const String& mime_type, Array<uint8_t>* data) const {
+ auto it = data_types_.find(mime_type);
+ if (it != data_types_.end())
+ *data = it->second.Clone();
+ }
+
+ private:
+ uint64_t sequence_number_;
+ Map<String, Array<uint8_t>> data_types_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClipboardData);
+};
+
+ClipboardImpl::ClipboardImpl() {
+ for (int i = 0; i < kNumClipboards; ++i)
+ clipboard_state_[i].reset(new ClipboardData);
+}
+
+ClipboardImpl::~ClipboardImpl() {
+}
+
+void ClipboardImpl::AddBinding(mojom::ClipboardRequest request) {
+ bindings_.AddBinding(this, std::move(request));
+}
+
+void ClipboardImpl::GetSequenceNumber(
+ Clipboard::Type clipboard_type,
+ const GetSequenceNumberCallback& callback) {
+ callback.Run(
+ clipboard_state_[static_cast<int>(clipboard_type)]->sequence_number());
+}
+
+void ClipboardImpl::GetAvailableMimeTypes(
+ Clipboard::Type clipboard_type,
+ const GetAvailableMimeTypesCallback& callback) {
+ int clipboard_num = static_cast<int>(clipboard_type);
+ callback.Run(clipboard_state_[clipboard_num]->sequence_number(),
+ clipboard_state_[clipboard_num]->GetMimeTypes());
+}
+
+void ClipboardImpl::ReadClipboardData(
+ Clipboard::Type clipboard_type,
+ const String& mime_type,
+ const ReadClipboardDataCallback& callback) {
+ int clipboard_num = static_cast<int>(clipboard_type);
+ Array<uint8_t> mime_data(nullptr);
+ uint64_t sequence = clipboard_state_[clipboard_num]->sequence_number();
+ clipboard_state_[clipboard_num]->GetData(mime_type, &mime_data);
+ callback.Run(sequence, std::move(mime_data));
+}
+
+void ClipboardImpl::WriteClipboardData(
+ Clipboard::Type clipboard_type,
+ Map<String, Array<uint8_t>> data,
+ const WriteClipboardDataCallback& callback) {
+ int clipboard_num = static_cast<int>(clipboard_type);
+ clipboard_state_[clipboard_num]->SetData(std::move(data));
+ callback.Run(clipboard_state_[clipboard_num]->sequence_number());
+}
+
+} // namespace clipboard
+} // namespace mus
diff --git a/chromium/components/mus/clipboard/clipboard_impl.h b/chromium/components/mus/clipboard/clipboard_impl.h
new file mode 100644
index 00000000000..7f62f1c1df5
--- /dev/null
+++ b/chromium/components/mus/clipboard/clipboard_impl.h
@@ -0,0 +1,65 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_CLIPBOARD_CLIPBOARD_IMPL_H_
+#define COMPONENTS_MUS_CLIPBOARD_CLIPBOARD_IMPL_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "components/mus/public/interfaces/clipboard.mojom.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+
+namespace mus {
+namespace clipboard {
+
+// Stub clipboard implementation.
+//
+// Eventually, we'll actually want to interact with the system clipboard, but
+// that's hard today because the system clipboard is asynchronous (on X11), the
+// ui::Clipboard interface is synchronous (which is what we'd use), mojo is
+// asynchronous across processes, and the WebClipboard interface is synchronous
+// (which is at least tractable).
+class ClipboardImpl : public mojom::Clipboard {
+ public:
+ // mojom::Clipboard exposes three possible clipboards.
+ static const int kNumClipboards = 3;
+
+ ClipboardImpl();
+ ~ClipboardImpl() override;
+
+ void AddBinding(mojom::ClipboardRequest request);
+
+ // mojom::Clipboard implementation.
+ void GetSequenceNumber(
+ mojom::Clipboard::Type clipboard_type,
+ const GetSequenceNumberCallback& callback) override;
+ void GetAvailableMimeTypes(
+ mojom::Clipboard::Type clipboard_types,
+ const GetAvailableMimeTypesCallback& callback) override;
+ void ReadClipboardData(mojom::Clipboard::Type clipboard_type,
+ const mojo::String& mime_type,
+ const ReadClipboardDataCallback& callback) override;
+ void WriteClipboardData(
+ mojom::Clipboard::Type clipboard_type,
+ mojo::Map<mojo::String, mojo::Array<uint8_t>> data,
+ const WriteClipboardDataCallback& callback) override;
+
+ private:
+ // Internal struct which stores the current state of the clipboard.
+ class ClipboardData;
+
+ // The current clipboard state. This is what is read from.
+ std::unique_ptr<ClipboardData> clipboard_state_[kNumClipboards];
+ mojo::BindingSet<mojom::Clipboard> bindings_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClipboardImpl);
+};
+
+} // namespace clipboard
+} // namespace mus
+
+#endif // COMPONENTS_MUS_CLIPBOARD_CLIPBOARD_IMPL_H_
diff --git a/chromium/components/mus/clipboard/clipboard_unittest.cc b/chromium/components/mus/clipboard/clipboard_unittest.cc
new file mode 100644
index 00000000000..764618395c7
--- /dev/null
+++ b/chromium/components/mus/clipboard/clipboard_unittest.cc
@@ -0,0 +1,145 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "components/mus/public/interfaces/clipboard.mojom.h"
+#include "mojo/common/common_type_converters.h"
+#include "services/shell/public/cpp/shell_connection.h"
+#include "services/shell/public/cpp/shell_test.h"
+
+using mojo::Array;
+using mojo::Map;
+using mojo::String;
+using mus::mojom::Clipboard;
+
+namespace mus {
+namespace clipboard {
+namespace {
+
+const char* kUninitialized = "Uninitialized data";
+const char* kPlainTextData = "Some plain data";
+const char* kHtmlData = "<html>data</html>";
+
+} // namespace
+
+class ClipboardAppTest : public shell::test::ShellTest {
+ public:
+ ClipboardAppTest() : ShellTest("exe:mus_clipboard_unittests") {}
+ ~ClipboardAppTest() override {}
+
+ // Overridden from shell::test::ShellTest:
+ void SetUp() override {
+ ShellTest::SetUp();
+
+ connector()->ConnectToInterface("mojo:mus", &clipboard_);
+ ASSERT_TRUE(clipboard_);
+ }
+
+ uint64_t GetSequenceNumber() {
+ uint64_t sequence_num = 999999;
+ EXPECT_TRUE(clipboard_->GetSequenceNumber(
+ Clipboard::Type::COPY_PASTE, &sequence_num));
+ return sequence_num;
+ }
+
+ std::vector<std::string> GetAvailableFormatMimeTypes() {
+ uint64_t sequence_num = 999999;
+ Array<String> types;
+ types.push_back(kUninitialized);
+ clipboard_->GetAvailableMimeTypes(
+ Clipboard::Type::COPY_PASTE,
+ &sequence_num, &types);
+ return types.To<std::vector<std::string>>();
+ }
+
+ bool GetDataOfType(const std::string& mime_type, std::string* data) {
+ bool valid = false;
+ Array<uint8_t> raw_data;
+ uint64_t sequence_number = 0;
+ clipboard_->ReadClipboardData(Clipboard::Type::COPY_PASTE, mime_type,
+ &sequence_number, &raw_data);
+ valid = !raw_data.is_null();
+ *data = raw_data.is_null() ? "" : raw_data.To<std::string>();
+ return valid;
+ }
+
+ void SetStringText(const std::string& data) {
+ uint64_t sequence_number;
+ Map<String, Array<uint8_t>> mime_data;
+ mime_data[mojom::kMimeTypeText] = Array<uint8_t>::From(data);
+ clipboard_->WriteClipboardData(Clipboard::Type::COPY_PASTE,
+ std::move(mime_data),
+ &sequence_number);
+ }
+
+ protected:
+ mojom::ClipboardPtr clipboard_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClipboardAppTest);
+};
+
+TEST_F(ClipboardAppTest, EmptyClipboardOK) {
+ EXPECT_EQ(0ul, GetSequenceNumber());
+ EXPECT_TRUE(GetAvailableFormatMimeTypes().empty());
+ std::string data;
+ EXPECT_FALSE(GetDataOfType(mojom::kMimeTypeText, &data));
+}
+
+TEST_F(ClipboardAppTest, CanReadBackText) {
+ std::string data;
+ EXPECT_EQ(0ul, GetSequenceNumber());
+ EXPECT_FALSE(GetDataOfType(mojom::kMimeTypeText, &data));
+
+ SetStringText(kPlainTextData);
+ EXPECT_EQ(1ul, GetSequenceNumber());
+
+ EXPECT_TRUE(GetDataOfType(mojom::kMimeTypeText, &data));
+ EXPECT_EQ(kPlainTextData, data);
+}
+
+TEST_F(ClipboardAppTest, CanSetMultipleDataTypesAtOnce) {
+ Map<String, Array<uint8_t>> mime_data;
+ mime_data[mojom::kMimeTypeText] =
+ Array<uint8_t>::From(std::string(kPlainTextData));
+ mime_data[mojom::kMimeTypeHTML] =
+ Array<uint8_t>::From(std::string(kHtmlData));
+
+ uint64_t sequence_num = 0;
+ clipboard_->WriteClipboardData(Clipboard::Type::COPY_PASTE,
+ std::move(mime_data),
+ &sequence_num);
+ EXPECT_EQ(1ul, sequence_num);
+
+ std::string data;
+ EXPECT_TRUE(GetDataOfType(mojom::kMimeTypeText, &data));
+ EXPECT_EQ(kPlainTextData, data);
+ EXPECT_TRUE(GetDataOfType(mojom::kMimeTypeHTML, &data));
+ EXPECT_EQ(kHtmlData, data);
+}
+
+TEST_F(ClipboardAppTest, CanClearClipboardWithZeroArray) {
+ std::string data;
+ SetStringText(kPlainTextData);
+ EXPECT_EQ(1ul, GetSequenceNumber());
+
+ EXPECT_TRUE(GetDataOfType(mojom::kMimeTypeText, &data));
+ EXPECT_EQ(kPlainTextData, data);
+
+ Map<String, Array<uint8_t>> mime_data;
+ uint64_t sequence_num = 0;
+ clipboard_->WriteClipboardData(Clipboard::Type::COPY_PASTE,
+ std::move(mime_data),
+ &sequence_num);
+
+ EXPECT_EQ(2ul, sequence_num);
+ EXPECT_FALSE(GetDataOfType(mojom::kMimeTypeText, &data));
+}
+
+} // namespace clipboard
+} // namespace mus
diff --git a/chromium/components/mus/clipboard/test_manifest.json b/chromium/components/mus/clipboard/test_manifest.json
new file mode 100644
index 00000000000..26e694cea02
--- /dev/null
+++ b/chromium/components/mus/clipboard/test_manifest.json
@@ -0,0 +1,10 @@
+{
+ "manifest_version": 1,
+ "name": "exe:mus_clipboard_unittests",
+ "display_name": "Clipboard Service Unittests",
+ "capabilities": {
+ "required": {
+ "mojo:mus": { "interfaces": [ "mus::mojom::Clipboard" ] }
+ }
+ }
+}
diff --git a/chromium/components/mus/common/DEPS b/chromium/components/mus/common/DEPS
new file mode 100644
index 00000000000..f77ec566793
--- /dev/null
+++ b/chromium/components/mus/common/DEPS
@@ -0,0 +1,12 @@
+include_rules = [
+ "+gpu/config",
+ "+gpu/command_buffer",
+ "+gpu/ipc/client",
+ "+gpu/ipc/common",
+]
+
+specific_include_rules = {
+ "run_all_shelltests\.cc": [
+ "+mojo/edk/embedder/embedder.h",
+ ],
+}
diff --git a/chromium/components/mus/common/event_matcher_util.cc b/chromium/components/mus/common/event_matcher_util.cc
new file mode 100644
index 00000000000..a82a12d3002
--- /dev/null
+++ b/chromium/components/mus/common/event_matcher_util.cc
@@ -0,0 +1,28 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/common/event_matcher_util.h"
+
+namespace mus {
+
+mojom::EventMatcherPtr CreateKeyMatcher(ui::mojom::KeyboardCode code,
+ int flags) {
+ mojom::EventMatcherPtr matcher(mojom::EventMatcher::New());
+ matcher->type_matcher = mojom::EventTypeMatcher::New();
+ matcher->flags_matcher = mojom::EventFlagsMatcher::New();
+ matcher->ignore_flags_matcher = mojom::EventFlagsMatcher::New();
+ // Ignoring these makes most accelerator scenarios more straight forward. Code
+ // that needs to check them can override this setting.
+ matcher->ignore_flags_matcher->flags = ui::mojom::kEventFlagCapsLockOn |
+ ui::mojom::kEventFlagScrollLockOn |
+ ui::mojom::kEventFlagNumLockOn;
+ matcher->key_matcher = mojom::KeyEventMatcher::New();
+
+ matcher->type_matcher->type = ui::mojom::EventType::KEY_PRESSED;
+ matcher->flags_matcher->flags = flags;
+ matcher->key_matcher->keyboard_code = code;
+ return matcher;
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/common/event_matcher_util.h b/chromium/components/mus/common/event_matcher_util.h
new file mode 100644
index 00000000000..2b99729ea2a
--- /dev/null
+++ b/chromium/components/mus/common/event_matcher_util.h
@@ -0,0 +1,22 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_COMMON_EVENT_MATCHER_UTIL_H_
+#define COMPONENTS_MUS_COMMON_EVENT_MATCHER_UTIL_H_
+
+#include "components/mus/common/mus_common_export.h"
+#include "components/mus/public/interfaces/event_matcher.mojom.h"
+#include "ui/events/mojo/event_constants.mojom.h"
+#include "ui/events/mojo/keyboard_codes.mojom.h"
+
+namespace mus {
+
+// |flags| is a bitfield of kEventFlag* and kMouseEventFlag* values in
+// input_event_constants.mojom.
+mojom::EventMatcherPtr MUS_COMMON_EXPORT
+CreateKeyMatcher(ui::mojom::KeyboardCode code, int flags);
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_COMMON_EVENT_MATCHER_UTIL_H_
diff --git a/chromium/components/mus/common/generic_shared_memory_id_generator.cc b/chromium/components/mus/common/generic_shared_memory_id_generator.cc
new file mode 100644
index 00000000000..622a53940b2
--- /dev/null
+++ b/chromium/components/mus/common/generic_shared_memory_id_generator.cc
@@ -0,0 +1,21 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/common/generic_shared_memory_id_generator.h"
+
+#include "base/atomic_sequence_num.h"
+
+namespace mus {
+namespace {
+
+// Global atomic to generate gpu memory buffer unique IDs.
+base::StaticAtomicSequenceNumber g_next_generic_shared_memory_id;
+
+} // namespace
+
+gfx::GenericSharedMemoryId GetNextGenericSharedMemoryId() {
+ return gfx::GenericSharedMemoryId(g_next_generic_shared_memory_id.GetNext());
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/common/generic_shared_memory_id_generator.h b/chromium/components/mus/common/generic_shared_memory_id_generator.h
new file mode 100644
index 00000000000..c462e093d3a
--- /dev/null
+++ b/chromium/components/mus/common/generic_shared_memory_id_generator.h
@@ -0,0 +1,19 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_COMMON_GENERIC_SHARED_MEMORY_ID_GENERATOR_H_
+#define COMPONENTS_MUS_COMMON_GENERIC_SHARED_MEMORY_ID_GENERATOR_H_
+
+#include "components/mus/common/mus_common_export.h"
+#include "ui/gfx/generic_shared_memory_id.h"
+
+namespace mus {
+
+// Returns the next GenericSharedMemoryId for the current process. This should
+// be used anywhere a new GenericSharedMemoryId is needed.
+MUS_COMMON_EXPORT gfx::GenericSharedMemoryId GetNextGenericSharedMemoryId();
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_COMMON_GENERIC_SHARED_MEMORY_ID_GENERATOR_H_
diff --git a/chromium/components/mus/common/gpu_memory_buffer_impl.cc b/chromium/components/mus/common/gpu_memory_buffer_impl.cc
new file mode 100644
index 00000000000..415f6548f14
--- /dev/null
+++ b/chromium/components/mus/common/gpu_memory_buffer_impl.cc
@@ -0,0 +1,46 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/common/gpu_memory_buffer_impl.h"
+
+namespace mus {
+
+GpuMemoryBufferImpl::GpuMemoryBufferImpl(gfx::GpuMemoryBufferId id,
+ const gfx::Size& size,
+ gfx::BufferFormat format)
+ : id_(id), size_(size), format_(format), mapped_(false) {}
+
+GpuMemoryBufferImpl::~GpuMemoryBufferImpl() {
+ DCHECK(!mapped_);
+}
+
+// static
+GpuMemoryBufferImpl* GpuMemoryBufferImpl::FromClientBuffer(
+ ClientBuffer buffer) {
+ return reinterpret_cast<GpuMemoryBufferImpl*>(buffer);
+}
+
+gfx::Size GpuMemoryBufferImpl::GetSize() const {
+ return size_;
+}
+
+gfx::BufferFormat GpuMemoryBufferImpl::GetFormat() const {
+ return format_;
+}
+
+gfx::GpuMemoryBufferId GpuMemoryBufferImpl::GetId() const {
+ return id_;
+}
+
+ClientBuffer GpuMemoryBufferImpl::AsClientBuffer() {
+ return reinterpret_cast<ClientBuffer>(this);
+}
+
+#if defined(USE_OZONE)
+scoped_refptr<ui::NativePixmap> GpuMemoryBufferImpl::GetNativePixmap() {
+ return scoped_refptr<ui::NativePixmap>();
+}
+#endif
+
+} // namespace mus
diff --git a/chromium/components/mus/common/gpu_memory_buffer_impl.h b/chromium/components/mus/common/gpu_memory_buffer_impl.h
new file mode 100644
index 00000000000..84f1f9d90ad
--- /dev/null
+++ b/chromium/components/mus/common/gpu_memory_buffer_impl.h
@@ -0,0 +1,61 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_COMMON_GPU_MEMORY_BUFFER_IMPL_H_
+#define COMPONENTS_MUS_COMMON_GPU_MEMORY_BUFFER_IMPL_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "components/mus/common/mus_common_export.h"
+#include "gpu/command_buffer/common/sync_token.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/gpu_memory_buffer.h"
+
+#if defined(USE_OZONE)
+#include "ui/ozone/public/native_pixmap.h"
+#endif
+
+namespace mus {
+
+// Provides common implementation of a GPU memory buffer.
+class MUS_COMMON_EXPORT GpuMemoryBufferImpl : public gfx::GpuMemoryBuffer {
+ public:
+ ~GpuMemoryBufferImpl() override;
+
+ // Type-checking upcast routine. Returns an NULL on failure.
+ static GpuMemoryBufferImpl* FromClientBuffer(ClientBuffer buffer);
+
+ // Overridden from gfx::GpuMemoryBuffer:
+ gfx::Size GetSize() const override;
+ gfx::BufferFormat GetFormat() const override;
+ gfx::GpuMemoryBufferId GetId() const override;
+ ClientBuffer AsClientBuffer() override;
+
+ // Returns the type of this GpuMemoryBufferImpl.
+ virtual gfx::GpuMemoryBufferType GetBufferType() const = 0;
+
+#if defined(USE_OZONE)
+ // Returns a ui::NativePixmap when one is available.
+ virtual scoped_refptr<ui::NativePixmap> GetNativePixmap();
+#endif
+
+ protected:
+ GpuMemoryBufferImpl(gfx::GpuMemoryBufferId id,
+ const gfx::Size& size,
+ gfx::BufferFormat format);
+
+ const gfx::GpuMemoryBufferId id_;
+ const gfx::Size size_;
+ const gfx::BufferFormat format_;
+ bool mapped_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GpuMemoryBufferImpl);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_COMMON_GPU_MEMORY_BUFFER_IMPL_H_
diff --git a/chromium/components/mus/common/gpu_service.cc b/chromium/components/mus/common/gpu_service.cc
new file mode 100644
index 00000000000..4eea3e45972
--- /dev/null
+++ b/chromium/components/mus/common/gpu_service.cc
@@ -0,0 +1,256 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/common/gpu_service.h"
+
+#include "base/command_line.h"
+#include "base/memory/singleton.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
+#include "components/mus/common/gpu_type_converters.h"
+#include "components/mus/common/switches.h"
+#include "components/mus/public/interfaces/gpu_service.mojom.h"
+#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+#include "services/shell/public/cpp/connector.h"
+
+namespace mus {
+
+namespace {
+
+void PostTask(scoped_refptr<base::SingleThreadTaskRunner> runner,
+ const tracked_objects::Location& from_here,
+ const base::Closure& callback) {
+ runner->PostTask(from_here, callback);
+}
+
+GpuService* g_gpu_service = nullptr;
+}
+
+GpuService::GpuService(shell::Connector* connector)
+ : main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ connector_(connector),
+ shutdown_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+ base::WaitableEvent::InitialState::NOT_SIGNALED),
+ io_thread_("GPUIOThread"),
+ gpu_memory_buffer_manager_(new MojoGpuMemoryBufferManager),
+ is_establishing_(false),
+ establishing_condition_(&lock_) {
+ DCHECK(main_task_runner_);
+ DCHECK(connector_);
+ base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0);
+ thread_options.priority = base::ThreadPriority::NORMAL;
+ CHECK(io_thread_.StartWithOptions(thread_options));
+}
+
+GpuService::~GpuService() {
+ DCHECK(IsMainThread());
+ if (gpu_channel_)
+ gpu_channel_->DestroyChannel();
+}
+
+// static
+bool GpuService::UseChromeGpuCommandBuffer() {
+// TODO(penghuang): Kludge: Running with Chrome GPU command buffer by default
+// breaks unit tests on Windows
+#if defined(OS_WIN)
+ return false;
+#else
+ return !base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kUseMojoGpuCommandBufferInMus);
+#endif
+}
+
+// static
+void GpuService::Initialize(shell::Connector* connector) {
+ DCHECK(!g_gpu_service);
+ g_gpu_service = new GpuService(connector);
+}
+
+// static
+void GpuService::Terminate() {
+ DCHECK(g_gpu_service);
+ delete g_gpu_service;
+ g_gpu_service = nullptr;
+}
+
+// static
+GpuService* GpuService::GetInstance() {
+ DCHECK(g_gpu_service);
+ return g_gpu_service;
+}
+
+void GpuService::EstablishGpuChannel(const base::Closure& callback) {
+ base::AutoLock auto_lock(lock_);
+ auto runner = base::ThreadTaskRunnerHandle::Get();
+ if (GetGpuChannelLocked()) {
+ runner->PostTask(FROM_HERE, callback);
+ return;
+ }
+
+ base::Closure wrapper_callback =
+ IsMainThread() ? callback
+ : base::Bind(PostTask, runner, FROM_HERE, callback);
+ establish_callbacks_.push_back(wrapper_callback);
+
+ if (!is_establishing_) {
+ is_establishing_ = true;
+ main_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&GpuService::EstablishGpuChannelOnMainThread,
+ base::Unretained(this)));
+ }
+}
+
+scoped_refptr<gpu::GpuChannelHost> GpuService::EstablishGpuChannelSync() {
+ base::AutoLock auto_lock(lock_);
+ if (GetGpuChannelLocked())
+ return gpu_channel_;
+
+ if (IsMainThread()) {
+ is_establishing_ = true;
+ EstablishGpuChannelOnMainThreadSyncLocked();
+ } else {
+ if (!is_establishing_) {
+ // Create an establishing gpu channel task, if there isn't one.
+ is_establishing_ = true;
+ main_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&GpuService::EstablishGpuChannelOnMainThread,
+ base::Unretained(this)));
+ }
+
+ // Wait until the pending establishing task is finished.
+ do {
+ establishing_condition_.Wait();
+ } while (is_establishing_);
+ }
+ return gpu_channel_;
+}
+
+scoped_refptr<gpu::GpuChannelHost> GpuService::GetGpuChannel() {
+ base::AutoLock auto_lock(lock_);
+ return GetGpuChannelLocked();
+}
+
+scoped_refptr<gpu::GpuChannelHost> GpuService::GetGpuChannelLocked() {
+ if (gpu_channel_ && gpu_channel_->IsLost()) {
+ main_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&gpu::GpuChannelHost::DestroyChannel, gpu_channel_));
+ gpu_channel_ = nullptr;
+ }
+ return gpu_channel_;
+}
+
+void GpuService::EstablishGpuChannelOnMainThread() {
+ base::AutoLock auto_lock(lock_);
+ DCHECK(IsMainThread());
+
+ // In GpuService::EstablishGpuChannelOnMainThreadSyncLocked(), we use the sync
+ // mojo EstablishGpuChannel call, after that call the gpu_service_ will be
+ // reset immediatelly. So gpu_service_ should be always null here.
+ DCHECK(!gpu_service_);
+
+ // is_establishing_ is false, it means GpuService::EstablishGpuChannelSync()
+ // has been used, and we don't need try to establish a new GPU channel
+ // anymore.
+ if (!is_establishing_)
+ return;
+
+ connector_->ConnectToInterface("mojo:mus", &gpu_service_);
+ const bool locked = false;
+ gpu_service_->EstablishGpuChannel(
+ base::Bind(&GpuService::EstablishGpuChannelOnMainThreadDone,
+ base::Unretained(this), locked));
+}
+
+void GpuService::EstablishGpuChannelOnMainThreadSyncLocked() {
+ DCHECK(IsMainThread());
+ DCHECK(is_establishing_);
+
+ // In browser process, EstablishGpuChannelSync() is only used by testing &
+ // GpuProcessTransportFactory::GetGLHelper(). For GetGLHelper(), it expects
+ // the gpu channel has been established, so it should not reach here.
+ // For testing, the asyc method should not be used.
+ // In renderer process, we only use EstablishGpuChannelSync().
+ // So the gpu_service_ should be null here.
+ DCHECK(!gpu_service_);
+
+ int client_id = 0;
+ mojom::ChannelHandlePtr channel_handle;
+ mojom::GpuInfoPtr gpu_info;
+ connector_->ConnectToInterface("mojo:mus", &gpu_service_);
+ {
+ base::AutoUnlock auto_unlock(lock_);
+ mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call;
+ if (!gpu_service_->EstablishGpuChannel(&client_id, &channel_handle,
+ &gpu_info)) {
+ DLOG(WARNING)
+ << "Channel encountered error while establishing gpu channel.";
+ return;
+ }
+ }
+ const bool locked = true;
+ EstablishGpuChannelOnMainThreadDone(
+ locked, client_id, std::move(channel_handle), std::move(gpu_info));
+}
+
+void GpuService::EstablishGpuChannelOnMainThreadDone(
+ bool locked,
+ int client_id,
+ mojom::ChannelHandlePtr channel_handle,
+ mojom::GpuInfoPtr gpu_info) {
+ DCHECK(IsMainThread());
+ scoped_refptr<gpu::GpuChannelHost> gpu_channel;
+ if (client_id) {
+ // TODO(penghuang): Get the real gpu info from mus.
+ gpu_channel = gpu::GpuChannelHost::Create(
+ this, client_id, gpu::GPUInfo(),
+ channel_handle.To<IPC::ChannelHandle>(), &shutdown_event_,
+ gpu_memory_buffer_manager_.get());
+ }
+
+ auto auto_lock = base::WrapUnique<base::AutoLock>(
+ locked ? nullptr : new base::AutoLock(lock_));
+ DCHECK(is_establishing_);
+ DCHECK(!gpu_channel_);
+
+ is_establishing_ = false;
+ gpu_channel_ = gpu_channel;
+ establishing_condition_.Broadcast();
+
+ for (const auto& i : establish_callbacks_)
+ i.Run();
+ establish_callbacks_.clear();
+ gpu_service_.reset();
+}
+
+bool GpuService::IsMainThread() {
+ return main_task_runner_->BelongsToCurrentThread();
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+GpuService::GetIOThreadTaskRunner() {
+ return io_thread_.task_runner();
+}
+
+std::unique_ptr<base::SharedMemory> GpuService::AllocateSharedMemory(
+ size_t size) {
+ mojo::ScopedSharedBufferHandle handle =
+ mojo::SharedBufferHandle::Create(size);
+ if (!handle.is_valid())
+ return nullptr;
+
+ base::SharedMemoryHandle platform_handle;
+ size_t shared_memory_size;
+ bool readonly;
+ MojoResult result = mojo::UnwrapSharedMemoryHandle(
+ std::move(handle), &platform_handle, &shared_memory_size, &readonly);
+ if (result != MOJO_RESULT_OK)
+ return nullptr;
+ DCHECK_EQ(shared_memory_size, size);
+
+ return base::MakeUnique<base::SharedMemory>(platform_handle, readonly);
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/common/gpu_service.h b/chromium/components/mus/common/gpu_service.h
new file mode 100644
index 00000000000..a3d5cd106f6
--- /dev/null
+++ b/chromium/components/mus/common/gpu_service.h
@@ -0,0 +1,86 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_COMMON_GPU_SERVICE_H_
+#define COMPONENTS_MUS_COMMON_GPU_SERVICE_H_
+
+#include <stdint.h>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "components/mus/common/mojo_gpu_memory_buffer_manager.h"
+#include "components/mus/common/mus_common_export.h"
+#include "components/mus/public/interfaces/gpu_service.mojom.h"
+#include "gpu/ipc/client/gpu_channel_host.h"
+
+namespace shell {
+class Connector;
+}
+
+namespace mus {
+
+class MUS_COMMON_EXPORT GpuService : public gpu::GpuChannelHostFactory {
+ public:
+ void EstablishGpuChannel(const base::Closure& callback);
+ scoped_refptr<gpu::GpuChannelHost> EstablishGpuChannelSync();
+ scoped_refptr<gpu::GpuChannelHost> GetGpuChannel();
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager() const {
+ return gpu_memory_buffer_manager_.get();
+ }
+
+ static bool UseChromeGpuCommandBuffer();
+
+ // The GpuService has to be initialized in the main thread before establishing
+ // the gpu channel.
+ static void Initialize(shell::Connector* connector);
+ // The GpuService has to be terminated in the main thread.
+ static void Terminate();
+ static GpuService* GetInstance();
+
+ private:
+ friend struct base::DefaultSingletonTraits<GpuService>;
+
+ explicit GpuService(shell::Connector* connector);
+ ~GpuService() override;
+
+ scoped_refptr<gpu::GpuChannelHost> GetGpuChannelLocked();
+ void EstablishGpuChannelOnMainThread();
+ void EstablishGpuChannelOnMainThreadSyncLocked();
+ void EstablishGpuChannelOnMainThreadDone(
+ bool locked,
+ int client_id,
+ mojom::ChannelHandlePtr channel_handle,
+ mojom::GpuInfoPtr gpu_info);
+
+ // gpu::GpuChannelHostFactory overrides:
+ bool IsMainThread() override;
+ scoped_refptr<base::SingleThreadTaskRunner> GetIOThreadTaskRunner() override;
+ std::unique_ptr<base::SharedMemory> AllocateSharedMemory(
+ size_t size) override;
+
+ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
+ shell::Connector* connector_;
+ base::WaitableEvent shutdown_event_;
+ base::Thread io_thread_;
+ std::unique_ptr<MojoGpuMemoryBufferManager> gpu_memory_buffer_manager_;
+
+ // Lock for |gpu_channel_|, |establish_callbacks_| & |is_establishing_|.
+ base::Lock lock_;
+ bool is_establishing_;
+ mus::mojom::GpuServicePtr gpu_service_;
+ scoped_refptr<gpu::GpuChannelHost> gpu_channel_;
+ std::vector<base::Closure> establish_callbacks_;
+ base::ConditionVariable establishing_condition_;
+
+ DISALLOW_COPY_AND_ASSIGN(GpuService);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_PUBLIC_CPP_LIB_GPU_SERVICE_CONNECTION_H_
diff --git a/chromium/components/mus/common/gpu_type_converters.cc b/chromium/components/mus/common/gpu_type_converters.cc
new file mode 100644
index 00000000000..7733c7d3bf7
--- /dev/null
+++ b/chromium/components/mus/common/gpu_type_converters.cc
@@ -0,0 +1,164 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/common/gpu_type_converters.h"
+
+#include "build/build_config.h"
+#include "gpu/config/gpu_info.h"
+#include "ipc/ipc_channel_handle.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+#include "ui/gfx/gpu_memory_buffer.h"
+
+#if defined(USE_OZONE)
+#include "ui/gfx/native_pixmap_handle_ozone.h"
+#endif
+
+namespace mojo {
+
+// static
+mus::mojom::ChannelHandlePtr
+TypeConverter<mus::mojom::ChannelHandlePtr, IPC::ChannelHandle>::Convert(
+ const IPC::ChannelHandle& handle) {
+ mus::mojom::ChannelHandlePtr result = mus::mojom::ChannelHandle::New();
+ result->name = handle.name;
+#if defined(OS_WIN)
+ // On windows, a pipe handle Will NOT be marshalled over IPC.
+ DCHECK(handle.pipe.handle == NULL);
+#else
+ DCHECK(handle.socket.auto_close || handle.socket.fd == -1);
+ base::PlatformFile platform_file = handle.socket.fd;
+ if (platform_file != -1)
+ result->socket = mojo::WrapPlatformFile(platform_file);
+#endif
+ return result;
+}
+
+// static
+IPC::ChannelHandle
+TypeConverter<IPC::ChannelHandle, mus::mojom::ChannelHandlePtr>::Convert(
+ const mus::mojom::ChannelHandlePtr& handle) {
+ if (handle.is_null())
+ return IPC::ChannelHandle();
+#if defined(OS_WIN)
+ // On windows, a pipe handle Will NOT be marshalled over IPC.
+ DCHECK(!handle->socket.is_valid());
+ return IPC::ChannelHandle(handle->name);
+#else
+ base::PlatformFile platform_file = -1;
+ mojo::UnwrapPlatformFile(std::move(handle->socket), &platform_file);
+ return IPC::ChannelHandle(handle->name,
+ base::FileDescriptor(platform_file, true));
+#endif
+}
+
+#if defined(USE_OZONE)
+// static
+mus::mojom::NativePixmapHandlePtr TypeConverter<
+ mus::mojom::NativePixmapHandlePtr,
+ gfx::NativePixmapHandle>::Convert(const gfx::NativePixmapHandle& handle) {
+ // TODO(penghuang); support NativePixmapHandle.
+ mus::mojom::NativePixmapHandlePtr result =
+ mus::mojom::NativePixmapHandle::New();
+ return result;
+}
+
+// static
+gfx::NativePixmapHandle
+TypeConverter<gfx::NativePixmapHandle, mus::mojom::NativePixmapHandlePtr>::
+ Convert(const mus::mojom::NativePixmapHandlePtr& handle) {
+ // TODO(penghuang); support NativePixmapHandle.
+ gfx::NativePixmapHandle result;
+ return result;
+}
+#endif
+
+// static
+mus::mojom::GpuMemoryBufferIdPtr TypeConverter<
+ mus::mojom::GpuMemoryBufferIdPtr,
+ gfx::GpuMemoryBufferId>::Convert(const gfx::GpuMemoryBufferId& id) {
+ mus::mojom::GpuMemoryBufferIdPtr result =
+ mus::mojom::GpuMemoryBufferId::New();
+ result->id = id.id;
+ return result;
+}
+
+// static
+gfx::GpuMemoryBufferId
+TypeConverter<gfx::GpuMemoryBufferId, mus::mojom::GpuMemoryBufferIdPtr>::
+ Convert(const mus::mojom::GpuMemoryBufferIdPtr& id) {
+ return gfx::GpuMemoryBufferId(id->id);
+}
+
+// static
+mus::mojom::GpuMemoryBufferHandlePtr TypeConverter<
+ mus::mojom::GpuMemoryBufferHandlePtr,
+ gfx::GpuMemoryBufferHandle>::Convert(const gfx::GpuMemoryBufferHandle&
+ handle) {
+ DCHECK(handle.type == gfx::SHARED_MEMORY_BUFFER);
+ mus::mojom::GpuMemoryBufferHandlePtr result =
+ mus::mojom::GpuMemoryBufferHandle::New();
+ result->type = static_cast<mus::mojom::GpuMemoryBufferType>(handle.type);
+ result->id = mus::mojom::GpuMemoryBufferId::From(handle.id);
+ base::PlatformFile platform_file;
+#if defined(OS_WIN)
+ platform_file = handle.handle.GetHandle();
+#else
+ DCHECK(handle.handle.auto_close || handle.handle.fd == -1);
+ platform_file = handle.handle.fd;
+#endif
+ result->buffer_handle = mojo::WrapPlatformFile(platform_file);
+ result->offset = handle.offset;
+ result->stride = handle.stride;
+#if defined(USE_OZONE)
+ result->native_pixmap_handle =
+ mus::mojom::NativePixmapHandle::From(handle.native_pixmap_handle);
+#endif
+ return result;
+}
+
+// static
+gfx::GpuMemoryBufferHandle TypeConverter<gfx::GpuMemoryBufferHandle,
+ mus::mojom::GpuMemoryBufferHandlePtr>::
+ Convert(const mus::mojom::GpuMemoryBufferHandlePtr& handle) {
+ DCHECK(handle->type == mus::mojom::GpuMemoryBufferType::SHARED_MEMORY);
+ gfx::GpuMemoryBufferHandle result;
+ result.type = static_cast<gfx::GpuMemoryBufferType>(handle->type);
+ result.id = handle->id.To<gfx::GpuMemoryBufferId>();
+ base::PlatformFile platform_file;
+ MojoResult unwrap_result = mojo::UnwrapPlatformFile(
+ std::move(handle->buffer_handle), &platform_file);
+ if (unwrap_result == MOJO_RESULT_OK) {
+#if defined(OS_WIN)
+ result.handle =
+ base::SharedMemoryHandle(platform_file, base::GetCurrentProcId());
+#else
+ result.handle = base::SharedMemoryHandle(platform_file, true);
+#endif
+ }
+ result.offset = handle->offset;
+ result.stride = handle->stride;
+#if defined(USE_OZONE)
+ result.native_pixmap_handle =
+ handle->native_pixmap_handle.To<gfx::NativePixmapHandle>();
+#else
+ DCHECK(handle->native_pixmap_handle.is_null());
+#endif
+ return result;
+}
+
+// static
+mus::mojom::GpuInfoPtr
+TypeConverter<mus::mojom::GpuInfoPtr, gpu::GPUInfo>::Convert(
+ const gpu::GPUInfo& input) {
+ mus::mojom::GpuInfoPtr result(mus::mojom::GpuInfo::New());
+ result->vendor_id = input.gpu.vendor_id;
+ result->device_id = input.gpu.device_id;
+ result->vendor_info = mojo::String::From<std::string>(input.gl_vendor);
+ result->renderer_info = mojo::String::From<std::string>(input.gl_renderer);
+ result->driver_version =
+ mojo::String::From<std::string>(input.driver_version);
+ return result;
+}
+
+} // namespace mojo
diff --git a/chromium/components/mus/common/gpu_type_converters.h b/chromium/components/mus/common/gpu_type_converters.h
new file mode 100644
index 00000000000..74c170c0437
--- /dev/null
+++ b/chromium/components/mus/common/gpu_type_converters.h
@@ -0,0 +1,95 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_COMMON_GPU_TYPE_CONVERTERS_H_
+#define COMPONENTS_MUS_COMMON_GPU_TYPE_CONVERTERS_H_
+
+#include "build/build_config.h"
+#include "components/mus/common/mus_common_export.h"
+#include "components/mus/public/interfaces/channel_handle.mojom.h"
+#include "components/mus/public/interfaces/gpu.mojom.h"
+#include "components/mus/public/interfaces/gpu_memory_buffer.mojom.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+
+namespace gfx {
+struct GpuMemoryBufferHandle;
+class GenericSharedMemoryId;
+using GpuMemoryBufferId = GenericSharedMemoryId;
+struct NativePixmapHandle;
+}
+
+namespace gpu {
+struct GPUInfo;
+}
+
+namespace IPC {
+struct ChannelHandle;
+}
+
+namespace mojo {
+
+template <>
+struct MUS_COMMON_EXPORT
+ TypeConverter<mus::mojom::ChannelHandlePtr, IPC::ChannelHandle> {
+ static mus::mojom::ChannelHandlePtr Convert(const IPC::ChannelHandle& handle);
+};
+
+template <>
+struct MUS_COMMON_EXPORT
+ TypeConverter<IPC::ChannelHandle, mus::mojom::ChannelHandlePtr> {
+ static IPC::ChannelHandle Convert(const mus::mojom::ChannelHandlePtr& handle);
+};
+
+#if defined(USE_OZONE)
+template <>
+struct MUS_COMMON_EXPORT
+ TypeConverter<mus::mojom::NativePixmapHandlePtr, gfx::NativePixmapHandle> {
+ static mus::mojom::NativePixmapHandlePtr Convert(
+ const gfx::NativePixmapHandle& handle);
+};
+
+template <>
+struct MUS_COMMON_EXPORT
+ TypeConverter<gfx::NativePixmapHandle, mus::mojom::NativePixmapHandlePtr> {
+ static gfx::NativePixmapHandle Convert(
+ const mus::mojom::NativePixmapHandlePtr& handle);
+};
+#endif
+
+template <>
+struct MUS_COMMON_EXPORT
+ TypeConverter<mus::mojom::GpuMemoryBufferIdPtr, gfx::GpuMemoryBufferId> {
+ static mus::mojom::GpuMemoryBufferIdPtr Convert(
+ const gfx::GpuMemoryBufferId& id);
+};
+
+template <>
+struct MUS_COMMON_EXPORT
+ TypeConverter<gfx::GpuMemoryBufferId, mus::mojom::GpuMemoryBufferIdPtr> {
+ static gfx::GpuMemoryBufferId Convert(
+ const mus::mojom::GpuMemoryBufferIdPtr& id);
+};
+
+template <>
+struct MUS_COMMON_EXPORT TypeConverter<mus::mojom::GpuMemoryBufferHandlePtr,
+ gfx::GpuMemoryBufferHandle> {
+ static mus::mojom::GpuMemoryBufferHandlePtr Convert(
+ const gfx::GpuMemoryBufferHandle& handle);
+};
+
+template <>
+struct MUS_COMMON_EXPORT TypeConverter<gfx::GpuMemoryBufferHandle,
+ mus::mojom::GpuMemoryBufferHandlePtr> {
+ static gfx::GpuMemoryBufferHandle Convert(
+ const mus::mojom::GpuMemoryBufferHandlePtr& handle);
+};
+
+template <>
+struct MUS_COMMON_EXPORT TypeConverter<mus::mojom::GpuInfoPtr, gpu::GPUInfo> {
+ static mus::mojom::GpuInfoPtr Convert(const gpu::GPUInfo& input);
+};
+
+} // namespace mojo
+
+#endif // COMPONENTS_MUS_COMMON_GPU_TYPE_CONVERTERS_H_
diff --git a/chromium/components/mus/common/gpu_type_converters_unittest.cc b/chromium/components/mus/common/gpu_type_converters_unittest.cc
new file mode 100644
index 00000000000..012664309d9
--- /dev/null
+++ b/chromium/components/mus/common/gpu_type_converters_unittest.cc
@@ -0,0 +1,94 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "base/files/scoped_file.h"
+#include "build/build_config.h"
+#include "components/mus/common/gpu_type_converters.h"
+#include "ipc/ipc_channel.h"
+#include "ipc/ipc_channel_handle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/buffer_types.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/gpu_memory_buffer.h"
+
+// Test for mojo TypeConverter of mus::mojom::ChannelHandle.
+TEST(MusGpuTypeConvertersTest, ChannelHandle) {
+ {
+ const std::string channel_name = "test_channel_name";
+ IPC::ChannelHandle handle(channel_name);
+
+ mus::mojom::ChannelHandlePtr mojo_handle =
+ mus::mojom::ChannelHandle::From(handle);
+ ASSERT_EQ(mojo_handle->name, channel_name);
+ EXPECT_FALSE(mojo_handle->socket.is_valid());
+
+ handle = mojo_handle.To<IPC::ChannelHandle>();
+ ASSERT_EQ(handle.name, channel_name);
+#if defined(OS_POSIX)
+ ASSERT_EQ(handle.socket.fd, -1);
+#endif
+ }
+
+#if defined(OS_POSIX)
+ {
+ const std::string channel_name = "test_channel_name";
+ int fd1 = -1;
+ int fd2 = -1;
+ bool result = IPC::SocketPair(&fd1, &fd2);
+ EXPECT_TRUE(result);
+
+ base::ScopedFD scoped_fd1(fd1);
+ base::ScopedFD scoped_fd2(fd2);
+ IPC::ChannelHandle handle(channel_name,
+ base::FileDescriptor(scoped_fd1.release(), true));
+
+ mus::mojom::ChannelHandlePtr mojo_handle =
+ mus::mojom::ChannelHandle::From(handle);
+ ASSERT_EQ(mojo_handle->name, channel_name);
+ EXPECT_TRUE(mojo_handle->socket.is_valid());
+
+ handle = mojo_handle.To<IPC::ChannelHandle>();
+ ASSERT_EQ(handle.name, channel_name);
+ ASSERT_NE(handle.socket.fd, -1);
+ EXPECT_TRUE(handle.socket.auto_close);
+ base::ScopedFD socped_fd3(handle.socket.fd);
+ }
+#endif
+}
+
+// Test for mojo TypeConverter of mus::mojom::GpuMemoryBufferHandle
+TEST(MusGpuTypeConvertersTest, GpuMemoryBufferHandle) {
+ const gfx::GpuMemoryBufferId kId(99);
+ const uint32_t kOffset = 126;
+ const int32_t kStride = 256;
+ base::SharedMemory shared_memory;
+ ASSERT_TRUE(shared_memory.CreateAnonymous(1024));
+ ASSERT_TRUE(shared_memory.Map(1024));
+
+ gfx::GpuMemoryBufferHandle handle;
+ handle.type = gfx::SHARED_MEMORY_BUFFER;
+ handle.id = kId;
+ handle.handle = base::SharedMemory::DuplicateHandle(shared_memory.handle());
+ handle.offset = kOffset;
+ handle.stride = kStride;
+
+ mus::mojom::GpuMemoryBufferHandlePtr gpu_handle =
+ mus::mojom::GpuMemoryBufferHandle::From<gfx::GpuMemoryBufferHandle>(
+ handle);
+ ASSERT_EQ(gpu_handle->type, mus::mojom::GpuMemoryBufferType::SHARED_MEMORY);
+ ASSERT_EQ(gpu_handle->id->id, 99);
+ ASSERT_EQ(gpu_handle->offset, kOffset);
+ ASSERT_EQ(gpu_handle->stride, kStride);
+
+ handle = gpu_handle.To<gfx::GpuMemoryBufferHandle>();
+ ASSERT_EQ(handle.type, gfx::SHARED_MEMORY_BUFFER);
+ ASSERT_EQ(handle.id, kId);
+ ASSERT_EQ(handle.offset, kOffset);
+ ASSERT_EQ(handle.stride, kStride);
+
+ base::SharedMemory shared_memory1(handle.handle, true);
+ ASSERT_TRUE(shared_memory1.Map(1024));
+}
diff --git a/chromium/components/mus/common/mojo_buffer_backing.cc b/chromium/components/mus/common/mojo_buffer_backing.cc
new file mode 100644
index 00000000000..ba9da7fcb0d
--- /dev/null
+++ b/chromium/components/mus/common/mojo_buffer_backing.cc
@@ -0,0 +1,34 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/common/mojo_buffer_backing.h"
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+
+namespace mus {
+
+MojoBufferBacking::MojoBufferBacking(mojo::ScopedSharedBufferMapping mapping,
+ size_t size)
+ : mapping_(std::move(mapping)), size_(size) {}
+
+MojoBufferBacking::~MojoBufferBacking() = default;
+
+// static
+std::unique_ptr<gpu::BufferBacking> MojoBufferBacking::Create(
+ mojo::ScopedSharedBufferHandle handle,
+ size_t size) {
+ mojo::ScopedSharedBufferMapping mapping = handle->Map(size);
+ if (!mapping)
+ return nullptr;
+ return base::MakeUnique<MojoBufferBacking>(std::move(mapping), size);
+}
+void* MojoBufferBacking::GetMemory() const {
+ return mapping_.get();
+}
+size_t MojoBufferBacking::GetSize() const {
+ return size_;
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/common/mojo_buffer_backing.h b/chromium/components/mus/common/mojo_buffer_backing.h
new file mode 100644
index 00000000000..3f6e33f721e
--- /dev/null
+++ b/chromium/components/mus/common/mojo_buffer_backing.h
@@ -0,0 +1,40 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_COMMON_MOJO_BUFFER_BACKING_H_
+#define COMPONENTS_MUS_COMMON_MOJO_BUFFER_BACKING_H_
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "components/mus/common/mus_common_export.h"
+#include "gpu/command_buffer/common/buffer.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mus {
+
+class MUS_COMMON_EXPORT MojoBufferBacking : public gpu::BufferBacking {
+ public:
+ MojoBufferBacking(mojo::ScopedSharedBufferMapping mapping, size_t size);
+ ~MojoBufferBacking() override;
+
+ static std::unique_ptr<gpu::BufferBacking> Create(
+ mojo::ScopedSharedBufferHandle handle,
+ size_t size);
+
+ void* GetMemory() const override;
+ size_t GetSize() const override;
+
+ private:
+ mojo::ScopedSharedBufferMapping mapping_;
+ size_t size_;
+
+ DISALLOW_COPY_AND_ASSIGN(MojoBufferBacking);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_COMMON_MOJO_BUFFER_BACKING_H_
diff --git a/chromium/components/mus/common/mojo_gpu_memory_buffer.cc b/chromium/components/mus/common/mojo_gpu_memory_buffer.cc
new file mode 100644
index 00000000000..00b046b068d
--- /dev/null
+++ b/chromium/components/mus/common/mojo_gpu_memory_buffer.cc
@@ -0,0 +1,107 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/common/mojo_gpu_memory_buffer.h"
+
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/shared_memory.h"
+#include "base/numerics/safe_conversions.h"
+#include "build/build_config.h"
+#include "mojo/public/cpp/system/buffer.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+#include "ui/gfx/buffer_format_util.h"
+
+namespace mus {
+
+MojoGpuMemoryBufferImpl::MojoGpuMemoryBufferImpl(
+ const gfx::Size& size,
+ gfx::BufferFormat format,
+ std::unique_ptr<base::SharedMemory> shared_memory)
+ : GpuMemoryBufferImpl(gfx::GenericSharedMemoryId(0), size, format),
+ shared_memory_(std::move(shared_memory)) {}
+
+// TODO(rjkroege): Support running a destructor callback as necessary.
+MojoGpuMemoryBufferImpl::~MojoGpuMemoryBufferImpl() {}
+
+std::unique_ptr<gfx::GpuMemoryBuffer> MojoGpuMemoryBufferImpl::Create(
+ const gfx::Size& size,
+ gfx::BufferFormat format,
+ gfx::BufferUsage usage) {
+ size_t bytes = gfx::BufferSizeForBufferFormat(size, format);
+
+ mojo::ScopedSharedBufferHandle handle =
+ mojo::SharedBufferHandle::Create(bytes);
+ if (!handle.is_valid())
+ return nullptr;
+
+ base::SharedMemoryHandle platform_handle;
+ size_t shared_memory_size;
+ bool readonly;
+ MojoResult result = mojo::UnwrapSharedMemoryHandle(
+ std::move(handle), &platform_handle, &shared_memory_size, &readonly);
+ if (result != MOJO_RESULT_OK)
+ return nullptr;
+ DCHECK_EQ(shared_memory_size, bytes);
+
+ auto shared_memory =
+ base::MakeUnique<base::SharedMemory>(platform_handle, readonly);
+ return base::WrapUnique<gfx::GpuMemoryBuffer>(
+ new MojoGpuMemoryBufferImpl(size, format, std::move(shared_memory)));
+}
+
+MojoGpuMemoryBufferImpl* MojoGpuMemoryBufferImpl::FromClientBuffer(
+ ClientBuffer buffer) {
+ return reinterpret_cast<MojoGpuMemoryBufferImpl*>(buffer);
+}
+
+const unsigned char* MojoGpuMemoryBufferImpl::GetMemory() const {
+ return static_cast<const unsigned char*>(shared_memory_->memory());
+}
+
+bool MojoGpuMemoryBufferImpl::Map() {
+ DCHECK(!mapped_);
+ if (!shared_memory_->Map(gfx::BufferSizeForBufferFormat(size_, format_)))
+ return false;
+ mapped_ = true;
+ return true;
+}
+
+void* MojoGpuMemoryBufferImpl::memory(size_t plane) {
+ DCHECK(mapped_);
+ DCHECK_LT(plane, gfx::NumberOfPlanesForBufferFormat(format_));
+ return reinterpret_cast<uint8_t*>(shared_memory_->memory()) +
+ gfx::BufferOffsetForBufferFormat(size_, format_, plane);
+}
+
+void MojoGpuMemoryBufferImpl::Unmap() {
+ DCHECK(mapped_);
+ shared_memory_->Unmap();
+ mapped_ = false;
+}
+
+int MojoGpuMemoryBufferImpl::stride(size_t plane) const {
+ DCHECK_LT(plane, gfx::NumberOfPlanesForBufferFormat(format_));
+ return base::checked_cast<int>(gfx::RowSizeForBufferFormat(
+ size_.width(), format_, static_cast<int>(plane)));
+}
+
+gfx::GpuMemoryBufferHandle MojoGpuMemoryBufferImpl::GetHandle() const {
+ gfx::GpuMemoryBufferHandle handle;
+ handle.type = gfx::SHARED_MEMORY_BUFFER;
+ handle.handle = shared_memory_->handle();
+ handle.offset = 0;
+ handle.stride = static_cast<int32_t>(
+ gfx::RowSizeForBufferFormat(size_.width(), format_, 0));
+
+ return handle;
+}
+
+gfx::GpuMemoryBufferType MojoGpuMemoryBufferImpl::GetBufferType() const {
+ return gfx::SHARED_MEMORY_BUFFER;
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/common/mojo_gpu_memory_buffer.h b/chromium/components/mus/common/mojo_gpu_memory_buffer.h
new file mode 100644
index 00000000000..ffcbf049185
--- /dev/null
+++ b/chromium/components/mus/common/mojo_gpu_memory_buffer.h
@@ -0,0 +1,54 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_COMMON_MOJO_GPU_MEMORY_BUFFER_H_
+#define COMPONENTS_MUS_COMMON_MOJO_GPU_MEMORY_BUFFER_H_
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "components/mus/common/gpu_memory_buffer_impl.h"
+#include "components/mus/common/mus_common_export.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/gpu_memory_buffer.h"
+
+namespace mus {
+
+class MUS_COMMON_EXPORT MojoGpuMemoryBufferImpl
+ : public mus::GpuMemoryBufferImpl {
+ public:
+ MojoGpuMemoryBufferImpl(const gfx::Size& size,
+ gfx::BufferFormat format,
+ std::unique_ptr<base::SharedMemory> shared_memory);
+ ~MojoGpuMemoryBufferImpl() override;
+
+ static std::unique_ptr<gfx::GpuMemoryBuffer> Create(const gfx::Size& size,
+ gfx::BufferFormat format,
+ gfx::BufferUsage usage);
+
+ static MojoGpuMemoryBufferImpl* FromClientBuffer(ClientBuffer buffer);
+
+ const unsigned char* GetMemory() const;
+
+ // Overridden from gfx::GpuMemoryBuffer:
+ bool Map() override;
+ void* memory(size_t plane) override;
+ void Unmap() override;
+ int stride(size_t plane) const override;
+ gfx::GpuMemoryBufferHandle GetHandle() const override;
+
+ // Overridden from gfx::GpuMemoryBufferImpl
+ gfx::GpuMemoryBufferType GetBufferType() const override;
+
+ private:
+ std::unique_ptr<base::SharedMemory> shared_memory_;
+
+ DISALLOW_COPY_AND_ASSIGN(MojoGpuMemoryBufferImpl);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_COMMON_MOJO_GPU_MEMORY_BUFFER_H_
diff --git a/chromium/components/mus/common/mojo_gpu_memory_buffer_manager.cc b/chromium/components/mus/common/mojo_gpu_memory_buffer_manager.cc
new file mode 100644
index 00000000000..51decafb326
--- /dev/null
+++ b/chromium/components/mus/common/mojo_gpu_memory_buffer_manager.cc
@@ -0,0 +1,46 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/common/mojo_gpu_memory_buffer_manager.h"
+
+#include "base/logging.h"
+#include "components/mus/common/mojo_gpu_memory_buffer.h"
+
+namespace mus {
+
+MojoGpuMemoryBufferManager::MojoGpuMemoryBufferManager() {}
+
+MojoGpuMemoryBufferManager::~MojoGpuMemoryBufferManager() {}
+
+std::unique_ptr<gfx::GpuMemoryBuffer>
+MojoGpuMemoryBufferManager::AllocateGpuMemoryBuffer(
+ const gfx::Size& size,
+ gfx::BufferFormat format,
+ gfx::BufferUsage usage,
+ gpu::SurfaceHandle surface_handle) {
+ return MojoGpuMemoryBufferImpl::Create(size, format, usage);
+}
+
+std::unique_ptr<gfx::GpuMemoryBuffer>
+MojoGpuMemoryBufferManager::CreateGpuMemoryBufferFromHandle(
+ const gfx::GpuMemoryBufferHandle& handle,
+ const gfx::Size& size,
+ gfx::BufferFormat format) {
+ NOTIMPLEMENTED();
+ return nullptr;
+}
+
+gfx::GpuMemoryBuffer*
+MojoGpuMemoryBufferManager::GpuMemoryBufferFromClientBuffer(
+ ClientBuffer buffer) {
+ return MojoGpuMemoryBufferImpl::FromClientBuffer(buffer);
+}
+
+void MojoGpuMemoryBufferManager::SetDestructionSyncToken(
+ gfx::GpuMemoryBuffer* buffer,
+ const gpu::SyncToken& sync_token) {
+ NOTIMPLEMENTED();
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/common/mojo_gpu_memory_buffer_manager.h b/chromium/components/mus/common/mojo_gpu_memory_buffer_manager.h
new file mode 100644
index 00000000000..e3ce95dfc97
--- /dev/null
+++ b/chromium/components/mus/common/mojo_gpu_memory_buffer_manager.h
@@ -0,0 +1,43 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_COMMON_MOJO_GPU_MEMORY_BUFFER_MANAGER_H_
+#define COMPONENTS_MUS_COMMON_MOJO_GPU_MEMORY_BUFFER_MANAGER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "components/mus/common/mus_common_export.h"
+#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
+
+namespace mus {
+
+class MUS_COMMON_EXPORT MojoGpuMemoryBufferManager
+ : public gpu::GpuMemoryBufferManager {
+ public:
+ MojoGpuMemoryBufferManager();
+ ~MojoGpuMemoryBufferManager() override;
+
+ // Overridden from gpu::GpuMemoryBufferManager:
+ std::unique_ptr<gfx::GpuMemoryBuffer> AllocateGpuMemoryBuffer(
+ const gfx::Size& size,
+ gfx::BufferFormat format,
+ gfx::BufferUsage usage,
+ gpu::SurfaceHandle surface_handle) override;
+ std::unique_ptr<gfx::GpuMemoryBuffer> CreateGpuMemoryBufferFromHandle(
+ const gfx::GpuMemoryBufferHandle& handle,
+ const gfx::Size& size,
+ gfx::BufferFormat format) override;
+ gfx::GpuMemoryBuffer* GpuMemoryBufferFromClientBuffer(
+ ClientBuffer buffer) override;
+ void SetDestructionSyncToken(gfx::GpuMemoryBuffer* buffer,
+ const gpu::SyncToken& sync_token) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MojoGpuMemoryBufferManager);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_COMMON_MOJO_GPU_MEMORY_BUFFER_MANAGER_H_
diff --git a/chromium/components/mus/common/mus_common_export.h b/chromium/components/mus/common/mus_common_export.h
new file mode 100644
index 00000000000..7e7526b6710
--- /dev/null
+++ b/chromium/components/mus/common/mus_common_export.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_COMMON_MUS_COMMON_EXPORT_H_
+#define COMPONENTS_MUS_COMMON_MUS_COMMON_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(MUS_COMMON_IMPLEMENTATION)
+#define MUS_COMMON_EXPORT __declspec(dllexport)
+#else
+#define MUS_COMMON_EXPORT __declspec(dllimport)
+#endif // defined(MUS_COMMON_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(MUS_COMMON_IMPLEMENTATION)
+#define MUS_COMMON_EXPORT __attribute__((visibility("default")))
+#else
+#define MUS_COMMON_EXPORT
+#endif
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define MUS_COMMON_EXPORT
+#endif
+
+#endif // COMPONENTS_MUS_COMMON_MUS_COMMON_EXPORT_H_
diff --git a/chromium/components/mus/common/mus_common_unittests_app_manifest.json b/chromium/components/mus/common/mus_common_unittests_app_manifest.json
new file mode 100644
index 00000000000..5240ae9b6a9
--- /dev/null
+++ b/chromium/components/mus/common/mus_common_unittests_app_manifest.json
@@ -0,0 +1,10 @@
+{
+ "manifest_version": 1,
+ "name": "mojo:mus_common_unittests_app",
+ "display_name": "Mus Common Unittests",
+ "capabilities": {
+ "required": {
+ "*": { "classes": [ "app" ] }
+ }
+ }
+}
diff --git a/chromium/components/mus/common/run_all_shelltests.cc b/chromium/components/mus/common/run_all_shelltests.cc
new file mode 100644
index 00000000000..e795983ec58
--- /dev/null
+++ b/chromium/components/mus/common/run_all_shelltests.cc
@@ -0,0 +1,34 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+#include "build/build_config.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "services/shell/background/background_shell_main.h"
+
+#if defined(USE_OZONE)
+#include "ui/ozone/public/ozone_platform.h"
+#endif
+
+class MusGpuTestSuite : public base::TestSuite {
+ public:
+ MusGpuTestSuite(int argc, char** argv) : base::TestSuite(argc, argv) {}
+
+ private:
+ void Initialize() override {
+ base::TestSuite::Initialize();
+#if defined(USE_OZONE)
+ ui::OzonePlatform::InitializeForGPU();
+#endif
+ }
+};
+
+int MasterProcessMain(int argc, char** argv) {
+ MusGpuTestSuite test_suite(argc, argv);
+ mojo::edk::Init();
+ return base::LaunchUnitTests(
+ argc, argv,
+ base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
+}
diff --git a/chromium/components/mus/common/switches.cc b/chromium/components/mus/common/switches.cc
new file mode 100644
index 00000000000..f9bead3ed54
--- /dev/null
+++ b/chromium/components/mus/common/switches.cc
@@ -0,0 +1,20 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/common/switches.h"
+
+namespace mus {
+namespace switches {
+
+// Use mojo GPU command buffer instead of Chrome GPU command buffer.
+const char kUseMojoGpuCommandBufferInMus[] =
+ "use-mojo-gpu-command-buffer-in-mus";
+
+// Initializes X11 in threaded mode, and sets the |override_redirect| flag when
+// creating X11 windows. Also, exposes the WindowServerTest interface to clients
+// when launched with this flag.
+const char kUseTestConfig[] = "use-test-config";
+
+} // namespace switches
+} // namespace mus
diff --git a/chromium/components/mus/common/switches.h b/chromium/components/mus/common/switches.h
new file mode 100644
index 00000000000..492aa4c3113
--- /dev/null
+++ b/chromium/components/mus/common/switches.h
@@ -0,0 +1,21 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_COMMON_SWITCHES_H_
+#define COMPONENTS_MUS_COMMON_SWITCHES_H_
+
+#include "components/mus/common/mus_common_export.h"
+
+namespace mus {
+namespace switches {
+
+// All args in alphabetical order. The switches should be documented
+// alongside the definition of their values in the .cc file.
+extern const char MUS_COMMON_EXPORT kUseMojoGpuCommandBufferInMus[];
+extern const char MUS_COMMON_EXPORT kUseTestConfig[];
+
+} // namespace switches
+} // namespace mus
+
+#endif // COMPONENTS_MUS_COMMON_SWITCHES_H_
diff --git a/chromium/components/mus/common/transient_window_utils.h b/chromium/components/mus/common/transient_window_utils.h
new file mode 100644
index 00000000000..17eedc63a25
--- /dev/null
+++ b/chromium/components/mus/common/transient_window_utils.h
@@ -0,0 +1,126 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_COMMON_TRANSIENT_WINDOW_UTILS_H_
+#define COMPONENTS_MUS_COMMON_TRANSIENT_WINDOW_UTILS_H_
+
+#include <stddef.h>
+
+#include <vector>
+
+#include "components/mus/public/interfaces/mus_constants.mojom.h"
+
+namespace mus {
+
+// Returns true if |window| has |ancestor| as a transient ancestor. A transient
+// ancestor is found by following the transient parent chain of the window.
+template <class T>
+bool HasTransientAncestor(const T* window, const T* ancestor) {
+ const T* transient_parent = window->transient_parent();
+ if (transient_parent == ancestor)
+ return true;
+ return transient_parent ? HasTransientAncestor(transient_parent, ancestor)
+ : false;
+}
+
+// Populates |ancestors| with all transient ancestors of |window| that are
+// siblings of |window|. Returns true if any ancestors were found, false if not.
+template <class T>
+bool GetAllTransientAncestors(T* window, std::vector<T*>* ancestors) {
+ T* parent = window->parent();
+ for (; window; window = window->transient_parent()) {
+ if (window->parent() == parent)
+ ancestors->push_back(window);
+ }
+ return !ancestors->empty();
+}
+
+// Replaces |window1| and |window2| with their possible transient ancestors that
+// are still siblings (have a common transient parent). |window1| and |window2|
+// are not modified if such ancestors cannot be found.
+template <class T>
+void FindCommonTransientAncestor(T** window1, T** window2) {
+ DCHECK(window1);
+ DCHECK(window2);
+ DCHECK(*window1);
+ DCHECK(*window2);
+ // Assemble chains of ancestors of both windows.
+ std::vector<T*> ancestors1;
+ std::vector<T*> ancestors2;
+ if (!GetAllTransientAncestors(*window1, &ancestors1) ||
+ !GetAllTransientAncestors(*window2, &ancestors2)) {
+ return;
+ }
+ // Walk the two chains backwards and look for the first difference.
+ auto it1 = ancestors1.rbegin();
+ auto it2 = ancestors2.rbegin();
+ for (; it1 != ancestors1.rend() && it2 != ancestors2.rend(); ++it1, ++it2) {
+ if (*it1 != *it2) {
+ *window1 = *it1;
+ *window2 = *it2;
+ break;
+ }
+ }
+}
+
+template <class T>
+bool AdjustStackingForTransientWindows(T** child,
+ T** target,
+ mojom::OrderDirection* direction,
+ T* stacking_target) {
+ if (stacking_target == *target)
+ return true;
+
+ // For windows that have transient children stack the transient ancestors that
+ // are siblings. This prevents one transient group from being inserted in the
+ // middle of another.
+ FindCommonTransientAncestor(child, target);
+
+ // When stacking above skip to the topmost transient descendant of the target.
+ if (*direction == mojom::OrderDirection::ABOVE &&
+ !HasTransientAncestor(*child, *target)) {
+ const std::vector<T*>& siblings((*child)->parent()->children());
+ size_t target_i =
+ std::find(siblings.begin(), siblings.end(), *target) - siblings.begin();
+ while (target_i + 1 < siblings.size() &&
+ HasTransientAncestor(siblings[target_i + 1], *target)) {
+ ++target_i;
+ }
+ *target = siblings[target_i];
+ }
+
+ return *child != *target;
+}
+
+// Stacks transient descendants of |window| that are its siblings just above it.
+// |GetStackingTarget| is a function that returns a marker associated with a
+// Window that indicates the current Window being stacked.
+// |Reorder| is a function that takes in two windows and orders the first
+// relative to the second based on the provided OrderDirection.
+template <class T>
+void RestackTransientDescendants(T* window,
+ T** (*GetStackingTarget)(T*),
+ void (*Reorder)(T*,
+ T*,
+ mojom::OrderDirection)) {
+ T* parent = window->parent();
+ if (!parent)
+ return;
+
+ // stack any transient children that share the same parent to be in front of
+ // |window_|. the existing stacking order is preserved by iterating backwards
+ // and always stacking on top.
+ std::vector<T*> children(parent->children());
+ for (auto it = children.rbegin(); it != children.rend(); ++it) {
+ if ((*it) != window && HasTransientAncestor(*it, window)) {
+ T* old_stacking_target = *GetStackingTarget(*it);
+ *GetStackingTarget(*it) = window;
+ Reorder(*it, window, mojom::OrderDirection::ABOVE);
+ *GetStackingTarget(*it) = old_stacking_target;
+ }
+ }
+}
+} // namespace mus
+
+#endif // COMPONENTS_MUS_COMMON_TRANSIENT_WINDOW_UTILS_H_
diff --git a/chromium/components/mus/common/types.h b/chromium/components/mus/common/types.h
new file mode 100644
index 00000000000..93b96683e6b
--- /dev/null
+++ b/chromium/components/mus/common/types.h
@@ -0,0 +1,25 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_COMMON_TYPES_H_
+#define COMPONENTS_MUS_COMMON_TYPES_H_
+
+#include <stdint.h>
+
+// Typedefs for the transport types. These typedefs match that of the mojom
+// file, see it for specifics.
+
+namespace mus {
+
+// Used to identify windows and change ids.
+typedef uint32_t Id;
+
+// Used to identify a client as well as a client-specific window id. For
+// example, the Id for a window consists of the ClientSpecificId of the client
+// and the ClientSpecificId of the window.
+typedef uint16_t ClientSpecificId;
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_COMMON_TYPES_H_
diff --git a/chromium/components/mus/common/util.h b/chromium/components/mus/common/util.h
new file mode 100644
index 00000000000..daa59efc6b1
--- /dev/null
+++ b/chromium/components/mus/common/util.h
@@ -0,0 +1,32 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_COMMON_UTIL_H_
+#define COMPONENTS_MUS_COMMON_UTIL_H_
+
+#include <stdint.h>
+
+#include "components/mus/common/types.h"
+
+// TODO(beng): #$*&@#(@ MacOSX SDK!
+#if defined(HiWord)
+#undef HiWord
+#endif
+#if defined(LoWord)
+#undef LoWord
+#endif
+
+namespace mus {
+
+inline uint16_t HiWord(uint32_t id) {
+ return static_cast<uint16_t>((id >> 16) & 0xFFFF);
+}
+
+inline uint16_t LoWord(uint32_t id) {
+ return static_cast<uint16_t>(id & 0xFFFF);
+}
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_COMMON_UTIL_H_
diff --git a/chromium/components/mus/demo/DEPS b/chromium/components/mus/demo/DEPS
new file mode 100644
index 00000000000..ce521506ba2
--- /dev/null
+++ b/chromium/components/mus/demo/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+components/bitmap_uploader",
+]
+
diff --git a/chromium/components/mus/demo/OWNERS b/chromium/components/mus/demo/OWNERS
new file mode 100644
index 00000000000..47351aafac9
--- /dev/null
+++ b/chromium/components/mus/demo/OWNERS
@@ -0,0 +1,2 @@
+kylechar@chromium.org
+rjkroege@chromium.org
diff --git a/chromium/components/mus/demo/main.cc b/chromium/components/mus/demo/main.cc
new file mode 100644
index 00000000000..62be1b7d81d
--- /dev/null
+++ b/chromium/components/mus/demo/main.cc
@@ -0,0 +1,13 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/demo/mus_demo.h"
+#include "mojo/public/c/system/main.h"
+#include "services/shell/public/cpp/application_runner.h"
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+ shell::ApplicationRunner runner(new mus_demo::MusDemo);
+ runner.set_message_loop_type(base::MessageLoop::TYPE_UI);
+ return runner.Run(shell_handle);
+}
diff --git a/chromium/components/mus/demo/manifest.json b/chromium/components/mus/demo/manifest.json
new file mode 100644
index 00000000000..7971b79d238
--- /dev/null
+++ b/chromium/components/mus/demo/manifest.json
@@ -0,0 +1,14 @@
+{
+ "manifest_version": 1,
+ "name": "mojo:mus_demo",
+ "display_name": "MUS Demo",
+ "capabilities": {
+ "required": {
+ "mojo:mus": {
+ "interfaces": [ "mus::mojom::WindowManagerWindowTreeFactory"],
+ "classes": [ "app" ]
+ }
+ }
+ }
+}
+
diff --git a/chromium/components/mus/demo/mus_demo.cc b/chromium/components/mus/demo/mus_demo.cc
new file mode 100644
index 00000000000..0853f5ab48c
--- /dev/null
+++ b/chromium/components/mus/demo/mus_demo.cc
@@ -0,0 +1,179 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/demo/mus_demo.h"
+
+#include "base/time/time.h"
+#include "components/bitmap_uploader/bitmap_uploader.h"
+#include "components/mus/common/gpu_service.h"
+#include "components/mus/public/cpp/window.h"
+#include "components/mus/public/cpp/window_tree_client.h"
+#include "services/shell/public/cpp/connector.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "third_party/skia/include/core/SkPaint.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace mus_demo {
+
+namespace {
+
+// Milliseconds between frames.
+const int64_t kFrameDelay = 33;
+
+// Size of square in pixels to draw.
+const int kSquareSize = 300;
+
+const SkColor kBgColor = SK_ColorRED;
+const SkColor kFgColor = SK_ColorYELLOW;
+
+void DrawSquare(const gfx::Rect& bounds, double angle, SkCanvas* canvas) {
+ // Create SkRect to draw centered inside the bounds.
+ gfx::Point top_left = bounds.CenterPoint();
+ top_left.Offset(-kSquareSize / 2, -kSquareSize / 2);
+ SkRect rect =
+ SkRect::MakeXYWH(top_left.x(), top_left.y(), kSquareSize, kSquareSize);
+
+ // Set SkPaint to fill solid color.
+ SkPaint paint;
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(kFgColor);
+
+ // Rotate the canvas.
+ const gfx::Size canvas_size = bounds.size();
+ if (angle != 0.0) {
+ canvas->translate(SkFloatToScalar(canvas_size.width() * 0.5f),
+ SkFloatToScalar(canvas_size.height() * 0.5f));
+ canvas->rotate(angle);
+ canvas->translate(-SkFloatToScalar(canvas_size.width() * 0.5f),
+ -SkFloatToScalar(canvas_size.height() * 0.5f));
+ }
+
+ canvas->drawRect(rect, paint);
+}
+
+} // namespace
+
+MusDemo::MusDemo() {}
+
+MusDemo::~MusDemo() {
+ delete window_tree_client_;
+}
+
+void MusDemo::Initialize(shell::Connector* connector,
+ const shell::Identity& identity,
+ uint32_t id) {
+ connector_ = connector;
+ mus::GpuService::Initialize(connector_);
+ window_tree_client_ = new mus::WindowTreeClient(this, this, nullptr);
+ window_tree_client_->ConnectAsWindowManager(connector);
+}
+
+bool MusDemo::AcceptConnection(shell::Connection* connection) {
+ return true;
+}
+
+void MusDemo::OnEmbed(mus::Window* window) {
+ // Not called for the WindowManager.
+ NOTREACHED();
+}
+
+void MusDemo::OnWindowTreeClientDestroyed(mus::WindowTreeClient* client) {
+ window_tree_client_ = nullptr;
+ timer_.Stop();
+}
+
+void MusDemo::OnEventObserved(const ui::Event& event, mus::Window* target) {}
+
+void MusDemo::SetWindowManagerClient(mus::WindowManagerClient* client) {}
+
+bool MusDemo::OnWmSetBounds(mus::Window* window, gfx::Rect* bounds) {
+ return true;
+}
+
+bool MusDemo::OnWmSetProperty(mus::Window* window,
+ const std::string& name,
+ std::unique_ptr<std::vector<uint8_t>>* new_data) {
+ return true;
+}
+
+mus::Window* MusDemo::OnWmCreateTopLevelWindow(
+ std::map<std::string, std::vector<uint8_t>>* properties) {
+ return nullptr;
+}
+
+void MusDemo::OnWmClientJankinessChanged(
+ const std::set<mus::Window*>& client_windows,
+ bool janky) {
+ // Don't care
+}
+
+void MusDemo::OnWmNewDisplay(mus::Window* window,
+ const display::Display& display) {
+ DCHECK(!window_); // Only support one display.
+ window_ = window;
+
+ // Initialize bitmap uploader for sending frames to MUS.
+ uploader_.reset(new bitmap_uploader::BitmapUploader(window_));
+ uploader_->Init(connector_);
+
+ // Draw initial frame and start the timer to regularly draw frames.
+ DrawFrame();
+ timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kFrameDelay),
+ base::Bind(&MusDemo::DrawFrame, base::Unretained(this)));
+}
+
+void MusDemo::OnAccelerator(uint32_t id, const ui::Event& event) {
+ // Don't care
+}
+
+void MusDemo::AllocBitmap() {
+ const gfx::Rect bounds = window_->GetBoundsInRoot();
+
+ // Allocate bitmap the same size as the window for drawing.
+ bitmap_.reset();
+ SkImageInfo image_info = SkImageInfo::MakeN32(bounds.width(), bounds.height(),
+ kPremul_SkAlphaType);
+ bitmap_.allocPixels(image_info);
+}
+
+void MusDemo::DrawFrame() {
+ angle_ += 2.0;
+ if (angle_ >= 360.0)
+ angle_ = 0.0;
+
+ const gfx::Rect bounds = window_->GetBoundsInRoot();
+
+ // Check that bitmap and window sizes match, otherwise reallocate bitmap.
+ const SkImageInfo info = bitmap_.info();
+ if (info.width() != bounds.width() || info.height() != bounds.height()) {
+ AllocBitmap();
+ }
+
+ // Draw the rotated square on background in bitmap.
+ SkCanvas canvas(bitmap_);
+ canvas.clear(kBgColor);
+ // TODO(kylechar): Add GL drawing instead of software rasterization in future.
+ DrawSquare(bounds, angle_, &canvas);
+ canvas.flush();
+
+ // Copy pixels data into vector that will be passed to BitmapUploader.
+ // TODO(rjkroege): Make a 1/0-copy bitmap uploader for the contents of a
+ // SkBitmap.
+ bitmap_.lockPixels();
+ const unsigned char* addr =
+ static_cast<const unsigned char*>(bitmap_.getPixels());
+ const int bytes = bounds.width() * bounds.height() * 4;
+ std::unique_ptr<std::vector<unsigned char>> data(
+ new std::vector<unsigned char>(addr, addr + bytes));
+ bitmap_.unlockPixels();
+
+ // Send frame to MUS via BitmapUploader.
+ uploader_->SetBitmap(bounds.width(), bounds.height(), std::move(data),
+ bitmap_uploader::BitmapUploader::BGRA);
+}
+
+} // namespace mus_demo
diff --git a/chromium/components/mus/demo/mus_demo.h b/chromium/components/mus/demo/mus_demo.h
new file mode 100644
index 00000000000..dbb62647294
--- /dev/null
+++ b/chromium/components/mus/demo/mus_demo.h
@@ -0,0 +1,92 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_DEMO_MUS_DEMO_H_
+#define COMPONENTS_MUS_DEMO_MUS_DEMO_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/timer/timer.h"
+#include "components/mus/public/cpp/window_manager_delegate.h"
+#include "components/mus/public/cpp/window_tree_client_delegate.h"
+#include "services/shell/public/cpp/shell_client.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+namespace bitmap_uploader {
+class BitmapUploader;
+}
+
+namespace mus_demo {
+
+// A simple MUS Demo mojo app. This app connects to the mojo:mus, creates a new
+// window and draws a spinning square in the center of the window. Provides a
+// simple way to demonstrate that the graphic stack works as intended.
+class MusDemo : public shell::ShellClient,
+ public mus::WindowTreeClientDelegate,
+ public mus::WindowManagerDelegate {
+ public:
+ MusDemo();
+ ~MusDemo() override;
+
+ private:
+ // shell::ShellClient:
+ void Initialize(shell::Connector* connector,
+ const shell::Identity& identity,
+ uint32_t id) override;
+ bool AcceptConnection(shell::Connection* connection) override;
+
+ // WindowTreeClientDelegate:
+ void OnEmbed(mus::Window* root) override;
+ void OnWindowTreeClientDestroyed(mus::WindowTreeClient* client) override;
+ void OnEventObserved(const ui::Event& event, mus::Window* target) override;
+
+ // WindowManagerDelegate:
+ void SetWindowManagerClient(mus::WindowManagerClient* client) override;
+ bool OnWmSetBounds(mus::Window* window, gfx::Rect* bounds) override;
+ bool OnWmSetProperty(
+ mus::Window* window,
+ const std::string& name,
+ std::unique_ptr<std::vector<uint8_t>>* new_data) override;
+ mus::Window* OnWmCreateTopLevelWindow(
+ std::map<std::string, std::vector<uint8_t>>* properties) override;
+ void OnWmClientJankinessChanged(const std::set<mus::Window*>& client_windows,
+ bool janky) override;
+ void OnWmNewDisplay(mus::Window* window,
+ const display::Display& display) override;
+ void OnAccelerator(uint32_t id, const ui::Event& event) override;
+
+ // Allocate a bitmap the same size as the window to draw into.
+ void AllocBitmap();
+
+ // Draws one frame, incrementing the rotation angle.
+ void DrawFrame();
+
+ shell::Connector* connector_ = nullptr;
+
+ mus::Window* window_ = nullptr;
+ mus::WindowTreeClient* window_tree_client_ = nullptr;
+
+ // Used to send frames to mus.
+ std::unique_ptr<bitmap_uploader::BitmapUploader> uploader_;
+
+ // Bitmap that is the same size as our client window area.
+ SkBitmap bitmap_;
+
+ // Timer for calling DrawFrame().
+ base::RepeatingTimer timer_;
+
+ // Current rotation angle for drawing.
+ double angle_ = 0.0;
+
+ DISALLOW_COPY_AND_ASSIGN(MusDemo);
+};
+
+} // namespace mus_demo
+
+#endif // COMPONENTS_MUS_DEMO_MUS_DEMO_H_
diff --git a/chromium/components/mus/gles2/DEPS b/chromium/components/mus/gles2/DEPS
new file mode 100644
index 00000000000..2ad979f2443
--- /dev/null
+++ b/chromium/components/mus/gles2/DEPS
@@ -0,0 +1,8 @@
+include_rules = [
+ "+cc",
+ "+components/gpu",
+ "+gpu",
+ "+mojo/converters",
+ "+mojo/public",
+ "+ui",
+]
diff --git a/chromium/components/mus/gles2/OWNERS b/chromium/components/mus/gles2/OWNERS
new file mode 100644
index 00000000000..1f1af6bcc54
--- /dev/null
+++ b/chromium/components/mus/gles2/OWNERS
@@ -0,0 +1,2 @@
+fsamuel@chromium.org
+rjkroege@chromium.org
diff --git a/chromium/components/mus/gles2/command_buffer_driver.cc b/chromium/components/mus/gles2/command_buffer_driver.cc
new file mode 100644
index 00000000000..1c35904e9f6
--- /dev/null
+++ b/chromium/components/mus/gles2/command_buffer_driver.cc
@@ -0,0 +1,568 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/gles2/command_buffer_driver.h"
+
+#include <stddef.h>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/shared_memory.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
+#include "components/mus/common/mojo_buffer_backing.h"
+#include "components/mus/gles2/gl_surface_adapter.h"
+#include "components/mus/gles2/gpu_memory_tracker.h"
+#include "components/mus/gles2/gpu_state.h"
+#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
+#include "gpu/command_buffer/service/command_buffer_service.h"
+#include "gpu/command_buffer/service/command_executor.h"
+#include "gpu/command_buffer/service/context_group.h"
+#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
+#include "gpu/command_buffer/service/image_manager.h"
+#include "gpu/command_buffer/service/mailbox_manager.h"
+#include "gpu/command_buffer/service/query_manager.h"
+#include "gpu/command_buffer/service/sync_point_manager.h"
+#include "gpu/command_buffer/service/transfer_buffer_manager.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+#include "ui/gfx/buffer_format_util.h"
+#include "ui/gfx/gpu_memory_buffer.h"
+#include "ui/gfx/vsync_provider.h"
+#include "ui/gl/gl_context.h"
+#include "ui/gl/gl_image_shared_memory.h"
+#include "ui/gl/gl_surface.h"
+#include "ui/gl/init/gl_factory.h"
+
+#if defined(USE_OZONE)
+#include "ui/gl/gl_image_ozone_native_pixmap.h"
+#endif
+
+namespace mus {
+
+namespace {
+
+// The first time polling a fence, delay some extra time to allow other
+// stubs to process some work, or else the timing of the fences could
+// allow a pattern of alternating fast and slow frames to occur.
+const int64_t kHandleMoreWorkPeriodMs = 2;
+const int64_t kHandleMoreWorkPeriodBusyMs = 1;
+
+// Prevents idle work from being starved.
+const int64_t kMaxTimeSinceIdleMs = 10;
+
+} // namespace
+
+CommandBufferDriver::Client::~Client() {}
+
+CommandBufferDriver::CommandBufferDriver(
+ gpu::CommandBufferNamespace command_buffer_namespace,
+ gpu::CommandBufferId command_buffer_id,
+ gfx::AcceleratedWidget widget,
+ scoped_refptr<GpuState> gpu_state)
+ : command_buffer_namespace_(command_buffer_namespace),
+ command_buffer_id_(command_buffer_id),
+ widget_(widget),
+ client_(nullptr),
+ gpu_state_(gpu_state),
+ previous_processed_num_(0),
+ weak_factory_(this) {
+ DCHECK_EQ(base::ThreadTaskRunnerHandle::Get(),
+ gpu_state_->command_buffer_task_runner()->task_runner());
+}
+
+CommandBufferDriver::~CommandBufferDriver() {
+ DCHECK(CalledOnValidThread());
+ DestroyDecoder();
+}
+
+bool CommandBufferDriver::Initialize(
+ mojo::ScopedSharedBufferHandle shared_state,
+ mojo::Array<int32_t> attribs) {
+ DCHECK(CalledOnValidThread());
+ gpu::gles2::ContextCreationAttribHelper attrib_helper;
+ if (!attrib_helper.Parse(attribs.storage()))
+ return false;
+ // TODO(piman): attribs can't currently represent gpu_preference.
+
+ const bool offscreen = widget_ == gfx::kNullAcceleratedWidget;
+ if (offscreen) {
+ surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size(0, 0));
+ } else {
+#if defined(USE_OZONE)
+ scoped_refptr<gl::GLSurface> underlying_surface =
+ gl::init::CreateSurfacelessViewGLSurface(widget_);
+ if (!underlying_surface)
+ underlying_surface = gl::init::CreateViewGLSurface(widget_);
+#else
+ scoped_refptr<gl::GLSurface> underlying_surface =
+ gl::init::CreateViewGLSurface(widget_);
+#endif
+ scoped_refptr<GLSurfaceAdapterMus> surface_adapter =
+ new GLSurfaceAdapterMus(underlying_surface);
+ surface_adapter->SetGpuCompletedSwapBuffersCallback(
+ base::Bind(&CommandBufferDriver::OnGpuCompletedSwapBuffers,
+ weak_factory_.GetWeakPtr()));
+ surface_ = surface_adapter;
+
+ gfx::VSyncProvider* vsync_provider =
+ surface_ ? surface_->GetVSyncProvider() : nullptr;
+ if (vsync_provider) {
+ vsync_provider->GetVSyncParameters(
+ base::Bind(&CommandBufferDriver::OnUpdateVSyncParameters,
+ weak_factory_.GetWeakPtr()));
+ }
+ }
+
+ if (!surface_.get())
+ return false;
+
+ // TODO(piman): virtual contexts.
+ context_ = gl::init::CreateGLContext(
+ gpu_state_->share_group(), surface_.get(), attrib_helper.gpu_preference);
+ if (!context_.get())
+ return false;
+
+ if (!context_->MakeCurrent(surface_.get()))
+ return false;
+
+ // TODO(piman): ShaderTranslatorCache is currently per-ContextGroup but
+ // only needs to be per-thread.
+ const bool bind_generates_resource = attrib_helper.bind_generates_resource;
+ scoped_refptr<gpu::gles2::FeatureInfo> feature_info =
+ new gpu::gles2::FeatureInfo(gpu_state_->gpu_driver_bug_workarounds());
+ // TODO(erikchen): The ContextGroup needs a reference to the
+ // GpuMemoryBufferManager.
+ scoped_refptr<gpu::gles2::ContextGroup> context_group =
+ new gpu::gles2::ContextGroup(
+ gpu_state_->gpu_preferences(), gpu_state_->mailbox_manager(),
+ new GpuMemoryTracker,
+ new gpu::gles2::ShaderTranslatorCache(gpu_state_->gpu_preferences()),
+ new gpu::gles2::FramebufferCompletenessCache, feature_info,
+ bind_generates_resource, nullptr);
+
+ command_buffer_.reset(
+ new gpu::CommandBufferService(context_group->transfer_buffer_manager()));
+
+ decoder_.reset(::gpu::gles2::GLES2Decoder::Create(context_group.get()));
+ executor_.reset(new gpu::CommandExecutor(command_buffer_.get(),
+ decoder_.get(), decoder_.get()));
+ sync_point_order_data_ = gpu::SyncPointOrderData::Create();
+ sync_point_client_ = gpu_state_->sync_point_manager()->CreateSyncPointClient(
+ sync_point_order_data_, GetNamespaceID(), command_buffer_id_);
+ decoder_->set_engine(executor_.get());
+ decoder_->SetFenceSyncReleaseCallback(base::Bind(
+ &CommandBufferDriver::OnFenceSyncRelease, base::Unretained(this)));
+ decoder_->SetWaitFenceSyncCallback(base::Bind(
+ &CommandBufferDriver::OnWaitFenceSync, base::Unretained(this)));
+ decoder_->SetDescheduleUntilFinishedCallback(base::Bind(
+ &CommandBufferDriver::OnDescheduleUntilFinished, base::Unretained(this)));
+ decoder_->SetRescheduleAfterFinishedCallback(base::Bind(
+ &CommandBufferDriver::OnRescheduleAfterFinished, base::Unretained(this)));
+
+ gpu::gles2::DisallowedFeatures disallowed_features;
+
+ if (!decoder_->Initialize(surface_, context_, offscreen, disallowed_features,
+ attrib_helper))
+ return false;
+
+ command_buffer_->SetPutOffsetChangeCallback(base::Bind(
+ &gpu::CommandExecutor::PutChanged, base::Unretained(executor_.get())));
+ command_buffer_->SetGetBufferChangeCallback(base::Bind(
+ &gpu::CommandExecutor::SetGetBuffer, base::Unretained(executor_.get())));
+ command_buffer_->SetParseErrorCallback(
+ base::Bind(&CommandBufferDriver::OnParseError, base::Unretained(this)));
+
+ // TODO(piman): other callbacks
+
+ const size_t kSize = sizeof(gpu::CommandBufferSharedState);
+ std::unique_ptr<gpu::BufferBacking> backing(
+ MojoBufferBacking::Create(std::move(shared_state), kSize));
+ if (!backing)
+ return false;
+
+ command_buffer_->SetSharedStateBuffer(std::move(backing));
+ gpu_state_->driver_manager()->AddDriver(this);
+ return true;
+}
+
+void CommandBufferDriver::SetGetBuffer(int32_t buffer) {
+ DCHECK(CalledOnValidThread());
+ command_buffer_->SetGetBuffer(buffer);
+}
+
+void CommandBufferDriver::Flush(int32_t put_offset) {
+ DCHECK(CalledOnValidThread());
+ if (!MakeCurrent())
+ return;
+
+ command_buffer_->Flush(put_offset);
+ ProcessPendingAndIdleWork();
+}
+
+void CommandBufferDriver::RegisterTransferBuffer(
+ int32_t id,
+ mojo::ScopedSharedBufferHandle transfer_buffer,
+ uint32_t size) {
+ DCHECK(CalledOnValidThread());
+ // Take ownership of the memory and map it into this process.
+ // This validates the size.
+ std::unique_ptr<gpu::BufferBacking> backing(
+ MojoBufferBacking::Create(std::move(transfer_buffer), size));
+ if (!backing) {
+ DVLOG(0) << "Failed to map shared memory.";
+ return;
+ }
+ command_buffer_->RegisterTransferBuffer(id, std::move(backing));
+}
+
+void CommandBufferDriver::DestroyTransferBuffer(int32_t id) {
+ DCHECK(CalledOnValidThread());
+ command_buffer_->DestroyTransferBuffer(id);
+}
+
+void CommandBufferDriver::CreateImage(int32_t id,
+ mojo::ScopedHandle memory_handle,
+ int32_t type,
+ const gfx::Size& size,
+ int32_t format,
+ int32_t internal_format) {
+ DCHECK(CalledOnValidThread());
+ if (!MakeCurrent())
+ return;
+
+ gpu::gles2::ImageManager* image_manager = decoder_->GetImageManager();
+ if (image_manager->LookupImage(id)) {
+ LOG(ERROR) << "Image already exists with same ID.";
+ return;
+ }
+
+ gfx::BufferFormat gpu_format = static_cast<gfx::BufferFormat>(format);
+ if (!gpu::IsGpuMemoryBufferFormatSupported(gpu_format,
+ decoder_->GetCapabilities())) {
+ LOG(ERROR) << "Format is not supported.";
+ return;
+ }
+
+ if (!gpu::IsImageSizeValidForGpuMemoryBufferFormat(size, gpu_format)) {
+ LOG(ERROR) << "Invalid image size for format.";
+ return;
+ }
+
+ if (!gpu::IsImageFormatCompatibleWithGpuMemoryBufferFormat(internal_format,
+ gpu_format)) {
+ LOG(ERROR) << "Incompatible image format.";
+ return;
+ }
+
+ if (type != gfx::SHARED_MEMORY_BUFFER) {
+ NOTIMPLEMENTED();
+ return;
+ }
+
+ base::PlatformFile platform_file;
+ MojoResult unwrap_result = mojo::UnwrapPlatformFile(std::move(memory_handle),
+ &platform_file);
+ if (unwrap_result != MOJO_RESULT_OK) {
+ NOTREACHED();
+ return;
+ }
+
+#if defined(OS_WIN)
+ base::SharedMemoryHandle handle(platform_file, base::GetCurrentProcId());
+#else
+ base::FileDescriptor handle(platform_file, false);
+#endif
+
+ scoped_refptr<gl::GLImageSharedMemory> image =
+ new gl::GLImageSharedMemory(size, internal_format);
+ // TODO(jam): also need a mojo enum for this enum
+ if (!image->Initialize(
+ handle, gfx::GpuMemoryBufferId(id), gpu_format, 0,
+ gfx::RowSizeForBufferFormat(size.width(), gpu_format, 0))) {
+ NOTREACHED();
+ return;
+ }
+
+ image_manager->AddImage(image.get(), id);
+}
+
+// TODO(rjkroege): It is conceivable that this code belongs in
+// ozone_gpu_memory_buffer.cc
+void CommandBufferDriver::CreateImageNativeOzone(int32_t id,
+ int32_t type,
+ gfx::Size size,
+ gfx::BufferFormat format,
+ uint32_t internal_format,
+ ui::NativePixmap* pixmap) {
+#if defined(USE_OZONE)
+ gpu::gles2::ImageManager* image_manager = decoder_->GetImageManager();
+ if (image_manager->LookupImage(id)) {
+ LOG(ERROR) << "Image already exists with same ID.";
+ return;
+ }
+
+ scoped_refptr<gl::GLImageOzoneNativePixmap> image =
+ new gl::GLImageOzoneNativePixmap(size, internal_format);
+ if (!image->Initialize(pixmap, format)) {
+ NOTREACHED();
+ return;
+ }
+
+ image_manager->AddImage(image.get(), id);
+#endif
+}
+
+void CommandBufferDriver::DestroyImage(int32_t id) {
+ DCHECK(CalledOnValidThread());
+ gpu::gles2::ImageManager* image_manager = decoder_->GetImageManager();
+ if (!image_manager->LookupImage(id)) {
+ LOG(ERROR) << "Image with ID doesn't exist.";
+ return;
+ }
+ if (!MakeCurrent())
+ return;
+ image_manager->RemoveImage(id);
+}
+
+bool CommandBufferDriver::IsScheduled() const {
+ DCHECK(CalledOnValidThread());
+ DCHECK(executor_);
+ return executor_->scheduled();
+}
+
+bool CommandBufferDriver::HasUnprocessedCommands() const {
+ DCHECK(CalledOnValidThread());
+ if (command_buffer_) {
+ gpu::CommandBuffer::State state = GetLastState();
+ return command_buffer_->GetPutOffset() != state.get_offset &&
+ !gpu::error::IsError(state.error);
+ }
+ return false;
+}
+
+gpu::Capabilities CommandBufferDriver::GetCapabilities() const {
+ DCHECK(CalledOnValidThread());
+ return decoder_->GetCapabilities();
+}
+
+gpu::CommandBuffer::State CommandBufferDriver::GetLastState() const {
+ DCHECK(CalledOnValidThread());
+ return command_buffer_->GetLastState();
+}
+
+uint32_t CommandBufferDriver::GetUnprocessedOrderNum() const {
+ DCHECK(CalledOnValidThread());
+ return sync_point_order_data_->unprocessed_order_num();
+}
+
+uint32_t CommandBufferDriver::GetProcessedOrderNum() const {
+ DCHECK(CalledOnValidThread());
+ return sync_point_order_data_->processed_order_num();
+}
+
+bool CommandBufferDriver::MakeCurrent() {
+ DCHECK(CalledOnValidThread());
+ if (!decoder_)
+ return false;
+ if (decoder_->MakeCurrent())
+ return true;
+ DLOG(ERROR) << "Context lost because MakeCurrent failed.";
+ gpu::error::ContextLostReason reason =
+ static_cast<gpu::error::ContextLostReason>(
+ decoder_->GetContextLostReason());
+ command_buffer_->SetContextLostReason(reason);
+ command_buffer_->SetParseError(gpu::error::kLostContext);
+ OnContextLost(reason);
+ return false;
+}
+
+void CommandBufferDriver::ProcessPendingAndIdleWork() {
+ DCHECK(CalledOnValidThread());
+ executor_->ProcessPendingQueries();
+ ScheduleDelayedWork(
+ base::TimeDelta::FromMilliseconds(kHandleMoreWorkPeriodMs));
+}
+
+void CommandBufferDriver::ScheduleDelayedWork(base::TimeDelta delay) {
+ DCHECK(CalledOnValidThread());
+ const bool has_more_work =
+ executor_->HasPendingQueries() || executor_->HasMoreIdleWork();
+ if (!has_more_work) {
+ last_idle_time_ = base::TimeTicks();
+ return;
+ }
+
+ const base::TimeTicks current_time = base::TimeTicks::Now();
+ // |process_delayed_work_time_| is set if processing of delayed work is
+ // already scheduled. Just update the time if already scheduled.
+ if (!process_delayed_work_time_.is_null()) {
+ process_delayed_work_time_ = current_time + delay;
+ return;
+ }
+
+ // Idle when no messages are processed between now and when PollWork is
+ // called.
+ previous_processed_num_ =
+ gpu_state_->driver_manager()->GetProcessedOrderNum();
+
+ if (last_idle_time_.is_null())
+ last_idle_time_ = current_time;
+
+ // scheduled() returns true after passing all unschedule fences and this is
+ // when we can start performing idle work. Idle work is done synchronously
+ // so we can set delay to 0 and instead poll for more work at the rate idle
+ // work is performed. This also ensures that idle work is done as
+ // efficiently as possible without any unnecessary delays.
+ if (executor_->scheduled() && executor_->HasMoreIdleWork())
+ delay = base::TimeDelta();
+
+ process_delayed_work_time_ = current_time + delay;
+ gpu_state_->command_buffer_task_runner()->task_runner()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&CommandBufferDriver::PollWork, weak_factory_.GetWeakPtr()),
+ delay);
+}
+
+void CommandBufferDriver::PollWork() {
+ DCHECK(CalledOnValidThread());
+ // Post another delayed task if we have not yet reached the time at which
+ // we should process delayed work.
+ base::TimeTicks current_time = base::TimeTicks::Now();
+ DCHECK(!process_delayed_work_time_.is_null());
+ if (process_delayed_work_time_ > current_time) {
+ gpu_state_->command_buffer_task_runner()->task_runner()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&CommandBufferDriver::PollWork, weak_factory_.GetWeakPtr()),
+ process_delayed_work_time_ - current_time);
+ return;
+ }
+ process_delayed_work_time_ = base::TimeTicks();
+ PerformWork();
+}
+
+void CommandBufferDriver::PerformWork() {
+ DCHECK(CalledOnValidThread());
+ if (!MakeCurrent())
+ return;
+
+ if (executor_) {
+ const uint32_t current_unprocessed_num =
+ gpu_state_->driver_manager()->GetUnprocessedOrderNum();
+ // We're idle when no messages were processed or scheduled.
+ bool is_idle = (previous_processed_num_ == current_unprocessed_num);
+ if (!is_idle && !last_idle_time_.is_null()) {
+ base::TimeDelta time_since_idle =
+ base::TimeTicks::Now() - last_idle_time_;
+ base::TimeDelta max_time_since_idle =
+ base::TimeDelta::FromMilliseconds(kMaxTimeSinceIdleMs);
+ // Force idle when it's been too long since last time we were idle.
+ if (time_since_idle > max_time_since_idle)
+ is_idle = true;
+ }
+
+ if (is_idle) {
+ last_idle_time_ = base::TimeTicks::Now();
+ executor_->PerformIdleWork();
+ }
+ executor_->ProcessPendingQueries();
+ }
+
+ ScheduleDelayedWork(
+ base::TimeDelta::FromMilliseconds(kHandleMoreWorkPeriodBusyMs));
+}
+
+void CommandBufferDriver::DestroyDecoder() {
+ DCHECK(CalledOnValidThread());
+ if (decoder_) {
+ gpu_state_->driver_manager()->RemoveDriver(this);
+ bool have_context = decoder_->MakeCurrent();
+ decoder_->Destroy(have_context);
+ decoder_.reset();
+ }
+}
+
+void CommandBufferDriver::OnUpdateVSyncParameters(
+ const base::TimeTicks timebase,
+ const base::TimeDelta interval) {
+ DCHECK(CalledOnValidThread());
+ if (client_)
+ client_->UpdateVSyncParameters(timebase, interval);
+}
+
+void CommandBufferDriver::OnFenceSyncRelease(uint64_t release) {
+ DCHECK(CalledOnValidThread());
+ if (!sync_point_client_->client_state()->IsFenceSyncReleased(release))
+ sync_point_client_->ReleaseFenceSync(release);
+}
+
+bool CommandBufferDriver::OnWaitFenceSync(
+ gpu::CommandBufferNamespace namespace_id,
+ gpu::CommandBufferId command_buffer_id,
+ uint64_t release) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(IsScheduled());
+ gpu::SyncPointManager* sync_point_manager = gpu_state_->sync_point_manager();
+ DCHECK(sync_point_manager);
+
+ scoped_refptr<gpu::SyncPointClientState> release_state =
+ sync_point_manager->GetSyncPointClientState(namespace_id,
+ command_buffer_id);
+
+ if (!release_state)
+ return true;
+
+ executor_->SetScheduled(false);
+ sync_point_client_->Wait(release_state.get(), release,
+ base::Bind(&gpu::CommandExecutor::SetScheduled,
+ executor_->AsWeakPtr(), true));
+ return executor_->scheduled();
+}
+
+void CommandBufferDriver::OnDescheduleUntilFinished() {
+ DCHECK(CalledOnValidThread());
+ DCHECK(IsScheduled());
+ DCHECK(executor_->HasMoreIdleWork());
+
+ executor_->SetScheduled(false);
+}
+
+void CommandBufferDriver::OnRescheduleAfterFinished() {
+ DCHECK(CalledOnValidThread());
+ DCHECK(!executor_->scheduled());
+
+ executor_->SetScheduled(true);
+}
+
+void CommandBufferDriver::OnParseError() {
+ DCHECK(CalledOnValidThread());
+ gpu::CommandBuffer::State state = GetLastState();
+ OnContextLost(state.context_lost_reason);
+}
+
+void CommandBufferDriver::OnContextLost(uint32_t reason) {
+ DCHECK(CalledOnValidThread());
+ if (client_)
+ client_->DidLoseContext(reason);
+}
+
+void CommandBufferDriver::SignalQuery(uint32_t query_id,
+ const base::Closure& callback) {
+ DCHECK(CalledOnValidThread());
+
+ gpu::gles2::QueryManager* query_manager = decoder_->GetQueryManager();
+ gpu::gles2::QueryManager::Query* query = query_manager->GetQuery(query_id);
+ if (query)
+ query->AddCallback(callback);
+ else
+ callback.Run();
+}
+
+void CommandBufferDriver::OnGpuCompletedSwapBuffers(gfx::SwapResult result) {
+ DCHECK(CalledOnValidThread());
+ if (client_) {
+ client_->OnGpuCompletedSwapBuffers(result);
+ }
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/gles2/command_buffer_driver.h b/chromium/components/mus/gles2/command_buffer_driver.h
new file mode 100644
index 00000000000..bb08ee255fe
--- /dev/null
+++ b/chromium/components/mus/gles2/command_buffer_driver.h
@@ -0,0 +1,173 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_GLES2_COMMAND_BUFFER_DRIVER_H_
+#define COMPONENTS_MUS_GLES2_COMMAND_BUFFER_DRIVER_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/time/time.h"
+#include "gpu/command_buffer/common/capabilities.h"
+#include "gpu/command_buffer/common/command_buffer.h"
+#include "gpu/command_buffer/common/command_buffer_id.h"
+#include "gpu/command_buffer/common/constants.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/system/buffer.h"
+#include "ui/gfx/buffer_types.h"
+#include "ui/gfx/geometry/mojo/geometry.mojom.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/swap_result.h"
+
+namespace gl {
+class GLContext;
+class GLSurface;
+}
+
+namespace gpu {
+class CommandBufferService;
+class CommandExecutor;
+class SyncPointClient;
+class SyncPointOrderData;
+
+namespace gles2 {
+class GLES2Decoder;
+} // namespace gles2
+
+} // namespace gpu
+
+namespace ui {
+class NativePixmap;
+}
+
+namespace mus {
+
+class GpuState;
+
+// This class receives CommandBuffer messages on the same thread as the native
+// viewport.
+class CommandBufferDriver : base::NonThreadSafe {
+ public:
+ class Client {
+ public:
+ virtual ~Client();
+ virtual void DidLoseContext(uint32_t reason) = 0;
+ virtual void UpdateVSyncParameters(const base::TimeTicks& timebase,
+ const base::TimeDelta& interval) = 0;
+ virtual void OnGpuCompletedSwapBuffers(gfx::SwapResult result) = 0;
+ };
+ CommandBufferDriver(gpu::CommandBufferNamespace command_buffer_namespace,
+ gpu::CommandBufferId command_buffer_id,
+ gfx::AcceleratedWidget widget,
+ scoped_refptr<GpuState> gpu_state);
+ ~CommandBufferDriver();
+
+ // The class owning the CommandBufferDriver instance (e.g. CommandBufferLocal)
+ // is itself the Client implementation so CommandBufferDriver does not own the
+ // client.
+ void set_client(Client* client) { client_ = client; }
+
+ bool Initialize(mojo::ScopedSharedBufferHandle shared_state,
+ mojo::Array<int32_t> attribs);
+ void SetGetBuffer(int32_t buffer);
+ void Flush(int32_t put_offset);
+ void RegisterTransferBuffer(int32_t id,
+ mojo::ScopedSharedBufferHandle transfer_buffer,
+ uint32_t size);
+ void DestroyTransferBuffer(int32_t id);
+ void CreateImage(int32_t id,
+ mojo::ScopedHandle memory_handle,
+ int32_t type,
+ const gfx::Size& size,
+ int32_t format,
+ int32_t internal_format);
+ void CreateImageNativeOzone(int32_t id,
+ int32_t type,
+ gfx::Size size,
+ gfx::BufferFormat format,
+ uint32_t internal_format,
+ ui::NativePixmap* pixmap);
+ void DestroyImage(int32_t id);
+ bool IsScheduled() const;
+ bool HasUnprocessedCommands() const;
+ gpu::Capabilities GetCapabilities() const;
+ gpu::CommandBuffer::State GetLastState() const;
+ gpu::CommandBufferNamespace GetNamespaceID() const {
+ return command_buffer_namespace_;
+ }
+ gpu::CommandBufferId GetCommandBufferID() const { return command_buffer_id_; }
+ gpu::SyncPointOrderData* sync_point_order_data() {
+ return sync_point_order_data_.get();
+ }
+ uint32_t GetUnprocessedOrderNum() const;
+ uint32_t GetProcessedOrderNum() const;
+ void SignalQuery(uint32_t query_id, const base::Closure& callback);
+
+ private:
+ bool MakeCurrent();
+
+ // Process pending queries and call |ScheduleDelayedWork| to schedule
+ // processing of delayed work.
+ void ProcessPendingAndIdleWork();
+
+ // Schedule processing of delayed work. This updates the time at which
+ // delayed work should be processed. |process_delayed_work_time_| is
+ // updated to current time + delay. Call this after processing some amount
+ // of delayed work.
+ void ScheduleDelayedWork(base::TimeDelta delay);
+
+ // Poll the command buffer to execute work.
+ void PollWork();
+ void PerformWork();
+
+ void DestroyDecoder();
+
+ // Callbacks:
+ void OnUpdateVSyncParameters(const base::TimeTicks timebase,
+ const base::TimeDelta interval);
+ void OnFenceSyncRelease(uint64_t release);
+ bool OnWaitFenceSync(gpu::CommandBufferNamespace namespace_id,
+ gpu::CommandBufferId command_buffer_id,
+ uint64_t release);
+ void OnDescheduleUntilFinished();
+ void OnRescheduleAfterFinished();
+ void OnParseError();
+ void OnContextLost(uint32_t reason);
+ void OnGpuCompletedSwapBuffers(gfx::SwapResult result);
+
+ const gpu::CommandBufferNamespace command_buffer_namespace_;
+ const gpu::CommandBufferId command_buffer_id_;
+ gfx::AcceleratedWidget widget_;
+ Client* client_; // NOT OWNED.
+ std::unique_ptr<gpu::CommandBufferService> command_buffer_;
+ std::unique_ptr<gpu::gles2::GLES2Decoder> decoder_;
+ std::unique_ptr<gpu::CommandExecutor> executor_;
+ scoped_refptr<gpu::SyncPointOrderData> sync_point_order_data_;
+ std::unique_ptr<gpu::SyncPointClient> sync_point_client_;
+ scoped_refptr<gl::GLContext> context_;
+ scoped_refptr<gl::GLSurface> surface_;
+ scoped_refptr<GpuState> gpu_state_;
+
+ scoped_refptr<base::SingleThreadTaskRunner> context_lost_task_runner_;
+ base::Callback<void(int32_t)> context_lost_callback_;
+
+ base::TimeTicks process_delayed_work_time_;
+ uint32_t previous_processed_num_;
+ base::TimeTicks last_idle_time_;
+
+ base::WeakPtrFactory<CommandBufferDriver> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(CommandBufferDriver);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_GLES2_COMMAND_BUFFER_DRIVER_H_
diff --git a/chromium/components/mus/gles2/command_buffer_driver_manager.cc b/chromium/components/mus/gles2/command_buffer_driver_manager.cc
new file mode 100644
index 00000000000..c8ff3b95a2f
--- /dev/null
+++ b/chromium/components/mus/gles2/command_buffer_driver_manager.cc
@@ -0,0 +1,50 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/gles2/command_buffer_driver_manager.h"
+
+#include <algorithm>
+
+#include "components/mus/gles2/command_buffer_driver.h"
+
+namespace mus {
+
+CommandBufferDriverManager::CommandBufferDriverManager() {}
+
+CommandBufferDriverManager::~CommandBufferDriverManager() {}
+
+void CommandBufferDriverManager::AddDriver(CommandBufferDriver* driver) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(std::find(drivers_.begin(), drivers_.end(), driver) == drivers_.end());
+ drivers_.push_back(driver);
+}
+
+void CommandBufferDriverManager::RemoveDriver(CommandBufferDriver* driver) {
+ DCHECK(CalledOnValidThread());
+ auto it = std::find(drivers_.begin(), drivers_.end(), driver);
+ DCHECK(it != drivers_.end());
+ drivers_.erase(it);
+}
+
+uint32_t CommandBufferDriverManager::GetUnprocessedOrderNum() const {
+ DCHECK(CalledOnValidThread());
+ uint32_t unprocessed_order_num = 0;
+ for (auto& d : drivers_) {
+ unprocessed_order_num =
+ std::max(unprocessed_order_num, d->GetUnprocessedOrderNum());
+ }
+ return unprocessed_order_num;
+}
+
+uint32_t CommandBufferDriverManager::GetProcessedOrderNum() const {
+ DCHECK(CalledOnValidThread());
+ uint32_t processed_order_num = 0;
+ for (auto& d : drivers_) {
+ processed_order_num =
+ std::max(processed_order_num, d->GetProcessedOrderNum());
+ }
+ return processed_order_num;
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/gles2/command_buffer_driver_manager.h b/chromium/components/mus/gles2/command_buffer_driver_manager.h
new file mode 100644
index 00000000000..b2f8e7c0900
--- /dev/null
+++ b/chromium/components/mus/gles2/command_buffer_driver_manager.h
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_GLES2_COMMAND_BUFFER_DRIVER_MANAGER_H_
+#define COMPONENTS_MUS_GLES2_COMMAND_BUFFER_DRIVER_MANAGER_H_
+
+#include <stdint.h>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/threading/non_thread_safe.h"
+
+namespace mus {
+
+class CommandBufferDriver;
+
+// This class manages all initialized |CommandBufferDriver|s.
+class CommandBufferDriverManager : base::NonThreadSafe {
+ public:
+ CommandBufferDriverManager();
+ ~CommandBufferDriverManager();
+
+ // Add a new initialized driver to the manager.
+ void AddDriver(CommandBufferDriver* driver);
+
+ // Remove a driver from the manager.
+ void RemoveDriver(CommandBufferDriver* driver);
+
+ // Return the global order number for the last unprocessed flush
+ // (|CommandBufferDriver::Flush|).
+ uint32_t GetUnprocessedOrderNum() const;
+
+ // Return the global order number for the last processed flush
+ // (|CommandBufferDriver::Flush|).
+ uint32_t GetProcessedOrderNum() const;
+
+ private:
+ std::vector<CommandBufferDriver*> drivers_;
+
+ DISALLOW_COPY_AND_ASSIGN(CommandBufferDriverManager);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_GLES2_COMMAND_BUFFER_DRIVER_MANAGER_H_
diff --git a/chromium/components/mus/gles2/command_buffer_impl.cc b/chromium/components/mus/gles2/command_buffer_impl.cc
new file mode 100644
index 00000000000..70de3c2aab6
--- /dev/null
+++ b/chromium/components/mus/gles2/command_buffer_impl.cc
@@ -0,0 +1,301 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/gles2/command_buffer_impl.h"
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "components/mus/common/gpu_type_converters.h"
+#include "components/mus/gles2/command_buffer_driver.h"
+#include "components/mus/gles2/gpu_state.h"
+#include "gpu/command_buffer/service/sync_point_manager.h"
+
+namespace mus {
+
+namespace {
+
+uint64_t g_next_command_buffer_id = 0;
+
+void RunInitializeCallback(
+ const mojom::CommandBuffer::InitializeCallback& callback,
+ mojom::CommandBufferInitializeResultPtr result) {
+ callback.Run(std::move(result));
+}
+
+void RunMakeProgressCallback(
+ const mojom::CommandBuffer::MakeProgressCallback& callback,
+ const gpu::CommandBuffer::State& state) {
+ callback.Run(state);
+}
+
+} // namespace
+
+CommandBufferImpl::CommandBufferImpl(
+ mojo::InterfaceRequest<mus::mojom::CommandBuffer> request,
+ scoped_refptr<GpuState> gpu_state)
+ : gpu_state_(gpu_state) {
+ // Bind |CommandBufferImpl| to the |request| in the GPU control thread.
+ gpu_state_->control_task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&CommandBufferImpl::BindToRequest,
+ base::Unretained(this), base::Passed(&request)));
+}
+
+void CommandBufferImpl::DidLoseContext(uint32_t reason) {
+ driver_->set_client(nullptr);
+ client_->Destroyed(reason, gpu::error::kLostContext);
+}
+
+void CommandBufferImpl::UpdateVSyncParameters(const base::TimeTicks& timebase,
+ const base::TimeDelta& interval) {
+}
+
+void CommandBufferImpl::OnGpuCompletedSwapBuffers(gfx::SwapResult result) {}
+
+CommandBufferImpl::~CommandBufferImpl() {
+}
+
+void CommandBufferImpl::Initialize(
+ mus::mojom::CommandBufferClientPtr client,
+ mojo::ScopedSharedBufferHandle shared_state,
+ mojo::Array<int32_t> attribs,
+ const mojom::CommandBuffer::InitializeCallback& callback) {
+ gpu_state_->command_buffer_task_runner()->task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&CommandBufferImpl::InitializeOnGpuThread,
+ base::Unretained(this), base::Passed(&client),
+ base::Passed(&shared_state), base::Passed(&attribs),
+ base::Bind(&RunInitializeCallback, callback)));
+}
+
+void CommandBufferImpl::SetGetBuffer(int32_t buffer) {
+ gpu_state_->command_buffer_task_runner()->PostTask(
+ driver_.get(), base::Bind(&CommandBufferImpl::SetGetBufferOnGpuThread,
+ base::Unretained(this), buffer));
+}
+
+void CommandBufferImpl::Flush(int32_t put_offset) {
+ gpu::SyncPointManager* sync_point_manager = gpu_state_->sync_point_manager();
+ const uint32_t order_num = driver_->sync_point_order_data()
+ ->GenerateUnprocessedOrderNumber(sync_point_manager);
+ gpu_state_->command_buffer_task_runner()->PostTask(
+ driver_.get(), base::Bind(&CommandBufferImpl::FlushOnGpuThread,
+ base::Unretained(this), put_offset, order_num));
+}
+
+void CommandBufferImpl::MakeProgress(
+ int32_t last_get_offset,
+ const mojom::CommandBuffer::MakeProgressCallback& callback) {
+ gpu_state_->command_buffer_task_runner()->PostTask(
+ driver_.get(), base::Bind(&CommandBufferImpl::MakeProgressOnGpuThread,
+ base::Unretained(this), last_get_offset,
+ base::Bind(&RunMakeProgressCallback,
+ callback)));
+}
+
+void CommandBufferImpl::RegisterTransferBuffer(
+ int32_t id,
+ mojo::ScopedSharedBufferHandle transfer_buffer,
+ uint32_t size) {
+ gpu_state_->command_buffer_task_runner()->PostTask(
+ driver_.get(),
+ base::Bind(&CommandBufferImpl::RegisterTransferBufferOnGpuThread,
+ base::Unretained(this), id, base::Passed(&transfer_buffer),
+ size));
+}
+
+void CommandBufferImpl::DestroyTransferBuffer(int32_t id) {
+ gpu_state_->command_buffer_task_runner()->PostTask(
+ driver_.get(),
+ base::Bind(&CommandBufferImpl::DestroyTransferBufferOnGpuThread,
+ base::Unretained(this), id));
+}
+
+void CommandBufferImpl::CreateImage(int32_t id,
+ mojo::ScopedHandle memory_handle,
+ int32_t type,
+ const gfx::Size& size,
+ int32_t format,
+ int32_t internal_format) {
+ gpu_state_->command_buffer_task_runner()->PostTask(
+ driver_.get(),
+ base::Bind(&CommandBufferImpl::CreateImageOnGpuThread,
+ base::Unretained(this), id, base::Passed(&memory_handle), type,
+ size, format, internal_format));
+}
+
+void CommandBufferImpl::DestroyImage(int32_t id) {
+ gpu_state_->command_buffer_task_runner()->PostTask(
+ driver_.get(), base::Bind(&CommandBufferImpl::DestroyImageOnGpuThread,
+ base::Unretained(this), id));
+}
+
+void CommandBufferImpl::CreateStreamTexture(
+ uint32_t client_texture_id,
+ const mojom::CommandBuffer::CreateStreamTextureCallback& callback) {
+ NOTIMPLEMENTED();
+}
+
+void CommandBufferImpl::TakeFrontBuffer(const gpu::Mailbox& mailbox) {
+ NOTIMPLEMENTED();
+}
+
+void CommandBufferImpl::ReturnFrontBuffer(const gpu::Mailbox& mailbox,
+ bool is_lost) {
+ NOTIMPLEMENTED();
+}
+
+void CommandBufferImpl::SignalQuery(uint32_t query, uint32_t signal_id) {
+ NOTIMPLEMENTED();
+}
+
+void CommandBufferImpl::SignalSyncToken(const gpu::SyncToken& sync_token,
+ uint32_t signal_id) {
+ NOTIMPLEMENTED();
+}
+
+void CommandBufferImpl::WaitForGetOffsetInRange(
+ int32_t start, int32_t end,
+ const mojom::CommandBuffer::WaitForGetOffsetInRangeCallback& callback) {
+ NOTIMPLEMENTED();
+}
+
+void CommandBufferImpl::WaitForTokenInRange(
+ int32_t start, int32_t end,
+ const mojom::CommandBuffer::WaitForGetOffsetInRangeCallback& callback) {
+ NOTIMPLEMENTED();
+}
+
+void CommandBufferImpl::BindToRequest(
+ mojo::InterfaceRequest<mus::mojom::CommandBuffer> request) {
+ binding_.reset(
+ new mojo::Binding<mus::mojom::CommandBuffer>(this, std::move(request)));
+ binding_->set_connection_error_handler(
+ base::Bind(&CommandBufferImpl::OnConnectionError,
+ base::Unretained(this)));
+}
+
+void CommandBufferImpl::InitializeOnGpuThread(
+ mojom::CommandBufferClientPtr client,
+ mojo::ScopedSharedBufferHandle shared_state,
+ mojo::Array<int32_t> attribs,
+ const base::Callback<
+ void(mojom::CommandBufferInitializeResultPtr)>& callback) {
+ DCHECK(!driver_);
+ driver_.reset(new CommandBufferDriver(
+ gpu::CommandBufferNamespace::MOJO,
+ gpu::CommandBufferId::FromUnsafeValue(++g_next_command_buffer_id),
+ gfx::kNullAcceleratedWidget, gpu_state_));
+ driver_->set_client(this);
+ client_ = mojo::MakeProxy(client.PassInterface());
+ bool result =
+ driver_->Initialize(std::move(shared_state), std::move(attribs));
+ mojom::CommandBufferInitializeResultPtr initialize_result;
+ if (result) {
+ initialize_result = mojom::CommandBufferInitializeResult::New();
+ initialize_result->command_buffer_namespace = driver_->GetNamespaceID();
+ initialize_result->command_buffer_id =
+ driver_->GetCommandBufferID().GetUnsafeValue();
+ initialize_result->capabilities = driver_->GetCapabilities();
+ }
+ gpu_state_->control_task_runner()->PostTask(
+ FROM_HERE, base::Bind(callback, base::Passed(&initialize_result)));
+}
+
+bool CommandBufferImpl::SetGetBufferOnGpuThread(int32_t buffer) {
+ DCHECK(driver_->IsScheduled());
+ driver_->SetGetBuffer(buffer);
+ return true;
+}
+
+bool CommandBufferImpl::FlushOnGpuThread(int32_t put_offset,
+ uint32_t order_num) {
+ DCHECK(driver_->IsScheduled());
+ driver_->sync_point_order_data()->BeginProcessingOrderNumber(order_num);
+ driver_->Flush(put_offset);
+
+ // Return false if the Flush is not finished, so the CommandBufferTaskRunner
+ // will not remove this task from the task queue.
+ const bool complete = !driver_->HasUnprocessedCommands();
+ if (!complete)
+ driver_->sync_point_order_data()->PauseProcessingOrderNumber(order_num);
+ else
+ driver_->sync_point_order_data()->FinishProcessingOrderNumber(order_num);
+ return complete;
+}
+
+bool CommandBufferImpl::MakeProgressOnGpuThread(
+ int32_t last_get_offset,
+ const base::Callback<void(const gpu::CommandBuffer::State&)>& callback) {
+ DCHECK(driver_->IsScheduled());
+ gpu_state_->control_task_runner()->PostTask(
+ FROM_HERE, base::Bind(callback, driver_->GetLastState()));
+ return true;
+}
+
+bool CommandBufferImpl::RegisterTransferBufferOnGpuThread(
+ int32_t id,
+ mojo::ScopedSharedBufferHandle transfer_buffer,
+ uint32_t size) {
+ DCHECK(driver_->IsScheduled());
+ driver_->RegisterTransferBuffer(id, std::move(transfer_buffer), size);
+ return true;
+}
+
+bool CommandBufferImpl::DestroyTransferBufferOnGpuThread(int32_t id) {
+ DCHECK(driver_->IsScheduled());
+ driver_->DestroyTransferBuffer(id);
+ return true;
+}
+
+bool CommandBufferImpl::CreateImageOnGpuThread(int32_t id,
+ mojo::ScopedHandle memory_handle,
+ int32_t type,
+ const gfx::Size& size,
+ int32_t format,
+ int32_t internal_format) {
+ DCHECK(driver_->IsScheduled());
+ driver_->CreateImage(id, std::move(memory_handle), type, std::move(size),
+ format, internal_format);
+ return true;
+}
+
+bool CommandBufferImpl::DestroyImageOnGpuThread(int32_t id) {
+ DCHECK(driver_->IsScheduled());
+ driver_->DestroyImage(id);
+ return true;
+}
+
+void CommandBufferImpl::OnConnectionError() {
+ // OnConnectionError() is called on the control thread |control_task_runner|.
+
+ // Before deleting, we need to delete |binding_| because it is bound to the
+ // current thread (|control_task_runner|).
+ binding_.reset();
+
+ // Objects we own (such as CommandBufferDriver) need to be destroyed on the
+ // thread we were created on. It's entirely possible we haven't or are in the
+ // process of creating |driver_|.
+ if (driver_) {
+ gpu_state_->command_buffer_task_runner()->PostTask(
+ driver_.get(), base::Bind(&CommandBufferImpl::DeleteOnGpuThread,
+ base::Unretained(this)));
+ } else {
+ gpu_state_->command_buffer_task_runner()->task_runner()->PostTask(
+ FROM_HERE, base::Bind(&CommandBufferImpl::DeleteOnGpuThread2,
+ base::Unretained(this)));
+ }
+}
+
+bool CommandBufferImpl::DeleteOnGpuThread() {
+ delete this;
+ return true;
+}
+
+void CommandBufferImpl::DeleteOnGpuThread2() {
+ delete this;
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/gles2/command_buffer_impl.h b/chromium/components/mus/gles2/command_buffer_impl.h
new file mode 100644
index 00000000000..a37f2d1ec43
--- /dev/null
+++ b/chromium/components/mus/gles2/command_buffer_impl.h
@@ -0,0 +1,126 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_GLES2_COMMAND_BUFFER_IMPL_H_
+#define COMPONENTS_MUS_GLES2_COMMAND_BUFFER_IMPL_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "components/mus/gles2/command_buffer_driver.h"
+#include "components/mus/public/interfaces/command_buffer.mojom.h"
+#include "gpu/command_buffer/common/command_buffer.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace mus {
+
+class CommandBufferDriver;
+class GpuState;
+
+// This class listens to the CommandBuffer message pipe on a low-latency thread
+// so that we can insert sync points without blocking on the GL driver. It
+// forwards most method calls to the CommandBufferDriver, which runs on the
+// same thread as the native viewport.
+class CommandBufferImpl : public mojom::CommandBuffer,
+ public CommandBufferDriver::Client {
+ public:
+ CommandBufferImpl(mojo::InterfaceRequest<CommandBuffer> request,
+ scoped_refptr<GpuState> gpu_state);
+
+ private:
+ class CommandBufferDriverClientImpl;
+ ~CommandBufferImpl() override;
+
+ // CommandBufferDriver::Client. All called on the GPU thread.
+ void DidLoseContext(uint32_t reason) override;
+ void UpdateVSyncParameters(const base::TimeTicks& timebase,
+ const base::TimeDelta& interval) override;
+ void OnGpuCompletedSwapBuffers(gfx::SwapResult result) override;
+
+ // mojom::CommandBuffer:
+ void Initialize(
+ mojom::CommandBufferClientPtr client,
+ mojo::ScopedSharedBufferHandle shared_state,
+ mojo::Array<int32_t> attribs,
+ const mojom::CommandBuffer::InitializeCallback& callback) override;
+ void SetGetBuffer(int32_t buffer) override;
+ void Flush(int32_t put_offset) override;
+ void MakeProgress(
+ int32_t last_get_offset,
+ const mojom::CommandBuffer::MakeProgressCallback& callback) override;
+ void RegisterTransferBuffer(int32_t id,
+ mojo::ScopedSharedBufferHandle transfer_buffer,
+ uint32_t size) override;
+ void DestroyTransferBuffer(int32_t id) override;
+ void CreateImage(int32_t id,
+ mojo::ScopedHandle memory_handle,
+ int32_t type,
+ const gfx::Size& size,
+ int32_t format,
+ int32_t internal_format) override;
+ void DestroyImage(int32_t id) override;
+ void CreateStreamTexture(
+ uint32_t client_texture_id,
+ const mojom::CommandBuffer::CreateStreamTextureCallback& callback
+ ) override;
+ void TakeFrontBuffer(const gpu::Mailbox& mailbox) override;
+ void ReturnFrontBuffer(const gpu::Mailbox& mailbox, bool is_lost) override;
+ void SignalQuery(uint32_t query, uint32_t signal_id) override;
+ void SignalSyncToken(const gpu::SyncToken& sync_token,
+ uint32_t signal_id) override;
+ void WaitForGetOffsetInRange(
+ int32_t start, int32_t end,
+ const mojom::CommandBuffer::WaitForGetOffsetInRangeCallback& callback
+ ) override;
+ void WaitForTokenInRange(
+ int32_t start, int32_t end,
+ const mojom::CommandBuffer::WaitForGetOffsetInRangeCallback& callback
+ ) override;
+
+ // All helper functions are called in the GPU therad.
+ void InitializeOnGpuThread(
+ mojom::CommandBufferClientPtr client,
+ mojo::ScopedSharedBufferHandle shared_state,
+ mojo::Array<int32_t> attribs,
+ const base::Callback<
+ void(mojom::CommandBufferInitializeResultPtr)>& callback);
+ bool SetGetBufferOnGpuThread(int32_t buffer);
+ bool FlushOnGpuThread(int32_t put_offset, uint32_t order_num);
+ bool MakeProgressOnGpuThread(
+ int32_t last_get_offset,
+ const base::Callback<void(const gpu::CommandBuffer::State&)>& callback);
+ bool RegisterTransferBufferOnGpuThread(
+ int32_t id,
+ mojo::ScopedSharedBufferHandle transfer_buffer,
+ uint32_t size);
+ bool DestroyTransferBufferOnGpuThread(int32_t id);
+ bool CreateImageOnGpuThread(int32_t id,
+ mojo::ScopedHandle memory_handle,
+ int32_t type,
+ const gfx::Size& size,
+ int32_t format,
+ int32_t internal_format);
+ bool DestroyImageOnGpuThread(int32_t id);
+
+ void BindToRequest(mojo::InterfaceRequest<CommandBuffer> request);
+
+ void OnConnectionError();
+ bool DeleteOnGpuThread();
+ void DeleteOnGpuThread2();
+
+ scoped_refptr<GpuState> gpu_state_;
+ std::unique_ptr<CommandBufferDriver> driver_;
+ std::unique_ptr<mojo::Binding<CommandBuffer>> binding_;
+ mojom::CommandBufferClientPtr client_;
+
+ DISALLOW_COPY_AND_ASSIGN(CommandBufferImpl);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_GLES2_COMMAND_BUFFER_IMPL_H_
diff --git a/chromium/components/mus/gles2/command_buffer_local.cc b/chromium/components/mus/gles2/command_buffer_local.cc
new file mode 100644
index 00000000000..434fb551525
--- /dev/null
+++ b/chromium/components/mus/gles2/command_buffer_local.cc
@@ -0,0 +1,576 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/gles2/command_buffer_local.h"
+
+#include "base/atomic_sequence_num.h"
+#include "base/bind.h"
+#include "base/memory/shared_memory.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/mus/common/gpu_type_converters.h"
+#include "components/mus/common/mojo_buffer_backing.h"
+#include "components/mus/common/mojo_gpu_memory_buffer.h"
+#include "components/mus/gles2/command_buffer_driver.h"
+#include "components/mus/gles2/command_buffer_local_client.h"
+#include "components/mus/gles2/gpu_memory_tracker.h"
+#include "components/mus/gles2/gpu_state.h"
+#include "gpu/command_buffer/client/gpu_control_client.h"
+#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
+#include "gpu/command_buffer/common/sync_token.h"
+#include "gpu/command_buffer/service/command_buffer_service.h"
+#include "gpu/command_buffer/service/context_group.h"
+#include "gpu/command_buffer/service/image_manager.h"
+#include "gpu/command_buffer/service/memory_tracking.h"
+#include "gpu/command_buffer/service/shader_translator_cache.h"
+#include "gpu/command_buffer/service/transfer_buffer_manager.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+#include "ui/gfx/buffer_format_util.h"
+#include "ui/gfx/vsync_provider.h"
+#include "ui/gl/gl_context.h"
+#include "ui/gl/gl_image_shared_memory.h"
+#include "ui/gl/gl_surface.h"
+
+namespace mus {
+
+namespace {
+
+uint64_t g_next_command_buffer_id = 0;
+
+bool CreateAndMapSharedBuffer(size_t size,
+ mojo::ScopedSharedBufferMapping* mapping,
+ mojo::ScopedSharedBufferHandle* handle) {
+ *handle = mojo::SharedBufferHandle::Create(size);
+ if (!handle->is_valid())
+ return false;
+
+ *mapping = (*handle)->Map(size);
+ if (!*mapping)
+ return false;
+
+ return true;
+}
+
+void PostTask(const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
+ const base::Closure& callback) {
+ task_runner->PostTask(FROM_HERE, callback);
+}
+}
+
+const unsigned int GL_READ_WRITE_CHROMIUM = 0x78F2;
+
+CommandBufferLocal::CommandBufferLocal(CommandBufferLocalClient* client,
+ gfx::AcceleratedWidget widget,
+ const scoped_refptr<GpuState>& gpu_state)
+ : widget_(widget),
+ gpu_state_(gpu_state),
+ client_(client),
+ client_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ gpu_control_client_(nullptr),
+ next_transfer_buffer_id_(0),
+ next_image_id_(0),
+ next_fence_sync_release_(1),
+ flushed_fence_sync_release_(0),
+ lost_context_(false),
+ sync_point_client_waiter_(
+ gpu_state->sync_point_manager()->CreateSyncPointClientWaiter()),
+ weak_factory_(this) {
+ weak_ptr_ = weak_factory_.GetWeakPtr();
+}
+
+void CommandBufferLocal::Destroy() {
+ DCHECK(CalledOnValidThread());
+ // After this |Destroy()| call, this object will not be used by client anymore
+ // and it will be deleted on the GPU thread. So we have to detach it from the
+ // client thread first.
+ DetachFromThread();
+
+ weak_factory_.InvalidateWeakPtrs();
+ // CommandBufferLocal is initialized on the GPU thread with
+ // InitializeOnGpuThread(), so we need delete memebers on the GPU thread
+ // too. Additionally we need to make sure we are deleted before returning,
+ // otherwise we may attempt to use the AcceleratedWidget which has since been
+ // destroyed.
+ base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+ gpu_state_->command_buffer_task_runner()->PostTask(
+ driver_.get(), base::Bind(&CommandBufferLocal::DeleteOnGpuThread,
+ base::Unretained(this), &event));
+ event.Wait();
+}
+
+bool CommandBufferLocal::Initialize() {
+ DCHECK(CalledOnValidThread());
+ base::ThreadRestrictions::ScopedAllowWait allow_wait;
+ base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+ bool result = false;
+ gpu_state_->command_buffer_task_runner()->task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&CommandBufferLocal::InitializeOnGpuThread,
+ base::Unretained(this), base::Unretained(&event),
+ base::Unretained(&result)));
+ event.Wait();
+ return result;
+}
+
+gpu::CommandBuffer::State CommandBufferLocal::GetLastState() {
+ DCHECK(CalledOnValidThread());
+ return last_state_;
+}
+
+int32_t CommandBufferLocal::GetLastToken() {
+ DCHECK(CalledOnValidThread());
+ TryUpdateState();
+ return last_state_.token;
+}
+
+void CommandBufferLocal::Flush(int32_t put_offset) {
+ DCHECK(CalledOnValidThread());
+ if (last_put_offset_ == put_offset)
+ return;
+
+ last_put_offset_ = put_offset;
+ gpu::SyncPointManager* sync_point_manager = gpu_state_->sync_point_manager();
+ const uint32_t order_num =
+ driver_->sync_point_order_data()->GenerateUnprocessedOrderNumber(
+ sync_point_manager);
+ gpu_state_->command_buffer_task_runner()->PostTask(
+ driver_.get(), base::Bind(&CommandBufferLocal::FlushOnGpuThread,
+ base::Unretained(this), put_offset, order_num));
+ flushed_fence_sync_release_ = next_fence_sync_release_ - 1;
+}
+
+void CommandBufferLocal::OrderingBarrier(int32_t put_offset) {
+ DCHECK(CalledOnValidThread());
+ // TODO(penghuang): Implement this more efficiently.
+ Flush(put_offset);
+}
+
+void CommandBufferLocal::WaitForTokenInRange(int32_t start, int32_t end) {
+ DCHECK(CalledOnValidThread());
+ TryUpdateState();
+ while (!InRange(start, end, last_state_.token) &&
+ last_state_.error == gpu::error::kNoError) {
+ MakeProgressAndUpdateState();
+ }
+}
+
+void CommandBufferLocal::WaitForGetOffsetInRange(int32_t start, int32_t end) {
+ DCHECK(CalledOnValidThread());
+ TryUpdateState();
+ while (!InRange(start, end, last_state_.get_offset) &&
+ last_state_.error == gpu::error::kNoError) {
+ MakeProgressAndUpdateState();
+ }
+}
+
+void CommandBufferLocal::SetGetBuffer(int32_t buffer) {
+ DCHECK(CalledOnValidThread());
+ gpu_state_->command_buffer_task_runner()->PostTask(
+ driver_.get(), base::Bind(&CommandBufferLocal::SetGetBufferOnGpuThread,
+ base::Unretained(this), buffer));
+ last_put_offset_ = -1;
+}
+
+scoped_refptr<gpu::Buffer> CommandBufferLocal::CreateTransferBuffer(
+ size_t size,
+ int32_t* id) {
+ DCHECK(CalledOnValidThread());
+ if (size >= std::numeric_limits<uint32_t>::max())
+ return nullptr;
+
+ mojo::ScopedSharedBufferMapping mapping;
+ mojo::ScopedSharedBufferHandle handle;
+ if (!CreateAndMapSharedBuffer(size, &mapping, &handle)) {
+ if (last_state_.error == gpu::error::kNoError)
+ last_state_.error = gpu::error::kLostContext;
+ return nullptr;
+ }
+
+ *id = ++next_transfer_buffer_id_;
+
+ gpu_state_->command_buffer_task_runner()->PostTask(
+ driver_.get(),
+ base::Bind(&CommandBufferLocal::RegisterTransferBufferOnGpuThread,
+ base::Unretained(this), *id, base::Passed(&handle),
+ static_cast<uint32_t>(size)));
+ std::unique_ptr<gpu::BufferBacking> backing(
+ new mus::MojoBufferBacking(std::move(mapping), size));
+ scoped_refptr<gpu::Buffer> buffer(new gpu::Buffer(std::move(backing)));
+ return buffer;
+}
+
+void CommandBufferLocal::DestroyTransferBuffer(int32_t id) {
+ DCHECK(CalledOnValidThread());
+ gpu_state_->command_buffer_task_runner()->PostTask(
+ driver_.get(),
+ base::Bind(&CommandBufferLocal::DestroyTransferBufferOnGpuThread,
+ base::Unretained(this), id));
+}
+
+void CommandBufferLocal::SetGpuControlClient(gpu::GpuControlClient* client) {
+ gpu_control_client_ = client;
+}
+
+gpu::Capabilities CommandBufferLocal::GetCapabilities() {
+ DCHECK(CalledOnValidThread());
+ return capabilities_;
+}
+
+int32_t CommandBufferLocal::CreateImage(ClientBuffer buffer,
+ size_t width,
+ size_t height,
+ unsigned internal_format) {
+ DCHECK(CalledOnValidThread());
+ int32_t new_id = ++next_image_id_;
+ gfx::Size size(static_cast<int32_t>(width), static_cast<int32_t>(height));
+
+ mus::MojoGpuMemoryBufferImpl* gpu_memory_buffer =
+ mus::MojoGpuMemoryBufferImpl::FromClientBuffer(buffer);
+
+ bool requires_sync_point = false;
+
+ if (gpu_memory_buffer->GetBufferType() == gfx::SHARED_MEMORY_BUFFER) {
+ gfx::GpuMemoryBufferHandle handle = gpu_memory_buffer->GetHandle();
+ // TODO(rjkroege): Verify that this is required and update appropriately.
+ base::SharedMemoryHandle dupd_handle =
+ base::SharedMemory::DuplicateHandle(handle.handle);
+#if defined(OS_WIN)
+ HANDLE platform_file = dupd_handle.GetHandle();
+#else
+ int platform_file = dupd_handle.fd;
+#endif
+
+ mojo::ScopedHandle scoped_handle = mojo::WrapPlatformFile(platform_file);
+ const int32_t format = static_cast<int32_t>(gpu_memory_buffer->GetFormat());
+ gpu_state_->command_buffer_task_runner()->PostTask(
+ driver_.get(),
+ base::Bind(&CommandBufferLocal::CreateImageOnGpuThread,
+ base::Unretained(this), new_id, base::Passed(&scoped_handle),
+ handle.type, base::Passed(&size), format, internal_format));
+#if defined(USE_OZONE)
+ } else if (gpu_memory_buffer->GetBufferType() == gfx::OZONE_NATIVE_PIXMAP) {
+ gpu_state_->command_buffer_task_runner()->PostTask(
+ driver_.get(),
+ base::Bind(&CommandBufferLocal::CreateImageNativeOzoneOnGpuThread,
+ base::Unretained(this), new_id,
+ gpu_memory_buffer->GetBufferType(),
+ gpu_memory_buffer->GetSize(), gpu_memory_buffer->GetFormat(),
+ internal_format,
+ base::RetainedRef(gpu_memory_buffer->GetNativePixmap())));
+#endif
+ } else {
+ NOTIMPLEMENTED();
+ return -1;
+ }
+
+ if (requires_sync_point) {
+ NOTIMPLEMENTED() << "Require sync points";
+ // TODO(jam): need to support this if we support types other than
+ // SHARED_MEMORY_BUFFER.
+ // gpu_memory_buffer_manager->SetDestructionSyncPoint(gpu_memory_buffer,
+ // InsertSyncPoint());
+ }
+
+ return new_id;
+}
+
+void CommandBufferLocal::DestroyImage(int32_t id) {
+ DCHECK(CalledOnValidThread());
+ gpu_state_->command_buffer_task_runner()->PostTask(
+ driver_.get(), base::Bind(&CommandBufferLocal::DestroyImageOnGpuThread,
+ base::Unretained(this), id));
+}
+
+int32_t CommandBufferLocal::CreateGpuMemoryBufferImage(size_t width,
+ size_t height,
+ unsigned internal_format,
+ unsigned usage) {
+ DCHECK(CalledOnValidThread());
+ DCHECK_EQ(usage, static_cast<unsigned>(GL_READ_WRITE_CHROMIUM));
+ std::unique_ptr<gfx::GpuMemoryBuffer> buffer(MojoGpuMemoryBufferImpl::Create(
+ gfx::Size(static_cast<int>(width), static_cast<int>(height)),
+ gpu::DefaultBufferFormatForImageFormat(internal_format),
+ gfx::BufferUsage::SCANOUT));
+ if (!buffer)
+ return -1;
+ return CreateImage(buffer->AsClientBuffer(), width, height, internal_format);
+}
+
+int32_t CommandBufferLocal::GetImageGpuMemoryBufferId(unsigned image_id) {
+ // TODO(erikchen): Once this class supports IOSurface GpuMemoryBuffer backed
+ // images, it will also need to keep a local cache from image id to
+ // GpuMemoryBuffer id.
+ NOTIMPLEMENTED();
+ return -1;
+}
+
+void CommandBufferLocal::SignalQuery(uint32_t query_id,
+ const base::Closure& callback) {
+ DCHECK(CalledOnValidThread());
+
+ gpu_state_->command_buffer_task_runner()->PostTask(
+ driver_.get(), base::Bind(&CommandBufferLocal::SignalQueryOnGpuThread,
+ base::Unretained(this), query_id, callback));
+}
+
+void CommandBufferLocal::SetLock(base::Lock* lock) {
+ DCHECK(CalledOnValidThread());
+ NOTIMPLEMENTED();
+}
+
+void CommandBufferLocal::EnsureWorkVisible() {
+ // This is only relevant for out-of-process command buffers.
+}
+
+gpu::CommandBufferNamespace CommandBufferLocal::GetNamespaceID() const {
+ DCHECK(CalledOnValidThread());
+ return gpu::CommandBufferNamespace::MOJO_LOCAL;
+}
+
+gpu::CommandBufferId CommandBufferLocal::GetCommandBufferID() const {
+ DCHECK(CalledOnValidThread());
+ return driver_->GetCommandBufferID();
+}
+
+int32_t CommandBufferLocal::GetExtraCommandBufferData() const {
+ DCHECK(CalledOnValidThread());
+ return 0;
+}
+
+uint64_t CommandBufferLocal::GenerateFenceSyncRelease() {
+ DCHECK(CalledOnValidThread());
+ return next_fence_sync_release_++;
+}
+
+bool CommandBufferLocal::IsFenceSyncRelease(uint64_t release) {
+ DCHECK(CalledOnValidThread());
+ return release != 0 && release < next_fence_sync_release_;
+}
+
+bool CommandBufferLocal::IsFenceSyncFlushed(uint64_t release) {
+ DCHECK(CalledOnValidThread());
+ return release != 0 && release <= flushed_fence_sync_release_;
+}
+
+bool CommandBufferLocal::IsFenceSyncFlushReceived(uint64_t release) {
+ DCHECK(CalledOnValidThread());
+ return IsFenceSyncFlushed(release);
+}
+
+void CommandBufferLocal::SignalSyncToken(const gpu::SyncToken& sync_token,
+ const base::Closure& callback) {
+ DCHECK(CalledOnValidThread());
+ scoped_refptr<gpu::SyncPointClientState> release_state =
+ gpu_state_->sync_point_manager()->GetSyncPointClientState(
+ sync_token.namespace_id(), sync_token.command_buffer_id());
+ if (!release_state ||
+ release_state->IsFenceSyncReleased(sync_token.release_count())) {
+ callback.Run();
+ return;
+ }
+
+ sync_point_client_waiter_->WaitOutOfOrderNonThreadSafe(
+ release_state.get(), sync_token.release_count(),
+ client_thread_task_runner_, callback);
+}
+
+bool CommandBufferLocal::CanWaitUnverifiedSyncToken(
+ const gpu::SyncToken* sync_token) {
+ DCHECK(CalledOnValidThread());
+ // Right now, MOJO_LOCAL is only used by trusted code, so it is safe to wait
+ // on a sync token in MOJO_LOCAL command buffer.
+ return sync_token->namespace_id() == gpu::CommandBufferNamespace::MOJO_LOCAL;
+}
+
+void CommandBufferLocal::DidLoseContext(uint32_t reason) {
+ if (client_) {
+ driver_->set_client(nullptr);
+ client_thread_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&CommandBufferLocal::DidLoseContextOnClientThread,
+ weak_ptr_, reason));
+ }
+}
+
+void CommandBufferLocal::UpdateVSyncParameters(
+ const base::TimeTicks& timebase,
+ const base::TimeDelta& interval) {
+ if (client_) {
+ client_thread_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&CommandBufferLocal::UpdateVSyncParametersOnClientThread,
+ weak_ptr_, timebase, interval));
+ }
+}
+
+void CommandBufferLocal::OnGpuCompletedSwapBuffers(gfx::SwapResult result) {
+ if (client_) {
+ client_thread_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&CommandBufferLocal::OnGpuCompletedSwapBuffersOnClientThread,
+ weak_ptr_, result));
+ }
+}
+
+CommandBufferLocal::~CommandBufferLocal() {}
+
+void CommandBufferLocal::TryUpdateState() {
+ if (last_state_.error == gpu::error::kNoError)
+ shared_state()->Read(&last_state_);
+}
+
+void CommandBufferLocal::MakeProgressAndUpdateState() {
+ base::ThreadRestrictions::ScopedAllowWait allow_wait;
+ base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+ gpu::CommandBuffer::State state;
+ gpu_state_->command_buffer_task_runner()->PostTask(
+ driver_.get(),
+ base::Bind(&CommandBufferLocal::MakeProgressOnGpuThread,
+ base::Unretained(this), base::Unretained(&event),
+ base::Unretained(&state)));
+ event.Wait();
+ if (state.generation - last_state_.generation < 0x80000000U)
+ last_state_ = state;
+}
+
+void CommandBufferLocal::InitializeOnGpuThread(base::WaitableEvent* event,
+ bool* result) {
+ driver_.reset(new CommandBufferDriver(
+ gpu::CommandBufferNamespace::MOJO_LOCAL,
+ gpu::CommandBufferId::FromUnsafeValue(++g_next_command_buffer_id),
+ widget_, gpu_state_));
+ driver_->set_client(this);
+ const size_t kSharedStateSize = sizeof(gpu::CommandBufferSharedState);
+ mojo::ScopedSharedBufferMapping mapping;
+ mojo::ScopedSharedBufferHandle handle;
+ *result = CreateAndMapSharedBuffer(kSharedStateSize, &shared_state_, &handle);
+
+ if (!*result) {
+ event->Signal();
+ return;
+ }
+
+ shared_state()->Initialize();
+
+ *result =
+ driver_->Initialize(std::move(handle), mojo::Array<int32_t>::New(0));
+ if (*result)
+ capabilities_ = driver_->GetCapabilities();
+ event->Signal();
+}
+
+bool CommandBufferLocal::FlushOnGpuThread(int32_t put_offset,
+ uint32_t order_num) {
+ DCHECK(driver_->IsScheduled());
+ driver_->sync_point_order_data()->BeginProcessingOrderNumber(order_num);
+ driver_->Flush(put_offset);
+
+ // Return false if the Flush is not finished, so the CommandBufferTaskRunner
+ // will not remove this task from the task queue.
+ const bool complete = !driver_->HasUnprocessedCommands();
+ if (complete)
+ driver_->sync_point_order_data()->FinishProcessingOrderNumber(order_num);
+ return complete;
+}
+
+bool CommandBufferLocal::SetGetBufferOnGpuThread(int32_t buffer) {
+ DCHECK(driver_->IsScheduled());
+ driver_->SetGetBuffer(buffer);
+ return true;
+}
+
+bool CommandBufferLocal::RegisterTransferBufferOnGpuThread(
+ int32_t id,
+ mojo::ScopedSharedBufferHandle transfer_buffer,
+ uint32_t size) {
+ DCHECK(driver_->IsScheduled());
+ driver_->RegisterTransferBuffer(id, std::move(transfer_buffer), size);
+ return true;
+}
+
+bool CommandBufferLocal::DestroyTransferBufferOnGpuThread(int32_t id) {
+ DCHECK(driver_->IsScheduled());
+ driver_->DestroyTransferBuffer(id);
+ return true;
+}
+
+bool CommandBufferLocal::CreateImageOnGpuThread(
+ int32_t id,
+ mojo::ScopedHandle memory_handle,
+ int32_t type,
+ const gfx::Size& size,
+ int32_t format,
+ int32_t internal_format) {
+ DCHECK(driver_->IsScheduled());
+ driver_->CreateImage(id, std::move(memory_handle), type, std::move(size),
+ format, internal_format);
+ return true;
+}
+
+bool CommandBufferLocal::CreateImageNativeOzoneOnGpuThread(
+ int32_t id,
+ int32_t type,
+ gfx::Size size,
+ gfx::BufferFormat format,
+ uint32_t internal_format,
+ ui::NativePixmap* pixmap) {
+ DCHECK(driver_->IsScheduled());
+ driver_->CreateImageNativeOzone(id, type, size, format, internal_format,
+ pixmap);
+ return true;
+}
+
+bool CommandBufferLocal::DestroyImageOnGpuThread(int32_t id) {
+ DCHECK(driver_->IsScheduled());
+ driver_->DestroyImage(id);
+ return true;
+}
+
+bool CommandBufferLocal::MakeProgressOnGpuThread(
+ base::WaitableEvent* event,
+ gpu::CommandBuffer::State* state) {
+ DCHECK(driver_->IsScheduled());
+ *state = driver_->GetLastState();
+ event->Signal();
+ return true;
+}
+
+bool CommandBufferLocal::DeleteOnGpuThread(base::WaitableEvent* event) {
+ delete this;
+ event->Signal();
+ return true;
+}
+
+bool CommandBufferLocal::SignalQueryOnGpuThread(uint32_t query_id,
+ const base::Closure& callback) {
+ // |callback| should run on the client thread.
+ driver_->SignalQuery(
+ query_id, base::Bind(&PostTask, client_thread_task_runner_, callback));
+ return true;
+}
+
+void CommandBufferLocal::DidLoseContextOnClientThread(uint32_t reason) {
+ DCHECK(gpu_control_client_);
+ if (!lost_context_)
+ gpu_control_client_->OnGpuControlLostContext();
+ lost_context_ = true;
+}
+
+void CommandBufferLocal::UpdateVSyncParametersOnClientThread(
+ const base::TimeTicks& timebase,
+ const base::TimeDelta& interval) {
+ if (client_)
+ client_->UpdateVSyncParameters(timebase, interval);
+}
+
+void CommandBufferLocal::OnGpuCompletedSwapBuffersOnClientThread(
+ gfx::SwapResult result) {
+ if (client_)
+ client_->GpuCompletedSwapBuffers(result);
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/gles2/command_buffer_local.h b/chromium/components/mus/gles2/command_buffer_local.h
new file mode 100644
index 00000000000..4d893322737
--- /dev/null
+++ b/chromium/components/mus/gles2/command_buffer_local.h
@@ -0,0 +1,185 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_GLES2_COMMAND_BUFFER_LOCAL_H_
+#define COMPONENTS_MUS_GLES2_COMMAND_BUFFER_LOCAL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/non_thread_safe.h"
+#include "components/mus/gles2/command_buffer_driver.h"
+#include "components/mus/public/interfaces/command_buffer.mojom.h"
+#include "gpu/command_buffer/client/gpu_control.h"
+#include "gpu/command_buffer/common/command_buffer.h"
+#include "gpu/command_buffer/common/command_buffer_id.h"
+#include "gpu/command_buffer/common/command_buffer_shared.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/gpu_memory_buffer.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace {
+class WaitableEvent;
+}
+
+namespace gl {
+class GLContext;
+class GLSurface;
+}
+
+namespace gpu {
+class GpuControlClient;
+class SyncPointClient;
+}
+
+namespace mus {
+
+class CommandBufferDriver;
+class CommandBufferLocalClient;
+class GpuState;
+
+// This class provides a wrapper around a CommandBufferDriver and a GpuControl
+// implementation to allow cc::Display to generate GL directly on the main
+// thread.
+class CommandBufferLocal : public gpu::CommandBuffer,
+ public gpu::GpuControl,
+ public CommandBufferDriver::Client,
+ base::NonThreadSafe {
+ public:
+ CommandBufferLocal(CommandBufferLocalClient* client,
+ gfx::AcceleratedWidget widget,
+ const scoped_refptr<GpuState>& gpu_state);
+
+ bool Initialize();
+ // Destroy the CommandBufferLocal. The client should not use this class
+ // after calling it.
+ void Destroy();
+
+ // gpu::CommandBuffer implementation:
+ gpu::CommandBuffer::State GetLastState() override;
+ int32_t GetLastToken() override;
+ void Flush(int32_t put_offset) override;
+ void OrderingBarrier(int32_t put_offset) override;
+ void WaitForTokenInRange(int32_t start, int32_t end) override;
+ void WaitForGetOffsetInRange(int32_t start, int32_t end) override;
+ void SetGetBuffer(int32_t buffer) override;
+ scoped_refptr<gpu::Buffer> CreateTransferBuffer(size_t size,
+ int32_t* id) override;
+ void DestroyTransferBuffer(int32_t id) override;
+
+ // gpu::GpuControl implementation:
+ void SetGpuControlClient(gpu::GpuControlClient*) override;
+ gpu::Capabilities GetCapabilities() override;
+ int32_t CreateImage(ClientBuffer buffer,
+ size_t width,
+ size_t height,
+ unsigned internalformat) override;
+ void DestroyImage(int32_t id) override;
+ int32_t CreateGpuMemoryBufferImage(size_t width,
+ size_t height,
+ unsigned internal_format,
+ unsigned usage) override;
+ int32_t GetImageGpuMemoryBufferId(unsigned image_id) override;
+ void SignalQuery(uint32_t query_id, const base::Closure& callback) override;
+ void SetLock(base::Lock*) override;
+ void EnsureWorkVisible() override;
+ gpu::CommandBufferNamespace GetNamespaceID() const override;
+ gpu::CommandBufferId GetCommandBufferID() const override;
+ int32_t GetExtraCommandBufferData() const override;
+ uint64_t GenerateFenceSyncRelease() override;
+ bool IsFenceSyncRelease(uint64_t release) override;
+ bool IsFenceSyncFlushed(uint64_t release) override;
+ bool IsFenceSyncFlushReceived(uint64_t release) override;
+ void SignalSyncToken(const gpu::SyncToken& sync_token,
+ const base::Closure& callback) override;
+ bool CanWaitUnverifiedSyncToken(const gpu::SyncToken* sync_token) override;
+
+ private:
+ // CommandBufferDriver::Client implementation. All called on GPU thread.
+ void DidLoseContext(uint32_t reason) override;
+ void UpdateVSyncParameters(const base::TimeTicks& timebase,
+ const base::TimeDelta& interval) override;
+ void OnGpuCompletedSwapBuffers(gfx::SwapResult result) override;
+
+ ~CommandBufferLocal() override;
+
+ gpu::CommandBufferSharedState* shared_state() const {
+ return reinterpret_cast<gpu::CommandBufferSharedState*>(
+ shared_state_.get());
+ }
+ void TryUpdateState();
+ void MakeProgressAndUpdateState();
+
+ // Helper functions are called in the GPU thread.
+ void InitializeOnGpuThread(base::WaitableEvent* event, bool* result);
+ bool FlushOnGpuThread(int32_t put_offset, uint32_t order_num);
+ bool SetGetBufferOnGpuThread(int32_t buffer);
+ bool RegisterTransferBufferOnGpuThread(
+ int32_t id,
+ mojo::ScopedSharedBufferHandle transfer_buffer,
+ uint32_t size);
+ bool DestroyTransferBufferOnGpuThread(int32_t id);
+ bool CreateImageOnGpuThread(int32_t id,
+ mojo::ScopedHandle memory_handle,
+ int32_t type,
+ const gfx::Size& size,
+ int32_t format,
+ int32_t internal_format);
+ bool CreateImageNativeOzoneOnGpuThread(int32_t id,
+ int32_t type,
+ gfx::Size size,
+ gfx::BufferFormat format,
+ uint32_t internal_format,
+ ui::NativePixmap* pixmap);
+ bool DestroyImageOnGpuThread(int32_t id);
+ bool MakeProgressOnGpuThread(base::WaitableEvent* event,
+ gpu::CommandBuffer::State* state);
+ bool DeleteOnGpuThread(base::WaitableEvent* event);
+ bool SignalQueryOnGpuThread(uint32_t query_id, const base::Closure& callback);
+
+ // Helper functions are called in the client thread.
+ void DidLoseContextOnClientThread(uint32_t reason);
+ void UpdateVSyncParametersOnClientThread(const base::TimeTicks& timebase,
+ const base::TimeDelta& interval);
+ void OnGpuCompletedSwapBuffersOnClientThread(gfx::SwapResult result);
+
+ gfx::AcceleratedWidget widget_;
+ scoped_refptr<GpuState> gpu_state_;
+ std::unique_ptr<CommandBufferDriver> driver_;
+ CommandBufferLocalClient* client_;
+ scoped_refptr<base::SingleThreadTaskRunner> client_thread_task_runner_;
+
+ // Members accessed on the client thread:
+ gpu::GpuControlClient* gpu_control_client_;
+ gpu::CommandBuffer::State last_state_;
+ mojo::ScopedSharedBufferMapping shared_state_;
+ int32_t last_put_offset_;
+ gpu::Capabilities capabilities_;
+ int32_t next_transfer_buffer_id_;
+ int32_t next_image_id_;
+ uint64_t next_fence_sync_release_;
+ uint64_t flushed_fence_sync_release_;
+ bool lost_context_;
+
+ // This sync point client is only for out of order Wait on client thread.
+ std::unique_ptr<gpu::SyncPointClient> sync_point_client_waiter_;
+
+ base::WeakPtr<CommandBufferLocal> weak_ptr_;
+
+ // This weak factory will be invalidated in the client thread, so all weak
+ // pointers have to be dereferenced in the client thread too.
+ base::WeakPtrFactory<CommandBufferLocal> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(CommandBufferLocal);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_GLES2_COMMAND_BUFFER_LOCAL_H_
diff --git a/chromium/components/mus/gles2/command_buffer_local_client.h b/chromium/components/mus/gles2/command_buffer_local_client.h
new file mode 100644
index 00000000000..c26cbdf4941
--- /dev/null
+++ b/chromium/components/mus/gles2/command_buffer_local_client.h
@@ -0,0 +1,31 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_GLES2_COMMAND_BUFFER_LOCAL_CLIENT_H_
+#define COMPONENTS_MUS_GLES2_COMMAND_BUFFER_LOCAL_CLIENT_H_
+
+#include <stdint.h>
+
+#include "ui/gfx/swap_result.h"
+
+namespace base {
+class TimeDelta;
+class TimeTicks;
+}
+
+namespace mus {
+
+class CommandBufferLocalClient {
+ public:
+ virtual void UpdateVSyncParameters(const base::TimeTicks& timebase,
+ const base::TimeDelta& interval) = 0;
+ virtual void GpuCompletedSwapBuffers(gfx::SwapResult result) = 0;
+
+ protected:
+ virtual ~CommandBufferLocalClient() {}
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_GLES2_COMMAND_BUFFER_LOCAL_CLIENT_H_
diff --git a/chromium/components/mus/gles2/command_buffer_task_runner.cc b/chromium/components/mus/gles2/command_buffer_task_runner.cc
new file mode 100644
index 00000000000..df1dc548b6a
--- /dev/null
+++ b/chromium/components/mus/gles2/command_buffer_task_runner.cc
@@ -0,0 +1,77 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/gles2/command_buffer_task_runner.h"
+
+#include "base/bind.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/mus/gles2/command_buffer_driver.h"
+
+namespace mus {
+
+CommandBufferTaskRunner::CommandBufferTaskRunner()
+ : task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ need_post_task_(true) {
+}
+
+bool CommandBufferTaskRunner::PostTask(
+ const CommandBufferDriver* driver,
+ const TaskCallback& task) {
+ base::AutoLock locker(lock_);
+ driver_map_[driver].push_back(task);
+ ScheduleTaskIfNecessaryLocked();
+ return true;
+}
+
+CommandBufferTaskRunner::~CommandBufferTaskRunner() {}
+
+bool CommandBufferTaskRunner::RunOneTaskInternalLocked() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ lock_.AssertAcquired();
+
+ for (auto it = driver_map_.begin(); it != driver_map_.end(); ++it) {
+ if (!it->first->IsScheduled())
+ continue;
+ TaskQueue& task_queue = it->second;
+ DCHECK(!task_queue.empty());
+ const TaskCallback& callback = task_queue.front();
+ bool complete = false;
+ {
+ base::AutoUnlock unlocker(lock_);
+ complete = callback.Run();
+ }
+ if (complete) {
+ // Only remove the task if it is complete.
+ task_queue.pop_front();
+ if (task_queue.empty())
+ driver_map_.erase(it);
+ }
+ return true;
+ }
+ return false;
+}
+
+void CommandBufferTaskRunner::ScheduleTaskIfNecessaryLocked() {
+ lock_.AssertAcquired();
+ if (!need_post_task_)
+ return;
+ task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&CommandBufferTaskRunner::RunCommandBufferTask, this));
+ need_post_task_ = false;
+}
+
+void CommandBufferTaskRunner::RunCommandBufferTask() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ base::AutoLock locker(lock_);
+
+ // Try executing all tasks in |driver_map_| until |RunOneTaskInternalLock()|
+ // returns false (Returning false means |driver_map_| is empty or all tasks
+ // in it aren't executable currently).
+ while (RunOneTaskInternalLocked())
+ ;
+ need_post_task_ = true;
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/gles2/command_buffer_task_runner.h b/chromium/components/mus/gles2/command_buffer_task_runner.h
new file mode 100644
index 00000000000..8ee317f5800
--- /dev/null
+++ b/chromium/components/mus/gles2/command_buffer_task_runner.h
@@ -0,0 +1,78 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_GLES2_COMMAND_BUFFER_TASK_RUNNER_H_
+#define COMPONENTS_MUS_GLES2_COMMAND_BUFFER_TASK_RUNNER_H_
+
+#include <deque>
+#include <map>
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_checker.h"
+
+namespace mus {
+
+class CommandBufferDriver;
+
+// This class maintains tasks submitted by |CommandBufferImpl|. Those tasks will
+// be executed on the main thread. But if the main thread is blocked in
+// |CommandBufferLocal::OnWaitFenceSync()| by waiting a sync point, the
+// |CommandBufferTaskRunner::RunOneTask()| could be used for executing a task
+// from other command buffers which may retire the sync point.
+class CommandBufferTaskRunner
+ : public base::RefCountedThreadSafe<CommandBufferTaskRunner> {
+ public:
+ CommandBufferTaskRunner();
+
+ // TaskCallback returns true if the task is completed and should be removed
+ // from the task queue, otherwise returns false.
+ typedef base::Callback<bool(void)> TaskCallback;
+ bool PostTask(const CommandBufferDriver* driver,
+ const TaskCallback& task);
+
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner() const {
+ return task_runner_;
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<CommandBufferTaskRunner>;
+
+ ~CommandBufferTaskRunner();
+
+ // Run one command buffer task from a scheduled command buffer.
+ // When there isn't any command buffer task, and if the |block| is false,
+ // this function will return false immediately, otherwise, this function
+ // will be blocked until a task is available for executing.
+ bool RunOneTaskInternalLocked();
+
+ // Post a task to the main thread to execute tasks in |driver_map_|, if it is
+ // necessary.
+ void ScheduleTaskIfNecessaryLocked();
+
+ // The callback function for executing tasks in |driver_map_|.
+ void RunCommandBufferTask();
+
+ base::ThreadChecker thread_checker_;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+ typedef std::deque<TaskCallback> TaskQueue;
+ typedef std::map<const CommandBufferDriver*, TaskQueue> DriverMap;
+ DriverMap driver_map_;
+ bool need_post_task_;
+
+ // The access lock for |driver_map_| and |need_post_task_|.
+ base::Lock lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(CommandBufferTaskRunner);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_GLES2_COMMAND_BUFFER_TASK_RUNNER_H_
diff --git a/chromium/components/mus/gles2/gl_surface_adapter.cc b/chromium/components/mus/gles2/gl_surface_adapter.cc
new file mode 100644
index 00000000000..df1761b5854
--- /dev/null
+++ b/chromium/components/mus/gles2/gl_surface_adapter.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/gles2/gl_surface_adapter.h"
+
+#include "base/bind.h"
+
+namespace mus {
+
+GLSurfaceAdapterMus::GLSurfaceAdapterMus(scoped_refptr<gl::GLSurface> surface)
+ : gl::GLSurfaceAdapter(surface.get()),
+ surface_(surface),
+ weak_ptr_factory_(this) {}
+
+GLSurfaceAdapterMus::~GLSurfaceAdapterMus() {}
+
+void GLSurfaceAdapterMus::SwapBuffersAsync(
+ const GLSurface::SwapCompletionCallback& callback) {
+ gl::GLSurfaceAdapter::SwapBuffersAsync(
+ base::Bind(&GLSurfaceAdapterMus::WrappedCallbackForSwapBuffersAsync,
+ weak_ptr_factory_.GetWeakPtr(), callback));
+}
+
+void GLSurfaceAdapterMus::PostSubBufferAsync(
+ int x,
+ int y,
+ int width,
+ int height,
+ const GLSurface::SwapCompletionCallback& callback) {
+ gl::GLSurfaceAdapter::PostSubBufferAsync(
+ x, y, width, height,
+ base::Bind(&GLSurfaceAdapterMus::WrappedCallbackForSwapBuffersAsync,
+ weak_ptr_factory_.GetWeakPtr(), callback));
+}
+
+void GLSurfaceAdapterMus::CommitOverlayPlanesAsync(
+ const GLSurface::SwapCompletionCallback& callback) {
+ gl::GLSurfaceAdapter::CommitOverlayPlanesAsync(
+ base::Bind(&GLSurfaceAdapterMus::WrappedCallbackForSwapBuffersAsync,
+ weak_ptr_factory_.GetWeakPtr(), callback));
+}
+
+void GLSurfaceAdapterMus::WrappedCallbackForSwapBuffersAsync(
+ const gl::GLSurface::SwapCompletionCallback& original_callback,
+ gfx::SwapResult result) {
+ if (!adapter_callback_.is_null())
+ adapter_callback_.Run(result);
+ original_callback.Run(result);
+}
+
+void GLSurfaceAdapterMus::SetGpuCompletedSwapBuffersCallback(
+ gl::GLSurface::SwapCompletionCallback callback) {
+ adapter_callback_ = std::move(callback);
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/gles2/gl_surface_adapter.h b/chromium/components/mus/gles2/gl_surface_adapter.h
new file mode 100644
index 00000000000..98da7530915
--- /dev/null
+++ b/chromium/components/mus/gles2/gl_surface_adapter.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_GLES2_GL_SURFACE_ADAPTER_H_
+#define COMPONENTS_MUS_GLES2_GL_SURFACE_ADAPTER_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "ui/gfx/swap_result.h"
+#include "ui/gl/gl_surface.h"
+
+namespace mus {
+
+// Wraps a GLSurface such that there is a real GLSurface instance acting as
+// delegate. Implements the GLSurface interface. The |gl::GLSurfaceAdapter|
+// superclass provides a default implementation that forwards all GLSurface
+// methods to the provided delegate. |GLSurfaceAdapterMus| overrides only the
+// necessary methods to augment the completion callback argument to
+// |SwapBuffersAsync|, |PostSubBufferAsync| and |CommitOverlayPlanesAsync| with
+// a callback provided with the |SetGpuCompletedSwapBuffersCallback| method.
+class GLSurfaceAdapterMus : public gl::GLSurfaceAdapter {
+ public:
+ // Creates a new |GLSurfaceAdapterMus| that delegates to |surface|.
+ explicit GLSurfaceAdapterMus(scoped_refptr<gl::GLSurface> surface);
+
+ // gl::GLSurfaceAdapter.
+ void SwapBuffersAsync(
+ const gl::GLSurface::SwapCompletionCallback& callback) override;
+ void PostSubBufferAsync(
+ int x,
+ int y,
+ int width,
+ int height,
+ const gl::GLSurface::SwapCompletionCallback& callback) override;
+ void CommitOverlayPlanesAsync(
+ const gl::GLSurface::SwapCompletionCallback& callback) override;
+
+ // Set an additional callback to run on SwapBuffers completion.
+ void SetGpuCompletedSwapBuffersCallback(
+ gl::GLSurface::SwapCompletionCallback callback);
+
+ private:
+ ~GLSurfaceAdapterMus() override;
+
+ // We wrap the callback given to the |gl::GLSurfaceAdapter| overrides above
+ // with an invocation of this function. It invokes |adapter_callback_| and the
+ // original |original_callback| provided by the original invoker of
+ // |SwapBuffersAsync| etc.
+ void WrappedCallbackForSwapBuffersAsync(
+ const gl::GLSurface::SwapCompletionCallback& original_callback,
+ gfx::SwapResult result);
+
+ gl::GLSurface::SwapCompletionCallback adapter_callback_;
+
+ // gl::GLSurfaceAdapter doesn't own the delegate surface so keep a reference
+ // here.
+ scoped_refptr<gl::GLSurface> surface_;
+
+ base::WeakPtrFactory<GLSurfaceAdapterMus> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(GLSurfaceAdapterMus);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_GLES2_GL_SURFACE_ADAPTER_H_
diff --git a/chromium/components/mus/gles2/gpu_impl.cc b/chromium/components/mus/gles2/gpu_impl.cc
new file mode 100644
index 00000000000..7ae0b6d0c25
--- /dev/null
+++ b/chromium/components/mus/gles2/gpu_impl.cc
@@ -0,0 +1,27 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/gles2/gpu_impl.h"
+
+#include "components/mus/common/gpu_type_converters.h"
+#include "components/mus/gles2/command_buffer_impl.h"
+
+namespace mus {
+
+GpuImpl::GpuImpl(mojo::InterfaceRequest<Gpu> request,
+ const scoped_refptr<GpuState>& state)
+ : binding_(this, std::move(request)), state_(state) {}
+
+GpuImpl::~GpuImpl() {}
+
+void GpuImpl::CreateOffscreenGLES2Context(
+ mojo::InterfaceRequest<mojom::CommandBuffer> request) {
+ new CommandBufferImpl(std::move(request), state_);
+}
+
+void GpuImpl::GetGpuInfo(const GetGpuInfoCallback& callback) {
+ callback.Run(mojom::GpuInfo::From<gpu::GPUInfo>(state_->gpu_info()));
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/gles2/gpu_impl.h b/chromium/components/mus/gles2/gpu_impl.h
new file mode 100644
index 00000000000..73fb66ed27a
--- /dev/null
+++ b/chromium/components/mus/gles2/gpu_impl.h
@@ -0,0 +1,39 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_GLES2_GPU_IMPL_H_
+#define COMPONENTS_MUS_GLES2_GPU_IMPL_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread.h"
+#include "components/mus/gles2/gpu_state.h"
+#include "components/mus/public/interfaces/command_buffer.mojom.h"
+#include "components/mus/public/interfaces/gpu.mojom.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "ui/gfx/geometry/mojo/geometry.mojom.h"
+
+namespace mus {
+
+class GpuImpl : public mojom::Gpu {
+ public:
+ GpuImpl(mojo::InterfaceRequest<mojom::Gpu> request,
+ const scoped_refptr<GpuState>& state);
+ ~GpuImpl() override;
+
+ private:
+ void CreateOffscreenGLES2Context(mojo::InterfaceRequest<mojom::CommandBuffer>
+ command_buffer_request) override;
+ void GetGpuInfo(const GetGpuInfoCallback& callback) override;
+
+ mojo::StrongBinding<Gpu> binding_;
+ scoped_refptr<GpuState> state_;
+
+ DISALLOW_COPY_AND_ASSIGN(GpuImpl);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_GLES2_GPU_IMPL_H_
diff --git a/chromium/components/mus/gles2/gpu_memory_tracker.cc b/chromium/components/mus/gles2/gpu_memory_tracker.cc
new file mode 100644
index 00000000000..0d9f12b989e
--- /dev/null
+++ b/chromium/components/mus/gles2/gpu_memory_tracker.cc
@@ -0,0 +1,36 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/gles2/gpu_memory_tracker.h"
+
+namespace mus {
+
+GpuMemoryTracker::GpuMemoryTracker() {}
+
+void GpuMemoryTracker::TrackMemoryAllocatedChange(
+ size_t old_size,
+ size_t new_size) {}
+
+bool GpuMemoryTracker::EnsureGPUMemoryAvailable(size_t size_needed) {
+ return true;
+}
+
+uint64_t GpuMemoryTracker::ClientTracingId() const {
+ return 0u;
+}
+
+int GpuMemoryTracker::ClientId() const {
+ return 0;
+}
+
+uint64_t GpuMemoryTracker::ShareGroupTracingGUID() const {
+ // TODO(rjkroege): This should return a GUID which identifies the share group
+ // we are tracking memory for. This GUID should correspond to the GUID of the
+ // first command buffer in the share group.
+ return 0;
+}
+
+GpuMemoryTracker::~GpuMemoryTracker() {}
+
+} // namespace mus
diff --git a/chromium/components/mus/gles2/gpu_memory_tracker.h b/chromium/components/mus/gles2/gpu_memory_tracker.h
new file mode 100644
index 00000000000..925ca978d19
--- /dev/null
+++ b/chromium/components/mus/gles2/gpu_memory_tracker.h
@@ -0,0 +1,39 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_GLES2_GPU_MEMORY_TRACKER_H_
+#define COMPONENTS_MUS_GLES2_GPU_MEMORY_TRACKER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "gpu/command_buffer/service/memory_tracking.h"
+
+namespace mus {
+
+// TODO(fsamuel, rjkroege): This is a stub implementation that needs to be
+// completed for proper memory tracking.
+class GpuMemoryTracker : public gpu::gles2::MemoryTracker {
+ public:
+ GpuMemoryTracker();
+
+ // gpu::MemoryTracker implementation:
+ void TrackMemoryAllocatedChange(
+ size_t old_size,
+ size_t new_size) override;
+ bool EnsureGPUMemoryAvailable(size_t size_needed) override;
+ uint64_t ClientTracingId() const override;
+ int ClientId() const override;
+ uint64_t ShareGroupTracingGUID() const override;
+
+ private:
+ ~GpuMemoryTracker() override;
+
+ DISALLOW_COPY_AND_ASSIGN(GpuMemoryTracker);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_GLES2_GPU_MEMORY_TRACKER_H_
diff --git a/chromium/components/mus/gles2/gpu_state.cc b/chromium/components/mus/gles2/gpu_state.cc
new file mode 100644
index 00000000000..ce00ae30af9
--- /dev/null
+++ b/chromium/components/mus/gles2/gpu_state.cc
@@ -0,0 +1,82 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/gles2/gpu_state.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread_restrictions.h"
+#include "gpu/config/gpu_info_collector.h"
+#include "ui/gl/gl_implementation.h"
+#include "ui/gl/init/gl_factory.h"
+
+#if defined(USE_OZONE)
+#include "ui/ozone/public/ozone_platform.h"
+#endif
+
+namespace mus {
+
+GpuState::GpuState()
+ : gpu_thread_("gpu_thread"),
+ control_thread_("gpu_command_buffer_control"),
+ gpu_driver_bug_workarounds_(base::CommandLine::ForCurrentProcess()),
+ hardware_rendering_available_(false) {
+ base::ThreadRestrictions::ScopedAllowWait allow_wait;
+ gpu_thread_.Start();
+ control_thread_.Start();
+ control_thread_task_runner_ = control_thread_.task_runner();
+ base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+ gpu_thread_.task_runner()->PostTask(
+ FROM_HERE, base::Bind(&GpuState::InitializeOnGpuThread,
+ base::Unretained(this), &event));
+ event.Wait();
+}
+
+GpuState::~GpuState() {}
+
+void GpuState::StopThreads() {
+ control_thread_.Stop();
+ gpu_thread_.task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&GpuState::DestroyGpuSpecificStateOnGpuThread, this));
+ gpu_thread_.Stop();
+}
+
+void GpuState::InitializeOnGpuThread(base::WaitableEvent* event) {
+#if defined(USE_OZONE)
+ ui::OzonePlatform::InitializeForGPU();
+#endif
+ hardware_rendering_available_ = gl::init::InitializeGLOneOff();
+ command_buffer_task_runner_ = new CommandBufferTaskRunner;
+ driver_manager_.reset(new CommandBufferDriverManager);
+ sync_point_manager_.reset(new gpu::SyncPointManager(true));
+ share_group_ = new gl::GLShareGroup;
+ mailbox_manager_ = new gpu::gles2::MailboxManagerImpl;
+
+ // TODO(penghuang): investigate why gpu::CollectBasicGraphicsInfo() failed on
+ // windows remote desktop.
+ const gl::GLImplementation impl = gl::GetGLImplementation();
+ if (impl != gl::kGLImplementationNone &&
+ impl != gl::kGLImplementationOSMesaGL &&
+ impl != gl::kGLImplementationMockGL) {
+ gpu::CollectInfoResult result = gpu::CollectBasicGraphicsInfo(&gpu_info_);
+ LOG_IF(ERROR, result != gpu::kCollectInfoSuccess)
+ << "Collect basic graphics info failed!";
+ }
+ if (impl != gl::kGLImplementationNone) {
+ gpu::CollectInfoResult result = gpu::CollectContextGraphicsInfo(&gpu_info_);
+ LOG_IF(ERROR, result != gpu::kCollectInfoSuccess)
+ << "Collect context graphics info failed!";
+ }
+ event->Signal();
+
+}
+
+void GpuState::DestroyGpuSpecificStateOnGpuThread() {
+ driver_manager_.reset();
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/gles2/gpu_state.h b/chromium/components/mus/gles2/gpu_state.h
new file mode 100644
index 00000000000..f30d485bfc7
--- /dev/null
+++ b/chromium/components/mus/gles2/gpu_state.h
@@ -0,0 +1,107 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_GLES2_GPU_STATE_H_
+#define COMPONENTS_MUS_GLES2_GPU_STATE_H_
+
+#include <memory>
+
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread.h"
+#include "components/mus/gles2/command_buffer_driver_manager.h"
+#include "components/mus/gles2/command_buffer_task_runner.h"
+#include "gpu/command_buffer/service/gpu_preferences.h"
+#include "gpu/command_buffer/service/mailbox_manager_impl.h"
+#include "gpu/command_buffer/service/sync_point_manager.h"
+#include "gpu/config/gpu_driver_bug_workarounds.h"
+#include "gpu/config/gpu_info.h"
+#include "ui/gl/gl_share_group.h"
+
+namespace {
+class WaitableEvent;
+}
+
+namespace mus {
+
+// We need to share these across all CommandBuffer instances so that contexts
+// they create can share resources with each other via mailboxes.
+class GpuState : public base::RefCountedThreadSafe<GpuState> {
+ public:
+ GpuState();
+
+ // We run the CommandBufferImpl on the control_task_runner, which forwards
+ // most method class to the CommandBufferDriver, which runs on the "driver",
+ // thread (i.e., the thread on which GpuImpl instances are created).
+ scoped_refptr<base::SingleThreadTaskRunner> control_task_runner() {
+ return control_thread_task_runner_;
+ }
+
+ void StopThreads();
+
+ const gpu::GpuPreferences& gpu_preferences() const {
+ return gpu_preferences_;
+ }
+
+ const gpu::GpuDriverBugWorkarounds& gpu_driver_bug_workarounds() const {
+ return gpu_driver_bug_workarounds_;
+ }
+
+ CommandBufferTaskRunner* command_buffer_task_runner() const {
+ return command_buffer_task_runner_.get();
+ }
+
+ CommandBufferDriverManager* driver_manager() const {
+ return driver_manager_.get();
+ }
+
+ // These objects are intended to be used on the "driver" thread (i.e., the
+ // thread on which GpuImpl instances are created).
+ gl::GLShareGroup* share_group() const { return share_group_.get(); }
+ gpu::gles2::MailboxManager* mailbox_manager() const {
+ return mailbox_manager_.get();
+ }
+ gpu::SyncPointManager* sync_point_manager() const {
+ return sync_point_manager_.get();
+ }
+
+ const gpu::GPUInfo& gpu_info() const {
+ return gpu_info_;
+ }
+
+ bool HardwareRenderingAvailable() const {
+ return hardware_rendering_available_;
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<GpuState>;
+ ~GpuState();
+
+ void InitializeOnGpuThread(base::WaitableEvent* event);
+
+ void DestroyGpuSpecificStateOnGpuThread();
+
+ // |gpu_thread_| is for executing OS GL calls.
+ base::Thread gpu_thread_;
+ // |control_thread_| is for mojo incoming calls of CommandBufferImpl.
+ base::Thread control_thread_;
+
+ // Same as control_thread_->task_runner(). The TaskRunner is cached as it may
+ // be needed during shutdown.
+ scoped_refptr<base::SingleThreadTaskRunner> control_thread_task_runner_;
+
+ gpu::GpuPreferences gpu_preferences_;
+ const gpu::GpuDriverBugWorkarounds gpu_driver_bug_workarounds_;
+ scoped_refptr<CommandBufferTaskRunner> command_buffer_task_runner_;
+ std::unique_ptr<CommandBufferDriverManager> driver_manager_;
+ std::unique_ptr<gpu::SyncPointManager> sync_point_manager_;
+ scoped_refptr<gl::GLShareGroup> share_group_;
+ scoped_refptr<gpu::gles2::MailboxManager> mailbox_manager_;
+ gpu::GPUInfo gpu_info_;
+ bool hardware_rendering_available_;
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_GLES2_GPU_STATE_H_
diff --git a/chromium/components/mus/gles2/ozone_gpu_memory_buffer.cc b/chromium/components/mus/gles2/ozone_gpu_memory_buffer.cc
new file mode 100644
index 00000000000..c53b51f5ade
--- /dev/null
+++ b/chromium/components/mus/gles2/ozone_gpu_memory_buffer.cc
@@ -0,0 +1,115 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/gles2/ozone_gpu_memory_buffer.h"
+
+#include "ui/gfx/buffer_format_util.h"
+#include "ui/ozone/public/client_native_pixmap.h"
+#include "ui/ozone/public/client_native_pixmap_factory.h"
+#include "ui/ozone/public/native_pixmap.h"
+#include "ui/ozone/public/ozone_platform.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
+
+namespace mus {
+
+OzoneGpuMemoryBuffer::OzoneGpuMemoryBuffer(
+ gfx::GpuMemoryBufferId id,
+ const gfx::Size& size,
+ gfx::BufferFormat format,
+ std::unique_ptr<ui::ClientNativePixmap> client_pixmap,
+ scoped_refptr<ui::NativePixmap> native_pixmap)
+ : GpuMemoryBufferImpl(id, size, format),
+ client_pixmap_(std::move(client_pixmap)),
+ native_pixmap_(native_pixmap) {}
+
+OzoneGpuMemoryBuffer::~OzoneGpuMemoryBuffer() {
+ DCHECK(!mapped_);
+}
+
+// static
+OzoneGpuMemoryBuffer* OzoneGpuMemoryBuffer::FromClientBuffer(
+ ClientBuffer buffer) {
+ return reinterpret_cast<OzoneGpuMemoryBuffer*>(buffer);
+}
+
+// static
+std::unique_ptr<gfx::GpuMemoryBuffer>
+OzoneGpuMemoryBuffer::CreateOzoneGpuMemoryBuffer(
+ const gfx::Size& size,
+ gfx::BufferFormat format,
+ gfx::BufferUsage usage,
+ gfx::AcceleratedWidget widget) {
+ scoped_refptr<ui::NativePixmap> pixmap =
+ ui::OzonePlatform::GetInstance()
+ ->GetSurfaceFactoryOzone()
+ ->CreateNativePixmap(widget, size, format, usage);
+
+ DCHECK(pixmap) << "need pixmap to exist!";
+
+ if (!pixmap.get()) {
+ DLOG(ERROR) << "Failed to create pixmap " << size.width() << "x"
+ << size.height() << " format " << static_cast<int>(format)
+ << ", usage " << static_cast<int>(usage);
+ return nullptr;
+ }
+
+ // We construct a ui::NativePixmapHandle
+ gfx::NativePixmapHandle native_pixmap_handle = pixmap->ExportHandle();
+ DCHECK(ui::ClientNativePixmapFactory::GetInstance())
+ << "need me a ClientNativePixmapFactory";
+ std::unique_ptr<ui::ClientNativePixmap> client_native_pixmap =
+ ui::ClientNativePixmapFactory::GetInstance()->ImportFromHandle(
+ native_pixmap_handle, size, usage);
+
+ std::unique_ptr<OzoneGpuMemoryBuffer> nb(
+ new OzoneGpuMemoryBuffer(gfx::GpuMemoryBufferId(0), size, format,
+ std::move(client_native_pixmap), pixmap));
+ return std::move(nb);
+}
+
+bool OzoneGpuMemoryBuffer::Map() {
+ DCHECK(!mapped_);
+ if (!client_pixmap_->Map())
+ return false;
+ mapped_ = true;
+ return mapped_;
+}
+
+void* OzoneGpuMemoryBuffer::memory(size_t plane) {
+ DCHECK(mapped_);
+ DCHECK_LT(plane, gfx::NumberOfPlanesForBufferFormat(format_));
+ return client_pixmap_->Map();
+}
+
+void OzoneGpuMemoryBuffer::Unmap() {
+ DCHECK(mapped_);
+ client_pixmap_->Unmap();
+ mapped_ = false;
+}
+
+int OzoneGpuMemoryBuffer::stride(size_t plane) const {
+ DCHECK_LT(plane, gfx::NumberOfPlanesForBufferFormat(format_));
+ int stride;
+ client_pixmap_->GetStride(&stride);
+ return stride;
+}
+
+gfx::GpuMemoryBufferHandle OzoneGpuMemoryBuffer::GetHandle() const {
+ gfx::GpuMemoryBufferHandle handle;
+ handle.type = gfx::OZONE_NATIVE_PIXMAP;
+ handle.id = id_;
+ return handle;
+}
+
+gfx::GpuMemoryBufferType OzoneGpuMemoryBuffer::GetBufferType() const {
+ return gfx::OZONE_NATIVE_PIXMAP;
+}
+
+#if defined(USE_OZONE)
+scoped_refptr<ui::NativePixmap> OzoneGpuMemoryBuffer::GetNativePixmap() {
+ return native_pixmap_;
+}
+#endif
+
+} // namespace mus
diff --git a/chromium/components/mus/gles2/ozone_gpu_memory_buffer.h b/chromium/components/mus/gles2/ozone_gpu_memory_buffer.h
new file mode 100644
index 00000000000..c5b2ea8a24b
--- /dev/null
+++ b/chromium/components/mus/gles2/ozone_gpu_memory_buffer.h
@@ -0,0 +1,71 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_GLES2_OZONE_GPU_MEMORY_BUFFER_
+#define COMPONENTS_MUS_GLES2_OZONE_GPU_MEMORY_BUFFER_
+
+#include <memory>
+
+#include "components/mus/common/gpu_memory_buffer_impl.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/gpu_memory_buffer.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace ui {
+class ClientNativePixmap;
+class NativePixmap;
+} // namespace ui
+
+namespace mus {
+
+// A not-mojo GpuMemoryBuffer implementation solely for use internally to mus
+// for scanout buffers. Note that OzoneGpuMemoryBuffer is for use on the client
+// (aka CC thread).
+class OzoneGpuMemoryBuffer : public mus::GpuMemoryBufferImpl {
+ public:
+ ~OzoneGpuMemoryBuffer() override;
+
+ // gfx::GpuMemoryBuffer implementation
+ bool Map() override;
+ void Unmap() override;
+ void* memory(size_t plane) override;
+ int stride(size_t plane) const override;
+ gfx::GpuMemoryBufferHandle GetHandle() const override;
+
+ // Returns the type of this GpuMemoryBufferImpl.
+ gfx::GpuMemoryBufferType GetBufferType() const override;
+ scoped_refptr<ui::NativePixmap> GetNativePixmap() override;
+
+ // Create a NativeBuffer. The implementation (mus-specific) will call directly
+ // into ozone to allocate the buffer. See the version in the .cc file.
+ static std::unique_ptr<gfx::GpuMemoryBuffer> CreateOzoneGpuMemoryBuffer(
+ const gfx::Size& size,
+ gfx::BufferFormat format,
+ gfx::BufferUsage usage,
+ gfx::AcceleratedWidget widget);
+
+ // Cribbed from content/common/gpu/client/gpu_memory_buffer_impl.h
+ static OzoneGpuMemoryBuffer* FromClientBuffer(ClientBuffer buffer);
+
+ private:
+ // TODO(rjkroege): It is conceivable that we do not need an |id| here. It
+ // would seem to be a legacy of content leaking into gfx. In a mojo context,
+ // perhaps it should be a mojo handle instead.
+ OzoneGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
+ const gfx::Size& size,
+ gfx::BufferFormat format,
+ std::unique_ptr<ui::ClientNativePixmap> client_pixmap,
+ scoped_refptr<ui::NativePixmap> native_pixmap);
+
+ // The real backing buffer.
+ // From content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap.h
+ std::unique_ptr<ui::ClientNativePixmap> client_pixmap_;
+ scoped_refptr<ui::NativePixmap> native_pixmap_;
+
+ DISALLOW_COPY_AND_ASSIGN(OzoneGpuMemoryBuffer);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_GLES2_OZONE_GPU_MEMORY_BUFFER_
diff --git a/chromium/components/mus/gles2/raster_thread_helper.cc b/chromium/components/mus/gles2/raster_thread_helper.cc
new file mode 100644
index 00000000000..93e37405e78
--- /dev/null
+++ b/chromium/components/mus/gles2/raster_thread_helper.cc
@@ -0,0 +1,27 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/gles2/raster_thread_helper.h"
+
+#include "base/logging.h"
+#include "base/threading/simple_thread.h"
+#include "cc/raster/single_thread_task_graph_runner.h"
+
+namespace gles2 {
+
+RasterThreadHelper::RasterThreadHelper()
+ : task_graph_runner_(new cc::SingleThreadTaskGraphRunner) {
+ task_graph_runner_->Start("CompositorTileWorker1",
+ base::SimpleThread::Options());
+}
+
+RasterThreadHelper::~RasterThreadHelper() {
+ task_graph_runner_->Shutdown();
+}
+
+cc::TaskGraphRunner* RasterThreadHelper::task_graph_runner() {
+ return task_graph_runner_.get();
+}
+
+} // namespace gles2
diff --git a/chromium/components/mus/gles2/raster_thread_helper.h b/chromium/components/mus/gles2/raster_thread_helper.h
new file mode 100644
index 00000000000..a43dfab0810
--- /dev/null
+++ b/chromium/components/mus/gles2/raster_thread_helper.h
@@ -0,0 +1,34 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_GLES2_RASTER_THREAD_HELPER_H_
+#define COMPONENTS_MUS_GLES2_RASTER_THREAD_HELPER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+
+namespace cc {
+class TaskGraphRunner;
+class SingleThreadTaskGraphRunner;
+}
+
+namespace gles2 {
+
+class RasterThreadHelper {
+ public:
+ RasterThreadHelper();
+ ~RasterThreadHelper();
+
+ cc::TaskGraphRunner* task_graph_runner();
+
+ private:
+ std::unique_ptr<cc::SingleThreadTaskGraphRunner> task_graph_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(RasterThreadHelper);
+};
+
+} // namespace gles2
+
+#endif // COMPONENTS_MUS_GLES2_RASTER_THREAD_HELPER_H_
diff --git a/chromium/components/mus/gpu/DEPS b/chromium/components/mus/gpu/DEPS
new file mode 100644
index 00000000000..004a2b9f68f
--- /dev/null
+++ b/chromium/components/mus/gpu/DEPS
@@ -0,0 +1,21 @@
+include_rules = [
+ "+gpu/command_buffer",
+ "+gpu/config",
+ "+gpu/ipc/common",
+ "+gpu/ipc/client/gpu_channel_host.h",
+ "+gpu/ipc/service",
+ "+media/gpu/ipc/service",
+]
+
+specific_include_rules = {
+ "gpu_service_mus\.h": [
+ # A GpuChannelHost is used by mus locally.
+ "+gpu/ipc/client/gpu_channel_host.h",
+ ],
+
+ "mus_gpu_memory_buffer_manager\.cc": [
+ # The GpuMemoryBufferManager implementation used by mus locally.
+ "+gpu/ipc/client/gpu_memory_buffer_impl.h",
+ "+gpu/ipc/client/gpu_memory_buffer_impl_shared_memory.h",
+ ]
+}
diff --git a/chromium/components/mus/gpu/OWNERS b/chromium/components/mus/gpu/OWNERS
new file mode 100644
index 00000000000..1f1af6bcc54
--- /dev/null
+++ b/chromium/components/mus/gpu/OWNERS
@@ -0,0 +1,2 @@
+fsamuel@chromium.org
+rjkroege@chromium.org
diff --git a/chromium/components/mus/gpu/display_compositor/compositor_frame_sink_delegate.h b/chromium/components/mus/gpu/display_compositor/compositor_frame_sink_delegate.h
new file mode 100644
index 00000000000..ee377aadc49
--- /dev/null
+++ b/chromium/components/mus/gpu/display_compositor/compositor_frame_sink_delegate.h
@@ -0,0 +1,27 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_GPU_DISPLAY_COMPOSITOR_COMPOSITOR_FRAME_SINK_DELEGATE_H_
+#define COMPONENTS_MUS_GPU_DISPLAY_COMPOSITOR_COMPOSITOR_FRAME_SINK_DELEGATE_H_
+
+#include "cc/surfaces/surface_id.h"
+
+namespace mus {
+namespace gpu {
+
+class CompositorFrameSinkImpl;
+
+// A CompositorFrameSinkDelegate decouples CompositorFrameSinks from their
+// factories enabling them to be unit tested.
+class CompositorFrameSinkDelegate {
+ public:
+ virtual void CompositorFrameSinkConnectionLost(int sink_id) = 0;
+
+ virtual cc::SurfaceId GenerateSurfaceId() = 0;
+};
+
+} // namespace gpu
+} // namespace mus
+
+#endif // COMPONENTS_MUS_GPU_DISPLAY_COMPOSITOR_COMPOSITOR_FRAME_SINK_DELEGATE_H_
diff --git a/chromium/components/mus/gpu/display_compositor/compositor_frame_sink_factory_impl.cc b/chromium/components/mus/gpu/display_compositor/compositor_frame_sink_factory_impl.cc
new file mode 100644
index 00000000000..5c52940a984
--- /dev/null
+++ b/chromium/components/mus/gpu/display_compositor/compositor_frame_sink_factory_impl.cc
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/gpu/display_compositor/compositor_frame_sink_factory_impl.h"
+
+#include "base/memory/ptr_util.h"
+#include "cc/surfaces/surface_id.h"
+#include "components/mus/gpu/display_compositor/compositor_frame_sink_impl.h"
+
+namespace mus {
+namespace gpu {
+
+CompositorFrameSinkFactoryImpl::CompositorFrameSinkFactoryImpl(
+ uint32_t client_id,
+ mojo::InterfaceRequest<mojom::CompositorFrameSinkFactory> request,
+ const scoped_refptr<SurfacesState>& surfaces_state)
+ : client_id_(client_id),
+ surfaces_state_(surfaces_state),
+ allocator_(client_id),
+ binding_(this, std::move(request)) {}
+
+CompositorFrameSinkFactoryImpl::~CompositorFrameSinkFactoryImpl() {}
+
+void CompositorFrameSinkFactoryImpl::CompositorFrameSinkConnectionLost(
+ int local_id) {
+ sinks_.erase(local_id);
+}
+
+cc::SurfaceId CompositorFrameSinkFactoryImpl::GenerateSurfaceId() {
+ return allocator_.GenerateId();
+}
+
+void CompositorFrameSinkFactoryImpl::CreateCompositorFrameSink(
+ uint32_t local_id,
+ uint64_t nonce,
+ mojo::InterfaceRequest<mojom::CompositorFrameSink> sink,
+ mojom::CompositorFrameSinkClientPtr client) {
+ // TODO(fsamuel): Use nonce once patch lands:
+ // https://codereview.chromium.org/1996783002/
+ sinks_[local_id] = base::WrapUnique(new CompositorFrameSinkImpl(
+ this, local_id, surfaces_state_, std::move(sink), std::move(client)));
+}
+
+} // namespace gpu
+} // namespace mus
diff --git a/chromium/components/mus/gpu/display_compositor/compositor_frame_sink_factory_impl.h b/chromium/components/mus/gpu/display_compositor/compositor_frame_sink_factory_impl.h
new file mode 100644
index 00000000000..50aa0a5a0d3
--- /dev/null
+++ b/chromium/components/mus/gpu/display_compositor/compositor_frame_sink_factory_impl.h
@@ -0,0 +1,55 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_GPU_DISPLAY_COMPOSITOR_COMPOSITOR_FRAME_SINK_FACTORY_IMPL_H_
+#define COMPONENTS_MUS_GPU_DISPLAY_COMPOSITOR_COMPOSITOR_FRAME_SINK_FACTORY_IMPL_H_
+
+#include "cc/surfaces/surface_id_allocator.h"
+#include "components/mus/gpu/display_compositor/compositor_frame_sink_delegate.h"
+#include "components/mus/public/interfaces/gpu/display_compositor.mojom.h"
+#include "components/mus/surfaces/surfaces_state.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+namespace mus {
+namespace gpu {
+
+class CompositorFrameSinkImpl;
+
+class CompositorFrameSinkFactoryImpl : public mojom::CompositorFrameSinkFactory,
+ public CompositorFrameSinkDelegate {
+ public:
+ CompositorFrameSinkFactoryImpl(
+ uint32_t client_id,
+ mojo::InterfaceRequest<mojom::CompositorFrameSinkFactory> request,
+ const scoped_refptr<SurfacesState>& surfaces_state);
+ ~CompositorFrameSinkFactoryImpl() override;
+
+ uint32_t client_id() const { return client_id_; }
+
+ void CompositorFrameSinkConnectionLost(int local_id) override;
+ cc::SurfaceId GenerateSurfaceId() override;
+
+ // mojom::CompositorFrameSinkFactory implementation.
+ void CreateCompositorFrameSink(
+ uint32_t local_id,
+ uint64_t nonce,
+ mojo::InterfaceRequest<mojom::CompositorFrameSink> sink,
+ mojom::CompositorFrameSinkClientPtr client) override;
+
+ private:
+ const uint32_t client_id_;
+ scoped_refptr<SurfacesState> surfaces_state_;
+ cc::SurfaceIdAllocator allocator_;
+ using CompositorFrameSinkMap =
+ std::map<uint32_t, std::unique_ptr<CompositorFrameSinkImpl>>;
+ CompositorFrameSinkMap sinks_;
+ mojo::StrongBinding<mojom::CompositorFrameSinkFactory> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(CompositorFrameSinkFactoryImpl);
+};
+
+} // namespace gpu
+} // namespace mus
+
+#endif // COMPONENTS_MUS_GPU_DISPLAY_COMPOSITOR_COMPOSITOR_FRAME_SINK_FACTORY_IMPL_H_
diff --git a/chromium/components/mus/gpu/display_compositor/compositor_frame_sink_impl.cc b/chromium/components/mus/gpu/display_compositor/compositor_frame_sink_impl.cc
new file mode 100644
index 00000000000..80bb15197df
--- /dev/null
+++ b/chromium/components/mus/gpu/display_compositor/compositor_frame_sink_impl.cc
@@ -0,0 +1,112 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/gpu/display_compositor/compositor_frame_sink_impl.h"
+
+#include "cc/ipc/compositor_frame.mojom.h"
+#include "cc/surfaces/surface_factory.h"
+#include "components/mus/gpu/display_compositor/compositor_frame_sink_delegate.h"
+
+namespace mus {
+namespace gpu {
+
+namespace {
+
+void CallCallback(
+ const mojom::CompositorFrameSink::SubmitCompositorFrameCallback& callback,
+ cc::SurfaceDrawStatus draw_status) {
+ callback.Run(static_cast<mojom::CompositorFrameDrawStatus>(draw_status));
+}
+}
+
+CompositorFrameSinkImpl::CompositorFrameSinkImpl(
+ CompositorFrameSinkDelegate* delegate,
+ int sink_id,
+ const scoped_refptr<SurfacesState>& surfaces_state,
+ mojo::InterfaceRequest<mojom::CompositorFrameSink> request,
+ mojom::CompositorFrameSinkClientPtr client)
+ : delegate_(delegate),
+ surfaces_state_(surfaces_state),
+ sink_id_(sink_id),
+ begin_frame_source_(nullptr),
+ needs_begin_frame_(false),
+ factory_(surfaces_state->manager(), this),
+ client_(std::move(client)),
+ binding_(this, std::move(request)) {
+ DCHECK(delegate_);
+ binding_.set_connection_error_handler(base::Bind(
+ &CompositorFrameSinkImpl::OnConnectionLost, base::Unretained(this)));
+}
+
+CompositorFrameSinkImpl::~CompositorFrameSinkImpl() {}
+
+void CompositorFrameSinkImpl::SetNeedsBeginFrame(bool needs_begin_frame) {
+ if (needs_begin_frame_ == needs_begin_frame)
+ return;
+
+ needs_begin_frame_ = needs_begin_frame;
+ if (begin_frame_source_) {
+ if (needs_begin_frame_)
+ begin_frame_source_->AddObserver(this);
+ else
+ begin_frame_source_->RemoveObserver(this);
+ }
+}
+
+void CompositorFrameSinkImpl::SubmitCompositorFrame(
+ cc::CompositorFrame compositor_frame,
+ const SubmitCompositorFrameCallback& callback) {
+ gfx::Size frame_size =
+ compositor_frame.delegated_frame_data->render_pass_list.back()
+ ->output_rect.size();
+ if (frame_size.IsEmpty() || frame_size != last_submitted_frame_size_) {
+ if (!surface_id_.is_null())
+ factory_.Destroy(surface_id_);
+ // TODO(fsamuel): The allocator should live on CompositorFrameSinkFactory.
+ surface_id_ = delegate_->GenerateSurfaceId();
+ factory_.Create(surface_id_);
+ last_submitted_frame_size_ = frame_size;
+ }
+ factory_.SubmitCompositorFrame(surface_id_, std::move(compositor_frame),
+ base::Bind(&CallCallback, callback));
+}
+
+void CompositorFrameSinkImpl::ReturnResources(
+ const cc::ReturnedResourceArray& resources) {
+ if (!client_)
+ return;
+
+ client_->ReturnResources(mojo::Array<cc::ReturnedResource>::From(resources));
+}
+
+void CompositorFrameSinkImpl::WillDrawSurface(const cc::SurfaceId& surface_id,
+ const gfx::Rect& damage_rect) {
+ NOTIMPLEMENTED();
+}
+
+void CompositorFrameSinkImpl::SetBeginFrameSource(
+ cc::BeginFrameSource* begin_frame_source) {
+ begin_frame_source_ = begin_frame_source;
+}
+
+void CompositorFrameSinkImpl::OnBeginFrame(const cc::BeginFrameArgs& args) {
+ // TODO(fsamuel): Implement this.
+}
+
+const cc::BeginFrameArgs& CompositorFrameSinkImpl::LastUsedBeginFrameArgs()
+ const {
+ // TODO(fsamuel): Implement this.
+ return last_used_begin_frame_args_;
+}
+
+void CompositorFrameSinkImpl::OnBeginFrameSourcePausedChanged(bool paused) {
+ // TODO(fsamuel): Implement this.
+}
+
+void CompositorFrameSinkImpl::OnConnectionLost() {
+ delegate_->CompositorFrameSinkConnectionLost(sink_id_);
+}
+
+} // namespace gpu
+} // namespace mus
diff --git a/chromium/components/mus/gpu/display_compositor/compositor_frame_sink_impl.h b/chromium/components/mus/gpu/display_compositor/compositor_frame_sink_impl.h
new file mode 100644
index 00000000000..e402a7ce2ce
--- /dev/null
+++ b/chromium/components/mus/gpu/display_compositor/compositor_frame_sink_impl.h
@@ -0,0 +1,74 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_GPU_DISPLAY_COMPOSITOR_COMPOSITOR_FRAME_SINK_IMPL_H_
+#define COMPONENTS_MUS_GPU_DISPLAY_COMPOSITOR_COMPOSITOR_FRAME_SINK_IMPL_H_
+
+#include "base/memory/ref_counted.h"
+#include "cc/scheduler/begin_frame_source.h"
+#include "cc/surfaces/surface_factory.h"
+#include "cc/surfaces/surface_factory_client.h"
+#include "cc/surfaces/surface_id.h"
+#include "components/mus/public/interfaces/gpu/display_compositor.mojom.h"
+#include "components/mus/surfaces/surfaces_state.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+namespace mus {
+namespace gpu {
+
+class CompositorFrameSinkDelegate;
+
+// A client presents visuals to screen by submitting CompositorFrames to a
+// CompositorFrameSink.
+class CompositorFrameSinkImpl : public cc::SurfaceFactoryClient,
+ public mojom::CompositorFrameSink,
+ public cc::BeginFrameObserver {
+ public:
+ CompositorFrameSinkImpl(
+ CompositorFrameSinkDelegate* delegate,
+ int sink_id,
+ const scoped_refptr<SurfacesState>& surfaces_state,
+ mojo::InterfaceRequest<mojom::CompositorFrameSink> request,
+ mojom::CompositorFrameSinkClientPtr client);
+ ~CompositorFrameSinkImpl() override;
+
+ // mojom::CompositorFrameSink implementation.
+ void SetNeedsBeginFrame(bool needs_begin_frame) override;
+ void SubmitCompositorFrame(
+ cc::CompositorFrame compositor_frame,
+ const SubmitCompositorFrameCallback& callback) override;
+
+ private:
+ // SurfaceFactoryClient implementation.
+ void ReturnResources(const cc::ReturnedResourceArray& resources) override;
+ void WillDrawSurface(const cc::SurfaceId& surface_id,
+ const gfx::Rect& damage_rect) override;
+ void SetBeginFrameSource(cc::BeginFrameSource* begin_frame_source) override;
+
+ // BeginFrameObserver implementation.
+ void OnBeginFrame(const cc::BeginFrameArgs& args) override;
+ const cc::BeginFrameArgs& LastUsedBeginFrameArgs() const override;
+ void OnBeginFrameSourcePausedChanged(bool paused) override;
+
+ void OnConnectionLost();
+
+ CompositorFrameSinkDelegate* const delegate_;
+ scoped_refptr<SurfacesState> surfaces_state_;
+ const int sink_id_;
+ cc::BeginFrameSource* begin_frame_source_;
+ bool needs_begin_frame_;
+ cc::BeginFrameArgs last_used_begin_frame_args_;
+ cc::SurfaceFactory factory_;
+ cc::SurfaceId surface_id_;
+ gfx::Size last_submitted_frame_size_;
+ mojom::CompositorFrameSinkClientPtr client_;
+ mojo::Binding<mojom::CompositorFrameSink> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(CompositorFrameSinkImpl);
+};
+
+} // namespace gpu
+} // namespace mus
+
+#endif // COMPONENTS_MUS_GPU_DISPLAY_COMPOSITOR_COMPOSITOR_FRAME_SINK_IMPL_H_
diff --git a/chromium/components/mus/gpu/display_compositor/display_compositor_impl.cc b/chromium/components/mus/gpu/display_compositor/display_compositor_impl.cc
new file mode 100644
index 00000000000..cf9a24df684
--- /dev/null
+++ b/chromium/components/mus/gpu/display_compositor/display_compositor_impl.cc
@@ -0,0 +1,28 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/gpu/display_compositor/display_compositor_impl.h"
+
+#include "components/mus/gpu/display_compositor/display_impl.h"
+
+namespace mus {
+namespace gpu {
+
+DisplayCompositorImpl::DisplayCompositorImpl(
+ mojo::InterfaceRequest<mojom::DisplayCompositor> request)
+ : binding_(this, std::move(request)) {}
+
+DisplayCompositorImpl::~DisplayCompositorImpl() {}
+
+void DisplayCompositorImpl::CreateDisplay(
+ int accelerated_widget,
+ mojo::InterfaceRequest<mojom::Display> display,
+ mojom::DisplayHostPtr host,
+ mojo::InterfaceRequest<mojom::CompositorFrameSink> sink,
+ mojom::CompositorFrameSinkClientPtr client) {
+ NOTIMPLEMENTED();
+}
+
+} // namespace gpu
+} // namespace mus
diff --git a/chromium/components/mus/gpu/display_compositor/display_compositor_impl.h b/chromium/components/mus/gpu/display_compositor/display_compositor_impl.h
new file mode 100644
index 00000000000..237cc108b7c
--- /dev/null
+++ b/chromium/components/mus/gpu/display_compositor/display_compositor_impl.h
@@ -0,0 +1,37 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_GPU_DISPLAY_COMPOSITOR_DISPLAY_COMPOSITOR_IMPL_H_
+#define COMPONENTS_MUS_GPU_DISPLAY_COMPOSITOR_DISPLAY_COMPOSITOR_IMPL_H_
+
+#include "components/mus/public/interfaces/gpu/display_compositor.mojom.h"
+#include "components/mus/public/interfaces/gpu/display_compositor_host.mojom.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+namespace mus {
+namespace gpu {
+
+class DisplayCompositorImpl : public mojom::DisplayCompositor {
+ public:
+ explicit DisplayCompositorImpl(
+ mojo::InterfaceRequest<mojom::DisplayCompositor> request);
+ ~DisplayCompositorImpl() override;
+
+ // mojom::DisplayCompositor implementation.
+ void CreateDisplay(int accelerated_widget,
+ mojo::InterfaceRequest<mojom::Display> display,
+ mojom::DisplayHostPtr host,
+ mojo::InterfaceRequest<mojom::CompositorFrameSink> sink,
+ mojom::CompositorFrameSinkClientPtr client) override;
+
+ private:
+ mojo::StrongBinding<mojom::DisplayCompositor> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(DisplayCompositorImpl);
+};
+
+} // namespace gpu
+} // namespace mus
+
+#endif // COMPONENTS_MUS_GPU_DISPLAY_COMPOSITOR_DISPLAY_COMPOSITOR_IMPL_H_
diff --git a/chromium/components/mus/gpu/display_compositor/display_impl.cc b/chromium/components/mus/gpu/display_compositor/display_impl.cc
new file mode 100644
index 00000000000..274c67acbb1
--- /dev/null
+++ b/chromium/components/mus/gpu/display_compositor/display_impl.cc
@@ -0,0 +1,43 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/gpu/display_compositor/display_impl.h"
+
+#include "components/mus/gpu/display_compositor/compositor_frame_sink_impl.h"
+
+namespace mus {
+namespace gpu {
+
+DisplayImpl::DisplayImpl(
+ int accelerated_widget,
+ mojo::InterfaceRequest<mojom::Display> display,
+ mojom::DisplayHostPtr host,
+ mojo::InterfaceRequest<mojom::CompositorFrameSink> sink,
+ mojom::CompositorFrameSinkClientPtr client,
+ const scoped_refptr<SurfacesState>& surfaces_state)
+ : binding_(this, std::move(display)) {
+ const uint32_t client_id = 1;
+ sink_.reset(new CompositorFrameSinkImpl(this, client_id, surfaces_state,
+ std::move(sink), std::move(client)));
+}
+
+DisplayImpl::~DisplayImpl() {}
+
+void DisplayImpl::CreateClient(
+ uint32_t client_id,
+ mojo::InterfaceRequest<mojom::DisplayClient> client) {
+ NOTIMPLEMENTED();
+}
+
+void DisplayImpl::CompositorFrameSinkConnectionLost(int sink_id) {
+ NOTIMPLEMENTED();
+}
+
+cc::SurfaceId DisplayImpl::GenerateSurfaceId() {
+ NOTIMPLEMENTED();
+ return cc::SurfaceId();
+}
+
+} // namespace gpu
+} // namespace mus
diff --git a/chromium/components/mus/gpu/display_compositor/display_impl.h b/chromium/components/mus/gpu/display_compositor/display_impl.h
new file mode 100644
index 00000000000..c667bcbde03
--- /dev/null
+++ b/chromium/components/mus/gpu/display_compositor/display_impl.h
@@ -0,0 +1,44 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_GPU_DISPLAY_COMPOSITOR_DISPLAY_IMPL_H_
+#define COMPONENTS_MUS_GPU_DISPLAY_COMPOSITOR_DISPLAY_IMPL_H_
+
+#include "components/mus/gpu/display_compositor/compositor_frame_sink_delegate.h"
+#include "components/mus/public/interfaces/gpu/display_compositor_host.mojom.h"
+#include "components/mus/surfaces/surfaces_state.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace mus {
+namespace gpu {
+
+class DisplayImpl : public mojom::Display, public CompositorFrameSinkDelegate {
+ public:
+ DisplayImpl(int accelerated_widget,
+ mojo::InterfaceRequest<mojom::Display> display,
+ mojom::DisplayHostPtr host,
+ mojo::InterfaceRequest<mojom::CompositorFrameSink> sink,
+ mojom::CompositorFrameSinkClientPtr client,
+ const scoped_refptr<SurfacesState>& surfaces_state);
+ ~DisplayImpl() override;
+
+ // mojom::Display implementation.
+ void CreateClient(
+ uint32_t client_id,
+ mojo::InterfaceRequest<mojom::DisplayClient> client) override;
+
+ private:
+ // CompositorFrameSinkDelegate implementation:
+ void CompositorFrameSinkConnectionLost(int sink_id) override;
+ cc::SurfaceId GenerateSurfaceId() override;
+
+ std::unique_ptr<CompositorFrameSinkImpl> sink_;
+ mojo::Binding<mojom::Display> binding_;
+ DISALLOW_COPY_AND_ASSIGN(DisplayImpl);
+};
+
+} // namespace gpu
+} // namespace mus
+
+#endif // COMPONENTS_MUS_GPU_DISPLAY_COMPOSITOR_DISPLAY_IMPL_H_
diff --git a/chromium/components/mus/gpu/gpu_service_impl.cc b/chromium/components/mus/gpu/gpu_service_impl.cc
new file mode 100644
index 00000000000..9ea83820ffe
--- /dev/null
+++ b/chromium/components/mus/gpu/gpu_service_impl.cc
@@ -0,0 +1,63 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/gpu/gpu_service_impl.h"
+
+#include "components/mus/common/gpu_type_converters.h"
+#include "components/mus/gpu/gpu_service_mus.h"
+#include "services/shell/public/cpp/connection.h"
+
+namespace mus {
+
+namespace {
+
+void EstablishGpuChannelDone(
+ const mojom::GpuService::EstablishGpuChannelCallback& callback,
+ int32_t client_id,
+ const IPC::ChannelHandle& channel_handle) {
+ // TODO(penghuang): Send the real GPUInfo to the client.
+ callback.Run(client_id, mojom::ChannelHandle::From(channel_handle),
+ mojom::GpuInfo::From<gpu::GPUInfo>(gpu::GPUInfo()));
+}
+}
+
+GpuServiceImpl::GpuServiceImpl(
+ mojo::InterfaceRequest<mojom::GpuService> request,
+ shell::Connection* connection)
+ : binding_(this, std::move(request)) {}
+
+GpuServiceImpl::~GpuServiceImpl() {}
+
+void GpuServiceImpl::EstablishGpuChannel(
+ const mojom::GpuService::EstablishGpuChannelCallback& callback) {
+ GpuServiceMus* service = GpuServiceMus::GetInstance();
+ // TODO(penghuang): crbug.com/617415 figure out how to generate a meaningful
+ // tracing id.
+ const uint64_t client_tracing_id = 0;
+ // TODO(penghuang): windows server may want to control those flags.
+ // Add a private interface for windows server.
+ const bool preempts = false;
+ const bool allow_view_command_buffers = false;
+ const bool allow_real_time_streams = false;
+ service->EstablishGpuChannel(
+ client_tracing_id, preempts, allow_view_command_buffers,
+ allow_real_time_streams, base::Bind(&EstablishGpuChannelDone, callback));
+}
+
+void GpuServiceImpl::CreateGpuMemoryBuffer(
+ mojom::GpuMemoryBufferIdPtr id,
+ const gfx::Size& size,
+ mojom::BufferFormat format,
+ mojom::BufferUsage usage,
+ uint64_t surface_id,
+ const mojom::GpuService::CreateGpuMemoryBufferCallback& callback) {
+ NOTIMPLEMENTED();
+}
+
+void GpuServiceImpl::DestroyGpuMemoryBuffer(mojom::GpuMemoryBufferIdPtr id,
+ const gpu::SyncToken& sync_token) {
+ NOTIMPLEMENTED();
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/gpu/gpu_service_impl.h b/chromium/components/mus/gpu/gpu_service_impl.h
new file mode 100644
index 00000000000..9d5f66ec715
--- /dev/null
+++ b/chromium/components/mus/gpu/gpu_service_impl.h
@@ -0,0 +1,49 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_GPU_GPU_SERVICE_IMPL_H_
+#define COMPONENTS_MUS_GPU_GPU_SERVICE_IMPL_H_
+
+#include "components/mus/public/interfaces/gpu_memory_buffer.mojom.h"
+#include "components/mus/public/interfaces/gpu_service.mojom.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+namespace shell {
+class Connection;
+}
+
+namespace mus {
+
+class GpuServiceImpl : public mojom::GpuService {
+ public:
+ GpuServiceImpl(mojo::InterfaceRequest<mojom::GpuService> request,
+ shell::Connection* connection);
+ ~GpuServiceImpl() override;
+
+ // mojom::GpuService overrides:
+ void EstablishGpuChannel(
+ const mojom::GpuService::EstablishGpuChannelCallback& callback) override;
+
+ void CreateGpuMemoryBuffer(
+ mojom::GpuMemoryBufferIdPtr id,
+ const gfx::Size& size,
+ mojom::BufferFormat format,
+ mojom::BufferUsage usage,
+ uint64_t surface_id,
+ const mojom::GpuService::CreateGpuMemoryBufferCallback& callback)
+ override;
+
+ void DestroyGpuMemoryBuffer(mojom::GpuMemoryBufferIdPtr id,
+ const gpu::SyncToken& sync_token) override;
+
+ private:
+ mojo::StrongBinding<GpuService> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(GpuServiceImpl);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_GPU_GPU_SERVICE_IMPL_H_
diff --git a/chromium/components/mus/gpu/gpu_service_mus.cc b/chromium/components/mus/gpu/gpu_service_mus.cc
new file mode 100644
index 00000000000..43ddb21ab00
--- /dev/null
+++ b/chromium/components/mus/gpu/gpu_service_mus.cc
@@ -0,0 +1,273 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/gpu/gpu_service_mus.h"
+
+#include "base/memory/shared_memory.h"
+#include "base/memory/singleton.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
+#include "components/mus/gpu/mus_gpu_memory_buffer_manager.h"
+#include "gpu/command_buffer/service/gpu_switches.h"
+#include "gpu/command_buffer/service/sync_point_manager.h"
+#include "gpu/config/gpu_info_collector.h"
+#include "gpu/config/gpu_switches.h"
+#include "gpu/config/gpu_util.h"
+#include "gpu/ipc/common/gpu_memory_buffer_support.h"
+#include "gpu/ipc/common/memory_stats.h"
+#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
+#include "ipc/ipc_channel_handle.h"
+#include "ipc/ipc_sync_message_filter.h"
+#include "media/gpu/ipc/service/gpu_jpeg_decode_accelerator.h"
+#include "media/gpu/ipc/service/gpu_video_decode_accelerator.h"
+#include "media/gpu/ipc/service/gpu_video_encode_accelerator.h"
+#include "media/gpu/ipc/service/media_service.h"
+#include "ui/gl/gl_implementation.h"
+#include "ui/gl/gl_switches.h"
+#include "ui/gl/gpu_switching_manager.h"
+#include "ui/gl/init/gl_factory.h"
+#include "url/gurl.h"
+
+#if defined(USE_OZONE)
+#include "ui/ozone/public/ozone_platform.h"
+#endif
+
+namespace mus {
+namespace {
+
+const int kLocalGpuChannelClientId = 1;
+const uint64_t kLocalGpuChannelClientTracingId = 1;
+
+void EstablishGpuChannelDone(
+ int client_id,
+ const IPC::ChannelHandle* channel_handle,
+ const GpuServiceMus::EstablishGpuChannelCallback& callback) {
+ callback.Run(channel_handle ? client_id : -1, *channel_handle);
+}
+}
+
+GpuServiceMus::GpuServiceMus()
+ : next_client_id_(kLocalGpuChannelClientId),
+ main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ shutdown_event_(base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED),
+ gpu_thread_("GpuThread"),
+ io_thread_("GpuIOThread") {
+ Initialize();
+}
+
+GpuServiceMus::~GpuServiceMus() {
+ // Signal this event before destroying the child process. That way all
+ // background threads can cleanup.
+ // For example, in the renderer the RenderThread instances will be able to
+ // notice shutdown before the render process begins waiting for them to exit.
+ shutdown_event_.Signal();
+ io_thread_.Stop();
+}
+
+void GpuServiceMus::EstablishGpuChannel(
+ uint64_t client_tracing_id,
+ bool preempts,
+ bool allow_view_command_buffers,
+ bool allow_real_time_streams,
+ const EstablishGpuChannelCallback& callback) {
+ DCHECK(CalledOnValidThread());
+
+ if (!gpu_channel_manager_) {
+ callback.Run(-1, IPC::ChannelHandle());
+ return;
+ }
+
+ const int client_id = ++next_client_id_;
+ IPC::ChannelHandle* channel_handle = new IPC::ChannelHandle;
+ gpu_thread_.task_runner()->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&GpuServiceMus::EstablishGpuChannelOnGpuThread,
+ base::Unretained(this), client_id, client_tracing_id, preempts,
+ allow_view_command_buffers, allow_real_time_streams,
+ base::Unretained(channel_handle)),
+ base::Bind(&EstablishGpuChannelDone, client_id,
+ base::Owned(channel_handle), callback));
+}
+
+gfx::GpuMemoryBufferHandle GpuServiceMus::CreateGpuMemoryBuffer(
+ gfx::GpuMemoryBufferId id,
+ const gfx::Size& size,
+ gfx::BufferFormat format,
+ gfx::BufferUsage usage,
+ int client_id,
+ gpu::SurfaceHandle surface_handle) {
+ DCHECK(CalledOnValidThread());
+ return gpu_memory_buffer_factory_->CreateGpuMemoryBuffer(
+ id, size, format, usage, client_id, surface_handle);
+}
+
+void GpuServiceMus::DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
+ int client_id,
+ const gpu::SyncToken& sync_token) {
+ DCHECK(CalledOnValidThread());
+
+ if (gpu_channel_manager_)
+ gpu_channel_manager_->DestroyGpuMemoryBuffer(id, client_id, sync_token);
+}
+
+void GpuServiceMus::DidCreateOffscreenContext(const GURL& active_url) {
+ NOTIMPLEMENTED();
+}
+
+void GpuServiceMus::DidDestroyChannel(int client_id) {
+ media_service_->RemoveChannel(client_id);
+ NOTIMPLEMENTED();
+}
+
+void GpuServiceMus::DidDestroyOffscreenContext(const GURL& active_url) {
+ NOTIMPLEMENTED();
+}
+
+void GpuServiceMus::DidLoseContext(bool offscreen,
+ gpu::error::ContextLostReason reason,
+ const GURL& active_url) {
+ NOTIMPLEMENTED();
+}
+
+void GpuServiceMus::GpuMemoryUmaStats(const gpu::GPUMemoryUmaStats& params) {
+ NOTIMPLEMENTED();
+}
+
+void GpuServiceMus::StoreShaderToDisk(int client_id,
+ const std::string& key,
+ const std::string& shader) {
+ NOTIMPLEMENTED();
+}
+
+#if defined(OS_WIN)
+void GpuServiceMus::SendAcceleratedSurfaceCreatedChildWindow(
+ gpu::SurfaceHandle parent_window,
+ gpu::SurfaceHandle child_window) {
+ NOTIMPLEMENTED();
+}
+#endif
+
+void GpuServiceMus::SetActiveURL(const GURL& url) {
+ NOTIMPLEMENTED();
+}
+
+void GpuServiceMus::Initialize() {
+ DCHECK(CalledOnValidThread());
+ base::Thread::Options thread_options(base::MessageLoop::TYPE_DEFAULT, 0);
+ thread_options.priority = base::ThreadPriority::NORMAL;
+ CHECK(gpu_thread_.StartWithOptions(thread_options));
+
+ thread_options = base::Thread::Options(base::MessageLoop::TYPE_IO, 0);
+ thread_options.priority = base::ThreadPriority::NORMAL;
+#if defined(OS_ANDROID)
+ // TODO(reveman): Remove this in favor of setting it explicitly for each type
+ // of process.
+ thread_options.priority = base::ThreadPriority::DISPLAY;
+#endif
+ CHECK(io_thread_.StartWithOptions(thread_options));
+
+ IPC::ChannelHandle channel_handle;
+ base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+ gpu_thread_.task_runner()->PostTask(
+ FROM_HERE, base::Bind(&GpuServiceMus::InitializeOnGpuThread,
+ base::Unretained(this), &channel_handle, &event));
+ event.Wait();
+
+ gpu_memory_buffer_manager_local_.reset(
+ new MusGpuMemoryBufferManager(this, kLocalGpuChannelClientId));
+ gpu_channel_local_ = gpu::GpuChannelHost::Create(
+ this, kLocalGpuChannelClientId, gpu_info_, channel_handle,
+ &shutdown_event_, gpu_memory_buffer_manager_local_.get());
+}
+
+void GpuServiceMus::InitializeOnGpuThread(IPC::ChannelHandle* channel_handle,
+ base::WaitableEvent* event) {
+ gpu_info_.video_decode_accelerator_capabilities =
+ media::GpuVideoDecodeAccelerator::GetCapabilities(gpu_preferences_);
+ gpu_info_.video_encode_accelerator_supported_profiles =
+ media::GpuVideoEncodeAccelerator::GetSupportedProfiles(gpu_preferences_);
+ gpu_info_.jpeg_decode_accelerator_supported =
+ media::GpuJpegDecodeAccelerator::IsSupported();
+
+#if defined(USE_OZONE)
+ ui::OzonePlatform::InitializeForGPU();
+#endif
+
+ if (gpu::GetNativeGpuMemoryBufferType() != gfx::EMPTY_BUFFER) {
+ gpu_memory_buffer_factory_ =
+ gpu::GpuMemoryBufferFactory::CreateNativeType();
+ }
+
+ if (!gl::init::InitializeGLOneOff())
+ VLOG(1) << "gl::init::InitializeGLOneOff failed";
+
+ DCHECK(!owned_sync_point_manager_);
+ const bool allow_threaded_wait = false;
+ owned_sync_point_manager_.reset(
+ new gpu::SyncPointManager(allow_threaded_wait));
+
+ // Defer creation of the render thread. This is to prevent it from handling
+ // IPC messages before the sandbox has been enabled and all other necessary
+ // initialization has succeeded.
+ // TODO(penghuang): implement a watchdog.
+ gpu::GpuWatchdog* watchdog = nullptr;
+ gpu_channel_manager_.reset(new gpu::GpuChannelManager(
+ gpu_preferences_, this, watchdog,
+ base::ThreadTaskRunnerHandle::Get().get(), io_thread_.task_runner().get(),
+ &shutdown_event_, owned_sync_point_manager_.get(),
+ gpu_memory_buffer_factory_.get()));
+
+ media_service_.reset(new media::MediaService(gpu_channel_manager_.get()));
+
+ const bool preempts = true;
+ const bool allow_view_command_buffers = true;
+ const bool allow_real_time_streams = true;
+ EstablishGpuChannelOnGpuThread(
+ kLocalGpuChannelClientId, kLocalGpuChannelClientTracingId, preempts,
+ allow_view_command_buffers, allow_real_time_streams, channel_handle);
+ event->Signal();
+}
+
+void GpuServiceMus::EstablishGpuChannelOnGpuThread(
+ int client_id,
+ uint64_t client_tracing_id,
+ bool preempts,
+ bool allow_view_command_buffers,
+ bool allow_real_time_streams,
+ IPC::ChannelHandle* channel_handle) {
+ if (gpu_channel_manager_) {
+ *channel_handle = gpu_channel_manager_->EstablishChannel(
+ client_id, client_tracing_id, preempts, allow_view_command_buffers,
+ allow_real_time_streams);
+ media_service_->AddChannel(client_id);
+ }
+}
+
+bool GpuServiceMus::IsMainThread() {
+ return main_task_runner_->BelongsToCurrentThread();
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+GpuServiceMus::GetIOThreadTaskRunner() {
+ return io_thread_.task_runner();
+}
+
+std::unique_ptr<base::SharedMemory> GpuServiceMus::AllocateSharedMemory(
+ size_t size) {
+ std::unique_ptr<base::SharedMemory> shm(new base::SharedMemory());
+ if (!shm->CreateAnonymous(size))
+ return std::unique_ptr<base::SharedMemory>();
+ return shm;
+}
+
+// static
+GpuServiceMus* GpuServiceMus::GetInstance() {
+ return base::Singleton<GpuServiceMus,
+ base::LeakySingletonTraits<GpuServiceMus>>::get();
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/gpu/gpu_service_mus.h b/chromium/components/mus/gpu/gpu_service_mus.h
new file mode 100644
index 00000000000..b6bfd1b69f5
--- /dev/null
+++ b/chromium/components/mus/gpu/gpu_service_mus.h
@@ -0,0 +1,173 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_GPU_GPU_SERVICE_MUS_H_
+#define COMPONENTS_MUS_GPU_GPU_SERVICE_MUS_H_
+
+#include "base/callback.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/threading/thread.h"
+#include "build/build_config.h"
+#include "gpu/command_buffer/service/gpu_preferences.h"
+#include "gpu/config/gpu_info.h"
+#include "gpu/ipc/client/gpu_channel_host.h"
+#include "gpu/ipc/common/surface_handle.h"
+#include "gpu/ipc/service/gpu_channel.h"
+#include "gpu/ipc/service/gpu_channel_manager.h"
+#include "gpu/ipc/service/gpu_channel_manager_delegate.h"
+#include "gpu/ipc/service/gpu_config.h"
+#include "gpu/ipc/service/x_util.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace base {
+template <typename T>
+struct DefaultSingletonTraits;
+}
+
+namespace IPC {
+struct ChannelHandle;
+}
+
+namespace gpu {
+class GpuChannelHost;
+class GpuMemoryBufferFactory;
+class SyncPointManager;
+}
+
+namespace media {
+class MediaService;
+}
+
+namespace mus {
+
+class MusGpuMemoryBufferManager;
+
+// TODO(fsamuel): GpuServiceMus is intended to be the Gpu thread within Mus.
+// Similar to GpuChildThread, it is a GpuChannelManagerDelegate and will have a
+// GpuChannelManager.
+class GpuServiceMus : public gpu::GpuChannelManagerDelegate,
+ public gpu::GpuChannelHostFactory,
+ public base::NonThreadSafe {
+ public:
+ typedef base::Callback<void(int client_id, const IPC::ChannelHandle&)>
+ EstablishGpuChannelCallback;
+ void EstablishGpuChannel(uint64_t client_tracing_id,
+ bool preempts,
+ bool allow_view_command_buffers,
+ bool allow_real_time_streams,
+ const EstablishGpuChannelCallback& callback);
+ gfx::GpuMemoryBufferHandle CreateGpuMemoryBuffer(
+ gfx::GpuMemoryBufferId id,
+ const gfx::Size& size,
+ gfx::BufferFormat format,
+ gfx::BufferUsage usage,
+ int client_id,
+ gpu::SurfaceHandle surface_handle);
+ gfx::GpuMemoryBufferHandle CreateGpuMemoryBufferFromeHandle(
+ gfx::GpuMemoryBufferHandle buffer_handle,
+ gfx::GpuMemoryBufferId id,
+ const gfx::Size& size,
+ gfx::BufferFormat format,
+ int client_id);
+ void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
+ int client_id,
+ const gpu::SyncToken& sync_token);
+
+ gpu::GpuChannelManager* gpu_channel_manager() const {
+ return gpu_channel_manager_.get();
+ }
+
+ gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory() const {
+ return gpu_memory_buffer_factory_.get();
+ }
+
+ scoped_refptr<gpu::GpuChannelHost> gpu_channel_local() const {
+ return gpu_channel_local_;
+ }
+
+ const gpu::GPUInfo& gpu_info() const { return gpu_info_; }
+
+ // GpuChannelManagerDelegate overrides:
+ void DidCreateOffscreenContext(const GURL& active_url) override;
+ void DidDestroyChannel(int client_id) override;
+ void DidDestroyOffscreenContext(const GURL& active_url) override;
+ void DidLoseContext(bool offscreen,
+ gpu::error::ContextLostReason reason,
+ const GURL& active_url) override;
+ void GpuMemoryUmaStats(const gpu::GPUMemoryUmaStats& params) override;
+ void StoreShaderToDisk(int client_id,
+ const std::string& key,
+ const std::string& shader) override;
+#if defined(OS_WIN)
+ void SendAcceleratedSurfaceCreatedChildWindow(
+ gpu::SurfaceHandle parent_window,
+ gpu::SurfaceHandle child_window) override;
+#endif
+ void SetActiveURL(const GURL& url) override;
+
+ // GpuChannelHostFactory overrides:
+ bool IsMainThread() override;
+ scoped_refptr<base::SingleThreadTaskRunner> GetIOThreadTaskRunner() override;
+ std::unique_ptr<base::SharedMemory> AllocateSharedMemory(
+ size_t size) override;
+
+ static GpuServiceMus* GetInstance();
+
+ private:
+ friend struct base::DefaultSingletonTraits<GpuServiceMus>;
+
+ GpuServiceMus();
+ ~GpuServiceMus() override;
+
+ void Initialize();
+ void InitializeOnGpuThread(IPC::ChannelHandle* channel_handle,
+ base::WaitableEvent* event);
+ void EstablishGpuChannelOnGpuThread(int client_id,
+ uint64_t client_tracing_id,
+ bool preempts,
+ bool allow_view_command_buffers,
+ bool allow_real_time_streams,
+ IPC::ChannelHandle* channel_handle);
+
+ // The next client id.
+ int next_client_id_;
+
+ // The main thread task runner.
+ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
+
+ // An event that will be signalled when we shutdown.
+ base::WaitableEvent shutdown_event_;
+
+ // The main thread for GpuService.
+ base::Thread gpu_thread_;
+
+ // The thread that handles IO events for GpuService.
+ base::Thread io_thread_;
+
+ std::unique_ptr<gpu::SyncPointManager> owned_sync_point_manager_;
+
+ std::unique_ptr<gpu::GpuChannelManager> gpu_channel_manager_;
+
+ std::unique_ptr<media::MediaService> media_service_;
+
+ std::unique_ptr<gpu::GpuMemoryBufferFactory> gpu_memory_buffer_factory_;
+
+ // A GPU memory buffer manager used locally.
+ std::unique_ptr<MusGpuMemoryBufferManager> gpu_memory_buffer_manager_local_;
+
+ // A GPU channel used locally.
+ scoped_refptr<gpu::GpuChannelHost> gpu_channel_local_;
+
+ gpu::GpuPreferences gpu_preferences_;
+
+ // Information about the GPU, such as device and vendor ID.
+ gpu::GPUInfo gpu_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(GpuServiceMus);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_GPU_GPU_SERVICE_MUS_H_
diff --git a/chromium/components/mus/gpu/mus_gpu_memory_buffer_manager.cc b/chromium/components/mus/gpu/mus_gpu_memory_buffer_manager.cc
new file mode 100644
index 00000000000..d9ecbd3257a
--- /dev/null
+++ b/chromium/components/mus/gpu/mus_gpu_memory_buffer_manager.cc
@@ -0,0 +1,115 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/gpu/mus_gpu_memory_buffer_manager.h"
+
+#include "base/logging.h"
+#include "components/mus/common/generic_shared_memory_id_generator.h"
+#include "components/mus/gpu/gpu_service_mus.h"
+#include "gpu/ipc/client/gpu_memory_buffer_impl.h"
+#include "gpu/ipc/client/gpu_memory_buffer_impl_shared_memory.h"
+#include "gpu/ipc/common/gpu_memory_buffer_support.h"
+#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
+
+namespace mus {
+
+namespace {
+
+MusGpuMemoryBufferManager* g_gpu_memory_buffer_manager = nullptr;
+
+bool IsNativeGpuMemoryBufferFactoryConfigurationSupported(
+ gfx::BufferFormat format,
+ gfx::BufferUsage usage) {
+ switch (gpu::GetNativeGpuMemoryBufferType()) {
+ case gfx::SHARED_MEMORY_BUFFER:
+ return false;
+ case gfx::IO_SURFACE_BUFFER:
+ case gfx::SURFACE_TEXTURE_BUFFER:
+ case gfx::OZONE_NATIVE_PIXMAP:
+ return gpu::IsNativeGpuMemoryBufferConfigurationSupported(format, usage);
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+}
+
+MusGpuMemoryBufferManager* MusGpuMemoryBufferManager::current() {
+ return g_gpu_memory_buffer_manager;
+}
+
+MusGpuMemoryBufferManager::MusGpuMemoryBufferManager(GpuServiceMus* gpu_service,
+ int client_id)
+ : gpu_service_(gpu_service), client_id_(client_id), weak_factory_(this) {
+ DCHECK(!g_gpu_memory_buffer_manager);
+ g_gpu_memory_buffer_manager = this;
+}
+
+MusGpuMemoryBufferManager::~MusGpuMemoryBufferManager() {
+ g_gpu_memory_buffer_manager = nullptr;
+}
+
+std::unique_ptr<gfx::GpuMemoryBuffer>
+MusGpuMemoryBufferManager::AllocateGpuMemoryBuffer(
+ const gfx::Size& size,
+ gfx::BufferFormat format,
+ gfx::BufferUsage usage,
+ gpu::SurfaceHandle surface_handle) {
+ gfx::GpuMemoryBufferId id = GetNextGenericSharedMemoryId();
+ const bool is_native =
+ IsNativeGpuMemoryBufferFactoryConfigurationSupported(format, usage);
+ if (is_native) {
+ gfx::GpuMemoryBufferHandle handle =
+ gpu_service_->gpu_memory_buffer_factory()->CreateGpuMemoryBuffer(
+ id, size, format, usage, client_id_, surface_handle);
+ if (handle.is_null())
+ return nullptr;
+ return gpu::GpuMemoryBufferImpl::CreateFromHandle(
+ handle, size, format, usage,
+ base::Bind(&MusGpuMemoryBufferManager::DestroyGpuMemoryBuffer,
+ weak_factory_.GetWeakPtr(), id, client_id_, is_native));
+ }
+
+ DCHECK(gpu::GpuMemoryBufferImplSharedMemory::IsUsageSupported(usage))
+ << static_cast<int>(usage);
+ return gpu::GpuMemoryBufferImplSharedMemory::Create(
+ id, size, format,
+ base::Bind(&MusGpuMemoryBufferManager::DestroyGpuMemoryBuffer,
+ weak_factory_.GetWeakPtr(), id, client_id_, is_native));
+}
+
+std::unique_ptr<gfx::GpuMemoryBuffer>
+MusGpuMemoryBufferManager::CreateGpuMemoryBufferFromHandle(
+ const gfx::GpuMemoryBufferHandle& handle,
+ const gfx::Size& size,
+ gfx::BufferFormat format) {
+ NOTIMPLEMENTED();
+ return nullptr;
+}
+
+gfx::GpuMemoryBuffer*
+MusGpuMemoryBufferManager::GpuMemoryBufferFromClientBuffer(
+ ClientBuffer buffer) {
+ return gpu::GpuMemoryBufferImpl::FromClientBuffer(buffer);
+}
+
+void MusGpuMemoryBufferManager::SetDestructionSyncToken(
+ gfx::GpuMemoryBuffer* buffer,
+ const gpu::SyncToken& sync_token) {
+ static_cast<gpu::GpuMemoryBufferImpl*>(buffer)->set_destruction_sync_token(
+ sync_token);
+}
+
+void MusGpuMemoryBufferManager::DestroyGpuMemoryBuffer(
+ gfx::GpuMemoryBufferId id,
+ int client_id,
+ bool is_native,
+ const gpu::SyncToken& sync_token) {
+ if (is_native) {
+ gpu_service_->gpu_channel_manager()->DestroyGpuMemoryBuffer(id, client_id,
+ sync_token);
+ }
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/gpu/mus_gpu_memory_buffer_manager.h b/chromium/components/mus/gpu/mus_gpu_memory_buffer_manager.h
new file mode 100644
index 00000000000..b705dd68bb8
--- /dev/null
+++ b/chromium/components/mus/gpu/mus_gpu_memory_buffer_manager.h
@@ -0,0 +1,57 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_GPU_MUS_GPU_MEMORY_BUFFER_MANAGER_H_
+#define COMPONENTS_MUS_GPU_MUS_GPU_MEMORY_BUFFER_MANAGER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
+
+namespace mus {
+
+class GpuServiceMus;
+
+// This GpuMemoryBufferManager is for establishing a GpuChannelHost used by
+// mus locally.
+class MusGpuMemoryBufferManager : public gpu::GpuMemoryBufferManager {
+ public:
+ MusGpuMemoryBufferManager(GpuServiceMus* gpu_service, int client_id);
+ ~MusGpuMemoryBufferManager() override;
+
+ static MusGpuMemoryBufferManager* current();
+
+ // Overridden from gpu::GpuMemoryBufferManager:
+ std::unique_ptr<gfx::GpuMemoryBuffer> AllocateGpuMemoryBuffer(
+ const gfx::Size& size,
+ gfx::BufferFormat format,
+ gfx::BufferUsage usage,
+ gpu::SurfaceHandle surface_handle) override;
+ std::unique_ptr<gfx::GpuMemoryBuffer> CreateGpuMemoryBufferFromHandle(
+ const gfx::GpuMemoryBufferHandle& handle,
+ const gfx::Size& size,
+ gfx::BufferFormat format) override;
+ gfx::GpuMemoryBuffer* GpuMemoryBufferFromClientBuffer(
+ ClientBuffer buffer) override;
+ void SetDestructionSyncToken(gfx::GpuMemoryBuffer* buffer,
+ const gpu::SyncToken& sync_token) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MusGpuMemoryBufferManager);
+
+ void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
+ int client_id,
+ bool is_native,
+ const gpu::SyncToken& sync_token);
+
+ GpuServiceMus* gpu_service_;
+ const int client_id_;
+ base::WeakPtrFactory<MusGpuMemoryBufferManager> weak_factory_;
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_GPU_MUS_GPU_MEMORY_BUFFER_MANAGER_H_
diff --git a/chromium/components/mus/gpu/mus_gpu_unittests_app_manifest.json b/chromium/components/mus/gpu/mus_gpu_unittests_app_manifest.json
new file mode 100644
index 00000000000..26e8caa4802
--- /dev/null
+++ b/chromium/components/mus/gpu/mus_gpu_unittests_app_manifest.json
@@ -0,0 +1,10 @@
+{
+ "manifest_version": 1,
+ "name": "mojo:mus_gpu_unittests_app",
+ "display_name": "Mus GPU Unittests",
+ "capabilities": {
+ "required": {
+ "*": { "classes": [ "app" ] }
+ }
+ }
+}
diff --git a/chromium/components/mus/input_devices/input_device_server.cc b/chromium/components/mus/input_devices/input_device_server.cc
new file mode 100644
index 00000000000..3514eb35669
--- /dev/null
+++ b/chromium/components/mus/input_devices/input_device_server.cc
@@ -0,0 +1,110 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/input_devices/input_device_server.h"
+
+#include <utility>
+#include <vector>
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "ui/events/devices/input_device.h"
+#include "ui/events/devices/touchscreen_device.h"
+
+namespace mus {
+
+InputDeviceServer::InputDeviceServer() {}
+
+InputDeviceServer::~InputDeviceServer() {
+ if (manager_ && ui::DeviceDataManager::HasInstance()) {
+ manager_->RemoveObserver(this);
+ manager_ = nullptr;
+ }
+}
+
+void InputDeviceServer::RegisterAsObserver() {
+ if (!manager_ && ui::DeviceDataManager::HasInstance()) {
+ manager_ = ui::DeviceDataManager::GetInstance();
+ manager_->AddObserver(this);
+ }
+}
+
+bool InputDeviceServer::IsRegisteredAsObserver() const {
+ return manager_ != nullptr;
+}
+
+void InputDeviceServer::AddInterface(shell::Connection* connection) {
+ DCHECK(manager_);
+ connection->AddInterface<mojom::InputDeviceServer>(this);
+}
+
+void InputDeviceServer::AddObserver(
+ mojom::InputDeviceObserverMojoPtr observer) {
+ // We only want to send this message once, so we need to check to make sure
+ // device lists are actually complete before sending it to a new observer.
+ if (manager_->AreDeviceListsComplete())
+ SendDeviceListsComplete(observer.get());
+ observers_.AddPtr(std::move(observer));
+}
+
+void InputDeviceServer::OnKeyboardDeviceConfigurationChanged() {
+ if (!manager_->AreDeviceListsComplete())
+ return;
+
+ auto& devices = manager_->GetKeyboardDevices();
+ observers_.ForAllPtrs([&devices](mojom::InputDeviceObserverMojo* observer) {
+ observer->OnKeyboardDeviceConfigurationChanged(devices);
+ });
+}
+
+void InputDeviceServer::OnTouchscreenDeviceConfigurationChanged() {
+ if (!manager_->AreDeviceListsComplete())
+ return;
+
+ auto& devices = manager_->GetTouchscreenDevices();
+ observers_.ForAllPtrs([&devices](mojom::InputDeviceObserverMojo* observer) {
+ observer->OnTouchscreenDeviceConfigurationChanged(devices);
+ });
+}
+
+void InputDeviceServer::OnMouseDeviceConfigurationChanged() {
+ if (!manager_->AreDeviceListsComplete())
+ return;
+
+ auto& devices = manager_->GetMouseDevices();
+ observers_.ForAllPtrs([&devices](mojom::InputDeviceObserverMojo* observer) {
+ observer->OnMouseDeviceConfigurationChanged(devices);
+ });
+}
+
+void InputDeviceServer::OnTouchpadDeviceConfigurationChanged() {
+ if (!manager_->AreDeviceListsComplete())
+ return;
+
+ auto& devices = manager_->GetTouchpadDevices();
+ observers_.ForAllPtrs([&devices](mojom::InputDeviceObserverMojo* observer) {
+ observer->OnTouchpadDeviceConfigurationChanged(devices);
+ });
+}
+
+void InputDeviceServer::OnDeviceListsComplete() {
+ observers_.ForAllPtrs([this](mojom::InputDeviceObserverMojo* observer) {
+ SendDeviceListsComplete(observer);
+ });
+}
+
+void InputDeviceServer::SendDeviceListsComplete(
+ mojom::InputDeviceObserverMojo* observer) {
+ DCHECK(manager_->AreDeviceListsComplete());
+
+ observer->OnDeviceListsComplete(
+ manager_->GetKeyboardDevices(), manager_->GetTouchscreenDevices(),
+ manager_->GetMouseDevices(), manager_->GetTouchpadDevices());
+}
+
+void InputDeviceServer::Create(shell::Connection* connection,
+ mojom::InputDeviceServerRequest request) {
+ bindings_.AddBinding(this, std::move(request));
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/input_devices/input_device_server.h b/chromium/components/mus/input_devices/input_device_server.h
new file mode 100644
index 00000000000..af0e0e704e6
--- /dev/null
+++ b/chromium/components/mus/input_devices/input_device_server.h
@@ -0,0 +1,69 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_INPUT_DEVICES_INPUT_DEVICE_SERVER_H_
+#define COMPONENTS_MUS_INPUT_DEVICES_INPUT_DEVICE_SERVER_H_
+
+#include "base/macros.h"
+#include "components/mus/public/interfaces/input_devices/input_device_server.mojom.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_ptr_set.h"
+#include "services/shell/public/cpp/connection.h"
+#include "services/shell/public/cpp/interface_factory.h"
+#include "ui/events/devices/device_data_manager.h"
+#include "ui/events/devices/input_device_event_observer.h"
+
+namespace mus {
+
+// Listens to DeviceDataManager for updates on input-devices and forwards those
+// updates to any registered InputDeviceObserverMojo in other processes via
+// Mojo IPC. This runs in the mus-ws process.
+class InputDeviceServer
+ : public shell::InterfaceFactory<mojom::InputDeviceServer>,
+ public mojom::InputDeviceServer,
+ public ui::InputDeviceEventObserver {
+ public:
+ InputDeviceServer();
+ ~InputDeviceServer() override;
+
+ // Registers this instance as a local observer with DeviceDataManager.
+ void RegisterAsObserver();
+ bool IsRegisteredAsObserver() const;
+
+ // Adds interface with the shell connection so remote observers can connect.
+ // You should have already called RegisterAsObserver() to get local
+ // input-device event updates and checked it was successful by calling
+ // IsRegisteredAsObserver().
+ void AddInterface(shell::Connection* connection);
+
+ // mojom::InputDeviceServer:
+ void AddObserver(mojom::InputDeviceObserverMojoPtr observer) override;
+
+ // ui::InputDeviceEventObserver:
+ void OnKeyboardDeviceConfigurationChanged() override;
+ void OnTouchscreenDeviceConfigurationChanged() override;
+ void OnMouseDeviceConfigurationChanged() override;
+ void OnTouchpadDeviceConfigurationChanged() override;
+ void OnDeviceListsComplete() override;
+
+ private:
+ // Sends the current state of all input-devices to an observer.
+ void SendDeviceListsComplete(mojom::InputDeviceObserverMojo* observer);
+
+ // mojo::InterfaceFactory<mojom::InputDeviceServer>:
+ void Create(shell::Connection* connection,
+ mojom::InputDeviceServerRequest request) override;
+
+ mojo::BindingSet<mojom::InputDeviceServer> bindings_;
+ mojo::InterfacePtrSet<mojom::InputDeviceObserverMojo> observers_;
+
+ // DeviceDataManager instance we are registered as an observer with.
+ ui::DeviceDataManager* manager_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(InputDeviceServer);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_INPUT_DEVICES_INPUT_DEVICE_SERVER_H_
diff --git a/chromium/components/mus/main.cc b/chromium/components/mus/main.cc
new file mode 100644
index 00000000000..32c40ef407a
--- /dev/null
+++ b/chromium/components/mus/main.cc
@@ -0,0 +1,13 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/mus_app.h"
+#include "mojo/public/c/system/main.h"
+#include "services/shell/public/cpp/application_runner.h"
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+ shell::ApplicationRunner runner(new mus::MusApp);
+ runner.set_message_loop_type(base::MessageLoop::TYPE_UI);
+ return runner.Run(shell_handle);
+}
diff --git a/chromium/components/mus/manifest.json b/chromium/components/mus/manifest.json
new file mode 100644
index 00000000000..1969bec66e7
--- /dev/null
+++ b/chromium/components/mus/manifest.json
@@ -0,0 +1,26 @@
+{
+ "manifest_version": 1,
+ "name": "mojo:mus",
+ "display_name": "UI Service",
+ "capabilities": {
+ "provided": {
+ // A collection of interfaces needed by a generic client of mus.
+ // Additional interfaces may be requested a-la-carte.
+ "app": [
+ "mus::mojom::Clipboard",
+ "mus::mojom::DisplayManager",
+ "mus::mojom::Gpu",
+ "mus::mojom::GpuService",
+ "mus::mojom::InputDeviceServer",
+ "mus::mojom::WindowTreeFactory"
+ ],
+ "test": [
+ "mus::mojom::WindowServerTest"
+ ]
+ },
+ "required": {
+ "*": { "classes": [ "app" ] },
+ "mojo:shell": { "classes": [ "shell:all_users", "shell:explicit_class" ] }
+ }
+ }
+}
diff --git a/chromium/components/mus/mus_app.cc b/chromium/components/mus/mus_app.cc
new file mode 100644
index 00000000000..8ec1901a0c6
--- /dev/null
+++ b/chromium/components/mus/mus_app.cc
@@ -0,0 +1,388 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/mus_app.h"
+
+#include <set>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/platform_thread.h"
+#include "build/build_config.h"
+#include "components/mus/clipboard/clipboard_impl.h"
+#include "components/mus/common/switches.h"
+#include "components/mus/gles2/gpu_impl.h"
+#include "components/mus/gpu/gpu_service_impl.h"
+#include "components/mus/gpu/gpu_service_mus.h"
+#include "components/mus/ws/display.h"
+#include "components/mus/ws/display_binding.h"
+#include "components/mus/ws/display_manager.h"
+#include "components/mus/ws/platform_screen.h"
+#include "components/mus/ws/user_activity_monitor.h"
+#include "components/mus/ws/user_display_manager.h"
+#include "components/mus/ws/window_server.h"
+#include "components/mus/ws/window_server_test_impl.h"
+#include "components/mus/ws/window_tree.h"
+#include "components/mus/ws/window_tree_binding.h"
+#include "components/mus/ws/window_tree_factory.h"
+#include "components/mus/ws/window_tree_host_factory.h"
+#include "mojo/public/c/system/main.h"
+#include "services/catalog/public/cpp/resource_loader.h"
+#include "services/shell/public/cpp/connection.h"
+#include "services/shell/public/cpp/connector.h"
+#include "services/tracing/public/cpp/tracing_impl.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/ui_base_paths.h"
+#include "ui/events/event_switches.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gl/gl_surface.h"
+
+#if defined(USE_X11)
+#include <X11/Xlib.h>
+#include "ui/platform_window/x11/x11_window.h"
+#elif defined(USE_OZONE)
+#include "ui/events/ozone/layout/keyboard_layout_engine.h"
+#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
+#include "ui/ozone/public/ozone_platform.h"
+#endif
+
+using shell::Connection;
+using mojo::InterfaceRequest;
+using mus::mojom::Gpu;
+using mus::mojom::WindowServerTest;
+using mus::mojom::WindowTreeHostFactory;
+
+namespace mus {
+
+namespace {
+
+const char kResourceFileStrings[] = "mus_app_resources_strings.pak";
+const char kResourceFile100[] = "mus_app_resources_100.pak";
+const char kResourceFile200[] = "mus_app_resources_200.pak";
+
+} // namespace
+
+// TODO(sky): this is a pretty typical pattern, make it easier to do.
+struct MusApp::PendingRequest {
+ shell::Connection* connection;
+ std::unique_ptr<mojom::WindowTreeFactoryRequest> wtf_request;
+ std::unique_ptr<mojom::DisplayManagerRequest> dm_request;
+};
+
+struct MusApp::UserState {
+ std::unique_ptr<clipboard::ClipboardImpl> clipboard;
+ std::unique_ptr<ws::WindowTreeHostFactory> window_tree_host_factory;
+};
+
+MusApp::MusApp()
+ : test_config_(false),
+ // TODO(penghuang): Kludge: Use mojo command buffer when running on
+ // Windows since chrome command buffer breaks unit tests
+#if defined(OS_WIN)
+ use_chrome_gpu_command_buffer_(false),
+#else
+ use_chrome_gpu_command_buffer_(true),
+#endif
+ platform_screen_(ws::PlatformScreen::Create()),
+ weak_ptr_factory_(this) {}
+
+MusApp::~MusApp() {
+ // Destroy |window_server_| first, since it depends on |event_source_|.
+ // WindowServer (or more correctly its Displays) may have state that needs to
+ // be destroyed before GpuState as well.
+ window_server_.reset();
+
+ if (platform_display_init_params_.gpu_state)
+ platform_display_init_params_.gpu_state->StopThreads();
+}
+
+void MusApp::InitializeResources(shell::Connector* connector) {
+ if (ui::ResourceBundle::HasSharedInstance())
+ return;
+
+ std::set<std::string> resource_paths;
+ resource_paths.insert(kResourceFileStrings);
+ resource_paths.insert(kResourceFile100);
+ resource_paths.insert(kResourceFile200);
+
+ catalog::ResourceLoader loader;
+ filesystem::mojom::DirectoryPtr directory;
+ connector->ConnectToInterface("mojo:catalog", &directory);
+ CHECK(loader.OpenFiles(std::move(directory), resource_paths));
+
+ ui::RegisterPathProvider();
+
+ // Initialize resource bundle with 1x and 2x cursor bitmaps.
+ ui::ResourceBundle::InitSharedInstanceWithPakFileRegion(
+ loader.TakeFile(kResourceFileStrings),
+ base::MemoryMappedFile::Region::kWholeFile);
+ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+ rb.AddDataPackFromFile(loader.TakeFile(kResourceFile100),
+ ui::SCALE_FACTOR_100P);
+ rb.AddDataPackFromFile(loader.TakeFile(kResourceFile200),
+ ui::SCALE_FACTOR_200P);
+}
+
+MusApp::UserState* MusApp::GetUserState(shell::Connection* connection) {
+ const ws::UserId& user_id = connection->GetRemoteIdentity().user_id();
+ auto it = user_id_to_user_state_.find(user_id);
+ if (it != user_id_to_user_state_.end())
+ return it->second.get();
+ user_id_to_user_state_[user_id] = base::WrapUnique(new UserState);
+ return user_id_to_user_state_[user_id].get();
+}
+
+void MusApp::AddUserIfNecessary(shell::Connection* connection) {
+ window_server_->user_id_tracker()->AddUserId(
+ connection->GetRemoteIdentity().user_id());
+}
+
+void MusApp::Initialize(shell::Connector* connector,
+ const shell::Identity& identity,
+ uint32_t id) {
+ platform_display_init_params_.surfaces_state = new SurfacesState;
+
+ base::PlatformThread::SetName("mus");
+ tracing_.Initialize(connector, identity.name());
+ TRACE_EVENT0("mus", "MusApp::Initialize started");
+
+ test_config_ = base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kUseTestConfig);
+// TODO(penghuang): Kludge: use mojo command buffer when running on Windows
+// since Chrome command buffer breaks unit tests
+#if defined(OS_WIN)
+ use_chrome_gpu_command_buffer_ = false;
+#else
+ use_chrome_gpu_command_buffer_ =
+ !base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kUseMojoGpuCommandBufferInMus);
+#endif
+#if defined(USE_X11)
+ XInitThreads();
+ if (test_config_)
+ ui::test::SetUseOverrideRedirectWindowByDefault(true);
+#endif
+
+ InitializeResources(connector);
+
+#if defined(USE_OZONE)
+ // The ozone platform can provide its own event source. So initialize the
+ // platform before creating the default event source.
+ // Because GL libraries need to be initialized before entering the sandbox,
+ // in MUS, |InitializeForUI| will load the GL libraries.
+ ui::OzonePlatform::InitParams params;
+ params.connector = connector;
+ params.single_process = false;
+
+ ui::OzonePlatform::InitializeForUI(params);
+
+ // TODO(kylechar): We might not always want a US keyboard layout.
+ ui::KeyboardLayoutEngineManager::GetKeyboardLayoutEngine()
+ ->SetCurrentLayoutByName("us");
+ client_native_pixmap_factory_ = ui::ClientNativePixmapFactory::Create();
+ ui::ClientNativePixmapFactory::SetInstance(
+ client_native_pixmap_factory_.get());
+
+ DCHECK(ui::ClientNativePixmapFactory::GetInstance());
+#endif
+
+// TODO(rjkroege): Enter sandbox here before we start threads in GpuState
+// http://crbug.com/584532
+
+#if !defined(OS_ANDROID)
+ event_source_ = ui::PlatformEventSource::CreateDefault();
+#endif
+
+ // This needs to happen after DeviceDataManager has been constructed. That
+ // happens either during OzonePlatform or PlatformEventSource initialization,
+ // so keep this line below both of those.
+ input_device_server_.RegisterAsObserver();
+
+ if (use_chrome_gpu_command_buffer_) {
+ GpuServiceMus::GetInstance();
+ } else {
+ // TODO(rjkroege): It is possible that we might want to generalize the
+ // GpuState object.
+ platform_display_init_params_.gpu_state = new GpuState();
+ }
+
+ // Gpu must be running before the PlatformScreen can be initialized.
+ platform_screen_->Init();
+ window_server_.reset(
+ new ws::WindowServer(this, platform_display_init_params_.surfaces_state));
+
+ // DeviceDataManager must be initialized before TouchController. On non-Linux
+ // platforms there is no DeviceDataManager so don't create touch controller.
+ if (ui::DeviceDataManager::HasInstance())
+ touch_controller_.reset(
+ new ws::TouchController(window_server_->display_manager()));
+}
+
+bool MusApp::AcceptConnection(Connection* connection) {
+ connection->AddInterface<mojom::Clipboard>(this);
+ connection->AddInterface<mojom::DisplayManager>(this);
+ connection->AddInterface<mojom::UserAccessManager>(this);
+ connection->AddInterface<mojom::UserActivityMonitor>(this);
+ connection->AddInterface<WindowTreeHostFactory>(this);
+ connection->AddInterface<mojom::WindowManagerWindowTreeFactory>(this);
+ connection->AddInterface<mojom::WindowTreeFactory>(this);
+ if (test_config_)
+ connection->AddInterface<WindowServerTest>(this);
+
+ if (use_chrome_gpu_command_buffer_) {
+ connection->AddInterface<mojom::GpuService>(this);
+ } else {
+ connection->AddInterface<Gpu>(this);
+ }
+
+ // On non-Linux platforms there will be no DeviceDataManager instance and no
+ // purpose in adding the Mojo interface to connect to.
+ if (input_device_server_.IsRegisteredAsObserver())
+ input_device_server_.AddInterface(connection);
+
+#if defined(USE_OZONE)
+ ui::OzonePlatform::GetInstance()->AddInterfaces(connection);
+#endif
+
+ return true;
+}
+
+void MusApp::OnFirstDisplayReady() {
+ PendingRequests requests;
+ requests.swap(pending_requests_);
+ for (auto& request : requests) {
+ if (request->wtf_request)
+ Create(request->connection, std::move(*request->wtf_request));
+ else
+ Create(request->connection, std::move(*request->dm_request));
+ }
+}
+
+void MusApp::OnNoMoreDisplays() {
+ // We may get here from the destructor, in which case there is no messageloop.
+ if (base::MessageLoop::current())
+ base::MessageLoop::current()->QuitWhenIdle();
+}
+
+bool MusApp::IsTestConfig() const {
+ return test_config_;
+}
+
+void MusApp::CreateDefaultDisplays() {
+ // An asynchronous callback will create the Displays once the physical
+ // displays are ready.
+ platform_screen_->ConfigurePhysicalDisplay(base::Bind(
+ &MusApp::OnCreatedPhysicalDisplay, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void MusApp::Create(shell::Connection* connection,
+ mojom::ClipboardRequest request) {
+ UserState* user_state = GetUserState(connection);
+ if (!user_state->clipboard)
+ user_state->clipboard.reset(new clipboard::ClipboardImpl);
+ user_state->clipboard->AddBinding(std::move(request));
+}
+
+void MusApp::Create(shell::Connection* connection,
+ mojom::DisplayManagerRequest request) {
+ // DisplayManagerObservers generally expect there to be at least one display.
+ if (!window_server_->display_manager()->has_displays()) {
+ std::unique_ptr<PendingRequest> pending_request(new PendingRequest);
+ pending_request->connection = connection;
+ pending_request->dm_request.reset(
+ new mojom::DisplayManagerRequest(std::move(request)));
+ pending_requests_.push_back(std::move(pending_request));
+ return;
+ }
+ window_server_->display_manager()
+ ->GetUserDisplayManager(connection->GetRemoteIdentity().user_id())
+ ->AddDisplayManagerBinding(std::move(request));
+}
+
+void MusApp::Create(shell::Connection* connection, mojom::GpuRequest request) {
+ if (use_chrome_gpu_command_buffer_)
+ return;
+ DCHECK(platform_display_init_params_.gpu_state);
+ new GpuImpl(std::move(request), platform_display_init_params_.gpu_state);
+}
+
+void MusApp::Create(shell::Connection* connection,
+ mojom::GpuServiceRequest request) {
+ if (!use_chrome_gpu_command_buffer_)
+ return;
+ new GpuServiceImpl(std::move(request), connection);
+}
+
+void MusApp::Create(shell::Connection* connection,
+ mojom::UserAccessManagerRequest request) {
+ window_server_->user_id_tracker()->Bind(std::move(request));
+}
+
+void MusApp::Create(shell::Connection* connection,
+ mojom::UserActivityMonitorRequest request) {
+ AddUserIfNecessary(connection);
+ const ws::UserId& user_id = connection->GetRemoteIdentity().user_id();
+ window_server_->GetUserActivityMonitorForUser(user_id)->Add(
+ std::move(request));
+}
+
+void MusApp::Create(shell::Connection* connection,
+ mojom::WindowManagerWindowTreeFactoryRequest request) {
+ AddUserIfNecessary(connection);
+ window_server_->window_manager_window_tree_factory_set()->Add(
+ connection->GetRemoteIdentity().user_id(), std::move(request));
+}
+
+void MusApp::Create(Connection* connection,
+ mojom::WindowTreeFactoryRequest request) {
+ AddUserIfNecessary(connection);
+ if (!window_server_->display_manager()->has_displays()) {
+ std::unique_ptr<PendingRequest> pending_request(new PendingRequest);
+ pending_request->connection = connection;
+ pending_request->wtf_request.reset(
+ new mojom::WindowTreeFactoryRequest(std::move(request)));
+ pending_requests_.push_back(std::move(pending_request));
+ return;
+ }
+ AddUserIfNecessary(connection);
+ new ws::WindowTreeFactory(
+ window_server_.get(), connection->GetRemoteIdentity().user_id(),
+ connection->GetRemoteIdentity().name(), std::move(request));
+}
+
+void MusApp::Create(Connection* connection,
+ mojom::WindowTreeHostFactoryRequest request) {
+ UserState* user_state = GetUserState(connection);
+ if (!user_state->window_tree_host_factory) {
+ user_state->window_tree_host_factory.reset(new ws::WindowTreeHostFactory(
+ window_server_.get(), connection->GetRemoteIdentity().user_id(),
+ platform_display_init_params_));
+ }
+ user_state->window_tree_host_factory->AddBinding(std::move(request));
+}
+
+void MusApp::Create(Connection* connection,
+ mojom::WindowServerTestRequest request) {
+ if (!test_config_)
+ return;
+ new ws::WindowServerTestImpl(window_server_.get(), std::move(request));
+}
+
+void MusApp::OnCreatedPhysicalDisplay(int64_t id, const gfx::Rect& bounds) {
+ platform_display_init_params_.display_bounds = bounds;
+ platform_display_init_params_.display_id = id;
+
+ // Display manages its own lifetime.
+ ws::Display* host_impl =
+ new ws::Display(window_server_.get(), platform_display_init_params_);
+ host_impl->Init(nullptr);
+
+ if (touch_controller_)
+ touch_controller_->UpdateTouchTransforms();
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/mus_app.h b/chromium/components/mus/mus_app.h
new file mode 100644
index 00000000000..6bffa5c1a72
--- /dev/null
+++ b/chromium/components/mus/mus_app.h
@@ -0,0 +1,182 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_MUS_APP_H_
+#define COMPONENTS_MUS_MUS_APP_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/mus/input_devices/input_device_server.h"
+#include "components/mus/public/interfaces/clipboard.mojom.h"
+#include "components/mus/public/interfaces/display.mojom.h"
+#include "components/mus/public/interfaces/gpu.mojom.h"
+#include "components/mus/public/interfaces/gpu_service.mojom.h"
+#include "components/mus/public/interfaces/user_access_manager.mojom.h"
+#include "components/mus/public/interfaces/user_activity_monitor.mojom.h"
+#include "components/mus/public/interfaces/window_manager_window_tree_factory.mojom.h"
+#include "components/mus/public/interfaces/window_server_test.mojom.h"
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "components/mus/public/interfaces/window_tree_host.mojom.h"
+#include "components/mus/ws/platform_display_init_params.h"
+#include "components/mus/ws/touch_controller.h"
+#include "components/mus/ws/user_id.h"
+#include "components/mus/ws/window_server_delegate.h"
+#include "services/shell/public/cpp/application_runner.h"
+#include "services/shell/public/cpp/interface_factory.h"
+#include "services/shell/public/cpp/shell_client.h"
+#include "services/tracing/public/cpp/tracing_impl.h"
+
+#if defined(USE_OZONE)
+#include "ui/ozone/public/client_native_pixmap_factory.h"
+#endif
+
+namespace gfx {
+class Rect;
+}
+
+namespace shell {
+class Connector;
+}
+
+namespace ui {
+class PlatformEventSource;
+}
+
+namespace mus {
+namespace ws {
+class ForwardingWindowManager;
+class PlatformScreen;
+class WindowServer;
+}
+
+class MusApp
+ : public shell::ShellClient,
+ public ws::WindowServerDelegate,
+ public shell::InterfaceFactory<mojom::Clipboard>,
+ public shell::InterfaceFactory<mojom::DisplayManager>,
+ public shell::InterfaceFactory<mojom::Gpu>,
+ public shell::InterfaceFactory<mojom::GpuService>,
+ public shell::InterfaceFactory<mojom::UserAccessManager>,
+ public shell::InterfaceFactory<mojom::UserActivityMonitor>,
+ public shell::InterfaceFactory<mojom::WindowManagerWindowTreeFactory>,
+ public shell::InterfaceFactory<mojom::WindowTreeFactory>,
+ public shell::InterfaceFactory<mojom::WindowTreeHostFactory>,
+ public shell::InterfaceFactory<mojom::WindowServerTest> {
+ public:
+ MusApp();
+ ~MusApp() override;
+
+ private:
+ // Holds InterfaceRequests received before the first WindowTreeHost Display
+ // has been established.
+ struct PendingRequest;
+ struct UserState;
+
+ using UserIdToUserState = std::map<ws::UserId, std::unique_ptr<UserState>>;
+
+ void InitializeResources(shell::Connector* connector);
+
+ // Returns the user specific state for the user id of |connection|. MusApp
+ // owns the return value.
+ // TODO(sky): if we allow removal of user ids then we need to close anything
+ // associated with the user (all incoming pipes...) on removal.
+ UserState* GetUserState(shell::Connection* connection);
+
+ void AddUserIfNecessary(shell::Connection* connection);
+
+ // shell::ShellClient:
+ void Initialize(shell::Connector* connector,
+ const shell::Identity& identity,
+ uint32_t id) override;
+ bool AcceptConnection(shell::Connection* connection) override;
+
+ // WindowServerDelegate:
+ void OnFirstDisplayReady() override;
+ void OnNoMoreDisplays() override;
+ bool IsTestConfig() const override;
+ void CreateDefaultDisplays() override;
+
+ // shell::InterfaceFactory<mojom::Clipboard> implementation.
+ void Create(shell::Connection* connection,
+ mojom::ClipboardRequest request) override;
+
+ // shell::InterfaceFactory<mojom::DisplayManager> implementation.
+ void Create(shell::Connection* connection,
+ mojom::DisplayManagerRequest request) override;
+
+ // shell::InterfaceFactory<mojom::Gpu> implementation.
+ void Create(shell::Connection* connection,
+ mojom::GpuRequest request) override;
+
+ // shell::InterfaceFactory<mojom::GpuService> implementation.
+ void Create(shell::Connection* connection,
+ mojom::GpuServiceRequest request) override;
+
+ // shell::InterfaceFactory<mojom::UserAccessManager> implementation.
+ void Create(shell::Connection* connection,
+ mojom::UserAccessManagerRequest request) override;
+
+ // shell::InterfaceFactory<mojom::UserActivityMonitor> implementation.
+ void Create(shell::Connection* connection,
+ mojom::UserActivityMonitorRequest request) override;
+
+ // shell::InterfaceFactory<mojom::WindowManagerWindowTreeFactory>
+ // implementation.
+ void Create(shell::Connection* connection,
+ mojom::WindowManagerWindowTreeFactoryRequest request) override;
+
+ // shell::InterfaceFactory<mojom::WindowTreeFactory>:
+ void Create(shell::Connection* connection,
+ mojom::WindowTreeFactoryRequest request) override;
+
+ // shell::InterfaceFactory<mojom::WindowTreeHostFactory>:
+ void Create(shell::Connection* connection,
+ mojom::WindowTreeHostFactoryRequest request) override;
+
+ // shell::InterfaceFactory<mojom::WindowServerTest> implementation.
+ void Create(shell::Connection* connection,
+ mojom::WindowServerTestRequest request) override;
+
+ // Callback for display configuration. |id| is the identifying token for the
+ // configured display that will identify a specific physical display across
+ // configuration changes. |bounds| is the bounds of the display in screen
+ // coordinates.
+ void OnCreatedPhysicalDisplay(int64_t id, const gfx::Rect& bounds);
+
+ ws::PlatformDisplayInitParams platform_display_init_params_;
+ std::unique_ptr<ws::WindowServer> window_server_;
+ std::unique_ptr<ui::PlatformEventSource> event_source_;
+ mojo::TracingImpl tracing_;
+ using PendingRequests = std::vector<std::unique_ptr<PendingRequest>>;
+ PendingRequests pending_requests_;
+
+ UserIdToUserState user_id_to_user_state_;
+
+ // Provides input-device information via Mojo IPC.
+ InputDeviceServer input_device_server_;
+
+ bool test_config_;
+ bool use_chrome_gpu_command_buffer_;
+#if defined(USE_OZONE)
+ std::unique_ptr<ui::ClientNativePixmapFactory> client_native_pixmap_factory_;
+#endif
+
+ std::unique_ptr<ws::PlatformScreen> platform_screen_;
+ std::unique_ptr<ws::TouchController> touch_controller_;
+
+ base::WeakPtrFactory<MusApp> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(MusApp);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_MUS_APP_H_
diff --git a/chromium/components/mus/public/cpp/DEPS b/chromium/components/mus/public/cpp/DEPS
new file mode 100644
index 00000000000..9be0bc0fccc
--- /dev/null
+++ b/chromium/components/mus/public/cpp/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+gpu",
+]
diff --git a/chromium/components/mus/public/cpp/context_provider.h b/chromium/components/mus/public/cpp/context_provider.h
new file mode 100644
index 00000000000..f16366e05c2
--- /dev/null
+++ b/chromium/components/mus/public/cpp/context_provider.h
@@ -0,0 +1,54 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_PUBLIC_CPP_CONTEXT_PROVIDER_H_
+#define COMPONENTS_MUS_PUBLIC_CPP_CONTEXT_PROVIDER_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "cc/output/context_provider.h"
+#include "components/mus/public/interfaces/command_buffer.mojom.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace shell {
+class Connector;
+}
+
+namespace mus {
+
+class GLES2Context;
+
+class ContextProvider : public cc::ContextProvider {
+ public:
+ explicit ContextProvider(shell::Connector* connector);
+
+ // cc::ContextProvider implementation.
+ bool BindToCurrentThread() override;
+ gpu::gles2::GLES2Interface* ContextGL() override;
+ gpu::ContextSupport* ContextSupport() override;
+ class GrContext* GrContext() override;
+ void InvalidateGrContext(uint32_t state) override;
+ base::Lock* GetLock() override;
+ gpu::Capabilities ContextCapabilities() override;
+ void DeleteCachedResources() override {}
+ void SetLostContextCallback(
+ const LostContextCallback& lost_context_callback) override {}
+
+ protected:
+ friend class base::RefCountedThreadSafe<ContextProvider>;
+ ~ContextProvider() override;
+
+ private:
+ std::unique_ptr<shell::Connector> connector_;
+ std::unique_ptr<GLES2Context> context_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContextProvider);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_PUBLIC_CPP_CONTEXT_PROVIDER_H_
diff --git a/chromium/components/mus/public/cpp/gles2_context.h b/chromium/components/mus/public/cpp/gles2_context.h
new file mode 100644
index 00000000000..03b069f478d
--- /dev/null
+++ b/chromium/components/mus/public/cpp/gles2_context.h
@@ -0,0 +1,61 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_PUBLIC_CPP_GLES2_CONTEXT_H_
+#define COMPONENTS_MUS_PUBLIC_CPP_GLES2_CONTEXT_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/mus/public/interfaces/command_buffer.mojom.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
+
+namespace gpu {
+class CommandBufferProxyImpl;
+class TransferBuffer;
+namespace gles2 {
+class GLES2CmdHelper;
+}
+}
+
+namespace shell {
+class Connector;
+}
+
+namespace mus {
+
+class CommandBufferClientImpl;
+
+class GLES2Context {
+ public:
+ ~GLES2Context();
+ gpu::gles2::GLES2Interface* interface() const {
+ return implementation_.get();
+ }
+ gpu::ContextSupport* context_support() const { return implementation_.get(); }
+
+ static std::unique_ptr<GLES2Context> CreateOffscreenContext(
+ const std::vector<int32_t>& attribs,
+ shell::Connector* connector);
+
+ private:
+ GLES2Context();
+ bool Initialize(const std::vector<int32_t>& attribs,
+ shell::Connector* connector);
+
+ std::unique_ptr<CommandBufferClientImpl> command_buffer_client_impl_;
+ std::unique_ptr<gpu::CommandBufferProxyImpl> command_buffer_proxy_impl_;
+ std::unique_ptr<gpu::gles2::GLES2CmdHelper> gles2_helper_;
+ std::unique_ptr<gpu::TransferBuffer> transfer_buffer_;
+ std::unique_ptr<gpu::gles2::GLES2Implementation> implementation_;
+
+ DISALLOW_COPY_AND_ASSIGN(GLES2Context);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_PUBLIC_CPP_GLES2_CONTEXT_H_
diff --git a/chromium/components/mus/public/cpp/input_devices/input_device_client.cc b/chromium/components/mus/public/cpp/input_devices/input_device_client.cc
new file mode 100644
index 00000000000..6b55d778f58
--- /dev/null
+++ b/chromium/components/mus/public/cpp/input_devices/input_device_client.cc
@@ -0,0 +1,112 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/public/cpp/input_devices/input_device_client.h"
+
+#include "base/logging.h"
+
+namespace mus {
+
+InputDeviceClient::InputDeviceClient() : binding_(this) {
+ InputDeviceManager::SetInstance(this);
+}
+
+InputDeviceClient::~InputDeviceClient() {
+ InputDeviceManager::ClearInstance();
+}
+
+void InputDeviceClient::Connect(mojom::InputDeviceServerPtr server) {
+ DCHECK(server.is_bound());
+
+ server->AddObserver(binding_.CreateInterfacePtrAndBind());
+}
+
+void InputDeviceClient::OnKeyboardDeviceConfigurationChanged(
+ mojo::Array<ui::InputDevice> devices) {
+ keyboard_devices_ = devices.To<std::vector<ui::InputDevice>>();
+ FOR_EACH_OBSERVER(ui::InputDeviceEventObserver, observers_,
+ OnKeyboardDeviceConfigurationChanged());
+}
+
+void InputDeviceClient::OnTouchscreenDeviceConfigurationChanged(
+ mojo::Array<ui::TouchscreenDevice> devices) {
+ touchscreen_devices_ = devices.To<std::vector<ui::TouchscreenDevice>>();
+ FOR_EACH_OBSERVER(ui::InputDeviceEventObserver, observers_,
+ OnTouchscreenDeviceConfigurationChanged());
+}
+
+void InputDeviceClient::OnMouseDeviceConfigurationChanged(
+ mojo::Array<ui::InputDevice> devices) {
+ mouse_devices_ = devices.To<std::vector<ui::InputDevice>>();
+ FOR_EACH_OBSERVER(ui::InputDeviceEventObserver, observers_,
+ OnMouseDeviceConfigurationChanged());
+}
+
+void InputDeviceClient::OnTouchpadDeviceConfigurationChanged(
+ mojo::Array<ui::InputDevice> devices) {
+ touchpad_devices_ = devices.To<std::vector<ui::InputDevice>>();
+ FOR_EACH_OBSERVER(ui::InputDeviceEventObserver, observers_,
+ OnTouchpadDeviceConfigurationChanged());
+}
+
+void InputDeviceClient::OnDeviceListsComplete(
+ mojo::Array<ui::InputDevice> keyboard_devices,
+ mojo::Array<ui::TouchscreenDevice> touchscreen_devices,
+ mojo::Array<ui::InputDevice> mouse_devices,
+ mojo::Array<ui::InputDevice> touchpad_devices) {
+ // Update the cached device lists if the received list isn't empty.
+ if (!keyboard_devices.empty())
+ OnKeyboardDeviceConfigurationChanged(std::move(keyboard_devices));
+ if (!touchscreen_devices.empty())
+ OnTouchscreenDeviceConfigurationChanged(std::move(touchscreen_devices));
+ if (!mouse_devices.empty())
+ OnMouseDeviceConfigurationChanged(std::move(mouse_devices));
+ if (!touchpad_devices.empty())
+ OnTouchpadDeviceConfigurationChanged(std::move(touchpad_devices));
+
+ if (!device_lists_complete_) {
+ device_lists_complete_ = true;
+ FOR_EACH_OBSERVER(ui::InputDeviceEventObserver, observers_,
+ OnDeviceListsComplete());
+ }
+}
+
+void InputDeviceClient::AddObserver(ui::InputDeviceEventObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void InputDeviceClient::RemoveObserver(ui::InputDeviceEventObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+const std::vector<ui::InputDevice>& InputDeviceClient::GetKeyboardDevices()
+ const {
+ return keyboard_devices_;
+}
+
+const std::vector<ui::TouchscreenDevice>&
+InputDeviceClient::GetTouchscreenDevices() const {
+ return touchscreen_devices_;
+}
+
+const std::vector<ui::InputDevice>& InputDeviceClient::GetMouseDevices() const {
+ return mouse_devices_;
+}
+
+const std::vector<ui::InputDevice>& InputDeviceClient::GetTouchpadDevices()
+ const {
+ return touchpad_devices_;
+}
+
+bool InputDeviceClient::AreDeviceListsComplete() const {
+ return device_lists_complete_;
+}
+
+bool InputDeviceClient::AreTouchscreensEnabled() const {
+ // TODO(kylechar): This obviously isn't right. We either need to pass this
+ // state around or modify the interface.
+ return true;
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/public/cpp/input_devices/input_device_client.h b/chromium/components/mus/public/cpp/input_devices/input_device_client.h
new file mode 100644
index 00000000000..88fa41d750f
--- /dev/null
+++ b/chromium/components/mus/public/cpp/input_devices/input_device_client.h
@@ -0,0 +1,84 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_PUBLIC_CPP_INPUT_DEVICES_INPUT_DEVICE_CLIENT_H_
+#define COMPONENTS_MUS_PUBLIC_CPP_INPUT_DEVICES_INPUT_DEVICE_CLIENT_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "components/mus/public/interfaces/input_devices/input_device_server.mojom.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "ui/events/devices/input_device.h"
+#include "ui/events/devices/input_device_event_observer.h"
+#include "ui/events/devices/input_device_manager.h"
+#include "ui/events/devices/touchscreen_device.h"
+
+namespace mus {
+
+// Allows in-process client code to register as a InputDeviceEventObserver and
+// get information about input-devices. InputDeviceClient itself acts as an
+// InputDeviceObserverMojo and registers to get updates from InputDeviceServer.
+// Essentially, InputDeviceClient forwards input-device events and caches
+// input-device state.
+class InputDeviceClient : public mojom::InputDeviceObserverMojo,
+ public ui::InputDeviceManager {
+ public:
+ InputDeviceClient();
+ ~InputDeviceClient() override;
+
+ // Connects to mojo:mus as an observer on InputDeviceServer to receive input
+ // device updates.
+ void Connect(mojom::InputDeviceServerPtr server);
+
+ // ui::InputDeviceManager:
+ const std::vector<ui::InputDevice>& GetKeyboardDevices() const override;
+ const std::vector<ui::TouchscreenDevice>& GetTouchscreenDevices()
+ const override;
+ const std::vector<ui::InputDevice>& GetMouseDevices() const override;
+ const std::vector<ui::InputDevice>& GetTouchpadDevices() const override;
+
+ bool AreDeviceListsComplete() const override;
+ bool AreTouchscreensEnabled() const override;
+
+ void AddObserver(ui::InputDeviceEventObserver* observer) override;
+ void RemoveObserver(ui::InputDeviceEventObserver* observer) override;
+
+ private:
+ // mojom::InputDeviceObserverMojo:
+ void OnKeyboardDeviceConfigurationChanged(
+ mojo::Array<ui::InputDevice> devices) override;
+ void OnTouchscreenDeviceConfigurationChanged(
+ mojo::Array<ui::TouchscreenDevice> devices) override;
+ void OnMouseDeviceConfigurationChanged(
+ mojo::Array<ui::InputDevice> devices) override;
+ void OnTouchpadDeviceConfigurationChanged(
+ mojo::Array<ui::InputDevice> devices) override;
+ void OnDeviceListsComplete(
+ mojo::Array<ui::InputDevice> keyboard_devices,
+ mojo::Array<ui::TouchscreenDevice> touchscreen_devices,
+ mojo::Array<ui::InputDevice> mouse_devices,
+ mojo::Array<ui::InputDevice> touchpad_devices) override;
+
+ mojo::Binding<mojom::InputDeviceObserverMojo> binding_;
+
+ // Holds the list of input devices and signal that we have received the lists
+ // after initialization.
+ std::vector<ui::InputDevice> keyboard_devices_;
+ std::vector<ui::TouchscreenDevice> touchscreen_devices_;
+ std::vector<ui::InputDevice> mouse_devices_;
+ std::vector<ui::InputDevice> touchpad_devices_;
+ bool device_lists_complete_ = false;
+
+ // List of in-process observers.
+ base::ObserverList<ui::InputDeviceEventObserver> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputDeviceClient);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_PUBLIC_CPP_INPUT_DEVICES_INPUT_DEVICE_CLIENT_H_
diff --git a/chromium/components/mus/public/cpp/input_event_handler.h b/chromium/components/mus/public/cpp/input_event_handler.h
new file mode 100644
index 00000000000..3d445b23bad
--- /dev/null
+++ b/chromium/components/mus/public/cpp/input_event_handler.h
@@ -0,0 +1,43 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_PUBLIC_CPP_INPUT_EVENT_HANDLER_H_
+#define COMPONENTS_MUS_PUBLIC_CPP_INPUT_EVENT_HANDLER_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+
+namespace ui {
+class Event;
+}
+
+namespace mus {
+
+class Window;
+
+namespace mojom {
+enum class EventResult;
+}
+
+// Responsible for processing input events for mus::Window.
+class InputEventHandler {
+ public:
+ // The event handler can asynchronously ack the event by taking ownership of
+ // the |ack_callback|. The callback takes an EventResult indicating if the
+ // handler has consumed the event. If the handler does not take ownership of
+ // the callback, then WindowTreeClient will ack the event as not consumed.
+ virtual void OnWindowInputEvent(
+ Window* target,
+ const ui::Event& event,
+ std::unique_ptr<base::Callback<void(mojom::EventResult)>>*
+ ack_callback) = 0;
+
+ protected:
+ virtual ~InputEventHandler() {}
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_PUBLIC_CPP_INPUT_EVENT_HANDLER_H_
diff --git a/chromium/components/mus/public/cpp/lib/DEPS b/chromium/components/mus/public/cpp/lib/DEPS
new file mode 100644
index 00000000000..c635ea6f5e4
--- /dev/null
+++ b/chromium/components/mus/public/cpp/lib/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+mojo/gpu"
+]
diff --git a/chromium/components/mus/public/cpp/lib/command_buffer_client_impl.cc b/chromium/components/mus/public/cpp/lib/command_buffer_client_impl.cc
new file mode 100644
index 00000000000..062c3f78477
--- /dev/null
+++ b/chromium/components/mus/public/cpp/lib/command_buffer_client_impl.cc
@@ -0,0 +1,370 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/public/cpp/lib/command_buffer_client_impl.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <limits>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/process/process_handle.h"
+#include "base/threading/thread_restrictions.h"
+#include "components/mus/common/gpu_type_converters.h"
+#include "components/mus/common/mojo_buffer_backing.h"
+#include "components/mus/common/mojo_gpu_memory_buffer.h"
+#include "gpu/command_buffer/client/gpu_control_client.h"
+#include "gpu/command_buffer/common/command_buffer_id.h"
+#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
+#include "gpu/command_buffer/common/sync_token.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+
+namespace mus {
+
+namespace {
+
+bool CreateAndMapSharedBuffer(size_t size,
+ mojo::ScopedSharedBufferMapping* mapping,
+ mojo::ScopedSharedBufferHandle* handle) {
+ *handle = mojo::SharedBufferHandle::Create(size);
+ if (!handle->is_valid())
+ return false;
+
+ *mapping = (*handle)->Map(size);
+ if (!*mapping)
+ return false;
+
+ return true;
+}
+
+void MakeProgressCallback(gpu::CommandBuffer::State* output,
+ const gpu::CommandBuffer::State& input) {
+ *output = input;
+}
+
+void InitializeCallback(mus::mojom::CommandBufferInitializeResultPtr* output,
+ mus::mojom::CommandBufferInitializeResultPtr input) {
+ *output = std::move(input);
+}
+
+} // namespace
+
+CommandBufferClientImpl::CommandBufferClientImpl(
+ const std::vector<int32_t>& attribs,
+ mus::mojom::CommandBufferPtr command_buffer_ptr)
+ : gpu_control_client_(nullptr),
+ destroyed_(false),
+ attribs_(attribs),
+ client_binding_(this),
+ command_buffer_(std::move(command_buffer_ptr)),
+ command_buffer_id_(),
+ last_put_offset_(-1),
+ next_transfer_buffer_id_(0),
+ next_image_id_(0),
+ next_fence_sync_release_(1),
+ flushed_fence_sync_release_(0) {
+ command_buffer_.set_connection_error_handler(
+ base::Bind(&CommandBufferClientImpl::Destroyed, base::Unretained(this),
+ gpu::error::kUnknown, gpu::error::kLostContext));
+}
+
+CommandBufferClientImpl::~CommandBufferClientImpl() {}
+
+bool CommandBufferClientImpl::Initialize() {
+ const size_t kSharedStateSize = sizeof(gpu::CommandBufferSharedState);
+ mojo::ScopedSharedBufferHandle handle;
+ bool result =
+ CreateAndMapSharedBuffer(kSharedStateSize, &shared_state_, &handle);
+ if (!result)
+ return false;
+
+ shared_state()->Initialize();
+
+ mus::mojom::CommandBufferClientPtr client_ptr;
+ client_binding_.Bind(GetProxy(&client_ptr));
+
+ mus::mojom::CommandBufferInitializeResultPtr initialize_result;
+ command_buffer_->Initialize(
+ std::move(client_ptr), std::move(handle),
+ mojo::Array<int32_t>::From(attribs_),
+ base::Bind(&InitializeCallback, &initialize_result));
+
+ base::ThreadRestrictions::ScopedAllowWait wait;
+ if (!command_buffer_.WaitForIncomingResponse()) {
+ VLOG(1) << "Channel encountered error while creating command buffer.";
+ return false;
+ }
+
+ if (!initialize_result) {
+ VLOG(1) << "Command buffer cannot be initialized successfully.";
+ return false;
+ }
+
+ DCHECK_EQ(gpu::CommandBufferNamespace::MOJO,
+ initialize_result->command_buffer_namespace);
+ command_buffer_id_ = gpu::CommandBufferId::FromUnsafeValue(
+ initialize_result->command_buffer_id);
+ capabilities_ = initialize_result->capabilities;
+ return true;
+}
+
+gpu::CommandBuffer::State CommandBufferClientImpl::GetLastState() {
+ return last_state_;
+}
+
+int32_t CommandBufferClientImpl::GetLastToken() {
+ TryUpdateState();
+ return last_state_.token;
+}
+
+void CommandBufferClientImpl::Flush(int32_t put_offset) {
+ if (last_put_offset_ == put_offset)
+ return;
+
+ last_put_offset_ = put_offset;
+ command_buffer_->Flush(put_offset);
+ flushed_fence_sync_release_ = next_fence_sync_release_ - 1;
+}
+
+void CommandBufferClientImpl::OrderingBarrier(int32_t put_offset) {
+ // TODO(jamesr): Implement this more efficiently.
+ Flush(put_offset);
+}
+
+void CommandBufferClientImpl::WaitForTokenInRange(int32_t start, int32_t end) {
+ TryUpdateState();
+ while (!InRange(start, end, last_state_.token) &&
+ last_state_.error == gpu::error::kNoError) {
+ MakeProgressAndUpdateState();
+ }
+}
+
+void CommandBufferClientImpl::WaitForGetOffsetInRange(int32_t start,
+ int32_t end) {
+ TryUpdateState();
+ while (!InRange(start, end, last_state_.get_offset) &&
+ last_state_.error == gpu::error::kNoError) {
+ MakeProgressAndUpdateState();
+ }
+}
+
+void CommandBufferClientImpl::SetGetBuffer(int32_t shm_id) {
+ command_buffer_->SetGetBuffer(shm_id);
+ last_put_offset_ = -1;
+}
+
+scoped_refptr<gpu::Buffer> CommandBufferClientImpl::CreateTransferBuffer(
+ size_t size,
+ int32_t* id) {
+ if (size >= std::numeric_limits<uint32_t>::max())
+ return NULL;
+
+ mojo::ScopedSharedBufferMapping mapping;
+ mojo::ScopedSharedBufferHandle handle;
+ if (!CreateAndMapSharedBuffer(size, &mapping, &handle)) {
+ if (last_state_.error == gpu::error::kNoError)
+ last_state_.error = gpu::error::kLostContext;
+ return NULL;
+ }
+
+ *id = ++next_transfer_buffer_id_;
+
+ command_buffer_->RegisterTransferBuffer(*id, std::move(handle),
+ static_cast<uint32_t>(size));
+
+ std::unique_ptr<gpu::BufferBacking> backing(
+ new mus::MojoBufferBacking(std::move(mapping), size));
+ scoped_refptr<gpu::Buffer> buffer(new gpu::Buffer(std::move(backing)));
+ return buffer;
+}
+
+void CommandBufferClientImpl::DestroyTransferBuffer(int32_t id) {
+ command_buffer_->DestroyTransferBuffer(id);
+}
+
+void CommandBufferClientImpl::SetGpuControlClient(gpu::GpuControlClient* c) {
+ gpu_control_client_ = c;
+}
+
+gpu::Capabilities CommandBufferClientImpl::GetCapabilities() {
+ return capabilities_;
+}
+
+int32_t CommandBufferClientImpl::CreateImage(ClientBuffer buffer,
+ size_t width,
+ size_t height,
+ unsigned internalformat) {
+ int32_t new_id = ++next_image_id_;
+
+ gfx::Size size(static_cast<int32_t>(width), static_cast<int32_t>(height));
+
+ mus::MojoGpuMemoryBufferImpl* gpu_memory_buffer =
+ mus::MojoGpuMemoryBufferImpl::FromClientBuffer(buffer);
+ gfx::GpuMemoryBufferHandle handle = gpu_memory_buffer->GetHandle();
+
+ bool requires_sync_point = false;
+ if (handle.type != gfx::SHARED_MEMORY_BUFFER) {
+ requires_sync_point = true;
+ NOTIMPLEMENTED();
+ return -1;
+ }
+
+ base::SharedMemoryHandle dupd_handle =
+ base::SharedMemory::DuplicateHandle(handle.handle);
+#if defined(OS_WIN)
+ HANDLE platform_handle = dupd_handle.GetHandle();
+#else
+ int platform_handle = dupd_handle.fd;
+#endif
+
+ mojo::ScopedHandle scoped_handle = mojo::WrapPlatformFile(platform_handle);
+ command_buffer_->CreateImage(
+ new_id, std::move(scoped_handle), handle.type, std::move(size),
+ static_cast<int32_t>(gpu_memory_buffer->GetFormat()), internalformat);
+ if (requires_sync_point) {
+ NOTIMPLEMENTED();
+ // TODO(jam): need to support this if we support types other than
+ // SHARED_MEMORY_BUFFER.
+ // gpu_memory_buffer_manager->SetDestructionSyncPoint(gpu_memory_buffer,
+ // InsertSyncPoint());
+ }
+
+ return new_id;
+}
+
+void CommandBufferClientImpl::DestroyImage(int32_t id) {
+ command_buffer_->DestroyImage(id);
+}
+
+int32_t CommandBufferClientImpl::CreateGpuMemoryBufferImage(
+ size_t width,
+ size_t height,
+ unsigned internalformat,
+ unsigned usage) {
+ std::unique_ptr<gfx::GpuMemoryBuffer> buffer(
+ mus::MojoGpuMemoryBufferImpl::Create(
+ gfx::Size(static_cast<int>(width), static_cast<int>(height)),
+ gpu::DefaultBufferFormatForImageFormat(internalformat),
+ gfx::BufferUsage::SCANOUT));
+ if (!buffer)
+ return -1;
+
+ return CreateImage(buffer->AsClientBuffer(), width, height, internalformat);
+}
+
+int32_t CommandBufferClientImpl::GetImageGpuMemoryBufferId(unsigned image_id) {
+ // TODO(erikchen): Once this class supports IOSurface GpuMemoryBuffer backed
+ // images, it will also need to keep a local cache from image id to
+ // GpuMemoryBuffer id.
+ NOTIMPLEMENTED();
+ return -1;
+}
+
+void CommandBufferClientImpl::SignalQuery(uint32_t query,
+ const base::Closure& callback) {
+ // TODO(piman)
+ NOTIMPLEMENTED();
+}
+
+void CommandBufferClientImpl::Destroyed(int32_t lost_reason, int32_t error) {
+ if (destroyed_)
+ return;
+ last_state_.context_lost_reason =
+ static_cast<gpu::error::ContextLostReason>(lost_reason);
+ last_state_.error = static_cast<gpu::error::Error>(error);
+ if (gpu_control_client_)
+ gpu_control_client_->OnGpuControlLostContext();
+ destroyed_ = true;
+}
+
+void CommandBufferClientImpl::SignalAck(uint32_t id) {}
+
+void CommandBufferClientImpl::SwapBuffersCompleted(int32_t result) {}
+
+void CommandBufferClientImpl::UpdateState(
+ const gpu::CommandBuffer::State& state) {}
+
+void CommandBufferClientImpl::UpdateVSyncParameters(int64_t timebase,
+ int64_t interval) {}
+
+void CommandBufferClientImpl::TryUpdateState() {
+ if (last_state_.error == gpu::error::kNoError)
+ shared_state()->Read(&last_state_);
+}
+
+void CommandBufferClientImpl::MakeProgressAndUpdateState() {
+ gpu::CommandBuffer::State state;
+ command_buffer_->MakeProgress(last_state_.get_offset,
+ base::Bind(&MakeProgressCallback, &state));
+
+ base::ThreadRestrictions::ScopedAllowWait wait;
+ if (!command_buffer_.WaitForIncomingResponse()) {
+ VLOG(1) << "Channel encountered error while waiting for command buffer.";
+ // TODO(piman): is it ok for this to re-enter?
+ Destroyed(gpu::error::kUnknown, gpu::error::kLostContext);
+ return;
+ }
+
+ if (state.generation - last_state_.generation < 0x80000000U)
+ last_state_ = state;
+}
+
+void CommandBufferClientImpl::SetLock(base::Lock* lock) {}
+
+void CommandBufferClientImpl::EnsureWorkVisible() {
+ // This is only relevant for out-of-process command buffers.
+}
+
+gpu::CommandBufferNamespace CommandBufferClientImpl::GetNamespaceID() const {
+ return gpu::CommandBufferNamespace::MOJO;
+}
+
+gpu::CommandBufferId CommandBufferClientImpl::GetCommandBufferID() const {
+ return command_buffer_id_;
+}
+
+int32_t CommandBufferClientImpl::GetExtraCommandBufferData() const {
+ return 0;
+}
+
+uint64_t CommandBufferClientImpl::GenerateFenceSyncRelease() {
+ return next_fence_sync_release_++;
+}
+
+bool CommandBufferClientImpl::IsFenceSyncRelease(uint64_t release) {
+ return release != 0 && release < next_fence_sync_release_;
+}
+
+bool CommandBufferClientImpl::IsFenceSyncFlushed(uint64_t release) {
+ return release != 0 && release <= flushed_fence_sync_release_;
+}
+
+bool CommandBufferClientImpl::IsFenceSyncFlushReceived(uint64_t release) {
+ return IsFenceSyncFlushed(release);
+}
+
+void CommandBufferClientImpl::SignalSyncToken(const gpu::SyncToken& sync_token,
+ const base::Closure& callback) {
+ // TODO(dyen)
+ NOTIMPLEMENTED();
+}
+
+bool CommandBufferClientImpl::CanWaitUnverifiedSyncToken(
+ const gpu::SyncToken* sync_token) {
+ // Right now, MOJO_LOCAL is only used by trusted code, so it is safe to wait
+ // on a sync token in MOJO_LOCAL command buffer.
+ if (sync_token->namespace_id() == gpu::CommandBufferNamespace::MOJO_LOCAL)
+ return true;
+
+ // It is also safe to wait on the same context.
+ if (sync_token->namespace_id() == gpu::CommandBufferNamespace::MOJO &&
+ sync_token->command_buffer_id() == GetCommandBufferID())
+ return true;
+
+ return false;
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/public/cpp/lib/command_buffer_client_impl.h b/chromium/components/mus/public/cpp/lib/command_buffer_client_impl.h
new file mode 100644
index 00000000000..80a46e93b7f
--- /dev/null
+++ b/chromium/components/mus/public/cpp/lib/command_buffer_client_impl.h
@@ -0,0 +1,117 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_PUBLIC_CPP_LIB_COMMAND_BUFFER_CLIENT_IMPL_H_
+#define COMPONENTS_MUS_PUBLIC_CPP_LIB_COMMAND_BUFFER_CLIENT_IMPL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/mus/public/interfaces/command_buffer.mojom.h"
+#include "gpu/command_buffer/client/gpu_control.h"
+#include "gpu/command_buffer/common/command_buffer.h"
+#include "gpu/command_buffer/common/command_buffer_id.h"
+#include "gpu/command_buffer/common/command_buffer_shared.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace base {
+class RunLoop;
+}
+
+namespace mus {
+class CommandBufferClientImpl;
+
+class CommandBufferClientImpl : public mus::mojom::CommandBufferClient,
+ public gpu::CommandBuffer,
+ public gpu::GpuControl {
+ public:
+ explicit CommandBufferClientImpl(
+ const std::vector<int32_t>& attribs,
+ mus::mojom::CommandBufferPtr command_buffer_ptr);
+ ~CommandBufferClientImpl() override;
+ bool Initialize();
+
+ // CommandBuffer implementation:
+ State GetLastState() override;
+ int32_t GetLastToken() override;
+ void Flush(int32_t put_offset) override;
+ void OrderingBarrier(int32_t put_offset) override;
+ void WaitForTokenInRange(int32_t start, int32_t end) override;
+ void WaitForGetOffsetInRange(int32_t start, int32_t end) override;
+ void SetGetBuffer(int32_t shm_id) override;
+ scoped_refptr<gpu::Buffer> CreateTransferBuffer(size_t size,
+ int32_t* id) override;
+ void DestroyTransferBuffer(int32_t id) override;
+
+ // gpu::GpuControl implementation:
+ void SetGpuControlClient(gpu::GpuControlClient*) override;
+ gpu::Capabilities GetCapabilities() override;
+ int32_t CreateImage(ClientBuffer buffer,
+ size_t width,
+ size_t height,
+ unsigned internalformat) override;
+ void DestroyImage(int32_t id) override;
+ int32_t CreateGpuMemoryBufferImage(size_t width,
+ size_t height,
+ unsigned internalformat,
+ unsigned usage) override;
+ int32_t GetImageGpuMemoryBufferId(unsigned image_id) override;
+ void SignalQuery(uint32_t query, const base::Closure& callback) override;
+ void SetLock(base::Lock*) override;
+ void EnsureWorkVisible() override;
+ gpu::CommandBufferNamespace GetNamespaceID() const override;
+ gpu::CommandBufferId GetCommandBufferID() const override;
+ int32_t GetExtraCommandBufferData() const override;
+ uint64_t GenerateFenceSyncRelease() override;
+ bool IsFenceSyncRelease(uint64_t release) override;
+ bool IsFenceSyncFlushed(uint64_t release) override;
+ bool IsFenceSyncFlushReceived(uint64_t release) override;
+ void SignalSyncToken(const gpu::SyncToken& sync_token,
+ const base::Closure& callback) override;
+ bool CanWaitUnverifiedSyncToken(const gpu::SyncToken* sync_token) override;
+
+ private:
+ // mus::mojom::CommandBufferClient implementation:
+ void Destroyed(int32_t lost_reason, int32_t error) override;
+ void SignalAck(uint32_t id) override;
+ void SwapBuffersCompleted(int32_t result) override;
+ void UpdateState(const gpu::CommandBuffer::State& state) override;
+ void UpdateVSyncParameters(int64_t timebase, int64_t interval) override;
+
+ void TryUpdateState();
+ void MakeProgressAndUpdateState();
+
+ gpu::CommandBufferSharedState* shared_state() const {
+ return reinterpret_cast<gpu::CommandBufferSharedState*>(
+ shared_state_.get());
+ }
+
+ gpu::GpuControlClient* gpu_control_client_;
+ bool destroyed_;
+ std::vector<int32_t> attribs_;
+ mojo::Binding<mus::mojom::CommandBufferClient> client_binding_;
+ mus::mojom::CommandBufferPtr command_buffer_;
+
+ gpu::CommandBufferId command_buffer_id_;
+ gpu::Capabilities capabilities_;
+ State last_state_;
+ mojo::ScopedSharedBufferMapping shared_state_;
+ int32_t last_put_offset_;
+ int32_t next_transfer_buffer_id_;
+
+ // Image IDs are allocated in sequence.
+ int next_image_id_;
+
+ uint64_t next_fence_sync_release_;
+ uint64_t flushed_fence_sync_release_;
+};
+
+} // mus
+
+#endif // COMPONENTS_MUS_PUBLIC_CPP_LIB_COMMAND_BUFFER_CLIENT_IMPL_H_
diff --git a/chromium/components/mus/public/cpp/lib/context_provider.cc b/chromium/components/mus/public/cpp/lib/context_provider.cc
new file mode 100644
index 00000000000..02717112dd7
--- /dev/null
+++ b/chromium/components/mus/public/cpp/lib/context_provider.cc
@@ -0,0 +1,61 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/public/cpp/context_provider.h"
+
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "components/mus/public/cpp/gles2_context.h"
+#include "services/shell/public/cpp/connector.h"
+
+namespace mus {
+
+ContextProvider::ContextProvider(shell::Connector* connector)
+ : connector_(connector->Clone()) {}
+
+bool ContextProvider::BindToCurrentThread() {
+ if (connector_) {
+ context_ = GLES2Context::CreateOffscreenContext(std::vector<int32_t>(),
+ connector_.get());
+ // We don't need the connector anymore, so release it.
+ connector_.reset();
+ }
+ return !!context_;
+}
+
+gpu::gles2::GLES2Interface* ContextProvider::ContextGL() {
+ return context_->interface();
+}
+
+gpu::ContextSupport* ContextProvider::ContextSupport() {
+ if (!context_)
+ return NULL;
+ return context_->context_support();
+}
+
+class GrContext* ContextProvider::GrContext() {
+ return NULL;
+}
+
+void ContextProvider::InvalidateGrContext(uint32_t state) {}
+
+gpu::Capabilities ContextProvider::ContextCapabilities() {
+ gpu::Capabilities capabilities;
+ // Enabled the CHROMIUM_image extension to use GpuMemoryBuffers. The
+ // implementation of which is used in CommandBufferDriver.
+ capabilities.image = true;
+ return capabilities;
+}
+
+base::Lock* ContextProvider::GetLock() {
+ // This context provider is not used on multiple threads.
+ NOTREACHED();
+ return nullptr;
+}
+
+ContextProvider::~ContextProvider() {
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/public/cpp/lib/gles2_context.cc b/chromium/components/mus/public/cpp/lib/gles2_context.cc
new file mode 100644
index 00000000000..84363b15cfe
--- /dev/null
+++ b/chromium/components/mus/public/cpp/lib/gles2_context.cc
@@ -0,0 +1,109 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/public/cpp/gles2_context.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <utility>
+
+#include "components/mus/common/gpu_service.h"
+#include "components/mus/public/cpp/lib/command_buffer_client_impl.h"
+#include "components/mus/public/interfaces/command_buffer.mojom.h"
+#include "components/mus/public/interfaces/gpu_service.mojom.h"
+#include "gpu/command_buffer/client/gles2_cmd_helper.h"
+#include "gpu/command_buffer/client/shared_memory_limits.h"
+#include "gpu/command_buffer/client/transfer_buffer.h"
+#include "gpu/ipc/client/command_buffer_proxy_impl.h"
+#include "mojo/public/cpp/system/core.h"
+#include "services/shell/public/cpp/connector.h"
+#include "url/gurl.h"
+
+namespace mus {
+
+GLES2Context::GLES2Context() {}
+
+GLES2Context::~GLES2Context() {}
+
+bool GLES2Context::Initialize(const std::vector<int32_t>& attribs,
+ shell::Connector* connector) {
+ gpu::CommandBuffer* command_buffer = nullptr;
+ gpu::GpuControl* gpu_control = nullptr;
+ // TODO(penghuang): Use type gpu::gles2::ContextCreationAttribHelper for
+ // attribs.
+ if (!mus::GpuService::UseChromeGpuCommandBuffer()) {
+ mojom::GpuPtr gpu;
+ connector->ConnectToInterface("mojo:mus", &gpu);
+ mojom::CommandBufferPtr command_buffer_ptr;
+ gpu->CreateOffscreenGLES2Context(GetProxy(&command_buffer_ptr));
+ command_buffer_client_impl_.reset(
+ new CommandBufferClientImpl(attribs, std::move(command_buffer_ptr)));
+ if (!command_buffer_client_impl_->Initialize())
+ return false;
+ command_buffer = command_buffer_client_impl_.get();
+ gpu_control = command_buffer_client_impl_.get();
+ } else {
+ scoped_refptr<gpu::GpuChannelHost> gpu_channel_host =
+ GpuService::GetInstance()->EstablishGpuChannelSync();
+ if (!gpu_channel_host)
+ return false;
+ gpu::SurfaceHandle surface_handle = gfx::kNullAcceleratedWidget;
+ // TODO(penghuang): support shared group.
+ gpu::CommandBufferProxyImpl* shared_command_buffer = nullptr;
+ gpu::GpuStreamId stream_id = gpu::GpuStreamId::GPU_STREAM_DEFAULT;
+ gpu::GpuStreamPriority stream_priority = gpu::GpuStreamPriority::NORMAL;
+ gpu::gles2::ContextCreationAttribHelper attributes;
+ // TODO(penghuang): figure a useful active_url.
+ GURL active_url;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+ base::ThreadTaskRunnerHandle::Get();
+ if (!attributes.Parse(attribs))
+ return false;
+ command_buffer_proxy_impl_ = gpu::CommandBufferProxyImpl::Create(
+ std::move(gpu_channel_host), surface_handle, shared_command_buffer,
+ stream_id, stream_priority, attributes, active_url,
+ std::move(task_runner));
+ if (!command_buffer_proxy_impl_)
+ return false;
+ command_buffer = command_buffer_proxy_impl_.get();
+ gpu_control = command_buffer_proxy_impl_.get();
+ }
+
+ constexpr gpu::SharedMemoryLimits default_limits;
+ gles2_helper_.reset(new gpu::gles2::GLES2CmdHelper(command_buffer));
+ if (!gles2_helper_->Initialize(default_limits.command_buffer_size))
+ return false;
+ gles2_helper_->SetAutomaticFlushes(false);
+ transfer_buffer_.reset(new gpu::TransferBuffer(gles2_helper_.get()));
+ gpu::Capabilities capabilities = gpu_control->GetCapabilities();
+ bool bind_generates_resource =
+ !!capabilities.bind_generates_resource_chromium;
+ // TODO(piman): Some contexts (such as compositor) want this to be true, so
+ // this needs to be a public parameter.
+ bool lose_context_when_out_of_memory = false;
+ bool support_client_side_arrays = false;
+ implementation_.reset(new gpu::gles2::GLES2Implementation(
+ gles2_helper_.get(), NULL, transfer_buffer_.get(),
+ bind_generates_resource, lose_context_when_out_of_memory,
+ support_client_side_arrays, gpu_control));
+ if (!implementation_->Initialize(default_limits.start_transfer_buffer_size,
+ default_limits.min_transfer_buffer_size,
+ default_limits.max_transfer_buffer_size,
+ default_limits.mapped_memory_reclaim_limit))
+ return false;
+ return true;
+}
+
+// static
+std::unique_ptr<GLES2Context> GLES2Context::CreateOffscreenContext(
+ const std::vector<int32_t>& attribs,
+ shell::Connector* connector) {
+ std::unique_ptr<GLES2Context> gles2_context(new GLES2Context);
+ if (!gles2_context->Initialize(attribs, connector))
+ gles2_context.reset();
+ return gles2_context;
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/public/cpp/lib/in_flight_change.cc b/chromium/components/mus/public/cpp/lib/in_flight_change.cc
new file mode 100644
index 00000000000..04878742350
--- /dev/null
+++ b/chromium/components/mus/public/cpp/lib/in_flight_change.cc
@@ -0,0 +1,220 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/public/cpp/lib/in_flight_change.h"
+
+#include "components/mus/public/cpp/lib/window_private.h"
+#include "components/mus/public/cpp/window_tree_client.h"
+
+namespace mus {
+
+// InFlightChange -------------------------------------------------------------
+
+InFlightChange::InFlightChange(Window* window, ChangeType type)
+ : window_(window), change_type_(type) {}
+
+InFlightChange::~InFlightChange() {}
+
+bool InFlightChange::Matches(const InFlightChange& change) const {
+ DCHECK(change.window_ == window_ && change.change_type_ == change_type_);
+ return true;
+}
+
+void InFlightChange::ChangeFailed() {}
+
+// InFlightBoundsChange -------------------------------------------------------
+
+InFlightBoundsChange::InFlightBoundsChange(Window* window,
+ const gfx::Rect& revert_bounds)
+ : InFlightChange(window, ChangeType::BOUNDS),
+ revert_bounds_(revert_bounds) {}
+
+void InFlightBoundsChange::SetRevertValueFrom(const InFlightChange& change) {
+ revert_bounds_ =
+ static_cast<const InFlightBoundsChange&>(change).revert_bounds_;
+}
+
+void InFlightBoundsChange::Revert() {
+ WindowPrivate(window()).LocalSetBounds(window()->bounds(), revert_bounds_);
+}
+
+// CrashInFlightChange --------------------------------------------------------
+
+CrashInFlightChange::CrashInFlightChange(Window* window, ChangeType type)
+ : InFlightChange(window, type) {}
+
+CrashInFlightChange::~CrashInFlightChange() {}
+
+void CrashInFlightChange::SetRevertValueFrom(const InFlightChange& change) {
+ CHECK(false);
+}
+
+void CrashInFlightChange::ChangeFailed() {
+ DLOG(ERROR) << "changed failed, type=" << static_cast<int>(change_type());
+ CHECK(false);
+}
+
+void CrashInFlightChange::Revert() {
+ CHECK(false);
+}
+
+// InFlightWindowChange -------------------------------------------------------
+
+InFlightWindowTreeClientChange::InFlightWindowTreeClientChange(
+ WindowTreeClient* client,
+ Window* revert_value,
+ ChangeType type)
+ : InFlightChange(nullptr, type),
+ client_(client),
+ revert_window_(nullptr) {
+ SetRevertWindow(revert_value);
+}
+
+InFlightWindowTreeClientChange::~InFlightWindowTreeClientChange() {
+ SetRevertWindow(nullptr);
+}
+
+void InFlightWindowTreeClientChange::SetRevertValueFrom(
+ const InFlightChange& change) {
+ SetRevertWindow(static_cast<const InFlightWindowTreeClientChange&>(change)
+ .revert_window_);
+}
+
+void InFlightWindowTreeClientChange::SetRevertWindow(Window* window) {
+ if (revert_window_)
+ revert_window_->RemoveObserver(this);
+ revert_window_ = window;
+ if (revert_window_)
+ revert_window_->AddObserver(this);
+}
+
+void InFlightWindowTreeClientChange::OnWindowDestroying(Window* window) {
+ SetRevertWindow(nullptr);
+}
+
+// InFlightCaptureChange ------------------------------------------------------
+
+InFlightCaptureChange::InFlightCaptureChange(
+ WindowTreeClient* client, Window* revert_value)
+ : InFlightWindowTreeClientChange(client,
+ revert_value,
+ ChangeType::CAPTURE) {}
+
+InFlightCaptureChange::~InFlightCaptureChange() {}
+
+void InFlightCaptureChange::Revert() {
+ client()->LocalSetCapture(revert_window());
+}
+
+// InFlightFocusChange --------------------------------------------------------
+
+InFlightFocusChange::InFlightFocusChange(
+ WindowTreeClient* client,
+ Window* revert_value)
+ : InFlightWindowTreeClientChange(client,
+ revert_value,
+ ChangeType::FOCUS) {}
+
+InFlightFocusChange::~InFlightFocusChange() {}
+
+void InFlightFocusChange::Revert() {
+ client()->LocalSetFocus(revert_window());
+}
+
+// InFlightPropertyChange -----------------------------------------------------
+
+InFlightPropertyChange::InFlightPropertyChange(
+ Window* window,
+ const std::string& property_name,
+ const mojo::Array<uint8_t>& revert_value)
+ : InFlightChange(window, ChangeType::PROPERTY),
+ property_name_(property_name),
+ revert_value_(revert_value.Clone()) {}
+
+InFlightPropertyChange::~InFlightPropertyChange() {}
+
+bool InFlightPropertyChange::Matches(const InFlightChange& change) const {
+ return static_cast<const InFlightPropertyChange&>(change).property_name_ ==
+ property_name_;
+}
+
+void InFlightPropertyChange::SetRevertValueFrom(const InFlightChange& change) {
+ revert_value_ =
+ static_cast<const InFlightPropertyChange&>(change).revert_value_.Clone();
+}
+
+void InFlightPropertyChange::Revert() {
+ WindowPrivate(window())
+ .LocalSetSharedProperty(property_name_, std::move(revert_value_));
+}
+
+// InFlightPredefinedCursorChange ---------------------------------------------
+
+InFlightPredefinedCursorChange::InFlightPredefinedCursorChange(
+ Window* window,
+ mojom::Cursor revert_value)
+ : InFlightChange(window, ChangeType::PREDEFINED_CURSOR),
+ revert_cursor_(revert_value) {}
+
+InFlightPredefinedCursorChange::~InFlightPredefinedCursorChange() {}
+
+void InFlightPredefinedCursorChange::SetRevertValueFrom(
+ const InFlightChange& change) {
+ revert_cursor_ =
+ static_cast<const InFlightPredefinedCursorChange&>(change).revert_cursor_;
+}
+
+void InFlightPredefinedCursorChange::Revert() {
+ WindowPrivate(window()).LocalSetPredefinedCursor(revert_cursor_);
+}
+
+// InFlightVisibleChange -------------------------------------------------------
+
+InFlightVisibleChange::InFlightVisibleChange(Window* window,
+ bool revert_value)
+ : InFlightChange(window, ChangeType::VISIBLE),
+ revert_visible_(revert_value) {}
+
+InFlightVisibleChange::~InFlightVisibleChange() {}
+
+void InFlightVisibleChange::SetRevertValueFrom(const InFlightChange& change) {
+ revert_visible_ =
+ static_cast<const InFlightVisibleChange&>(change).revert_visible_;
+}
+
+void InFlightVisibleChange::Revert() {
+ WindowPrivate(window()).LocalSetVisible(revert_visible_);
+}
+
+// InFlightOpacityChange -------------------------------------------------------
+
+InFlightOpacityChange::InFlightOpacityChange(Window* window, float revert_value)
+ : InFlightChange(window, ChangeType::OPACITY),
+ revert_opacity_(revert_value) {}
+
+InFlightOpacityChange::~InFlightOpacityChange() {}
+
+void InFlightOpacityChange::SetRevertValueFrom(const InFlightChange& change) {
+ revert_opacity_ =
+ static_cast<const InFlightOpacityChange&>(change).revert_opacity_;
+}
+
+void InFlightOpacityChange::Revert() {
+ WindowPrivate(window()).LocalSetOpacity(revert_opacity_);
+}
+
+// InFlightSetModalChange ------------------------------------------------------
+
+InFlightSetModalChange::InFlightSetModalChange(Window* window)
+ : InFlightChange(window, ChangeType::SET_MODAL) {}
+
+InFlightSetModalChange::~InFlightSetModalChange() {}
+
+void InFlightSetModalChange::SetRevertValueFrom(const InFlightChange& change) {}
+
+void InFlightSetModalChange::Revert() {
+ WindowPrivate(window()).LocalUnsetModal();
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/public/cpp/lib/in_flight_change.h b/chromium/components/mus/public/cpp/lib/in_flight_change.h
new file mode 100644
index 00000000000..bf77fc6e6b1
--- /dev/null
+++ b/chromium/components/mus/public/cpp/lib/in_flight_change.h
@@ -0,0 +1,298 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_PUBLIC_CPP_LIB_IN_FLIGHT_CHANGE_H_
+#define COMPONENTS_MUS_PUBLIC_CPP_LIB_IN_FLIGHT_CHANGE_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "components/mus/public/cpp/window_observer.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace mus {
+
+namespace mojom {
+enum class Cursor : int32_t;
+}
+
+class Window;
+class WindowTreeClient;
+
+enum class ChangeType {
+ ADD_CHILD,
+ ADD_TRANSIENT_WINDOW,
+ BOUNDS,
+ CAPTURE,
+ DELETE_WINDOW,
+ FOCUS,
+ NEW_WINDOW,
+ NEW_TOP_LEVEL_WINDOW,
+ OPACITY,
+ PREDEFINED_CURSOR,
+ PROPERTY,
+ REMOVE_CHILD,
+ REMOVE_TRANSIENT_WINDOW_FROM_PARENT,
+ REORDER,
+ SET_MODAL,
+ VISIBLE,
+};
+
+// InFlightChange is used to track function calls to the server and take the
+// appropriate action when the call fails, or the same property changes while
+// waiting for the response. When a function is called on the server an
+// InFlightChange is created. The function call is complete when
+// OnChangeCompleted() is received from the server. The following may occur
+// while waiting for a response:
+// . A new value is encountered from the server. For example, the bounds of
+// a window is locally changed and while waiting for the ack
+// OnWindowBoundsChanged() is received.
+// When this happens SetRevertValueFrom() is invoked on the InFlightChange.
+// The expectation is SetRevertValueFrom() takes the value to revert from the
+// supplied change.
+// . While waiting for the ack the property is again modified locally. When
+// this happens a new InFlightChange is created. Once the ack for the first
+// call is received, and the server rejected the change ChangeFailed() is
+// invoked on the first change followed by SetRevertValueFrom() on the second
+// InFlightChange. This allows the new change to update the value to revert
+// should the second call fail.
+// . If the server responds that the call failed and there is not another
+// InFlightChange for the same window outstanding, then ChangeFailed() is
+// invoked followed by Revert(). The expectation is Revert() sets the
+// appropriate value back on the Window.
+//
+// In general there are two classes of changes:
+// 1. We are the only side allowed to make the change.
+// 2. The change can also be applied by another client. For example, the
+// window manager may change the bounds as well as the local client.
+//
+// For (1) use CrashInFlightChange. As the name implies this change CHECKs that
+// the change succeeded. Use the following pattern for this. This code goes
+// where the change is sent to the server (in WindowTreeClient):
+// const uint32_t change_id =
+// ScheduleInFlightChange(base::WrapUnique(new CrashInFlightChange(
+// window, ChangeType::REORDER)));
+//
+// For (2) use the same pattern as (1), but in the on change callback from the
+// server (e.g. OnWindowBoundsChanged()) add the following:
+// // value_from_server is the value supplied from the server. It corresponds
+// // to the value of the property at the time the server processed the
+// // change. If the local change fails, this is the value reverted to.
+// InFlightBoundsChange new_change(window, value_from_server);
+// if (ApplyServerChangeToExistingInFlightChange(new_change)) {
+// // There was an in flight change for the same property. The in flight
+// // change takes the value to revert from |new_change|.
+// return;
+// }
+//
+// // else case is no flight in change and the new value can be applied
+// // immediately.
+// WindowPrivate(window).LocalSetValue(new_value_from_server);
+//
+class InFlightChange {
+ public:
+ InFlightChange(Window* window, ChangeType type);
+ virtual ~InFlightChange();
+
+ // NOTE: for properties not associated with any window window is null.
+ Window* window() { return window_; }
+ const Window* window() const { return window_; }
+ ChangeType change_type() const { return change_type_; }
+
+ // Returns true if the two changes are considered the same. This is only
+ // invoked if the window id and ChangeType match.
+ virtual bool Matches(const InFlightChange& change) const;
+
+ // Called in two cases:
+ // . When the corresponding property on the server changes. In this case
+ // |change| corresponds to the value from the server.
+ // . When |change| unsuccesfully completes.
+ virtual void SetRevertValueFrom(const InFlightChange& change) = 0;
+
+ // The change failed. Normally you need not take any action here as Revert()
+ // is called as appropriate.
+ virtual void ChangeFailed();
+
+ // The change failed and there is no pending change of the same type
+ // outstanding, revert the value.
+ virtual void Revert() = 0;
+
+ private:
+ Window* window_;
+ const ChangeType change_type_;
+};
+
+class InFlightBoundsChange : public InFlightChange {
+ public:
+ InFlightBoundsChange(Window* window, const gfx::Rect& revert_bounds);
+
+ // InFlightChange:
+ void SetRevertValueFrom(const InFlightChange& change) override;
+ void Revert() override;
+
+ private:
+ gfx::Rect revert_bounds_;
+
+ DISALLOW_COPY_AND_ASSIGN(InFlightBoundsChange);
+};
+
+// Inflight change that crashes on failure. This is useful for changes that are
+// expected to always complete.
+class CrashInFlightChange : public InFlightChange {
+ public:
+ CrashInFlightChange(Window* window, ChangeType type);
+ ~CrashInFlightChange() override;
+
+ // InFlightChange:
+ void SetRevertValueFrom(const InFlightChange& change) override;
+ void ChangeFailed() override;
+ void Revert() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CrashInFlightChange);
+};
+
+// Use this class for properties that are specific to the client, and not a
+// particular window. For example, only a single window can have focus, so focus
+// is specific to the client.
+//
+// This does not implement InFlightChange::Revert, subclasses must implement
+// that to update the WindowTreeClient.
+class InFlightWindowTreeClientChange : public InFlightChange,
+ public WindowObserver {
+ public:
+ InFlightWindowTreeClientChange(WindowTreeClient* client,
+ Window* revert_value,
+ ChangeType type);
+ ~InFlightWindowTreeClientChange() override;
+
+ // InFlightChange:
+ void SetRevertValueFrom(const InFlightChange& change) override;
+
+ protected:
+ WindowTreeClient* client() { return client_; }
+ Window* revert_window() { return revert_window_; }
+
+ private:
+ void SetRevertWindow(Window* window);
+
+ // WindowObserver:
+ void OnWindowDestroying(Window* window) override;
+
+ WindowTreeClient* client_;
+ Window* revert_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(InFlightWindowTreeClientChange);
+};
+
+class InFlightCaptureChange : public InFlightWindowTreeClientChange {
+ public:
+ InFlightCaptureChange(WindowTreeClient* client, Window* revert_value);
+ ~InFlightCaptureChange() override;
+
+ // InFlightChange:
+ void Revert() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(InFlightCaptureChange);
+};
+
+class InFlightFocusChange : public InFlightWindowTreeClientChange {
+ public:
+ InFlightFocusChange(WindowTreeClient* client, Window* revert_value);
+ ~InFlightFocusChange() override;
+
+ // InFlightChange:
+ void Revert() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(InFlightFocusChange);
+};
+
+class InFlightPropertyChange : public InFlightChange {
+ public:
+ InFlightPropertyChange(Window* window,
+ const std::string& property_name,
+ const mojo::Array<uint8_t>& revert_value);
+ ~InFlightPropertyChange() override;
+
+ // InFlightChange:
+ bool Matches(const InFlightChange& change) const override;
+ void SetRevertValueFrom(const InFlightChange& change) override;
+ void Revert() override;
+
+ private:
+ const std::string property_name_;
+ mojo::Array<uint8_t> revert_value_;
+
+ DISALLOW_COPY_AND_ASSIGN(InFlightPropertyChange);
+};
+
+class InFlightPredefinedCursorChange : public InFlightChange {
+ public:
+ InFlightPredefinedCursorChange(Window* window, mojom::Cursor revert_value);
+ ~InFlightPredefinedCursorChange() override;
+
+ // InFlightChange:
+ void SetRevertValueFrom(const InFlightChange& change) override;
+ void Revert() override;
+
+ private:
+ mojom::Cursor revert_cursor_;
+
+ DISALLOW_COPY_AND_ASSIGN(InFlightPredefinedCursorChange);
+};
+
+class InFlightVisibleChange : public InFlightChange {
+ public:
+ InFlightVisibleChange(Window* window, const bool revert_value);
+ ~InFlightVisibleChange() override;
+
+ // InFlightChange:
+ void SetRevertValueFrom(const InFlightChange& change) override;
+ void Revert() override;
+
+ private:
+ bool revert_visible_;
+
+ DISALLOW_COPY_AND_ASSIGN(InFlightVisibleChange);
+};
+
+class InFlightOpacityChange : public InFlightChange {
+ public:
+ InFlightOpacityChange(Window* window, float revert_value);
+ ~InFlightOpacityChange() override;
+
+ // InFlightChange:
+ void SetRevertValueFrom(const InFlightChange& change) override;
+ void Revert() override;
+
+ private:
+ float revert_opacity_;
+
+ DISALLOW_COPY_AND_ASSIGN(InFlightOpacityChange);
+};
+
+class InFlightSetModalChange : public InFlightChange {
+ public:
+ explicit InFlightSetModalChange(Window* window);
+ ~InFlightSetModalChange() override;
+
+ // InFlightChange:
+ void SetRevertValueFrom(const InFlightChange& change) override;
+ void Revert() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(InFlightSetModalChange);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_PUBLIC_CPP_LIB_IN_FLIGHT_CHANGE_H_
diff --git a/chromium/components/mus/public/cpp/lib/output_surface.cc b/chromium/components/mus/public/cpp/lib/output_surface.cc
new file mode 100644
index 00000000000..86462ec95f4
--- /dev/null
+++ b/chromium/components/mus/public/cpp/lib/output_surface.cc
@@ -0,0 +1,70 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/public/cpp/output_surface.h"
+
+#include "base/bind.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/output/compositor_frame_ack.h"
+#include "cc/output/output_surface_client.h"
+#include "components/mus/public/cpp/window_surface.h"
+
+namespace mus {
+
+OutputSurface::OutputSurface(
+ const scoped_refptr<cc::ContextProvider>& context_provider,
+ std::unique_ptr<mus::WindowSurface> surface)
+ : cc::OutputSurface(context_provider, nullptr, nullptr),
+ surface_(std::move(surface)) {
+ capabilities_.delegated_rendering = true;
+}
+
+OutputSurface::~OutputSurface() {}
+
+bool OutputSurface::BindToClient(cc::OutputSurfaceClient* client) {
+ surface_->BindToThread();
+ surface_->set_client(this);
+ return cc::OutputSurface::BindToClient(client);
+}
+
+void OutputSurface::DetachFromClient() {
+ surface_.reset();
+ cc::OutputSurface::DetachFromClient();
+}
+
+void OutputSurface::BindFramebuffer() {
+ // This is a delegating output surface, no framebuffer/direct drawing support.
+ NOTREACHED();
+}
+
+uint32_t OutputSurface::GetFramebufferCopyTextureFormat() {
+ // This is a delegating output surface, no framebuffer/direct drawing support.
+ NOTREACHED();
+ return 0;
+}
+
+void OutputSurface::SwapBuffers(cc::CompositorFrame frame) {
+ // TODO(fsamuel, rjkroege): We should probably throttle compositor frames.
+ client_->DidSwapBuffers();
+ // OutputSurface owns WindowSurface, and so if OutputSurface is
+ // destroyed then SubmitCompositorFrame's callback will never get called.
+ // Thus, base::Unretained is safe here.
+ surface_->SubmitCompositorFrame(
+ std::move(frame),
+ base::Bind(&OutputSurface::SwapBuffersComplete, base::Unretained(this)));
+}
+
+void OutputSurface::OnResourcesReturned(
+ mus::WindowSurface* surface,
+ mojo::Array<cc::ReturnedResource> resources) {
+ cc::CompositorFrameAck cfa;
+ cfa.resources = resources.To<cc::ReturnedResourceArray>();
+ ReclaimResources(&cfa);
+}
+
+void OutputSurface::SwapBuffersComplete() {
+ client_->DidSwapBuffersComplete();
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/public/cpp/lib/property_type_converters.cc b/chromium/components/mus/public/cpp/lib/property_type_converters.cc
new file mode 100644
index 00000000000..17cacf1f249
--- /dev/null
+++ b/chromium/components/mus/public/cpp/lib/property_type_converters.cc
@@ -0,0 +1,207 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/public/cpp/property_type_converters.h"
+
+#include <stdint.h>
+
+#include "base/strings/utf_string_conversions.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace {
+
+// Maximum allowed height or width of a bitmap, in pixels. This limit prevents
+// malformed bitmap headers from causing arbitrarily large memory allocations
+// for pixel data.
+const int kMaxBitmapSize = 4096;
+
+} // namespace
+
+namespace mojo {
+
+// static
+std::vector<uint8_t> TypeConverter<std::vector<uint8_t>, gfx::Rect>::Convert(
+ const gfx::Rect& input) {
+ std::vector<uint8_t> vec(16);
+ vec[0] = (input.x() >> 24) & 0xFF;
+ vec[1] = (input.x() >> 16) & 0xFF;
+ vec[2] = (input.x() >> 8) & 0xFF;
+ vec[3] = input.x() & 0xFF;
+ vec[4] = (input.y() >> 24) & 0xFF;
+ vec[5] = (input.y() >> 16) & 0xFF;
+ vec[6] = (input.y() >> 8) & 0xFF;
+ vec[7] = input.y() & 0xFF;
+ vec[8] = (input.width() >> 24) & 0xFF;
+ vec[9] = (input.width() >> 16) & 0xFF;
+ vec[10] = (input.width() >> 8) & 0xFF;
+ vec[11] = input.width() & 0xFF;
+ vec[12] = (input.height() >> 24) & 0xFF;
+ vec[13] = (input.height() >> 16) & 0xFF;
+ vec[14] = (input.height() >> 8) & 0xFF;
+ vec[15] = input.height() & 0xFF;
+ return vec;
+}
+
+// static
+gfx::Rect TypeConverter<gfx::Rect, std::vector<uint8_t>>::Convert(
+ const std::vector<uint8_t>& input) {
+ return gfx::Rect(
+ input[0] << 24 | input[1] << 16 | input[2] << 8 | input[3],
+ input[4] << 24 | input[5] << 16 | input[6] << 8 | input[7],
+ input[8] << 24 | input[9] << 16 | input[10] << 8 | input[11],
+ input[12] << 24 | input[13] << 16 | input[14] << 8 | input[15]);
+}
+
+// static
+std::vector<uint8_t> TypeConverter<std::vector<uint8_t>, gfx::Size>::Convert(
+ const gfx::Size& input) {
+ std::vector<uint8_t> vec(8);
+ vec[0] = (input.width() >> 24) & 0xFF;
+ vec[1] = (input.width() >> 16) & 0xFF;
+ vec[2] = (input.width() >> 8) & 0xFF;
+ vec[3] = input.width() & 0xFF;
+ vec[4] = (input.height() >> 24) & 0xFF;
+ vec[5] = (input.height() >> 16) & 0xFF;
+ vec[6] = (input.height() >> 8) & 0xFF;
+ vec[7] = input.height() & 0xFF;
+ return vec;
+}
+
+// static
+gfx::Size TypeConverter<gfx::Size, std::vector<uint8_t>>::Convert(
+ const std::vector<uint8_t>& input) {
+ return gfx::Size(input[0] << 24 | input[1] << 16 | input[2] << 8 | input[3],
+ input[4] << 24 | input[5] << 16 | input[6] << 8 | input[7]);
+}
+
+// static
+std::vector<uint8_t> TypeConverter<std::vector<uint8_t>, int32_t>::Convert(
+ const int32_t& input) {
+ std::vector<uint8_t> vec(4);
+ vec[0] = (input >> 24) & 0xFF;
+ vec[1] = (input >> 16) & 0xFF;
+ vec[2] = (input >> 8) & 0xFF;
+ vec[3] = input & 0xFF;
+ return vec;
+}
+
+// static
+int32_t TypeConverter<int32_t, std::vector<uint8_t>>::Convert(
+ const std::vector<uint8_t>& input) {
+ return input[0] << 24 | input[1] << 16 | input[2] << 8 | input[3];
+}
+
+// static
+std::vector<uint8_t>
+TypeConverter<std::vector<uint8_t>, base::string16>::Convert(
+ const base::string16& input) {
+ return ConvertTo<std::vector<uint8_t>>(base::UTF16ToUTF8(input));
+}
+
+// static
+base::string16 TypeConverter<base::string16, std::vector<uint8_t>>::Convert(
+ const std::vector<uint8_t>& input) {
+ return base::UTF8ToUTF16(ConvertTo<std::string>(input));
+}
+
+// static
+std::vector<uint8_t> TypeConverter<std::vector<uint8_t>, std::string>::Convert(
+ const std::string& input) {
+ return std::vector<uint8_t>(input.begin(), input.end());
+}
+
+// static
+std::string TypeConverter<std::string, std::vector<uint8_t>>::Convert(
+ const std::vector<uint8_t>& input) {
+ return std::string(input.begin(), input.end());
+}
+
+// static
+std::vector<uint8_t> TypeConverter<std::vector<uint8_t>, SkBitmap>::Convert(
+ const SkBitmap& input) {
+ // Empty images are valid to serialize and are represented by an empty vector.
+ if (input.isNull())
+ return std::vector<uint8_t>();
+
+ // Only RGBA 8888 bitmaps with premultiplied alpha are supported.
+ if (input.colorType() != kBGRA_8888_SkColorType ||
+ input.alphaType() != kPremul_SkAlphaType) {
+ NOTREACHED();
+ return std::vector<uint8_t>();
+ }
+
+ // Sanity check the bitmap size.
+ int width = input.width();
+ int height = input.height();
+ if (width < 0 || width > kMaxBitmapSize || height < 0 ||
+ height > kMaxBitmapSize) {
+ NOTREACHED();
+ return std::vector<uint8_t>();
+ }
+
+ // Serialize the bitmap. The size is restricted so only 2 bytes are required
+ // per dimension.
+ std::vector<uint8_t> vec(4 + input.getSize());
+ vec[0] = (width >> 8) & 0xFF;
+ vec[1] = width & 0xFF;
+ vec[2] = (height >> 8) & 0xFF;
+ vec[3] = height & 0xFF;
+ if (!input.copyPixelsTo(&vec[4], input.getSize()))
+ return std::vector<uint8_t>();
+ return vec;
+}
+
+// static
+SkBitmap TypeConverter<SkBitmap, std::vector<uint8_t>>::Convert(
+ const std::vector<uint8_t>& input) {
+ // Empty images are represented by empty vectors.
+ if (input.empty())
+ return SkBitmap();
+
+ // Read and sanity check size.
+ int width = input[0] << 8 | input[1];
+ int height = input[2] << 8 | input[3];
+ if (width < 0 || width > kMaxBitmapSize || height < 0 ||
+ height > kMaxBitmapSize) {
+ NOTREACHED();
+ return SkBitmap();
+ }
+
+ // Try to allocate a bitmap of the appropriate size.
+ SkBitmap bitmap;
+ if (!bitmap.tryAllocPixels(SkImageInfo::Make(
+ width, height, kBGRA_8888_SkColorType, kPremul_SkAlphaType))) {
+ return SkBitmap();
+ }
+
+ // Ensure the vector contains the right amount of data.
+ if (input.size() != bitmap.getSize() + 4) {
+ NOTREACHED();
+ return SkBitmap();
+ }
+
+ // Read the pixel data.
+ SkAutoLockPixels lock(bitmap);
+ memcpy(bitmap.getPixels(), &input[4], bitmap.getSize());
+ return bitmap;
+}
+
+// static
+std::vector<uint8_t> TypeConverter<std::vector<uint8_t>, bool>::Convert(
+ bool input) {
+ std::vector<uint8_t> vec(1);
+ vec[0] = input ? 1 : 0;
+ return vec;
+}
+
+// static
+bool TypeConverter<bool, std::vector<uint8_t>>::Convert(
+ const std::vector<uint8_t>& input) {
+ // Empty vectors are interpreted as false.
+ return !input.empty() && (input[0] == 1);
+}
+
+} // namespace mojo
diff --git a/chromium/components/mus/public/cpp/lib/scoped_window_ptr.cc b/chromium/components/mus/public/cpp/lib/scoped_window_ptr.cc
new file mode 100644
index 00000000000..360da1ff4b5
--- /dev/null
+++ b/chromium/components/mus/public/cpp/lib/scoped_window_ptr.cc
@@ -0,0 +1,44 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/public/cpp/scoped_window_ptr.h"
+
+#include "components/mus/public/cpp/window.h"
+#include "components/mus/public/cpp/window_observer.h"
+#include "components/mus/public/cpp/window_tree_client.h"
+
+namespace mus {
+
+ScopedWindowPtr::ScopedWindowPtr(Window* window) : window_(window) {
+ window_->AddObserver(this);
+}
+
+ScopedWindowPtr::~ScopedWindowPtr() {
+ if (window_)
+ DeleteWindowOrWindowManager(window_);
+ DetachFromWindow();
+}
+
+// static
+void ScopedWindowPtr::DeleteWindowOrWindowManager(Window* window) {
+ if (window->window_tree()->GetRoots().count(window) > 0)
+ delete window->window_tree();
+ else
+ window->Destroy();
+}
+
+void ScopedWindowPtr::DetachFromWindow() {
+ if (!window_)
+ return;
+
+ window_->RemoveObserver(this);
+ window_ = nullptr;
+}
+
+void ScopedWindowPtr::OnWindowDestroying(Window* window) {
+ DCHECK_EQ(window_, window);
+ DetachFromWindow();
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/public/cpp/lib/window.cc b/chromium/components/mus/public/cpp/lib/window.cc
new file mode 100644
index 00000000000..83785c4544e
--- /dev/null
+++ b/chromium/components/mus/public/cpp/lib/window.cc
@@ -0,0 +1,891 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/public/cpp/window.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <set>
+#include <string>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "components/mus/common/transient_window_utils.h"
+#include "components/mus/public/cpp/lib/window_private.h"
+#include "components/mus/public/cpp/property_type_converters.h"
+#include "components/mus/public/cpp/window_observer.h"
+#include "components/mus/public/cpp/window_property.h"
+#include "components/mus/public/cpp/window_surface.h"
+#include "components/mus/public/cpp/window_tracker.h"
+#include "components/mus/public/cpp/window_tree_client.h"
+#include "components/mus/public/interfaces/window_manager.mojom.h"
+#include "ui/display/display.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace mus {
+
+namespace {
+
+void NotifyWindowTreeChangeAtReceiver(
+ Window* receiver,
+ const WindowObserver::TreeChangeParams& params,
+ bool change_applied) {
+ WindowObserver::TreeChangeParams local_params = params;
+ local_params.receiver = receiver;
+ if (change_applied) {
+ FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(receiver).observers(),
+ OnTreeChanged(local_params));
+ } else {
+ FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(receiver).observers(),
+ OnTreeChanging(local_params));
+ }
+}
+
+void NotifyWindowTreeChangeUp(Window* start_at,
+ const WindowObserver::TreeChangeParams& params,
+ bool change_applied) {
+ for (Window* current = start_at; current; current = current->parent())
+ NotifyWindowTreeChangeAtReceiver(current, params, change_applied);
+}
+
+void NotifyWindowTreeChangeDown(Window* start_at,
+ const WindowObserver::TreeChangeParams& params,
+ bool change_applied) {
+ NotifyWindowTreeChangeAtReceiver(start_at, params, change_applied);
+ Window::Children::const_iterator it = start_at->children().begin();
+ for (; it != start_at->children().end(); ++it)
+ NotifyWindowTreeChangeDown(*it, params, change_applied);
+}
+
+void NotifyWindowTreeChange(const WindowObserver::TreeChangeParams& params,
+ bool change_applied) {
+ NotifyWindowTreeChangeDown(params.target, params, change_applied);
+ if (params.old_parent)
+ NotifyWindowTreeChangeUp(params.old_parent, params, change_applied);
+ if (params.new_parent)
+ NotifyWindowTreeChangeUp(params.new_parent, params, change_applied);
+}
+
+class ScopedTreeNotifier {
+ public:
+ ScopedTreeNotifier(Window* target, Window* old_parent, Window* new_parent) {
+ params_.target = target;
+ params_.old_parent = old_parent;
+ params_.new_parent = new_parent;
+ NotifyWindowTreeChange(params_, false);
+ }
+ ~ScopedTreeNotifier() { NotifyWindowTreeChange(params_, true); }
+
+ private:
+ WindowObserver::TreeChangeParams params_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedTreeNotifier);
+};
+
+void RemoveChildImpl(Window* child, Window::Children* children) {
+ Window::Children::iterator it =
+ std::find(children->begin(), children->end(), child);
+ if (it != children->end()) {
+ children->erase(it);
+ WindowPrivate(child).ClearParent();
+ }
+}
+
+class OrderChangedNotifier {
+ public:
+ OrderChangedNotifier(Window* window,
+ Window* relative_window,
+ mojom::OrderDirection direction)
+ : window_(window),
+ relative_window_(relative_window),
+ direction_(direction) {}
+
+ ~OrderChangedNotifier() {}
+
+ void NotifyWindowReordering() {
+ FOR_EACH_OBSERVER(
+ WindowObserver, *WindowPrivate(window_).observers(),
+ OnWindowReordering(window_, relative_window_, direction_));
+ }
+
+ void NotifyWindowReordered() {
+ FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(window_).observers(),
+ OnWindowReordered(window_, relative_window_, direction_));
+ }
+
+ private:
+ Window* window_;
+ Window* relative_window_;
+ mojom::OrderDirection direction_;
+
+ DISALLOW_COPY_AND_ASSIGN(OrderChangedNotifier);
+};
+
+class ScopedSetBoundsNotifier {
+ public:
+ ScopedSetBoundsNotifier(Window* window,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds)
+ : window_(window), old_bounds_(old_bounds), new_bounds_(new_bounds) {
+ FOR_EACH_OBSERVER(
+ WindowObserver, *WindowPrivate(window_).observers(),
+ OnWindowBoundsChanging(window_, old_bounds_, new_bounds_));
+ }
+ ~ScopedSetBoundsNotifier() {
+ FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(window_).observers(),
+ OnWindowBoundsChanged(window_, old_bounds_, new_bounds_));
+ }
+
+ private:
+ Window* window_;
+ const gfx::Rect old_bounds_;
+ const gfx::Rect new_bounds_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedSetBoundsNotifier);
+};
+
+// Some operations are only permitted in the client that created the window.
+bool OwnsWindow(WindowTreeClient* client, Window* window) {
+ return !client || client->OwnsWindow(window);
+}
+
+bool IsClientRoot(Window* window) {
+ return window->window_tree() &&
+ window->window_tree()->GetRoots().count(window) > 0;
+}
+
+bool OwnsWindowOrIsRoot(Window* window) {
+ return OwnsWindow(window->window_tree(), window) || IsClientRoot(window);
+}
+
+void EmptyEmbedCallback(bool result) {}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// Window, public:
+
+void Window::Destroy() {
+ if (!OwnsWindowOrIsRoot(this))
+ return;
+
+ if (client_)
+ client_->DestroyWindow(this);
+ while (!children_.empty()) {
+ Window* child = children_.front();
+ if (!OwnsWindow(client_, child)) {
+ WindowPrivate(child).ClearParent();
+ children_.erase(children_.begin());
+ } else {
+ child->Destroy();
+ DCHECK(std::find(children_.begin(), children_.end(), child) ==
+ children_.end());
+ }
+ }
+ LocalDestroy();
+}
+
+void Window::SetBounds(const gfx::Rect& bounds) {
+ if (!OwnsWindowOrIsRoot(this))
+ return;
+ if (bounds_ == bounds)
+ return;
+ if (client_)
+ client_->SetBounds(this, bounds_, bounds);
+ LocalSetBounds(bounds_, bounds);
+}
+
+gfx::Rect Window::GetBoundsInRoot() const {
+ gfx::Vector2d offset;
+ for (const Window* w = parent(); w != nullptr; w = w->parent())
+ offset += w->bounds().OffsetFromOrigin();
+ return bounds() + offset;
+}
+
+void Window::SetClientArea(
+ const gfx::Insets& client_area,
+ const std::vector<gfx::Rect>& additional_client_areas) {
+ if (!OwnsWindowOrIsRoot(this))
+ return;
+
+ if (client_)
+ client_->SetClientArea(server_id_, client_area,
+ additional_client_areas);
+ LocalSetClientArea(client_area, additional_client_areas);
+}
+
+void Window::SetHitTestMask(const gfx::Rect& mask) {
+ if (!OwnsWindowOrIsRoot(this))
+ return;
+
+ if (hit_test_mask_ && *hit_test_mask_ == mask)
+ return;
+
+ if (client_)
+ client_->SetHitTestMask(server_id_, mask);
+ hit_test_mask_.reset(new gfx::Rect(mask));
+}
+
+void Window::ClearHitTestMask() {
+ if (!OwnsWindowOrIsRoot(this))
+ return;
+
+ if (!hit_test_mask_)
+ return;
+
+ if (client_)
+ client_->ClearHitTestMask(server_id_);
+ hit_test_mask_.reset();
+}
+
+void Window::SetVisible(bool value) {
+ if (visible_ == value)
+ return;
+
+ if (client_)
+ client_->SetVisible(this, value);
+ LocalSetVisible(value);
+}
+
+void Window::SetOpacity(float opacity) {
+ if (client_)
+ client_->SetOpacity(this, opacity);
+ LocalSetOpacity(opacity);
+}
+
+void Window::SetPredefinedCursor(mus::mojom::Cursor cursor_id) {
+ if (cursor_id_ == cursor_id)
+ return;
+
+ if (client_)
+ client_->SetPredefinedCursor(server_id_, cursor_id);
+ LocalSetPredefinedCursor(cursor_id);
+}
+
+bool Window::IsDrawn() const {
+ if (!visible_)
+ return false;
+ return parent_ ? parent_->IsDrawn() : parent_drawn_;
+}
+
+std::unique_ptr<WindowSurface> Window::RequestSurface(mojom::SurfaceType type) {
+ std::unique_ptr<WindowSurfaceBinding> surface_binding;
+ std::unique_ptr<WindowSurface> surface =
+ WindowSurface::Create(&surface_binding);
+ AttachSurface(type, std::move(surface_binding));
+ return surface;
+}
+
+void Window::AttachSurface(
+ mojom::SurfaceType type,
+ std::unique_ptr<WindowSurfaceBinding> surface_binding) {
+ window_tree()->AttachSurface(
+ server_id_, type, std::move(surface_binding->surface_request_),
+ mojo::MakeProxy(std::move(surface_binding->surface_client_)));
+}
+
+void Window::ClearSharedProperty(const std::string& name) {
+ SetSharedPropertyInternal(name, nullptr);
+}
+
+bool Window::HasSharedProperty(const std::string& name) const {
+ return properties_.count(name) > 0;
+}
+
+void Window::AddObserver(WindowObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void Window::RemoveObserver(WindowObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+const Window* Window::GetRoot() const {
+ const Window* root = this;
+ for (const Window* parent = this; parent; parent = parent->parent())
+ root = parent;
+ return root;
+}
+
+void Window::AddChild(Window* child) {
+ // TODO(beng): not necessarily valid to all clients, but possibly to the
+ // embeddee in an embedder-embeddee relationship.
+ if (client_)
+ CHECK_EQ(child->client_, client_);
+ // Roots can not be added as children of other windows.
+ if (window_tree() && window_tree()->IsRoot(child))
+ return;
+ LocalAddChild(child);
+ if (client_)
+ client_->AddChild(this, child->server_id());
+}
+
+void Window::RemoveChild(Window* child) {
+ // TODO(beng): not necessarily valid to all clients, but possibly to the
+ // embeddee in an embedder-embeddee relationship.
+ if (client_)
+ CHECK_EQ(child->client_, client_);
+ LocalRemoveChild(child);
+ if (client_)
+ client_->RemoveChild(this, child->server_id());
+}
+
+void Window::Reorder(Window* relative, mojom::OrderDirection direction) {
+ if (!LocalReorder(relative, direction))
+ return;
+ if (client_)
+ client_->Reorder(this, relative->server_id(), direction);
+}
+
+void Window::MoveToFront() {
+ if (!parent_ || parent_->children_.back() == this)
+ return;
+ Reorder(parent_->children_.back(), mojom::OrderDirection::ABOVE);
+}
+
+void Window::MoveToBack() {
+ if (!parent_ || parent_->children_.front() == this)
+ return;
+ Reorder(parent_->children_.front(), mojom::OrderDirection::BELOW);
+}
+
+bool Window::Contains(const Window* child) const {
+ if (!child)
+ return false;
+ if (child == this)
+ return true;
+ if (client_)
+ CHECK_EQ(child->client_, client_);
+ for (const Window* p = child->parent(); p; p = p->parent()) {
+ if (p == this)
+ return true;
+ }
+ return false;
+}
+
+void Window::AddTransientWindow(Window* transient_window) {
+ // A system modal window cannot become a transient child.
+ DCHECK(!transient_window->is_modal() || transient_window->transient_parent());
+
+ if (client_)
+ CHECK_EQ(transient_window->client_, client_);
+ LocalAddTransientWindow(transient_window);
+ if (client_)
+ client_->AddTransientWindow(this, transient_window->server_id());
+}
+
+void Window::RemoveTransientWindow(Window* transient_window) {
+ if (client_)
+ CHECK_EQ(transient_window->window_tree(), client_);
+ LocalRemoveTransientWindow(transient_window);
+ if (client_)
+ client_->RemoveTransientWindowFromParent(transient_window);
+}
+
+void Window::SetModal() {
+ if (is_modal_)
+ return;
+
+ LocalSetModal();
+ if (client_)
+ client_->SetModal(this);
+}
+
+Window* Window::GetChildByLocalId(int id) {
+ if (id == local_id_)
+ return this;
+ // TODO(beng): this could be improved depending on how we decide to own
+ // windows.
+ for (Window* child : children_) {
+ Window* matching_child = child->GetChildByLocalId(id);
+ if (matching_child)
+ return matching_child;
+ }
+ return nullptr;
+}
+
+void Window::SetTextInputState(mojo::TextInputStatePtr state) {
+ if (client_)
+ client_->SetWindowTextInputState(server_id_, std::move(state));
+}
+
+void Window::SetImeVisibility(bool visible, mojo::TextInputStatePtr state) {
+ // SetImeVisibility() shouldn't be used if the window is not editable.
+ DCHECK(state.is_null() || state->type != mojo::TextInputType::NONE);
+ if (client_)
+ client_->SetImeVisibility(server_id_, visible, std::move(state));
+}
+
+bool Window::HasCapture() const {
+ return client_ && client_->GetCaptureWindow() == this;
+}
+
+void Window::SetCapture() {
+ if (client_)
+ client_->SetCapture(this);
+}
+
+void Window::ReleaseCapture() {
+ if (client_)
+ client_->ReleaseCapture(this);
+}
+
+void Window::SetFocus() {
+ if (client_ && IsDrawn())
+ client_->SetFocus(this);
+}
+
+bool Window::HasFocus() const {
+ return client_ && client_->GetFocusedWindow() == this;
+}
+
+void Window::SetCanFocus(bool can_focus) {
+ if (client_)
+ client_->SetCanFocus(server_id_, can_focus);
+}
+
+void Window::Embed(mus::mojom::WindowTreeClientPtr client, uint32_t flags) {
+ Embed(std::move(client), base::Bind(&EmptyEmbedCallback), flags);
+}
+
+void Window::Embed(mus::mojom::WindowTreeClientPtr client,
+ const EmbedCallback& callback,
+ uint32_t flags) {
+ if (PrepareForEmbed())
+ client_->Embed(server_id_, std::move(client), flags, callback);
+ else
+ callback.Run(false);
+}
+
+void Window::RequestClose() {
+ if (client_)
+ client_->RequestClose(this);
+}
+
+std::string Window::GetName() const {
+ if (HasSharedProperty(mojom::WindowManager::kName_Property))
+ return GetSharedProperty<std::string>(mojom::WindowManager::kName_Property);
+
+ return std::string();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Window, protected:
+
+Window::Window() : Window(nullptr, static_cast<Id>(-1)) {}
+
+Window::~Window() {
+ FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowDestroying(this));
+ if (client_)
+ client_->OnWindowDestroying(this);
+
+ if (HasFocus()) {
+ // The focused window is being removed. When this happens the server
+ // advances focus. We don't want to randomly pick a Window to get focus, so
+ // we update local state only, and wait for the next focus change from the
+ // server.
+ client_->LocalSetFocus(nullptr);
+ }
+
+ // Remove from transient parent.
+ if (transient_parent_)
+ transient_parent_->LocalRemoveTransientWindow(this);
+
+ // Remove transient children.
+ while (!transient_children_.empty()) {
+ Window* transient_child = transient_children_.front();
+ LocalRemoveTransientWindow(transient_child);
+ transient_child->LocalDestroy();
+ DCHECK(transient_children_.empty() ||
+ transient_children_.front() != transient_child);
+ }
+
+ if (parent_)
+ parent_->LocalRemoveChild(this);
+
+ // We may still have children. This can happen if the embedder destroys the
+ // root while we're still alive.
+ while (!children_.empty()) {
+ Window* child = children_.front();
+ LocalRemoveChild(child);
+ DCHECK(children_.empty() || children_.front() != child);
+ }
+
+ // Clear properties.
+ for (auto& pair : prop_map_) {
+ if (pair.second.deallocator)
+ (*pair.second.deallocator)(pair.second.value);
+ }
+ prop_map_.clear();
+
+ FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowDestroyed(this));
+
+ // Invoke after observers so that can clean up any internal state observers
+ // may have changed.
+ if (window_tree())
+ window_tree()->OnWindowDestroyed(this);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Window, private:
+
+Window::Window(WindowTreeClient* client, Id id)
+ : client_(client),
+ server_id_(id),
+ parent_(nullptr),
+ stacking_target_(nullptr),
+ transient_parent_(nullptr),
+ is_modal_(false),
+ // Matches aura, see aura::Window for details.
+ observers_(base::ObserverList<WindowObserver>::NOTIFY_EXISTING_ONLY),
+ input_event_handler_(nullptr),
+ visible_(false),
+ opacity_(1.0f),
+ display_id_(display::Display::kInvalidDisplayID),
+ cursor_id_(mojom::Cursor::CURSOR_NULL),
+ parent_drawn_(false) {}
+
+void Window::SetSharedPropertyInternal(const std::string& name,
+ const std::vector<uint8_t>* value) {
+ if (!OwnsWindowOrIsRoot(this))
+ return;
+
+ if (client_) {
+ mojo::Array<uint8_t> transport_value(nullptr);
+ if (value) {
+ transport_value.resize(value->size());
+ if (value->size())
+ memcpy(&transport_value.front(), &(value->front()), value->size());
+ }
+ // TODO: add test coverage of this (450303).
+ client_->SetProperty(this, name, std::move(transport_value));
+ }
+ LocalSetSharedProperty(name, value);
+}
+
+int64_t Window::SetLocalPropertyInternal(const void* key,
+ const char* name,
+ PropertyDeallocator deallocator,
+ int64_t value,
+ int64_t default_value) {
+ int64_t old = GetLocalPropertyInternal(key, default_value);
+ if (value == default_value) {
+ prop_map_.erase(key);
+ } else {
+ Value prop_value;
+ prop_value.name = name;
+ prop_value.value = value;
+ prop_value.deallocator = deallocator;
+ prop_map_[key] = prop_value;
+ }
+ FOR_EACH_OBSERVER(WindowObserver, observers_,
+ OnWindowLocalPropertyChanged(this, key, old));
+ return old;
+}
+
+int64_t Window::GetLocalPropertyInternal(const void* key,
+ int64_t default_value) const {
+ std::map<const void*, Value>::const_iterator iter = prop_map_.find(key);
+ if (iter == prop_map_.end())
+ return default_value;
+ return iter->second.value;
+}
+
+void Window::LocalDestroy() {
+ delete this;
+}
+
+void Window::LocalAddChild(Window* child) {
+ ScopedTreeNotifier notifier(child, child->parent(), this);
+ if (child->parent())
+ RemoveChildImpl(child, &child->parent_->children_);
+ children_.push_back(child);
+ child->parent_ = this;
+ child->display_id_ = display_id_;
+}
+
+void Window::LocalRemoveChild(Window* child) {
+ DCHECK_EQ(this, child->parent());
+ ScopedTreeNotifier notifier(child, this, nullptr);
+ RemoveChildImpl(child, &children_);
+}
+
+void Window::LocalAddTransientWindow(Window* transient_window) {
+ if (transient_window->transient_parent())
+ RemoveTransientWindowImpl(transient_window);
+ transient_children_.push_back(transient_window);
+ transient_window->transient_parent_ = this;
+
+ // Restack |transient_window| properly above its transient parent, if they
+ // share the same parent.
+ if (transient_window->parent() == parent())
+ RestackTransientDescendants(this, &GetStackingTarget,
+ &ReorderWithoutNotification);
+
+ // TODO(fsamuel): We might want a notification here.
+}
+
+void Window::LocalRemoveTransientWindow(Window* transient_window) {
+ DCHECK_EQ(this, transient_window->transient_parent());
+ RemoveTransientWindowImpl(transient_window);
+ // TODO(fsamuel): We might want a notification here.
+}
+
+void Window::LocalSetModal() {
+ is_modal_ = true;
+}
+
+bool Window::LocalReorder(Window* relative, mojom::OrderDirection direction) {
+ OrderChangedNotifier notifier(this, relative, direction);
+ return ReorderImpl(this, relative, direction, &notifier);
+}
+
+void Window::LocalSetBounds(const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {
+ // If this client owns the window, then it should be the only one to change
+ // the bounds.
+ DCHECK(!OwnsWindow(client_, this) || old_bounds == bounds_);
+ ScopedSetBoundsNotifier notifier(this, old_bounds, new_bounds);
+ bounds_ = new_bounds;
+}
+
+void Window::LocalSetClientArea(
+ const gfx::Insets& new_client_area,
+ const std::vector<gfx::Rect>& additional_client_areas) {
+ const std::vector<gfx::Rect> old_additional_client_areas =
+ additional_client_areas_;
+ const gfx::Insets old_client_area = client_area_;
+ client_area_ = new_client_area;
+ additional_client_areas_ = additional_client_areas;
+ FOR_EACH_OBSERVER(WindowObserver, observers_,
+ OnWindowClientAreaChanged(this, old_client_area,
+ old_additional_client_areas));
+}
+
+void Window::LocalSetDisplay(int64_t display_id) {
+ display_id_ = display_id;
+ // TODO(sad): Notify observers (of this window, and of the descendant windows)
+ // when a window moves from one display into another. https://crbug.com/614887
+}
+
+void Window::LocalSetParentDrawn(bool value) {
+ if (parent_drawn_ == value)
+ return;
+
+ // As IsDrawn() is derived from |visible_| and |parent_drawn_|, only send
+ // drawn notification is the value of IsDrawn() is really changing.
+ if (IsDrawn() == value) {
+ parent_drawn_ = value;
+ return;
+ }
+ FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowDrawnChanging(this));
+ parent_drawn_ = value;
+ FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowDrawnChanged(this));
+}
+
+void Window::LocalSetVisible(bool visible) {
+ if (visible_ == visible)
+ return;
+
+ FOR_EACH_OBSERVER(WindowObserver, observers_,
+ OnWindowVisibilityChanging(this));
+ visible_ = visible;
+ NotifyWindowVisibilityChanged(this);
+}
+
+void Window::LocalSetOpacity(float opacity) {
+ if (opacity_ == opacity)
+ return;
+
+ float old_opacity = opacity_;
+ opacity_ = opacity;
+ FOR_EACH_OBSERVER(WindowObserver, observers_,
+ OnWindowOpacityChanged(this, old_opacity, opacity_));
+}
+
+void Window::LocalSetPredefinedCursor(mojom::Cursor cursor_id) {
+ if (cursor_id_ == cursor_id)
+ return;
+
+ cursor_id_ = cursor_id;
+ FOR_EACH_OBSERVER(WindowObserver, observers_,
+ OnWindowPredefinedCursorChanged(this, cursor_id));
+}
+
+void Window::LocalSetSharedProperty(const std::string& name,
+ const std::vector<uint8_t>* value) {
+ std::vector<uint8_t> old_value;
+ std::vector<uint8_t>* old_value_ptr = nullptr;
+ auto it = properties_.find(name);
+ if (it != properties_.end()) {
+ old_value = it->second;
+ old_value_ptr = &old_value;
+
+ if (value && old_value == *value)
+ return;
+ } else if (!value) {
+ // This property isn't set in |properties_| and |value| is nullptr, so
+ // there's no change.
+ return;
+ }
+
+ if (value) {
+ properties_[name] = *value;
+ } else if (it != properties_.end()) {
+ properties_.erase(it);
+ }
+
+ FOR_EACH_OBSERVER(
+ WindowObserver, observers_,
+ OnWindowSharedPropertyChanged(this, name, old_value_ptr, value));
+}
+
+void Window::NotifyWindowStackingChanged() {
+ if (stacking_target_) {
+ Children::const_iterator window_i = std::find(
+ parent()->children().begin(), parent()->children().end(), this);
+ DCHECK(window_i != parent()->children().end());
+ if (window_i != parent()->children().begin() &&
+ (*(window_i - 1) == stacking_target_))
+ return;
+ }
+ RestackTransientDescendants(this, &GetStackingTarget,
+ &ReorderWithoutNotification);
+}
+
+void Window::NotifyWindowVisibilityChanged(Window* target) {
+ if (!NotifyWindowVisibilityChangedDown(target)) {
+ return; // |this| has been deleted.
+ }
+ NotifyWindowVisibilityChangedUp(target);
+}
+
+bool Window::NotifyWindowVisibilityChangedAtReceiver(Window* target) {
+ // |this| may be deleted during a call to OnWindowVisibilityChanged() on one
+ // of the observers. We create an local observer for that. In that case we
+ // exit without further access to any members.
+ WindowTracker tracker;
+ tracker.Add(this);
+ FOR_EACH_OBSERVER(WindowObserver, observers_,
+ OnWindowVisibilityChanged(target));
+ return tracker.Contains(this);
+}
+
+bool Window::NotifyWindowVisibilityChangedDown(Window* target) {
+ if (!NotifyWindowVisibilityChangedAtReceiver(target))
+ return false; // |this| was deleted.
+ std::set<const Window*> child_already_processed;
+ bool child_destroyed = false;
+ do {
+ child_destroyed = false;
+ for (Window::Children::const_iterator it = children_.begin();
+ it != children_.end(); ++it) {
+ if (!child_already_processed.insert(*it).second)
+ continue;
+ if (!(*it)->NotifyWindowVisibilityChangedDown(target)) {
+ // |*it| was deleted, |it| is invalid and |children_| has changed. We
+ // exit the current for-loop and enter a new one.
+ child_destroyed = true;
+ break;
+ }
+ }
+ } while (child_destroyed);
+ return true;
+}
+
+void Window::NotifyWindowVisibilityChangedUp(Window* target) {
+ // Start with the parent as we already notified |this|
+ // in NotifyWindowVisibilityChangedDown.
+ for (Window* window = parent(); window; window = window->parent()) {
+ bool ret = window->NotifyWindowVisibilityChangedAtReceiver(target);
+ DCHECK(ret);
+ }
+}
+
+bool Window::PrepareForEmbed() {
+ if (!OwnsWindow(client_, this))
+ return false;
+
+ while (!children_.empty())
+ RemoveChild(children_[0]);
+ return true;
+}
+
+void Window::RemoveTransientWindowImpl(Window* transient_window) {
+ Window::Children::iterator it = std::find(
+ transient_children_.begin(), transient_children_.end(), transient_window);
+ if (it != transient_children_.end()) {
+ transient_children_.erase(it);
+ transient_window->transient_parent_ = nullptr;
+ }
+ // If |transient_window| and its former transient parent share the same
+ // parent, |transient_window| should be restacked properly so it is not among
+ // transient children of its former parent, anymore.
+ if (parent() == transient_window->parent())
+ RestackTransientDescendants(this, &GetStackingTarget,
+ &ReorderWithoutNotification);
+
+ // TOOD(fsamuel): We might want to notify observers here.
+}
+
+// static
+void Window::ReorderWithoutNotification(Window* window,
+ Window* relative,
+ mojom::OrderDirection direction) {
+ ReorderImpl(window, relative, direction, nullptr);
+}
+
+// static
+// Returns true if the order actually changed.
+bool Window::ReorderImpl(Window* window,
+ Window* relative,
+ mojom::OrderDirection direction,
+ OrderChangedNotifier* notifier) {
+ DCHECK(relative);
+ DCHECK_NE(window, relative);
+ DCHECK_EQ(window->parent(), relative->parent());
+ DCHECK(window->parent());
+
+ if (!AdjustStackingForTransientWindows(&window, &relative, &direction,
+ window->stacking_target_))
+ return false;
+
+ const size_t child_i = std::find(window->parent_->children_.begin(),
+ window->parent_->children_.end(), window) -
+ window->parent_->children_.begin();
+ const size_t target_i =
+ std::find(window->parent_->children_.begin(),
+ window->parent_->children_.end(), relative) -
+ window->parent_->children_.begin();
+ if ((direction == mojom::OrderDirection::ABOVE && child_i == target_i + 1) ||
+ (direction == mojom::OrderDirection::BELOW && child_i + 1 == target_i)) {
+ return false;
+ }
+
+ if (notifier)
+ notifier->NotifyWindowReordering();
+
+ const size_t dest_i = direction == mojom::OrderDirection::ABOVE
+ ? (child_i < target_i ? target_i : target_i + 1)
+ : (child_i < target_i ? target_i - 1 : target_i);
+ window->parent_->children_.erase(window->parent_->children_.begin() +
+ child_i);
+ window->parent_->children_.insert(window->parent_->children_.begin() + dest_i,
+ window);
+
+ window->NotifyWindowStackingChanged();
+
+ if (notifier)
+ notifier->NotifyWindowReordered();
+
+ return true;
+}
+
+// static
+Window** Window::GetStackingTarget(Window* window) {
+ return &window->stacking_target_;
+}
+} // namespace mus
diff --git a/chromium/components/mus/public/cpp/lib/window_observer.cc b/chromium/components/mus/public/cpp/lib/window_observer.cc
new file mode 100644
index 00000000000..26e65078f77
--- /dev/null
+++ b/chromium/components/mus/public/cpp/lib/window_observer.cc
@@ -0,0 +1,18 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/public/cpp/window_observer.h"
+
+namespace mus {
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowObserver, public:
+
+WindowObserver::TreeChangeParams::TreeChangeParams()
+ : target(nullptr),
+ old_parent(nullptr),
+ new_parent(nullptr),
+ receiver(nullptr) {}
+
+} // namespace mus
diff --git a/chromium/components/mus/public/cpp/lib/window_private.cc b/chromium/components/mus/public/cpp/lib/window_private.cc
new file mode 100644
index 00000000000..11c7ebeefae
--- /dev/null
+++ b/chromium/components/mus/public/cpp/lib/window_private.cc
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/public/cpp/lib/window_private.h"
+
+namespace mus {
+
+WindowPrivate::WindowPrivate(Window* window) : window_(window) {
+ CHECK(window);
+}
+
+WindowPrivate::~WindowPrivate() {}
+
+// static
+Window* WindowPrivate::LocalCreate() {
+ return new Window;
+}
+
+void WindowPrivate::LocalSetSharedProperty(const std::string& name,
+ mojo::Array<uint8_t> new_data) {
+ std::vector<uint8_t> data;
+ std::vector<uint8_t>* data_ptr = nullptr;
+ if (!new_data.is_null()) {
+ data = new_data.To<std::vector<uint8_t>>();
+ data_ptr = &data;
+ }
+ LocalSetSharedProperty(name, data_ptr);
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/public/cpp/lib/window_private.h b/chromium/components/mus/public/cpp/lib/window_private.h
new file mode 100644
index 00000000000..4b2640e8b37
--- /dev/null
+++ b/chromium/components/mus/public/cpp/lib/window_private.h
@@ -0,0 +1,100 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_PUBLIC_CPP_LIB_WINDOW_PRIVATE_H_
+#define COMPONENTS_MUS_PUBLIC_CPP_LIB_WINDOW_PRIVATE_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "components/mus/public/cpp/window.h"
+#include "mojo/public/cpp/bindings/array.h"
+
+namespace mus {
+
+// This class is a friend of a Window and contains functions to mutate internal
+// state of Window.
+class WindowPrivate {
+ public:
+ explicit WindowPrivate(Window* window);
+ ~WindowPrivate();
+
+ // Creates and returns a new Window. Caller owns the return value.
+ static Window* LocalCreate();
+
+ base::ObserverList<WindowObserver>* observers() {
+ return &window_->observers_;
+ }
+
+ void ClearParent() { window_->parent_ = nullptr; }
+
+ void ClearTransientParent() { window_->transient_parent_ = nullptr; }
+
+ void set_visible(bool visible) { window_->visible_ = visible; }
+
+ void set_parent_drawn(bool drawn) { window_->parent_drawn_ = drawn; }
+ bool parent_drawn() { return window_->parent_drawn_; }
+
+ void set_server_id(Id id) { window_->server_id_ = id; }
+ Id server_id() { return window_->server_id_; }
+
+ void set_client(WindowTreeClient* client) {
+ window_->client_ = client;
+ }
+
+ void set_properties(const std::map<std::string, std::vector<uint8_t>>& data) {
+ window_->properties_ = data;
+ }
+
+ void LocalSetDisplay(int64_t new_display) {
+ window_->LocalSetDisplay(new_display);
+ }
+
+ void LocalDestroy() { window_->LocalDestroy(); }
+ void LocalAddChild(Window* child) { window_->LocalAddChild(child); }
+ void LocalRemoveChild(Window* child) { window_->LocalRemoveChild(child); }
+ void LocalAddTransientWindow(Window* child) {
+ window_->LocalAddTransientWindow(child);
+ }
+ void LocalRemoveTransientWindow(Window* child) {
+ window_->LocalRemoveTransientWindow(child);
+ }
+ void LocalUnsetModal() { window_->is_modal_ = false; }
+ void LocalReorder(Window* relative, mojom::OrderDirection direction) {
+ window_->LocalReorder(relative, direction);
+ }
+ void LocalSetBounds(const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {
+ window_->LocalSetBounds(old_bounds, new_bounds);
+ }
+ void LocalSetClientArea(
+ const gfx::Insets& client_area,
+ const std::vector<gfx::Rect>& additional_client_areas) {
+ window_->LocalSetClientArea(client_area, additional_client_areas);
+ }
+ void LocalSetParentDrawn(bool drawn) { window_->LocalSetParentDrawn(drawn); }
+ void LocalSetVisible(bool visible) { window_->LocalSetVisible(visible); }
+ void LocalSetOpacity(float opacity) { window_->LocalSetOpacity(opacity); }
+ void LocalSetPredefinedCursor(mojom::Cursor cursor) {
+ window_->LocalSetPredefinedCursor(cursor);
+ }
+ void LocalSetSharedProperty(const std::string& name,
+ mojo::Array<uint8_t> new_data);
+ void LocalSetSharedProperty(const std::string& name,
+ const std::vector<uint8_t>* data) {
+ window_->LocalSetSharedProperty(name, data);
+ }
+ void NotifyWindowStackingChanged() { window_->NotifyWindowStackingChanged(); }
+
+ private:
+ Window* window_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowPrivate);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_PUBLIC_CPP_LIB_WINDOW_PRIVATE_H_
diff --git a/chromium/components/mus/public/cpp/lib/window_surface.cc b/chromium/components/mus/public/cpp/lib/window_surface.cc
new file mode 100644
index 00000000000..1f0b840a102
--- /dev/null
+++ b/chromium/components/mus/public/cpp/lib/window_surface.cc
@@ -0,0 +1,69 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/public/cpp/window_surface.h"
+
+#include "base/memory/ptr_util.h"
+#include "components/mus/public/cpp/window_surface_client.h"
+
+namespace mus {
+
+// static
+std::unique_ptr<WindowSurface> WindowSurface::Create(
+ std::unique_ptr<WindowSurfaceBinding>* surface_binding) {
+ mojom::SurfacePtr surface;
+ mojom::SurfaceClientPtr surface_client;
+ mojo::InterfaceRequest<mojom::SurfaceClient> surface_client_request =
+ GetProxy(&surface_client);
+
+ surface_binding->reset(new WindowSurfaceBinding(
+ GetProxy(&surface), surface_client.PassInterface()));
+ return base::WrapUnique(new WindowSurface(surface.PassInterface(),
+ std::move(surface_client_request)));
+}
+
+WindowSurface::~WindowSurface() {}
+
+void WindowSurface::BindToThread() {
+ DCHECK(!thread_checker_);
+ thread_checker_.reset(new base::ThreadChecker());
+ surface_.Bind(std::move(surface_info_));
+ client_binding_.reset(new mojo::Binding<mojom::SurfaceClient>(
+ this, std::move(client_request_)));
+}
+
+void WindowSurface::SubmitCompositorFrame(cc::CompositorFrame frame,
+ const base::Closure& callback) {
+ DCHECK(thread_checker_);
+ DCHECK(thread_checker_->CalledOnValidThread());
+ if (!surface_)
+ return;
+ surface_->SubmitCompositorFrame(std::move(frame), callback);
+}
+
+WindowSurface::WindowSurface(
+ mojo::InterfacePtrInfo<mojom::Surface> surface_info,
+ mojo::InterfaceRequest<mojom::SurfaceClient> client_request)
+ : client_(nullptr),
+ surface_info_(std::move(surface_info)),
+ client_request_(std::move(client_request)) {}
+
+void WindowSurface::ReturnResources(
+ mojo::Array<cc::ReturnedResource> resources) {
+ DCHECK(thread_checker_);
+ DCHECK(thread_checker_->CalledOnValidThread());
+ if (!client_)
+ return;
+ client_->OnResourcesReturned(this, std::move(resources));
+}
+
+WindowSurfaceBinding::~WindowSurfaceBinding() {}
+
+WindowSurfaceBinding::WindowSurfaceBinding(
+ mojo::InterfaceRequest<mojom::Surface> surface_request,
+ mojo::InterfacePtrInfo<mojom::SurfaceClient> surface_client)
+ : surface_request_(std::move(surface_request)),
+ surface_client_(std::move(surface_client)) {}
+
+} // namespace mus
diff --git a/chromium/components/mus/public/cpp/lib/window_tree_client.cc b/chromium/components/mus/public/cpp/lib/window_tree_client.cc
new file mode 100644
index 00000000000..7a43101ff6a
--- /dev/null
+++ b/chromium/components/mus/public/cpp/lib/window_tree_client.cc
@@ -0,0 +1,1181 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/public/cpp/window_tree_client.h"
+
+#include <stddef.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "components/mus/common/util.h"
+#include "components/mus/public/cpp/input_event_handler.h"
+#include "components/mus/public/cpp/lib/in_flight_change.h"
+#include "components/mus/public/cpp/lib/window_private.h"
+#include "components/mus/public/cpp/window_manager_delegate.h"
+#include "components/mus/public/cpp/window_observer.h"
+#include "components/mus/public/cpp/window_tracker.h"
+#include "components/mus/public/cpp/window_tree_client_delegate.h"
+#include "components/mus/public/cpp/window_tree_client_observer.h"
+#include "components/mus/public/interfaces/window_manager_window_tree_factory.mojom.h"
+#include "services/shell/public/cpp/connector.h"
+#include "ui/display/mojo/display_type_converters.h"
+#include "ui/events/event.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace mus {
+
+void DeleteWindowTreeClient(WindowTreeClient* client) { delete client; }
+
+Id MakeTransportId(ClientSpecificId client_id, ClientSpecificId local_id) {
+ return (client_id << 16) | local_id;
+}
+
+Id server_id(Window* window) {
+ return WindowPrivate(window).server_id();
+}
+
+// Helper called to construct a local window object from transport data.
+Window* AddWindowToClient(WindowTreeClient* client,
+ Window* parent,
+ const mojom::WindowDataPtr& window_data) {
+ // We don't use the ctor that takes a WindowTreeClient here, since it will
+ // call back to the service and attempt to create a new window.
+ Window* window = WindowPrivate::LocalCreate();
+ WindowPrivate private_window(window);
+ private_window.set_client(client);
+ private_window.set_server_id(window_data->window_id);
+ private_window.set_visible(window_data->visible);
+ private_window.set_properties(
+ window_data->properties
+ .To<std::map<std::string, std::vector<uint8_t>>>());
+ client->AddWindow(window);
+ private_window.LocalSetBounds(gfx::Rect(), window_data->bounds);
+ if (parent)
+ WindowPrivate(parent).LocalAddChild(window);
+ return window;
+}
+
+Window* BuildWindowTree(WindowTreeClient* client,
+ const mojo::Array<mojom::WindowDataPtr>& windows,
+ Window* initial_parent) {
+ std::vector<Window*> parents;
+ Window* root = NULL;
+ Window* last_window = NULL;
+ if (initial_parent)
+ parents.push_back(initial_parent);
+ for (size_t i = 0; i < windows.size(); ++i) {
+ if (last_window && windows[i]->parent_id == server_id(last_window)) {
+ parents.push_back(last_window);
+ } else if (!parents.empty()) {
+ while (server_id(parents.back()) != windows[i]->parent_id)
+ parents.pop_back();
+ }
+ Window* window = AddWindowToClient(
+ client, !parents.empty() ? parents.back() : NULL, windows[i]);
+ if (!last_window)
+ root = window;
+ last_window = window;
+ }
+ return root;
+}
+
+WindowTreeClient::WindowTreeClient(
+ WindowTreeClientDelegate* delegate,
+ WindowManagerDelegate* window_manager_delegate,
+ mojo::InterfaceRequest<mojom::WindowTreeClient> request)
+ : client_id_(0),
+ next_window_id_(1),
+ next_change_id_(1),
+ delegate_(delegate),
+ window_manager_delegate_(window_manager_delegate),
+ capture_window_(nullptr),
+ focused_window_(nullptr),
+ binding_(this),
+ tree_(nullptr),
+ delete_on_no_roots_(!window_manager_delegate),
+ in_destructor_(false),
+ weak_factory_(this) {
+ // Allow for a null request in tests.
+ if (request.is_pending())
+ binding_.Bind(std::move(request));
+ if (window_manager_delegate)
+ window_manager_delegate->SetWindowManagerClient(this);
+}
+
+WindowTreeClient::~WindowTreeClient() {
+ in_destructor_ = true;
+
+ std::vector<Window*> non_owned;
+ WindowTracker tracker;
+ while (!windows_.empty()) {
+ IdToWindowMap::iterator it = windows_.begin();
+ if (OwnsWindow(it->second)) {
+ it->second->Destroy();
+ } else {
+ tracker.Add(it->second);
+ windows_.erase(it);
+ }
+ }
+
+ // Delete the non-owned windows last. In the typical case these are roots. The
+ // exception is the window manager and embed roots, which may know about
+ // other random windows that it doesn't own.
+ // NOTE: we manually delete as we're a friend.
+ while (!tracker.windows().empty())
+ delete tracker.windows().front();
+
+ FOR_EACH_OBSERVER(WindowTreeClientObserver, observers_,
+ OnWillDestroyClient(this));
+
+ delegate_->OnWindowTreeClientDestroyed(this);
+}
+
+void WindowTreeClient::ConnectViaWindowTreeFactory(
+ shell::Connector* connector) {
+ // Clients created with no root shouldn't delete automatically.
+ delete_on_no_roots_ = false;
+
+ // The client id doesn't really matter, we use 101 purely for debugging.
+ client_id_ = 101;
+
+ mojom::WindowTreeFactoryPtr factory;
+ connector->ConnectToInterface("mojo:mus", &factory);
+ mojom::WindowTreePtr window_tree;
+ factory->CreateWindowTree(GetProxy(&window_tree),
+ binding_.CreateInterfacePtrAndBind());
+ SetWindowTree(std::move(window_tree));
+}
+
+void WindowTreeClient::ConnectAsWindowManager(shell::Connector* connector) {
+ DCHECK(window_manager_delegate_);
+
+ mojom::WindowManagerWindowTreeFactoryPtr factory;
+ connector->ConnectToInterface("mojo:mus", &factory);
+ mojom::WindowTreePtr window_tree;
+ factory->CreateWindowTree(GetProxy(&window_tree),
+ binding_.CreateInterfacePtrAndBind());
+ SetWindowTree(std::move(window_tree));
+}
+
+void WindowTreeClient::WaitForEmbed() {
+ DCHECK(roots_.empty());
+ // OnEmbed() is the first function called.
+ binding_.WaitForIncomingMethodCall();
+ // TODO(sky): deal with pipe being closed before we get OnEmbed().
+}
+
+void WindowTreeClient::DestroyWindow(Window* window) {
+ DCHECK(tree_);
+ const uint32_t change_id = ScheduleInFlightChange(base::WrapUnique(
+ new CrashInFlightChange(window, ChangeType::DELETE_WINDOW)));
+ tree_->DeleteWindow(change_id, server_id(window));
+}
+
+void WindowTreeClient::AddChild(Window* parent, Id child_id) {
+ DCHECK(tree_);
+ const uint32_t change_id = ScheduleInFlightChange(
+ base::WrapUnique(new CrashInFlightChange(parent, ChangeType::ADD_CHILD)));
+ tree_->AddWindow(change_id, parent->server_id(), child_id);
+}
+
+void WindowTreeClient::RemoveChild(Window* parent, Id child_id) {
+ DCHECK(tree_);
+ const uint32_t change_id = ScheduleInFlightChange(base::WrapUnique(
+ new CrashInFlightChange(parent, ChangeType::REMOVE_CHILD)));
+ tree_->RemoveWindowFromParent(change_id, child_id);
+}
+
+void WindowTreeClient::AddTransientWindow(Window* window,
+ Id transient_window_id) {
+ DCHECK(tree_);
+ const uint32_t change_id = ScheduleInFlightChange(base::WrapUnique(
+ new CrashInFlightChange(window, ChangeType::ADD_TRANSIENT_WINDOW)));
+ tree_->AddTransientWindow(change_id, server_id(window), transient_window_id);
+}
+
+void WindowTreeClient::RemoveTransientWindowFromParent(Window* window) {
+ DCHECK(tree_);
+ const uint32_t change_id =
+ ScheduleInFlightChange(base::WrapUnique(new CrashInFlightChange(
+ window, ChangeType::REMOVE_TRANSIENT_WINDOW_FROM_PARENT)));
+ tree_->RemoveTransientWindowFromParent(change_id, server_id(window));
+}
+
+void WindowTreeClient::SetModal(Window* window) {
+ DCHECK(tree_);
+ const uint32_t change_id = ScheduleInFlightChange(
+ base::WrapUnique(new InFlightSetModalChange(window)));
+ tree_->SetModal(change_id, server_id(window));
+}
+
+void WindowTreeClient::Reorder(Window* window,
+ Id relative_window_id,
+ mojom::OrderDirection direction) {
+ DCHECK(tree_);
+ const uint32_t change_id = ScheduleInFlightChange(
+ base::WrapUnique(new CrashInFlightChange(window, ChangeType::REORDER)));
+ tree_->ReorderWindow(change_id, server_id(window), relative_window_id,
+ direction);
+}
+
+bool WindowTreeClient::OwnsWindow(Window* window) const {
+ // Windows created via CreateTopLevelWindow() are not owned by us, but have
+ // our client id.
+ return HiWord(server_id(window)) == client_id_ &&
+ roots_.count(window) == 0;
+}
+
+void WindowTreeClient::SetBounds(Window* window,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& bounds) {
+ DCHECK(tree_);
+ const uint32_t change_id = ScheduleInFlightChange(
+ base::WrapUnique(new InFlightBoundsChange(window, old_bounds)));
+ tree_->SetWindowBounds(change_id, server_id(window), bounds);
+}
+
+void WindowTreeClient::SetCapture(Window* window) {
+ // In order for us to get here we had to have exposed a window, which implies
+ // we got a client.
+ DCHECK(tree_);
+ if (capture_window_ == window)
+ return;
+ const uint32_t change_id = ScheduleInFlightChange(
+ base::WrapUnique(new InFlightCaptureChange(this, capture_window_)));
+ tree_->SetCapture(change_id, server_id(window));
+ LocalSetCapture(window);
+}
+
+void WindowTreeClient::ReleaseCapture(Window* window) {
+ // In order for us to get here we had to have exposed a window, which implies
+ // we got a client.
+ DCHECK(tree_);
+ if (capture_window_ != window)
+ return;
+ const uint32_t change_id = ScheduleInFlightChange(
+ base::WrapUnique(new InFlightCaptureChange(this, window)));
+ tree_->ReleaseCapture(change_id, server_id(window));
+ LocalSetCapture(nullptr);
+}
+
+void WindowTreeClient::SetClientArea(
+ Id window_id,
+ const gfx::Insets& client_area,
+ const std::vector<gfx::Rect>& additional_client_areas) {
+ DCHECK(tree_);
+ tree_->SetClientArea(window_id, client_area, additional_client_areas);
+}
+
+void WindowTreeClient::SetHitTestMask(Id window_id, const gfx::Rect& mask) {
+ DCHECK(tree_);
+ tree_->SetHitTestMask(window_id, mask);
+}
+
+void WindowTreeClient::ClearHitTestMask(Id window_id) {
+ DCHECK(tree_);
+ tree_->SetHitTestMask(window_id, {});
+}
+
+void WindowTreeClient::SetFocus(Window* window) {
+ // In order for us to get here we had to have exposed a window, which implies
+ // we got a client.
+ DCHECK(tree_);
+ const uint32_t change_id = ScheduleInFlightChange(
+ base::WrapUnique(new InFlightFocusChange(this, focused_window_)));
+ tree_->SetFocus(change_id, window ? server_id(window) : 0);
+ LocalSetFocus(window);
+}
+
+void WindowTreeClient::SetCanFocus(Id window_id, bool can_focus) {
+ DCHECK(tree_);
+ tree_->SetCanFocus(window_id, can_focus);
+}
+
+void WindowTreeClient::SetPredefinedCursor(Id window_id,
+ mus::mojom::Cursor cursor_id) {
+ DCHECK(tree_);
+
+ Window* window = GetWindowByServerId(window_id);
+ if (!window)
+ return;
+
+ // We make an inflight change thing here.
+ const uint32_t change_id = ScheduleInFlightChange(base::WrapUnique(
+ new InFlightPredefinedCursorChange(window, window->predefined_cursor())));
+ tree_->SetPredefinedCursor(change_id, window_id, cursor_id);
+}
+
+void WindowTreeClient::SetVisible(Window* window, bool visible) {
+ DCHECK(tree_);
+ const uint32_t change_id = ScheduleInFlightChange(
+ base::WrapUnique(new InFlightVisibleChange(window, !visible)));
+ tree_->SetWindowVisibility(change_id, server_id(window), visible);
+}
+
+void WindowTreeClient::SetOpacity(Window* window, float opacity) {
+ DCHECK(tree_);
+ const uint32_t change_id = ScheduleInFlightChange(
+ base::WrapUnique(new InFlightOpacityChange(window, window->opacity())));
+ tree_->SetWindowOpacity(change_id, server_id(window), opacity);
+}
+
+void WindowTreeClient::SetProperty(Window* window,
+ const std::string& name,
+ mojo::Array<uint8_t> data) {
+ DCHECK(tree_);
+
+ mojo::Array<uint8_t> old_value(nullptr);
+ if (window->HasSharedProperty(name))
+ old_value = mojo::Array<uint8_t>::From(window->properties_[name]);
+
+ const uint32_t change_id = ScheduleInFlightChange(
+ base::WrapUnique(new InFlightPropertyChange(window, name, old_value)));
+ tree_->SetWindowProperty(change_id, server_id(window), mojo::String(name),
+ std::move(data));
+}
+
+void WindowTreeClient::SetWindowTextInputState(
+ Id window_id,
+ mojo::TextInputStatePtr state) {
+ DCHECK(tree_);
+ tree_->SetWindowTextInputState(window_id, std::move(state));
+}
+
+void WindowTreeClient::SetImeVisibility(Id window_id,
+ bool visible,
+ mojo::TextInputStatePtr state) {
+ DCHECK(tree_);
+ tree_->SetImeVisibility(window_id, visible, std::move(state));
+}
+
+void WindowTreeClient::Embed(Id window_id,
+ mojom::WindowTreeClientPtr client,
+ uint32_t flags,
+ const mojom::WindowTree::EmbedCallback& callback) {
+ DCHECK(tree_);
+ tree_->Embed(window_id, std::move(client), flags, callback);
+}
+
+void WindowTreeClient::RequestClose(Window* window) {
+ if (window_manager_internal_client_)
+ window_manager_internal_client_->WmRequestClose(server_id(window));
+}
+
+void WindowTreeClient::AttachSurface(
+ Id window_id,
+ mojom::SurfaceType type,
+ mojo::InterfaceRequest<mojom::Surface> surface,
+ mojom::SurfaceClientPtr client) {
+ DCHECK(tree_);
+ tree_->AttachSurface(window_id, type, std::move(surface), std::move(client));
+}
+
+void WindowTreeClient::LocalSetCapture(Window* window) {
+ if (capture_window_ == window)
+ return;
+ Window* lost_capture = capture_window_;
+ capture_window_ = window;
+ if (lost_capture) {
+ FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(lost_capture).observers(),
+ OnWindowLostCapture(lost_capture));
+ }
+}
+
+void WindowTreeClient::LocalSetFocus(Window* focused) {
+ Window* blurred = focused_window_;
+ // Update |focused_window_| before calling any of the observers, so that the
+ // observers get the correct result from calling |Window::HasFocus()|,
+ // |WindowTreeClient::GetFocusedWindow()| etc.
+ focused_window_ = focused;
+ if (blurred) {
+ FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(blurred).observers(),
+ OnWindowFocusChanged(focused, blurred));
+ }
+ if (focused) {
+ FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(focused).observers(),
+ OnWindowFocusChanged(focused, blurred));
+ }
+ FOR_EACH_OBSERVER(WindowTreeClientObserver, observers_,
+ OnWindowTreeFocusChanged(focused, blurred));
+}
+
+void WindowTreeClient::AddWindow(Window* window) {
+ DCHECK(windows_.find(server_id(window)) == windows_.end());
+ windows_[server_id(window)] = window;
+}
+
+void WindowTreeClient::OnWindowDestroying(Window* window) {
+ if (window == capture_window_) {
+ // Normally the queue updates itself upon window destruction. However since
+ // |window| is being destroyed, it will not be possible to notify its
+ // observers of the lost capture. Update local state now.
+ LocalSetCapture(nullptr);
+ }
+ // For |focused_window_| window destruction clears the entire focus state.
+}
+
+void WindowTreeClient::OnWindowDestroyed(Window* window) {
+ windows_.erase(server_id(window));
+
+ for (auto& entry : embedded_windows_) {
+ auto it = entry.second.find(window);
+ if (it != entry.second.end()) {
+ entry.second.erase(it);
+ break;
+ }
+ }
+
+ // Remove any InFlightChanges associated with the window.
+ std::set<uint32_t> in_flight_change_ids_to_remove;
+ for (const auto& pair : in_flight_map_) {
+ if (pair.second->window() == window)
+ in_flight_change_ids_to_remove.insert(pair.first);
+ }
+ for (auto change_id : in_flight_change_ids_to_remove)
+ in_flight_map_.erase(change_id);
+
+ if (roots_.erase(window) > 0 && roots_.empty() && delete_on_no_roots_ &&
+ !in_destructor_) {
+ delete this;
+ }
+}
+
+Window* WindowTreeClient::GetWindowByServerId(Id id) {
+ IdToWindowMap::const_iterator it = windows_.find(id);
+ return it != windows_.end() ? it->second : NULL;
+}
+
+InFlightChange* WindowTreeClient::GetOldestInFlightChangeMatching(
+ const InFlightChange& change) {
+ for (const auto& pair : in_flight_map_) {
+ if (pair.second->window() == change.window() &&
+ pair.second->change_type() == change.change_type() &&
+ pair.second->Matches(change)) {
+ return pair.second.get();
+ }
+ }
+ return nullptr;
+}
+
+uint32_t WindowTreeClient::ScheduleInFlightChange(
+ std::unique_ptr<InFlightChange> change) {
+ DCHECK(!change->window() ||
+ windows_.count(change->window()->server_id()) > 0);
+ const uint32_t change_id = next_change_id_++;
+ in_flight_map_[change_id] = std::move(change);
+ return change_id;
+}
+
+bool WindowTreeClient::ApplyServerChangeToExistingInFlightChange(
+ const InFlightChange& change) {
+ InFlightChange* existing_change = GetOldestInFlightChangeMatching(change);
+ if (!existing_change)
+ return false;
+
+ existing_change->SetRevertValueFrom(change);
+ return true;
+}
+
+Window* WindowTreeClient::NewWindowImpl(
+ NewWindowType type,
+ const Window::SharedProperties* properties) {
+ DCHECK(tree_);
+ Window* window =
+ new Window(this, MakeTransportId(client_id_, next_window_id_++));
+ if (properties)
+ window->properties_ = *properties;
+ AddWindow(window);
+
+ const uint32_t change_id = ScheduleInFlightChange(base::WrapUnique(
+ new CrashInFlightChange(window, type == NewWindowType::CHILD
+ ? ChangeType::NEW_WINDOW
+ : ChangeType::NEW_TOP_LEVEL_WINDOW)));
+ mojo::Map<mojo::String, mojo::Array<uint8_t>> transport_properties;
+ if (properties) {
+ transport_properties =
+ mojo::Map<mojo::String, mojo::Array<uint8_t>>::From(*properties);
+ }
+ if (type == NewWindowType::CHILD) {
+ tree_->NewWindow(change_id, server_id(window),
+ std::move(transport_properties));
+ } else {
+ roots_.insert(window);
+ tree_->NewTopLevelWindow(change_id, server_id(window),
+ std::move(transport_properties));
+ }
+ return window;
+}
+
+void WindowTreeClient::SetWindowTree(mojom::WindowTreePtr window_tree_ptr) {
+ tree_ptr_ = std::move(window_tree_ptr);
+ tree_ = tree_ptr_.get();
+
+ tree_ptr_->GetCursorLocationMemory(
+ base::Bind(&WindowTreeClient::OnReceivedCursorLocationMemory,
+ weak_factory_.GetWeakPtr()));
+
+ tree_ptr_.set_connection_error_handler(base::Bind(
+ &WindowTreeClient::OnConnectionLost, weak_factory_.GetWeakPtr()));
+
+ if (window_manager_delegate_) {
+ tree_ptr_->GetWindowManagerClient(GetProxy(&window_manager_internal_client_,
+ tree_ptr_.associated_group()));
+ }
+}
+
+void WindowTreeClient::OnConnectionLost() {
+ delete this;
+}
+
+void WindowTreeClient::OnEmbedImpl(mojom::WindowTree* window_tree,
+ ClientSpecificId client_id,
+ mojom::WindowDataPtr root_data,
+ int64_t display_id,
+ Id focused_window_id,
+ bool drawn) {
+ // WARNING: this is only called if WindowTreeClient was created as the
+ // result of an embedding.
+ tree_ = window_tree;
+ client_id_ = client_id;
+
+ DCHECK(roots_.empty());
+ Window* root = AddWindowToClient(this, nullptr, root_data);
+ WindowPrivate(root).LocalSetDisplay(display_id);
+ roots_.insert(root);
+
+ focused_window_ = GetWindowByServerId(focused_window_id);
+
+ WindowPrivate(root).LocalSetParentDrawn(drawn);
+
+ delegate_->OnEmbed(root);
+
+ if (focused_window_) {
+ FOR_EACH_OBSERVER(WindowTreeClientObserver, observers_,
+ OnWindowTreeFocusChanged(focused_window_, nullptr));
+ }
+}
+
+void WindowTreeClient::WmNewDisplayAddedImpl(const display::Display& display,
+ mojom::WindowDataPtr root_data,
+ bool parent_drawn) {
+ DCHECK(window_manager_delegate_);
+
+ Window* root = AddWindowToClient(this, nullptr, root_data);
+ WindowPrivate(root).LocalSetDisplay(display.id());
+ WindowPrivate(root).LocalSetParentDrawn(parent_drawn);
+ roots_.insert(root);
+
+ window_manager_delegate_->OnWmNewDisplay(root, display);
+}
+
+void WindowTreeClient::OnReceivedCursorLocationMemory(
+ mojo::ScopedSharedBufferHandle handle) {
+ cursor_location_mapping_ = handle->Map(sizeof(base::subtle::Atomic32));
+ DCHECK(cursor_location_mapping_);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowTreeClient, WindowTreeClient implementation:
+
+void WindowTreeClient::SetDeleteOnNoRoots(bool value) {
+ delete_on_no_roots_ = value;
+}
+
+const std::set<Window*>& WindowTreeClient::GetRoots() {
+ return roots_;
+}
+
+Window* WindowTreeClient::GetFocusedWindow() {
+ return focused_window_;
+}
+
+void WindowTreeClient::ClearFocus() {
+ if (!focused_window_)
+ return;
+
+ SetFocus(nullptr);
+}
+
+gfx::Point WindowTreeClient::GetCursorScreenPoint() {
+ // We raced initialization. Return (0, 0).
+ if (!cursor_location_memory())
+ return gfx::Point();
+
+ base::subtle::Atomic32 location =
+ base::subtle::NoBarrier_Load(cursor_location_memory());
+ return gfx::Point(static_cast<int16_t>(location >> 16),
+ static_cast<int16_t>(location & 0xFFFF));
+}
+
+void WindowTreeClient::SetEventObserver(mojom::EventMatcherPtr matcher) {
+ if (matcher.is_null()) {
+ has_event_observer_ = false;
+ tree_->SetEventObserver(nullptr, 0u);
+ } else {
+ has_event_observer_ = true;
+ event_observer_id_++;
+ tree_->SetEventObserver(std::move(matcher), event_observer_id_);
+ }
+}
+
+Window* WindowTreeClient::NewWindow(
+ const Window::SharedProperties* properties) {
+ return NewWindowImpl(NewWindowType::CHILD, properties);
+}
+
+Window* WindowTreeClient::NewTopLevelWindow(
+ const Window::SharedProperties* properties) {
+ Window* window = NewWindowImpl(NewWindowType::TOP_LEVEL, properties);
+ // Assume newly created top level windows are drawn by default, otherwise
+ // requests to focus will fail. We will get the real value in
+ // OnTopLevelCreated().
+ window->LocalSetParentDrawn(true);
+ return window;
+}
+
+#if !defined(NDEBUG)
+std::string WindowTreeClient::GetDebugWindowHierarchy() const {
+ std::string result;
+ for (Window* root : roots_)
+ BuildDebugInfo(std::string(), root, &result);
+ return result;
+}
+
+void WindowTreeClient::BuildDebugInfo(const std::string& depth,
+ Window* window,
+ std::string* result) const {
+ std::string name = window->GetName();
+ *result += base::StringPrintf(
+ "%sid=%d visible=%s bounds=%d,%d %dx%d %s\n", depth.c_str(),
+ window->server_id(), window->visible() ? "true" : "false",
+ window->bounds().x(), window->bounds().y(), window->bounds().width(),
+ window->bounds().height(), !name.empty() ? name.c_str() : "(no name)");
+ for (Window* child : window->children())
+ BuildDebugInfo(depth + " ", child, result);
+}
+#endif // !defined(NDEBUG)
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowTreeClient, WindowTreeClient implementation:
+
+void WindowTreeClient::AddObserver(WindowTreeClientObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void WindowTreeClient::RemoveObserver(WindowTreeClientObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void WindowTreeClient::OnEmbed(ClientSpecificId client_id,
+ mojom::WindowDataPtr root_data,
+ mojom::WindowTreePtr tree,
+ int64_t display_id,
+ Id focused_window_id,
+ bool drawn) {
+ DCHECK(!tree_ptr_);
+ tree_ptr_ = std::move(tree);
+ tree_ptr_.set_connection_error_handler(
+ base::Bind(&DeleteWindowTreeClient, this));
+
+ if (window_manager_delegate_) {
+ tree_ptr_->GetWindowManagerClient(GetProxy(&window_manager_internal_client_,
+ tree_ptr_.associated_group()));
+ }
+
+ OnEmbedImpl(tree_ptr_.get(), client_id, std::move(root_data), display_id,
+ focused_window_id, drawn);
+}
+
+void WindowTreeClient::OnEmbeddedAppDisconnected(Id window_id) {
+ Window* window = GetWindowByServerId(window_id);
+ if (window) {
+ FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(window).observers(),
+ OnWindowEmbeddedAppDisconnected(window));
+ }
+}
+
+void WindowTreeClient::OnUnembed(Id window_id) {
+ Window* window = GetWindowByServerId(window_id);
+ if (!window)
+ return;
+
+ delegate_->OnUnembed(window);
+ WindowPrivate(window).LocalDestroy();
+}
+
+void WindowTreeClient::OnLostCapture(Id window_id) {
+ Window* window = GetWindowByServerId(window_id);
+ if (!window)
+ return;
+
+ InFlightCaptureChange reset_change(this, nullptr);
+ if (ApplyServerChangeToExistingInFlightChange(reset_change))
+ return;
+
+ LocalSetCapture(nullptr);
+}
+
+void WindowTreeClient::OnTopLevelCreated(uint32_t change_id,
+ mojom::WindowDataPtr data,
+ int64_t display_id,
+ bool drawn) {
+ // The server ack'd the top level window we created and supplied the state
+ // of the window at the time the server created it. For properties we do not
+ // have changes in flight for we can update them immediately. For properties
+ // with changes in flight we set the revert value from the server.
+
+ if (!in_flight_map_.count(change_id)) {
+ // The window may have been destroyed locally before the server could finish
+ // creating the window, and before the server received the notification that
+ // the window has been destroyed.
+ return;
+ }
+ std::unique_ptr<InFlightChange> change(std::move(in_flight_map_[change_id]));
+ in_flight_map_.erase(change_id);
+
+ Window* window = change->window();
+ WindowPrivate window_private(window);
+
+ // Drawn state and display-id always come from the server (they can't be
+ // modified locally).
+ window_private.LocalSetParentDrawn(drawn);
+ window_private.LocalSetDisplay(display_id);
+
+ // The default visibilty is false, we only need update visibility if it
+ // differs from that.
+ if (data->visible) {
+ InFlightVisibleChange visible_change(window, data->visible);
+ InFlightChange* current_change =
+ GetOldestInFlightChangeMatching(visible_change);
+ if (current_change)
+ current_change->SetRevertValueFrom(visible_change);
+ else
+ window_private.LocalSetVisible(true);
+ }
+
+ const gfx::Rect bounds(data->bounds);
+ {
+ InFlightBoundsChange bounds_change(window, bounds);
+ InFlightChange* current_change =
+ GetOldestInFlightChangeMatching(bounds_change);
+ if (current_change)
+ current_change->SetRevertValueFrom(bounds_change);
+ else if (window->bounds() != bounds)
+ window_private.LocalSetBounds(window->bounds(), bounds);
+ }
+
+ // There is currently no API to bulk set properties, so we iterate over each
+ // property individually.
+ Window::SharedProperties properties =
+ data->properties.To<std::map<std::string, std::vector<uint8_t>>>();
+ for (const auto& pair : properties) {
+ InFlightPropertyChange property_change(
+ window, pair.first, mojo::Array<uint8_t>::From(pair.second));
+ InFlightChange* current_change =
+ GetOldestInFlightChangeMatching(property_change);
+ if (current_change)
+ current_change->SetRevertValueFrom(property_change);
+ else
+ window_private.LocalSetSharedProperty(pair.first, &(pair.second));
+ }
+
+ // Top level windows should not have a parent.
+ DCHECK_EQ(0u, data->parent_id);
+}
+
+void WindowTreeClient::OnWindowBoundsChanged(Id window_id,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {
+ Window* window = GetWindowByServerId(window_id);
+ if (!window)
+ return;
+
+ InFlightBoundsChange new_change(window, new_bounds);
+ if (ApplyServerChangeToExistingInFlightChange(new_change))
+ return;
+
+ WindowPrivate(window).LocalSetBounds(old_bounds, new_bounds);
+}
+
+void WindowTreeClient::OnClientAreaChanged(
+ uint32_t window_id,
+ const gfx::Insets& new_client_area,
+ mojo::Array<gfx::Rect> new_additional_client_areas) {
+ Window* window = GetWindowByServerId(window_id);
+ if (window) {
+ WindowPrivate(window).LocalSetClientArea(
+ new_client_area,
+ new_additional_client_areas.To<std::vector<gfx::Rect>>());
+ }
+}
+
+void WindowTreeClient::OnTransientWindowAdded(
+ uint32_t window_id,
+ uint32_t transient_window_id) {
+ Window* window = GetWindowByServerId(window_id);
+ Window* transient_window = GetWindowByServerId(transient_window_id);
+ // window or transient_window or both may be null if a local delete occurs
+ // with an in flight add from the server.
+ if (window && transient_window)
+ WindowPrivate(window).LocalAddTransientWindow(transient_window);
+}
+
+void WindowTreeClient::OnTransientWindowRemoved(
+ uint32_t window_id,
+ uint32_t transient_window_id) {
+ Window* window = GetWindowByServerId(window_id);
+ Window* transient_window = GetWindowByServerId(transient_window_id);
+ // window or transient_window or both may be null if a local delete occurs
+ // with an in flight delete from the server.
+ if (window && transient_window)
+ WindowPrivate(window).LocalRemoveTransientWindow(transient_window);
+}
+
+void WindowTreeClient::OnWindowHierarchyChanged(
+ Id window_id,
+ Id old_parent_id,
+ Id new_parent_id,
+ mojo::Array<mojom::WindowDataPtr> windows) {
+ Window* initial_parent =
+ windows.size() ? GetWindowByServerId(windows[0]->parent_id) : NULL;
+
+ const bool was_window_known = GetWindowByServerId(window_id) != nullptr;
+
+ BuildWindowTree(this, windows, initial_parent);
+
+ // If the window was not known, then BuildWindowTree() will have created it
+ // and parented the window.
+ if (!was_window_known)
+ return;
+
+ Window* new_parent = GetWindowByServerId(new_parent_id);
+ Window* old_parent = GetWindowByServerId(old_parent_id);
+ Window* window = GetWindowByServerId(window_id);
+ if (new_parent)
+ WindowPrivate(new_parent).LocalAddChild(window);
+ else
+ WindowPrivate(old_parent).LocalRemoveChild(window);
+}
+
+void WindowTreeClient::OnWindowReordered(Id window_id,
+ Id relative_window_id,
+ mojom::OrderDirection direction) {
+ Window* window = GetWindowByServerId(window_id);
+ Window* relative_window = GetWindowByServerId(relative_window_id);
+ if (window && relative_window)
+ WindowPrivate(window).LocalReorder(relative_window, direction);
+}
+
+void WindowTreeClient::OnWindowDeleted(Id window_id) {
+ Window* window = GetWindowByServerId(window_id);
+ if (window)
+ WindowPrivate(window).LocalDestroy();
+}
+
+Window* WindowTreeClient::GetCaptureWindow() {
+ return capture_window_;
+}
+
+void WindowTreeClient::OnWindowVisibilityChanged(Id window_id,
+ bool visible) {
+ Window* window = GetWindowByServerId(window_id);
+ if (!window)
+ return;
+
+ InFlightVisibleChange new_change(window, visible);
+ if (ApplyServerChangeToExistingInFlightChange(new_change))
+ return;
+
+ WindowPrivate(window).LocalSetVisible(visible);
+}
+
+void WindowTreeClient::OnWindowOpacityChanged(Id window_id,
+ float old_opacity,
+ float new_opacity) {
+ Window* window = GetWindowByServerId(window_id);
+ if (!window)
+ return;
+
+ InFlightOpacityChange new_change(window, new_opacity);
+ if (ApplyServerChangeToExistingInFlightChange(new_change))
+ return;
+
+ WindowPrivate(window).LocalSetOpacity(new_opacity);
+}
+
+void WindowTreeClient::OnWindowParentDrawnStateChanged(Id window_id,
+ bool drawn) {
+ Window* window = GetWindowByServerId(window_id);
+ if (window)
+ WindowPrivate(window).LocalSetParentDrawn(drawn);
+}
+
+void WindowTreeClient::OnWindowSharedPropertyChanged(
+ Id window_id,
+ const mojo::String& name,
+ mojo::Array<uint8_t> new_data) {
+ Window* window = GetWindowByServerId(window_id);
+ if (!window)
+ return;
+
+ InFlightPropertyChange new_change(window, name, new_data);
+ if (ApplyServerChangeToExistingInFlightChange(new_change))
+ return;
+
+ WindowPrivate(window).LocalSetSharedProperty(name, std::move(new_data));
+}
+
+void WindowTreeClient::OnWindowInputEvent(uint32_t event_id,
+ Id window_id,
+ std::unique_ptr<ui::Event> event,
+ uint32_t event_observer_id) {
+ DCHECK(event);
+ Window* window = GetWindowByServerId(window_id); // May be null.
+
+ // Non-zero event_observer_id means it matched an event observer on the
+ // server.
+ if (event_observer_id != 0 && has_event_observer_ &&
+ event_observer_id == event_observer_id_)
+ delegate_->OnEventObserved(*event.get(), window);
+
+ if (!window || !window->input_event_handler_) {
+ tree_->OnWindowInputEventAck(event_id, mojom::EventResult::UNHANDLED);
+ return;
+ }
+
+ std::unique_ptr<base::Callback<void(mojom::EventResult)>> ack_callback(
+ new base::Callback<void(mojom::EventResult)>(
+ base::Bind(&mojom::WindowTree::OnWindowInputEventAck,
+ base::Unretained(tree_), event_id)));
+
+ // TODO(moshayedi): crbug.com/617222. No need to convert to ui::MouseEvent or
+ // ui::TouchEvent once we have proper support for pointer events.
+ if (event->IsMousePointerEvent()) {
+ window->input_event_handler_->OnWindowInputEvent(
+ window, ui::MouseEvent(*event->AsPointerEvent()), &ack_callback);
+ } else if (event->IsTouchPointerEvent()) {
+ window->input_event_handler_->OnWindowInputEvent(
+ window, ui::TouchEvent(*event->AsPointerEvent()), &ack_callback);
+ } else {
+ window->input_event_handler_->OnWindowInputEvent(window, *event.get(),
+ &ack_callback);
+ }
+
+ // The handler did not take ownership of the callback, so we send the ack,
+ // marking the event as not consumed.
+ if (ack_callback)
+ ack_callback->Run(mojom::EventResult::UNHANDLED);
+}
+
+void WindowTreeClient::OnEventObserved(std::unique_ptr<ui::Event> event,
+ uint32_t event_observer_id) {
+ DCHECK(event);
+ if (has_event_observer_ && event_observer_id == event_observer_id_)
+ delegate_->OnEventObserved(*event.get(), nullptr /* target */);
+}
+
+void WindowTreeClient::OnWindowFocused(Id focused_window_id) {
+ Window* focused_window = GetWindowByServerId(focused_window_id);
+ InFlightFocusChange new_change(this, focused_window);
+ if (ApplyServerChangeToExistingInFlightChange(new_change))
+ return;
+
+ LocalSetFocus(focused_window);
+}
+
+void WindowTreeClient::OnWindowPredefinedCursorChanged(
+ Id window_id,
+ mojom::Cursor cursor) {
+ Window* window = GetWindowByServerId(window_id);
+ if (!window)
+ return;
+
+ InFlightPredefinedCursorChange new_change(window, cursor);
+ if (ApplyServerChangeToExistingInFlightChange(new_change))
+ return;
+
+ WindowPrivate(window).LocalSetPredefinedCursor(cursor);
+}
+
+void WindowTreeClient::OnChangeCompleted(uint32_t change_id, bool success) {
+ std::unique_ptr<InFlightChange> change(std::move(in_flight_map_[change_id]));
+ in_flight_map_.erase(change_id);
+ if (!change)
+ return;
+
+ if (!success)
+ change->ChangeFailed();
+
+ InFlightChange* next_change = GetOldestInFlightChangeMatching(*change);
+ if (next_change) {
+ if (!success)
+ next_change->SetRevertValueFrom(*change);
+ } else if (!success) {
+ change->Revert();
+ }
+}
+
+void WindowTreeClient::GetWindowManager(
+ mojo::AssociatedInterfaceRequest<WindowManager> internal) {
+ window_manager_internal_.reset(
+ new mojo::AssociatedBinding<mojom::WindowManager>(this,
+ std::move(internal)));
+}
+
+void WindowTreeClient::RequestClose(uint32_t window_id) {
+ Window* window = GetWindowByServerId(window_id);
+ if (!window || !IsRoot(window))
+ return;
+
+ FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(window).observers(),
+ OnRequestClose(window));
+}
+
+void WindowTreeClient::OnConnect(ClientSpecificId client_id) {
+ client_id_ = client_id;
+}
+
+void WindowTreeClient::WmNewDisplayAdded(mojom::DisplayPtr display,
+ mojom::WindowDataPtr root_data,
+ bool parent_drawn) {
+ WmNewDisplayAddedImpl(display.To<display::Display>(), std::move(root_data),
+ parent_drawn);
+}
+
+void WindowTreeClient::WmSetBounds(uint32_t change_id,
+ Id window_id,
+ const gfx::Rect& transit_bounds) {
+ Window* window = GetWindowByServerId(window_id);
+ bool result = false;
+ if (window) {
+ DCHECK(window_manager_delegate_);
+ gfx::Rect bounds = transit_bounds;
+ result = window_manager_delegate_->OnWmSetBounds(window, &bounds);
+ if (result) {
+ // If the resulting bounds differ return false. Returning false ensures
+ // the client applies the bounds we set below.
+ result = bounds == transit_bounds;
+ window->SetBounds(bounds);
+ }
+ }
+ if (window_manager_internal_client_)
+ window_manager_internal_client_->WmResponse(change_id, result);
+}
+
+void WindowTreeClient::WmSetProperty(uint32_t change_id,
+ Id window_id,
+ const mojo::String& name,
+ mojo::Array<uint8_t> transit_data) {
+ Window* window = GetWindowByServerId(window_id);
+ bool result = false;
+ if (window) {
+ DCHECK(window_manager_delegate_);
+ std::unique_ptr<std::vector<uint8_t>> data;
+ if (!transit_data.is_null()) {
+ data.reset(
+ new std::vector<uint8_t>(transit_data.To<std::vector<uint8_t>>()));
+ }
+ result = window_manager_delegate_->OnWmSetProperty(window, name, &data);
+ if (result) {
+ // If the resulting bounds differ return false. Returning false ensures
+ // the client applies the bounds we set below.
+ window->SetSharedPropertyInternal(name, data.get());
+ }
+ }
+ if (window_manager_internal_client_)
+ window_manager_internal_client_->WmResponse(change_id, result);
+}
+
+void WindowTreeClient::WmCreateTopLevelWindow(
+ uint32_t change_id,
+ ClientSpecificId requesting_client_id,
+ mojo::Map<mojo::String, mojo::Array<uint8_t>> transport_properties) {
+ std::map<std::string, std::vector<uint8_t>> properties =
+ transport_properties.To<std::map<std::string, std::vector<uint8_t>>>();
+ Window* window =
+ window_manager_delegate_->OnWmCreateTopLevelWindow(&properties);
+ embedded_windows_[requesting_client_id].insert(window);
+ if (window_manager_internal_client_) {
+ window_manager_internal_client_->OnWmCreatedTopLevelWindow(
+ change_id, server_id(window));
+ }
+}
+
+void WindowTreeClient::WmClientJankinessChanged(ClientSpecificId client_id,
+ bool janky) {
+ if (window_manager_delegate_) {
+ auto it = embedded_windows_.find(client_id);
+ CHECK(it != embedded_windows_.end());
+ window_manager_delegate_->OnWmClientJankinessChanged(
+ embedded_windows_[client_id], janky);
+ }
+}
+
+void WindowTreeClient::OnAccelerator(uint32_t id,
+ std::unique_ptr<ui::Event> event) {
+ DCHECK(event);
+ window_manager_delegate_->OnAccelerator(id, *event.get());
+}
+
+void WindowTreeClient::SetFrameDecorationValues(
+ mojom::FrameDecorationValuesPtr values) {
+ if (window_manager_internal_client_) {
+ window_manager_internal_client_->WmSetFrameDecorationValues(
+ std::move(values));
+ }
+}
+
+void WindowTreeClient::SetNonClientCursor(Window* window,
+ mus::mojom::Cursor cursor_id) {
+ window_manager_internal_client_->WmSetNonClientCursor(server_id(window),
+ cursor_id);
+}
+
+void WindowTreeClient::AddAccelerator(
+ uint32_t id,
+ mojom::EventMatcherPtr event_matcher,
+ const base::Callback<void(bool)>& callback) {
+ if (window_manager_internal_client_) {
+ window_manager_internal_client_->AddAccelerator(
+ id, std::move(event_matcher), callback);
+ }
+}
+
+void WindowTreeClient::RemoveAccelerator(uint32_t id) {
+ if (window_manager_internal_client_) {
+ window_manager_internal_client_->RemoveAccelerator(id);
+ }
+}
+
+void WindowTreeClient::AddActivationParent(Window* window) {
+ if (window_manager_internal_client_)
+ window_manager_internal_client_->AddActivationParent(server_id(window));
+}
+
+void WindowTreeClient::RemoveActivationParent(Window* window) {
+ if (window_manager_internal_client_)
+ window_manager_internal_client_->RemoveActivationParent(server_id(window));
+}
+
+void WindowTreeClient::ActivateNextWindow() {
+ if (window_manager_internal_client_)
+ window_manager_internal_client_->ActivateNextWindow();
+}
+
+void WindowTreeClient::SetUnderlaySurfaceOffsetAndExtendedHitArea(
+ Window* window,
+ const gfx::Vector2d& offset,
+ const gfx::Insets& hit_area) {
+ if (window_manager_internal_client_) {
+ window_manager_internal_client_->SetUnderlaySurfaceOffsetAndExtendedHitArea(
+ server_id(window), offset.x(), offset.y(), hit_area);
+ }
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/public/cpp/lib/window_tree_client_delegate.cc b/chromium/components/mus/public/cpp/lib/window_tree_client_delegate.cc
new file mode 100644
index 00000000000..ed53012b227
--- /dev/null
+++ b/chromium/components/mus/public/cpp/lib/window_tree_client_delegate.cc
@@ -0,0 +1,11 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/public/cpp/window_tree_client_delegate.h"
+
+namespace mus {
+
+void WindowTreeClientDelegate::OnUnembed(Window* root) {}
+
+} // namespace mus
diff --git a/chromium/components/mus/public/cpp/lib/window_tree_host_factory.cc b/chromium/components/mus/public/cpp/lib/window_tree_host_factory.cc
new file mode 100644
index 00000000000..0d55bae13fe
--- /dev/null
+++ b/chromium/components/mus/public/cpp/lib/window_tree_host_factory.cc
@@ -0,0 +1,32 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/public/cpp/window_tree_host_factory.h"
+
+#include "components/mus/public/cpp/window_tree_client.h"
+#include "components/mus/public/cpp/window_tree_client_delegate.h"
+#include "services/shell/public/cpp/connector.h"
+
+namespace mus {
+
+void CreateWindowTreeHost(mojom::WindowTreeHostFactory* factory,
+ WindowTreeClientDelegate* delegate,
+ mojom::WindowTreeHostPtr* host,
+ WindowManagerDelegate* window_manager_delegate) {
+ mojom::WindowTreeClientPtr tree_client;
+ new WindowTreeClient(delegate, window_manager_delegate,
+ GetProxy(&tree_client));
+ factory->CreateWindowTreeHost(GetProxy(host), std::move(tree_client));
+}
+
+void CreateWindowTreeHost(shell::Connector* connector,
+ WindowTreeClientDelegate* delegate,
+ mojom::WindowTreeHostPtr* host,
+ WindowManagerDelegate* window_manager_delegate) {
+ mojom::WindowTreeHostFactoryPtr factory;
+ connector->ConnectToInterface("mojo:mus", &factory);
+ CreateWindowTreeHost(factory.get(), delegate, host, window_manager_delegate);
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/public/cpp/output_surface.h b/chromium/components/mus/public/cpp/output_surface.h
new file mode 100644
index 00000000000..1b1feccff37
--- /dev/null
+++ b/chromium/components/mus/public/cpp/output_surface.h
@@ -0,0 +1,45 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_PUBLIC_CPP_OUTPUT_SURFACE_H_
+#define COMPONENTS_MUS_PUBLIC_CPP_OUTPUT_SURFACE_H_
+
+#include "base/macros.h"
+#include "cc/output/output_surface.h"
+#include "cc/surfaces/surface_id.h"
+#include "components/mus/public/cpp/window_surface.h"
+#include "components/mus/public/cpp/window_surface_client.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace mus {
+
+class OutputSurface : public cc::OutputSurface, public WindowSurfaceClient {
+ public:
+ OutputSurface(const scoped_refptr<cc::ContextProvider>& context_provider,
+ std::unique_ptr<WindowSurface> surface);
+ ~OutputSurface() override;
+
+ // cc::OutputSurface implementation.
+ void SwapBuffers(cc::CompositorFrame frame) override;
+ bool BindToClient(cc::OutputSurfaceClient* client) override;
+ void DetachFromClient() override;
+ void BindFramebuffer() override;
+ uint32_t GetFramebufferCopyTextureFormat() override;
+
+ private:
+ // WindowSurfaceClient implementation:
+ void OnResourcesReturned(
+ WindowSurface* surface,
+ mojo::Array<cc::ReturnedResource> resources) override;
+
+ void SwapBuffersComplete();
+
+ std::unique_ptr<WindowSurface> surface_;
+
+ DISALLOW_COPY_AND_ASSIGN(OutputSurface);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_PUBLIC_CPP_OUTPUT_SURFACE_H_
diff --git a/chromium/components/mus/public/cpp/property_type_converters.h b/chromium/components/mus/public/cpp/property_type_converters.h
new file mode 100644
index 00000000000..5676253e810
--- /dev/null
+++ b/chromium/components/mus/public/cpp/property_type_converters.h
@@ -0,0 +1,96 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_PUBLIC_CPP_PROPERTY_TYPE_CONVERTERS_H_
+#define COMPONENTS_MUS_PUBLIC_CPP_PROPERTY_TYPE_CONVERTERS_H_
+
+#include <stdint.h>
+#include <vector>
+
+#include "base/strings/string16.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+
+class SkBitmap;
+
+namespace gfx {
+class Rect;
+class Size;
+}
+
+namespace mojo {
+
+// TODO(beng): these methods serialize types used for standard properties
+// to vectors of bytes used by Window::SetSharedProperty().
+// replace this with serialization code generated @ bindings.
+// This would be especially useful for SkBitmap, which could be
+// replaced with the skia.Bitmap mojom struct serialization.
+
+template <>
+struct TypeConverter<std::vector<uint8_t>, gfx::Rect> {
+ static std::vector<uint8_t> Convert(const gfx::Rect& input);
+};
+template <>
+struct TypeConverter<gfx::Rect, std::vector<uint8_t>> {
+ static gfx::Rect Convert(const std::vector<uint8_t>& input);
+};
+
+template <>
+struct TypeConverter<std::vector<uint8_t>, gfx::Size> {
+ static std::vector<uint8_t> Convert(const gfx::Size& input);
+};
+template <>
+struct TypeConverter<gfx::Size, std::vector<uint8_t>> {
+ static gfx::Size Convert(const std::vector<uint8_t>& input);
+};
+
+template <>
+struct TypeConverter<std::vector<uint8_t>, int32_t> {
+ static std::vector<uint8_t> Convert(const int32_t& input);
+};
+template <>
+struct TypeConverter<int32_t, std::vector<uint8_t>> {
+ static int32_t Convert(const std::vector<uint8_t>& input);
+};
+
+template <>
+struct TypeConverter<std::vector<uint8_t>, base::string16> {
+ static std::vector<uint8_t> Convert(const base::string16& input);
+};
+template <>
+struct TypeConverter<base::string16, std::vector<uint8_t>> {
+ static base::string16 Convert(const std::vector<uint8_t>& input);
+};
+
+template <>
+struct TypeConverter<std::vector<uint8_t>, std::string> {
+ static std::vector<uint8_t> Convert(const std::string& input);
+};
+template <>
+struct TypeConverter<std::string, std::vector<uint8_t>> {
+ static std::string Convert(const std::vector<uint8_t>& input);
+};
+
+// NOTE: These methods only serialize and deserialize the common case of RGBA
+// 8888 bitmaps with premultiplied alpha.
+template <>
+struct TypeConverter<std::vector<uint8_t>, SkBitmap> {
+ static std::vector<uint8_t> Convert(const SkBitmap& input);
+};
+template <>
+struct TypeConverter<SkBitmap, std::vector<uint8_t>> {
+ static SkBitmap Convert(const std::vector<uint8_t>& input);
+};
+
+template <>
+struct TypeConverter<std::vector<uint8_t>, bool> {
+ static std::vector<uint8_t> Convert(bool input);
+};
+template <>
+struct TypeConverter<bool, std::vector<uint8_t>> {
+ static bool Convert(const std::vector<uint8_t>& input);
+};
+
+} // namespace mojo
+
+#endif // COMPONENTS_MUS_PUBLIC_CPP_PROPERTY_TYPE_CONVERTERS_H_
diff --git a/chromium/components/mus/public/cpp/scoped_window_ptr.h b/chromium/components/mus/public/cpp/scoped_window_ptr.h
new file mode 100644
index 00000000000..b94e0f945f3
--- /dev/null
+++ b/chromium/components/mus/public/cpp/scoped_window_ptr.h
@@ -0,0 +1,42 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_PUBLIC_CPP_SCOPED_WINDOW_PTR_H_
+#define COMPONENTS_MUS_PUBLIC_CPP_SCOPED_WINDOW_PTR_H_
+
+#include "base/macros.h"
+#include "components/mus/public/cpp/window_observer.h"
+
+namespace mus {
+
+// Wraps a Window, taking overship of the Window. Also deals with Window being
+// destroyed while ScopedWindowPtr still exists.
+class ScopedWindowPtr : public WindowObserver {
+ public:
+ explicit ScopedWindowPtr(Window* window);
+ ~ScopedWindowPtr() override;
+
+ // Destroys |window|. If |window| is a root of the WindowManager than the
+ // WindowManager is destroyed (which in turn destroys |window|).
+ //
+ // NOTE: this function (and class) are only useful for clients that only
+ // ever have a single root.
+ static void DeleteWindowOrWindowManager(Window* window);
+
+ Window* window() { return window_; }
+ const Window* window() const { return window_; }
+
+ private:
+ void DetachFromWindow();
+
+ void OnWindowDestroying(Window* window) override;
+
+ Window* window_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedWindowPtr);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_PUBLIC_CPP_SCOPED_WINDOW_PTR_H_
diff --git a/chromium/components/mus/public/cpp/window.h b/chromium/components/mus/public/cpp/window.h
new file mode 100644
index 00000000000..eabc6098a5b
--- /dev/null
+++ b/chromium/components/mus/public/cpp/window.h
@@ -0,0 +1,356 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_PUBLIC_CPP_WINDOW_H_
+#define COMPONENTS_MUS_PUBLIC_CPP_WINDOW_H_
+
+#include <stdint.h>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "components/mus/common/types.h"
+#include "components/mus/public/interfaces/mus_constants.mojom.h"
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "services/shell/public/interfaces/interface_provider.mojom.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace gfx {
+class Size;
+}
+
+namespace mus {
+
+class InputEventHandler;
+class ServiceProviderImpl;
+class WindowObserver;
+class WindowSurface;
+class WindowSurfaceBinding;
+class WindowTreeClient;
+class WindowTreeClientPrivate;
+
+namespace {
+class OrderChangedNotifier;
+}
+
+// Defined in window_property.h (which we do not include)
+template <typename T>
+struct WindowProperty;
+
+// Windows are owned by the WindowTreeClient. See WindowTreeClientDelegate for
+// details on ownership.
+//
+// TODO(beng): Right now, you'll have to implement a WindowObserver to track
+// destruction and NULL any pointers you have.
+// Investigate some kind of smart pointer or weak pointer for these.
+class Window {
+ public:
+ using Children = std::vector<Window*>;
+ using EmbedCallback = base::Callback<void(bool)>;
+ using PropertyDeallocator = void (*)(int64_t value);
+ using SharedProperties = std::map<std::string, std::vector<uint8_t>>;
+
+ // Destroys this window and all its children. Destruction is allowed for
+ // windows that were created by this connection, or the roots. For windows
+ // from other connections (except the roots), Destroy() does nothing. If the
+ // destruction is allowed observers are notified and the Window is
+ // immediately deleted.
+ void Destroy();
+
+ WindowTreeClient* window_tree() { return client_; }
+
+ // The local_id is provided for client code. The local_id is not set or
+ // manipulated by mus. The default value is -1.
+ void set_local_id(int id) { local_id_ = id; }
+ int local_id() const { return local_id_; }
+
+ int64_t display_id() const { return display_id_; }
+
+ // Geometric disposition relative to parent window.
+ const gfx::Rect& bounds() const { return bounds_; }
+ void SetBounds(const gfx::Rect& bounds);
+
+ // Geometric disposition relative to root window.
+ gfx::Rect GetBoundsInRoot() const;
+
+ const gfx::Insets& client_area() const { return client_area_; }
+ const std::vector<gfx::Rect>& additional_client_areas() {
+ return additional_client_areas_;
+ }
+ void SetClientArea(const gfx::Insets& new_client_area) {
+ SetClientArea(new_client_area, std::vector<gfx::Rect>());
+ }
+ void SetClientArea(const gfx::Insets& new_client_area,
+ const std::vector<gfx::Rect>& additional_client_areas);
+
+ // Mouse events outside the hit test mask do not hit the window. Returns null
+ // if there is no mask.
+ const gfx::Rect* hit_test_mask() const { return hit_test_mask_.get(); }
+ void SetHitTestMask(const gfx::Rect& mask_rect);
+ void ClearHitTestMask();
+
+ // Visibility (also see IsDrawn()). When created windows are hidden.
+ bool visible() const { return visible_; }
+ void SetVisible(bool value);
+
+ float opacity() const { return opacity_; }
+ void SetOpacity(float opacity);
+
+ // Cursors
+ mojom::Cursor predefined_cursor() const { return cursor_id_; }
+ void SetPredefinedCursor(mus::mojom::Cursor cursor_id);
+
+ // A Window is drawn if the Window and all its ancestors are visible and the
+ // Window is attached to the root.
+ bool IsDrawn() const;
+
+ std::unique_ptr<WindowSurface> RequestSurface(mojom::SurfaceType type);
+
+ void AttachSurface(mojom::SurfaceType type,
+ std::unique_ptr<WindowSurfaceBinding> surface_binding);
+
+ // The template-ized versions of the following methods rely on the presence
+ // of a mojo::TypeConverter<const std::vector<uint8_t>, T>.
+ // Sets a shared property on the window which is sent to the window server and
+ // shared with other clients that can view this window.
+ template <typename T>
+ void SetSharedProperty(const std::string& name, const T& data);
+ // Gets a shared property set on the window. The property must exist. Call
+ // HasSharedProperty() before calling.
+ template <typename T>
+ T GetSharedProperty(const std::string& name) const;
+ // Removes the shared property.
+ void ClearSharedProperty(const std::string& name);
+ bool HasSharedProperty(const std::string& name) const;
+
+ // TODO(beng): Test only, should move to a helper.
+ const SharedProperties& shared_properties() { return properties_; }
+
+ // Sets the |value| of the given window |property|. Setting to the default
+ // value (e.g., NULL) removes the property. The caller is responsible for the
+ // lifetime of any object set as a property on the Window.
+ //
+ // These properties are not visible to the window server.
+ template <typename T>
+ void SetLocalProperty(const WindowProperty<T>* property, T value);
+
+ // Returns the value of the given window |property|. Returns the
+ // property-specific default value if the property was not previously set.
+ //
+ // These properties are only visible in the current process and are not
+ // shared with other mojo services.
+ template <typename T>
+ T GetLocalProperty(const WindowProperty<T>* property) const;
+
+ // Sets the |property| to its default value. Useful for avoiding a cast when
+ // setting to NULL.
+ //
+ // These properties are only visible in the current process and are not
+ // shared with other mojo services.
+ template <typename T>
+ void ClearLocalProperty(const WindowProperty<T>* property);
+
+ void set_input_event_handler(InputEventHandler* input_event_handler) {
+ input_event_handler_ = input_event_handler;
+ }
+
+ // Observation.
+ void AddObserver(WindowObserver* observer);
+ void RemoveObserver(WindowObserver* observer);
+
+ // Tree.
+ Window* parent() { return parent_; }
+ const Window* parent() const { return parent_; }
+
+ Window* GetRoot() {
+ return const_cast<Window*>(const_cast<const Window*>(this)->GetRoot());
+ }
+ const Window* GetRoot() const;
+
+ void AddChild(Window* child);
+ void RemoveChild(Window* child);
+ const Children& children() const { return children_; }
+
+ void Reorder(Window* relative, mojom::OrderDirection direction);
+ void MoveToFront();
+ void MoveToBack();
+
+ // Returns true if |child| is this or a descendant of this.
+ bool Contains(const Window* child) const;
+
+ void AddTransientWindow(Window* transient_window);
+ void RemoveTransientWindow(Window* transient_window);
+
+ // TODO(fsamuel): Figure out if we want to refactor transient window
+ // management into a separate class.
+ // Transient tree.
+ Window* transient_parent() { return transient_parent_; }
+ const Window* transient_parent() const { return transient_parent_; }
+ const Children& transient_children() const { return transient_children_; }
+
+ void SetModal();
+ bool is_modal() const { return is_modal_; }
+
+ Window* GetChildByLocalId(int id);
+
+ void SetTextInputState(mojo::TextInputStatePtr state);
+ void SetImeVisibility(bool visible, mojo::TextInputStatePtr state);
+
+ bool HasCapture() const;
+ void SetCapture();
+ void ReleaseCapture();
+
+ // Focus. See WindowTreeClient::ClearFocus() to reset focus.
+ void SetFocus();
+ bool HasFocus() const;
+ void SetCanFocus(bool can_focus);
+
+ // Embedding. See window_tree.mojom for details.
+ void Embed(mus::mojom::WindowTreeClientPtr client, uint32_t flags = 0);
+
+ // NOTE: callback is run synchronously if Embed() is not allowed on this
+ // Window.
+ void Embed(mus::mojom::WindowTreeClientPtr client,
+ const EmbedCallback& callback,
+ uint32_t flags = 0);
+
+ // TODO(sky): this API is only applicable to the WindowManager. Move it
+ // to a better place.
+ void RequestClose();
+
+ // Returns an internal name, set by a client app when it creates a window.
+ std::string GetName() const;
+
+ protected:
+ // This class is subclassed only by test classes that provide a public ctor.
+ Window();
+ ~Window();
+
+ private:
+ friend class WindowPrivate;
+ friend class WindowTreeClient;
+ friend class WindowTreeClientPrivate;
+
+ Window(WindowTreeClient* client, Id id);
+
+ // Used to identify this Window on the server. Clients can not change this
+ // value.
+ Id server_id() const { return server_id_; }
+
+ // Applies a shared property change locally and forwards to the server. If
+ // |data| is null, this property is deleted.
+ void SetSharedPropertyInternal(const std::string& name,
+ const std::vector<uint8_t>* data);
+ // Called by the public {Set,Get,Clear}Property functions.
+ int64_t SetLocalPropertyInternal(const void* key,
+ const char* name,
+ PropertyDeallocator deallocator,
+ int64_t value,
+ int64_t default_value);
+ int64_t GetLocalPropertyInternal(const void* key,
+ int64_t default_value) const;
+
+ void LocalDestroy();
+ void LocalAddChild(Window* child);
+ void LocalRemoveChild(Window* child);
+ void LocalAddTransientWindow(Window* transient_window);
+ void LocalRemoveTransientWindow(Window* transient_window);
+ void LocalSetModal();
+ // Returns true if the order actually changed.
+ bool LocalReorder(Window* relative, mojom::OrderDirection direction);
+ void LocalSetBounds(const gfx::Rect& old_bounds, const gfx::Rect& new_bounds);
+ void LocalSetClientArea(
+ const gfx::Insets& new_client_area,
+ const std::vector<gfx::Rect>& additional_client_areas);
+ void LocalSetParentDrawn(bool drawn);
+ void LocalSetDisplay(int64_t display_id);
+ void LocalSetVisible(bool visible);
+ void LocalSetOpacity(float opacity);
+ void LocalSetPredefinedCursor(mojom::Cursor cursor_id);
+ void LocalSetSharedProperty(const std::string& name,
+ const std::vector<uint8_t>* data);
+
+ // Notifies this winodw that its stacking position has changed.
+ void NotifyWindowStackingChanged();
+ // Methods implementing visibility change notifications. See WindowObserver
+ // for more details.
+ void NotifyWindowVisibilityChanged(Window* target);
+ // Notifies this window's observers. Returns false if |this| was deleted
+ // during the call (by an observer), otherwise true.
+ bool NotifyWindowVisibilityChangedAtReceiver(Window* target);
+ // Notifies this window and its child hierarchy. Returns false if |this| was
+ // deleted during the call (by an observer), otherwise true.
+ bool NotifyWindowVisibilityChangedDown(Window* target);
+ // Notifies this window and its parent hierarchy.
+ void NotifyWindowVisibilityChangedUp(Window* target);
+
+ // Returns true if embed is allowed for this node. If embedding is allowed all
+ // the children are removed.
+ bool PrepareForEmbed();
+
+ void RemoveTransientWindowImpl(Window* child);
+ static void ReorderWithoutNotification(Window* window,
+ Window* relative,
+ mojom::OrderDirection direction);
+ static bool ReorderImpl(Window* window,
+ Window* relative,
+ mojom::OrderDirection direction,
+ OrderChangedNotifier* notifier);
+
+ // Returns a pointer to the stacking target that can be used by
+ // RestackTransientDescendants.
+ static Window** GetStackingTarget(Window* window);
+
+ WindowTreeClient* client_;
+ Id server_id_;
+ int local_id_ = -1;
+ Window* parent_;
+ Children children_;
+
+ Window* stacking_target_;
+ Window* transient_parent_;
+ Children transient_children_;
+
+ bool is_modal_;
+
+ base::ObserverList<WindowObserver> observers_;
+ InputEventHandler* input_event_handler_;
+
+ gfx::Rect bounds_;
+ gfx::Insets client_area_;
+ std::vector<gfx::Rect> additional_client_areas_;
+ std::unique_ptr<gfx::Rect> hit_test_mask_;
+
+ bool visible_;
+ float opacity_;
+ int64_t display_id_;
+
+ mojom::Cursor cursor_id_;
+
+ SharedProperties properties_;
+
+ // Drawn state of our parent. This is only meaningful for root Windows, in
+ // which the parent Window isn't exposed to the client.
+ bool parent_drawn_;
+
+ // Value struct to keep the name and deallocator for this property.
+ // Key cannot be used for this purpose because it can be char* or
+ // WindowProperty<>.
+ struct Value {
+ const char* name;
+ int64_t value;
+ PropertyDeallocator deallocator;
+ };
+
+ std::map<const void*, Value> prop_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(Window);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_PUBLIC_CPP_WINDOW_H_
diff --git a/chromium/components/mus/public/cpp/window_manager_delegate.h b/chromium/components/mus/public/cpp/window_manager_delegate.h
new file mode 100644
index 00000000000..99ddf34cc4e
--- /dev/null
+++ b/chromium/components/mus/public/cpp/window_manager_delegate.h
@@ -0,0 +1,116 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_PUBLIC_CPP_WINDOW_MANAGER_DELEGATE_H_
+#define COMPONENTS_MUS_PUBLIC_CPP_WINDOW_MANAGER_DELEGATE_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "components/mus/public/interfaces/cursor.mojom.h"
+#include "components/mus/public/interfaces/event_matcher.mojom.h"
+#include "components/mus/public/interfaces/window_manager_constants.mojom.h"
+#include "ui/events/mojo/event.mojom.h"
+
+namespace display {
+class Display;
+}
+
+namespace gfx {
+class Insets;
+class Rect;
+class Vector2d;
+}
+
+namespace ui {
+class Event;
+}
+
+namespace mus {
+
+class Window;
+
+// See the mojom with the same name for details on the functions in this
+// interface.
+class WindowManagerClient {
+ public:
+ virtual void SetFrameDecorationValues(
+ mojom::FrameDecorationValuesPtr values) = 0;
+ virtual void SetNonClientCursor(Window* window,
+ mojom::Cursor non_client_cursor) = 0;
+
+ virtual void AddAccelerator(uint32_t id,
+ mojom::EventMatcherPtr event_matcher,
+ const base::Callback<void(bool)>& callback) = 0;
+ virtual void RemoveAccelerator(uint32_t id) = 0;
+ virtual void AddActivationParent(Window* window) = 0;
+ virtual void RemoveActivationParent(Window* window) = 0;
+ virtual void ActivateNextWindow() = 0;
+ virtual void SetUnderlaySurfaceOffsetAndExtendedHitArea(
+ Window* window,
+ const gfx::Vector2d& offset,
+ const gfx::Insets& hit_area) = 0;
+
+ protected:
+ virtual ~WindowManagerClient() {}
+};
+
+// Used by clients implementing a window manager.
+// TODO(sky): this should be called WindowManager, but that's rather confusing
+// currently.
+class WindowManagerDelegate {
+ public:
+ // Called once to give the delegate access to functions only exposed to
+ // the WindowManager.
+ virtual void SetWindowManagerClient(WindowManagerClient* client) = 0;
+
+ // A client requested the bounds of |window| to change to |bounds|. Return
+ // true if the bounds are allowed to change. A return value of false
+ // indicates the change is not allowed.
+ // NOTE: This should not change the bounds of |window|. Instead return the
+ // bounds the window should be in |bounds|.
+ virtual bool OnWmSetBounds(Window* window, gfx::Rect* bounds) = 0;
+
+ // A client requested the shared property named |name| to change to
+ // |new_data|. Return true to allow the change to |new_data|, false
+ // otherwise.
+ virtual bool OnWmSetProperty(
+ Window* window,
+ const std::string& name,
+ std::unique_ptr<std::vector<uint8_t>>* new_data) = 0;
+
+ // A client has requested a new top level window. The delegate should create
+ // and parent the window appropriately and return it. |properties| is the
+ // supplied properties from the client requesting the new window. The
+ // delegate may modify |properties| before calling NewWindow(), but the
+ // delegate does *not* own |properties|, they are valid only for the life
+ // of OnWmCreateTopLevelWindow().
+ virtual Window* OnWmCreateTopLevelWindow(
+ std::map<std::string, std::vector<uint8_t>>* properties) = 0;
+
+ // Called when a Mus client's jankiness changes. |windows| is the set of
+ // windows owned by the window manager in which the client is embedded.
+ virtual void OnWmClientJankinessChanged(
+ const std::set<Window*>& client_windows,
+ bool janky) = 0;
+
+ // Called when a display is added. |window| is the root of the window tree for
+ // the specified display.
+ virtual void OnWmNewDisplay(Window* window,
+ const display::Display& display) = 0;
+
+ virtual void OnAccelerator(uint32_t id, const ui::Event& event) = 0;
+
+ protected:
+ virtual ~WindowManagerDelegate() {}
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_PUBLIC_CPP_WINDOW_MANAGER_DELEGATE_H_
diff --git a/chromium/components/mus/public/cpp/window_observer.h b/chromium/components/mus/public/cpp/window_observer.h
new file mode 100644
index 00000000000..f1dc27913d7
--- /dev/null
+++ b/chromium/components/mus/public/cpp/window_observer.h
@@ -0,0 +1,110 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_PUBLIC_CPP_WINDOW_OBSERVER_H_
+#define COMPONENTS_MUS_PUBLIC_CPP_WINDOW_OBSERVER_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "components/mus/public/cpp/window.h"
+
+namespace mus {
+
+class Window;
+
+// A note on -ing and -ed suffixes:
+//
+// -ing methods are called before changes are applied to the local window model.
+// -ed methods are called after changes are applied to the local window model.
+//
+// If the change originated from another connection to the window manager, it's
+// possible that the change has already been applied to the service-side model
+// prior to being called, so for example in the case of OnWindowDestroying(),
+// it's possible the window has already been destroyed on the service side.
+
+class WindowObserver {
+ public:
+ struct TreeChangeParams {
+ TreeChangeParams();
+ Window* target;
+ Window* old_parent;
+ Window* new_parent;
+ Window* receiver;
+ };
+
+ virtual void OnTreeChanging(const TreeChangeParams& params) {}
+ virtual void OnTreeChanged(const TreeChangeParams& params) {}
+
+ virtual void OnWindowReordering(Window* window,
+ Window* relative_window,
+ mojom::OrderDirection direction) {}
+ virtual void OnWindowReordered(Window* window,
+ Window* relative_window,
+ mojom::OrderDirection direction) {}
+
+ virtual void OnWindowDestroying(Window* window) {}
+ virtual void OnWindowDestroyed(Window* window) {}
+
+ virtual void OnWindowBoundsChanging(Window* window,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {}
+ virtual void OnWindowBoundsChanged(Window* window,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {}
+ virtual void OnWindowLostCapture(Window* window) {}
+ virtual void OnWindowClientAreaChanged(
+ Window* window,
+ const gfx::Insets& old_client_area,
+ const std::vector<gfx::Rect>& old_additional_client_areas) {}
+
+ virtual void OnWindowFocusChanged(Window* gained_focus, Window* lost_focus) {}
+
+ virtual void OnWindowPredefinedCursorChanged(Window* window,
+ mojom::Cursor cursor) {}
+ virtual void OnWindowVisibilityChanging(Window* window) {}
+ virtual void OnWindowVisibilityChanged(Window* window) {}
+ virtual void OnWindowOpacityChanged(Window* window,
+ float old_opacity,
+ float new_opacity) {}
+
+ // Invoked when this Window's shared properties have changed. This can either
+ // be caused by SetSharedProperty() being called locally, or by us receiving
+ // a mojo message that this property has changed. If this property has been
+ // added, |old_data| is null. If this property was removed, |new_data| is
+ // null.
+ virtual void OnWindowSharedPropertyChanged(
+ Window* window,
+ const std::string& name,
+ const std::vector<uint8_t>* old_data,
+ const std::vector<uint8_t>* new_data) {}
+
+ // Invoked when SetProperty() or ClearProperty() is called on the window.
+ // |key| is either a WindowProperty<T>* (SetProperty, ClearProperty). Either
+ // way, it can simply be compared for equality with the property
+ // constant. |old| is the old property value, which must be cast to the
+ // appropriate type before use.
+ virtual void OnWindowLocalPropertyChanged(Window* window,
+ const void* key,
+ intptr_t old) {}
+
+ virtual void OnWindowEmbeddedAppDisconnected(Window* window) {}
+
+ // Sent when the drawn state changes. This is only sent for the root nodes
+ // when embedded.
+ virtual void OnWindowDrawnChanging(Window* window) {}
+ virtual void OnWindowDrawnChanged(Window* window) {}
+
+ // The WindowManager has requested the window to close. If the observer
+ // allows the close it should destroy the window as appropriate.
+ virtual void OnRequestClose(Window* window) {}
+
+ protected:
+ virtual ~WindowObserver() {}
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_PUBLIC_CPP_WINDOW_OBSERVER_H_
diff --git a/chromium/components/mus/public/cpp/window_property.h b/chromium/components/mus/public/cpp/window_property.h
new file mode 100644
index 00000000000..3299ac9db98
--- /dev/null
+++ b/chromium/components/mus/public/cpp/window_property.h
@@ -0,0 +1,156 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_PUBLIC_CPP_WINDOW_PROPERTY_H_
+#define COMPONENTS_MUS_PUBLIC_CPP_WINDOW_PROPERTY_H_
+
+#include <stdint.h>
+
+// To define a new WindowProperty:
+//
+// #include "components/mus/public/cpp/window_property.h"
+//
+// MUS_DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(FOO_EXPORT, MyType);
+// namespace foo {
+// // Use this to define an exported property that is primitive,
+// // or a pointer you don't want automatically deleted.
+// MUS_DEFINE_WINDOW_PROPERTY_KEY(MyType, kMyKey, MyDefault);
+//
+// // Use this to define an exported property whose value is a heap
+// // allocated object, and has to be owned and freed by the window.
+// MUS_DEFINE_OWNED_WINDOW_PROPERTY_KEY(gfx::Rect, kRestoreBoundsKey,
+// nullptr);
+//
+// // Use this to define a non exported property that is primitive,
+// // or a pointer you don't want to automatically deleted, and is used
+// // only in a specific file. This will define the property in an unnamed
+// // namespace which cannot be accessed from another file.
+// MUS_DEFINE_LOCAL_WINDOW_PROPERTY_KEY(MyType, kMyKey, MyDefault);
+//
+// } // foo namespace
+//
+// To define a new type used for WindowProperty.
+//
+// // outside all namespaces:
+// MUS_DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(FOO_EXPORT, MyType)
+//
+// If a property type is not exported, use
+// MUS_DECLARE_WINDOW_PROPERTY_TYPE(MyType) which is a shorthand for
+// MUS_DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(, MyType).
+
+namespace mus {
+
+template <typename T>
+void Window::SetSharedProperty(const std::string& name, const T& data) {
+ const std::vector<uint8_t> bytes =
+ mojo::TypeConverter<std::vector<uint8_t>, T>::Convert(data);
+ SetSharedPropertyInternal(name, &bytes);
+}
+
+template <typename T>
+T Window::GetSharedProperty(const std::string& name) const {
+ DCHECK(HasSharedProperty(name));
+ auto it = properties_.find(name);
+ return mojo::TypeConverter<T, std::vector<uint8_t>>::Convert(it->second);
+}
+
+namespace {
+
+// No single new-style cast works for every conversion to/from int64_t, so we
+// need this helper class. A third specialization is needed for bool because
+// MSVC warning C4800 (forcing value to bool) is not suppressed by an explicit
+// cast (!).
+template <typename T>
+class WindowPropertyCaster {
+ public:
+ static int64_t ToInt64(T x) { return static_cast<int64_t>(x); }
+ static T FromInt64(int64_t x) { return static_cast<T>(x); }
+};
+template <typename T>
+class WindowPropertyCaster<T*> {
+ public:
+ static int64_t ToInt64(T* x) { return reinterpret_cast<int64_t>(x); }
+ static T* FromInt64(int64_t x) { return reinterpret_cast<T*>(x); }
+};
+template <>
+class WindowPropertyCaster<bool> {
+ public:
+ static int64_t ToInt64(bool x) { return static_cast<int64_t>(x); }
+ static bool FromInt64(int64_t x) { return x != 0; }
+};
+
+} // namespace
+
+template <typename T>
+struct WindowProperty {
+ T default_value;
+ const char* name;
+ Window::PropertyDeallocator deallocator;
+};
+
+template <typename T>
+void Window::SetLocalProperty(const WindowProperty<T>* property, T value) {
+ int64_t old = SetLocalPropertyInternal(
+ property, property->name,
+ value == property->default_value ? nullptr : property->deallocator,
+ WindowPropertyCaster<T>::ToInt64(value),
+ WindowPropertyCaster<T>::ToInt64(property->default_value));
+ if (property->deallocator &&
+ old != WindowPropertyCaster<T>::ToInt64(property->default_value)) {
+ (*property->deallocator)(old);
+ }
+}
+
+template <typename T>
+T Window::GetLocalProperty(const WindowProperty<T>* property) const {
+ return WindowPropertyCaster<T>::FromInt64(GetLocalPropertyInternal(
+ property, WindowPropertyCaster<T>::ToInt64(property->default_value)));
+}
+
+template <typename T>
+void Window::ClearLocalProperty(const WindowProperty<T>* property) {
+ SetLocalProperty(property, property->default_value);
+}
+
+} // namespace mus
+
+// Macros to instantiate the property getter/setter template functions.
+#define MUS_DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(EXPORT, T) \
+ template EXPORT void mus::Window::SetLocalProperty( \
+ const mus::WindowProperty<T>*, T); \
+ template EXPORT T mus::Window::GetLocalProperty( \
+ const mus::WindowProperty<T>*) const; \
+ template EXPORT void mus::Window::ClearLocalProperty( \
+ const mus::WindowProperty<T>*);
+#define MUS_DECLARE_WINDOW_PROPERTY_TYPE(T) \
+ MUS_DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(, T)
+
+#define MUS_DEFINE_WINDOW_PROPERTY_KEY(TYPE, NAME, DEFAULT) \
+ static_assert(sizeof(TYPE) <= sizeof(int64_t), \
+ "Property type must fit in 64 bits"); \
+ namespace { \
+ const mus::WindowProperty<TYPE> NAME##_Value = {DEFAULT, #NAME, nullptr}; \
+ } \
+ const mus::WindowProperty<TYPE>* const NAME = &NAME##_Value;
+
+#define MUS_DEFINE_LOCAL_WINDOW_PROPERTY_KEY(TYPE, NAME, DEFAULT) \
+ static_assert(sizeof(TYPE) <= sizeof(int64_t), \
+ "Property type must fit in 64 bits"); \
+ namespace { \
+ const mus::WindowProperty<TYPE> NAME##_Value = {DEFAULT, #NAME, nullptr}; \
+ const mus::WindowProperty<TYPE>* const NAME = &NAME##_Value; \
+ }
+
+#define MUS_DEFINE_OWNED_WINDOW_PROPERTY_KEY(TYPE, NAME, DEFAULT) \
+ namespace { \
+ void Deallocator##NAME(int64_t p) { \
+ enum { type_must_be_complete = sizeof(TYPE) }; \
+ delete mus::WindowPropertyCaster<TYPE*>::FromInt64(p); \
+ } \
+ const mus::WindowProperty<TYPE*> NAME##_Value = {DEFAULT, #NAME, \
+ &Deallocator##NAME}; \
+ } \
+ const mus::WindowProperty<TYPE*>* const NAME = &NAME##_Value;
+
+#endif // COMPONENTS_MUS_PUBLIC_CPP_WINDOW_PROPERTY_H_
diff --git a/chromium/components/mus/public/cpp/window_surface.h b/chromium/components/mus/public/cpp/window_surface.h
new file mode 100644
index 00000000000..97af8123d61
--- /dev/null
+++ b/chromium/components/mus/public/cpp/window_surface.h
@@ -0,0 +1,85 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_PUBLIC_CPP_WINDOW_SURFACE_H_
+#define COMPONENTS_MUS_PUBLIC_CPP_WINDOW_SURFACE_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "base/threading/thread_checker.h"
+#include "components/mus/public/interfaces/surface.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/interface_ptr_info.h"
+
+namespace mus {
+
+class WindowSurfaceBinding;
+class WindowSurfaceClient;
+class Window;
+
+// A WindowSurface is wrapper to simplify submitting CompositorFrames to
+// Windows, and receiving ReturnedResources.
+class WindowSurface : public mojom::SurfaceClient {
+ public:
+ // static
+ static std::unique_ptr<WindowSurface> Create(
+ std::unique_ptr<WindowSurfaceBinding>* surface_binding);
+
+ ~WindowSurface() override;
+
+ // Called to indicate that the current thread has assumed control of this
+ // object.
+ void BindToThread();
+
+ void SubmitCompositorFrame(cc::CompositorFrame frame,
+ const base::Closure& callback);
+
+ void set_client(WindowSurfaceClient* client) { client_ = client; }
+
+ private:
+ friend class Window;
+
+ WindowSurface(mojo::InterfacePtrInfo<mojom::Surface> surface_info,
+ mojo::InterfaceRequest<mojom::SurfaceClient> client_request);
+
+ // SurfaceClient implementation:
+ void ReturnResources(mojo::Array<cc::ReturnedResource> resources) override;
+
+ WindowSurfaceClient* client_;
+ mojo::InterfacePtrInfo<mojom::Surface> surface_info_;
+ mojo::InterfaceRequest<mojom::SurfaceClient> client_request_;
+ mojom::SurfacePtr surface_;
+ std::unique_ptr<mojo::Binding<mojom::SurfaceClient>> client_binding_;
+ std::unique_ptr<base::ThreadChecker> thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowSurface);
+};
+
+// A WindowSurfaceBinding is a bundle of mojo interfaces that are to be used by
+// or implemented by the Mus window server when passed into
+// Window::AttachSurface. WindowSurfaceBinding has no standalone functionality.
+class WindowSurfaceBinding {
+ public:
+ ~WindowSurfaceBinding();
+
+ private:
+ friend class WindowSurface;
+ friend class Window;
+
+ WindowSurfaceBinding(
+ mojo::InterfaceRequest<mojom::Surface> surface_request,
+ mojo::InterfacePtrInfo<mojom::SurfaceClient> surface_client);
+
+ mojo::InterfaceRequest<mojom::Surface> surface_request_;
+ mojo::InterfacePtrInfo<mojom::SurfaceClient> surface_client_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowSurfaceBinding);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_PUBLIC_CPP_WINDOW_SURFACE_H_
diff --git a/chromium/components/mus/public/cpp/window_surface_client.h b/chromium/components/mus/public/cpp/window_surface_client.h
new file mode 100644
index 00000000000..1208d2028f2
--- /dev/null
+++ b/chromium/components/mus/public/cpp/window_surface_client.h
@@ -0,0 +1,24 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_PUBLIC_CPP_WINDOW_SURFACE_CLIENT_H_
+#define COMPONENTS_MUS_PUBLIC_CPP_WINDOW_SURFACE_CLIENT_H_
+
+namespace mus {
+
+class WindowSurface;
+
+class WindowSurfaceClient {
+ public:
+ virtual void OnResourcesReturned(
+ WindowSurface* surface,
+ mojo::Array<cc::ReturnedResource> resources) = 0;
+
+ protected:
+ virtual ~WindowSurfaceClient() {}
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_PUBLIC_CPP_WINDOW_SURFACE_CLIENT_H_
diff --git a/chromium/components/mus/public/cpp/window_tracker.h b/chromium/components/mus/public/cpp/window_tracker.h
new file mode 100644
index 00000000000..db277531495
--- /dev/null
+++ b/chromium/components/mus/public/cpp/window_tracker.h
@@ -0,0 +1,21 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_PUBLIC_CPP_WINDOW_TRACKER_H_
+#define COMPONENTS_MUS_PUBLIC_CPP_WINDOW_TRACKER_H_
+
+#include <stdint.h>
+#include <set>
+
+#include "base/macros.h"
+#include "components/mus/public/cpp/window_observer.h"
+#include "ui/base/window_tracker_template.h"
+
+namespace mus {
+
+using WindowTracker = ui::WindowTrackerTemplate<Window, WindowObserver>;
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_PUBLIC_CPP_WINDOW_TRACKER_H_
diff --git a/chromium/components/mus/public/cpp/window_tree_client.h b/chromium/components/mus/public/cpp/window_tree_client.h
new file mode 100644
index 00000000000..24ef564f3f5
--- /dev/null
+++ b/chromium/components/mus/public/cpp/window_tree_client.h
@@ -0,0 +1,407 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_PUBLIC_CPP_WINDOW_TREE_CLIENT_H_
+#define COMPONENTS_MUS_PUBLIC_CPP_WINDOW_TREE_CLIENT_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/atomicops.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "components/mus/common/types.h"
+#include "components/mus/public/cpp/window.h"
+#include "components/mus/public/cpp/window_manager_delegate.h"
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+namespace display {
+class Display;
+}
+
+namespace gfx {
+class Insets;
+class Size;
+}
+
+namespace shell {
+class Connector;
+}
+
+namespace mus {
+class InFlightChange;
+class WindowTreeClientDelegate;
+class WindowTreeClientPrivate;
+class WindowTreeClientObserver;
+enum class ChangeType;
+
+// Manages the connection with mus.
+//
+// WindowTreeClient is deleted by any of the following:
+// . If all the roots of the connection are destroyed and the connection is
+// configured to delete when there are no roots (true if the WindowTreeClient
+// is created with a mojom::WindowTreeClientRequest). This happens
+// if the owner of the roots Embed()s another app in all the roots, or all
+// the roots are explicitly deleted.
+// . The connection to mus is lost.
+// . Explicitly by way of calling delete.
+//
+// When WindowTreeClient is deleted all windows are deleted (and observers
+// notified). This is followed by calling
+// WindowTreeClientDelegate::OnWindowTreeClientDestroyed().
+class WindowTreeClient : public mojom::WindowTreeClient,
+ public mojom::WindowManager,
+ public WindowManagerClient {
+ public:
+ WindowTreeClient(WindowTreeClientDelegate* delegate,
+ WindowManagerDelegate* window_manager_delegate,
+ mojom::WindowTreeClientRequest request);
+ ~WindowTreeClient() override;
+
+ // Establishes the connection by way of the WindowTreeFactory.
+ void ConnectViaWindowTreeFactory(shell::Connector* connector);
+
+ // Establishes the connection by way of WindowManagerWindowTreeFactory.
+ void ConnectAsWindowManager(shell::Connector* connector);
+
+ // Wait for OnEmbed(), returning when done.
+ void WaitForEmbed();
+
+ bool connected() const { return tree_ != nullptr; }
+ ClientSpecificId client_id() const { return client_id_; }
+
+ // API exposed to the window implementations that pushes local changes to the
+ // service.
+ void DestroyWindow(Window* window);
+
+ // These methods take TransportIds. For windows owned by the current client,
+ // the client id high word can be zero. In all cases, the TransportId 0x1
+ // refers to the root window.
+ void AddChild(Window* parent, Id child_id);
+ void RemoveChild(Window* parent, Id child_id);
+
+ void AddTransientWindow(Window* window, Id transient_window_id);
+ void RemoveTransientWindowFromParent(Window* window);
+
+ void SetModal(Window* window);
+
+ void Reorder(Window* window,
+ Id relative_window_id,
+ mojom::OrderDirection direction);
+
+ // Returns true if the specified window was created by this client.
+ bool OwnsWindow(Window* window) const;
+
+ void SetBounds(Window* window,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& bounds);
+ void SetCapture(Window* window);
+ void ReleaseCapture(Window* window);
+ void SetClientArea(Id window_id,
+ const gfx::Insets& client_area,
+ const std::vector<gfx::Rect>& additional_client_areas);
+ void SetHitTestMask(Id window_id, const gfx::Rect& mask);
+ void ClearHitTestMask(Id window_id);
+ void SetFocus(Window* window);
+ void SetCanFocus(Id window_id, bool can_focus);
+ void SetPredefinedCursor(Id window_id, mus::mojom::Cursor cursor_id);
+ void SetVisible(Window* window, bool visible);
+ void SetOpacity(Window* window, float opacity);
+ void SetProperty(Window* window,
+ const std::string& name,
+ mojo::Array<uint8_t> data);
+ void SetWindowTextInputState(Id window_id, mojo::TextInputStatePtr state);
+ void SetImeVisibility(Id window_id,
+ bool visible,
+ mojo::TextInputStatePtr state);
+
+ void Embed(Id window_id,
+ mojom::WindowTreeClientPtr client,
+ uint32_t flags,
+ const mojom::WindowTree::EmbedCallback& callback);
+
+ void RequestClose(Window* window);
+
+ void AttachSurface(Id window_id,
+ mojom::SurfaceType type,
+ mojo::InterfaceRequest<mojom::Surface> surface,
+ mojom::SurfaceClientPtr client);
+
+ // Sets the input capture to |window| without notifying the server.
+ void LocalSetCapture(Window* window);
+ // Sets focus to |window| without notifying the server.
+ void LocalSetFocus(Window* window);
+
+ // Start/stop tracking windows. While tracked, they can be retrieved via
+ // WindowTreeClient::GetWindowById.
+ void AddWindow(Window* window);
+
+ bool IsRoot(Window* window) const { return roots_.count(window) > 0; }
+
+ void OnWindowDestroying(Window* window);
+
+ // Called after the window's observers have been notified of destruction (as
+ // the last step of ~Window).
+ void OnWindowDestroyed(Window* window);
+
+ Window* GetWindowByServerId(Id id);
+
+ // Sets whether this is deleted when there are no roots. The default is to
+ // delete when there are no roots.
+ void SetDeleteOnNoRoots(bool value);
+
+ // Returns the root of this connection.
+ const std::set<Window*>& GetRoots();
+
+ // Returns the Window with input capture; null if no window has requested
+ // input capture, or if another app has capture.
+ Window* GetCaptureWindow();
+
+ // Returns the focused window; null if focus is not yet known or another app
+ // is focused.
+ Window* GetFocusedWindow();
+
+ // Sets focus to null. This does nothing if focus is currently null.
+ void ClearFocus();
+
+ // Returns the current location of the mouse on screen. Note: this method may
+ // race the asynchronous initialization; but in that case we return (0, 0).
+ gfx::Point GetCursorScreenPoint();
+
+ // See description in window_tree.mojom. When an existing event observer is
+ // updated or cleared then any future events from the server for that observer
+ // will be ignored.
+ void SetEventObserver(mojom::EventMatcherPtr matcher);
+
+ // Creates and returns a new Window (which is owned by the window server).
+ // Windows are initially hidden, use SetVisible(true) to show.
+ Window* NewWindow() { return NewWindow(nullptr); }
+ Window* NewWindow(
+ const std::map<std::string, std::vector<uint8_t>>* properties);
+ Window* NewTopLevelWindow(
+ const std::map<std::string, std::vector<uint8_t>>* properties);
+
+ void AddObserver(WindowTreeClientObserver* observer);
+ void RemoveObserver(WindowTreeClientObserver* observer);
+
+#if !defined(NDEBUG)
+ std::string GetDebugWindowHierarchy() const;
+ void BuildDebugInfo(const std::string& depth,
+ Window* window,
+ std::string* result) const;
+#endif
+
+ private:
+ friend class WindowTreeClientPrivate;
+
+ enum class NewWindowType {
+ CHILD,
+ TOP_LEVEL,
+ };
+
+ using IdToWindowMap = std::map<Id, Window*>;
+
+ // TODO(sky): this assumes change_ids never wrap, which is a bad assumption.
+ using InFlightMap = std::map<uint32_t, std::unique_ptr<InFlightChange>>;
+
+ // Returns the oldest InFlightChange that matches |change|.
+ InFlightChange* GetOldestInFlightChangeMatching(const InFlightChange& change);
+
+ // See InFlightChange for details on how InFlightChanges are used.
+ uint32_t ScheduleInFlightChange(std::unique_ptr<InFlightChange> change);
+
+ // Returns true if there is an InFlightChange that matches |change|. If there
+ // is an existing change SetRevertValueFrom() is invoked on it. Returns false
+ // if there is no InFlightChange matching |change|.
+ // See InFlightChange for details on how InFlightChanges are used.
+ bool ApplyServerChangeToExistingInFlightChange(const InFlightChange& change);
+
+ Window* NewWindowImpl(NewWindowType type,
+ const Window::SharedProperties* properties);
+
+ // Sets the mojom::WindowTree implementation.
+ void SetWindowTree(mojom::WindowTreePtr window_tree_ptr);
+
+ // Called when the mojom::WindowTree connection is lost, deletes this.
+ void OnConnectionLost();
+
+ // OnEmbed() calls into this. Exposed as a separate function for testing.
+ void OnEmbedImpl(mojom::WindowTree* window_tree,
+ ClientSpecificId client_id,
+ mojom::WindowDataPtr root_data,
+ int64_t display_id,
+ Id focused_window_id,
+ bool drawn);
+
+ // Called by WmNewDisplayAdded().
+ void WmNewDisplayAddedImpl(const display::Display& display,
+ mojom::WindowDataPtr root_data,
+ bool parent_drawn);
+
+ void OnReceivedCursorLocationMemory(mojo::ScopedSharedBufferHandle handle);
+
+ // Overridden from WindowTreeClient:
+ void OnEmbed(ClientSpecificId client_id,
+ mojom::WindowDataPtr root,
+ mojom::WindowTreePtr tree,
+ int64_t display_id,
+ Id focused_window_id,
+ bool drawn) override;
+ void OnEmbeddedAppDisconnected(Id window_id) override;
+ void OnUnembed(Id window_id) override;
+ void OnLostCapture(Id window_id) override;
+ void OnTopLevelCreated(uint32_t change_id,
+ mojom::WindowDataPtr data,
+ int64_t display_id,
+ bool drawn) override;
+ void OnWindowBoundsChanged(Id window_id,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) override;
+ void OnClientAreaChanged(
+ uint32_t window_id,
+ const gfx::Insets& new_client_area,
+ mojo::Array<gfx::Rect> new_additional_client_areas) override;
+ void OnTransientWindowAdded(uint32_t window_id,
+ uint32_t transient_window_id) override;
+ void OnTransientWindowRemoved(uint32_t window_id,
+ uint32_t transient_window_id) override;
+ void OnWindowHierarchyChanged(
+ Id window_id,
+ Id old_parent_id,
+ Id new_parent_id,
+ mojo::Array<mojom::WindowDataPtr> windows) override;
+ void OnWindowReordered(Id window_id,
+ Id relative_window_id,
+ mojom::OrderDirection direction) override;
+ void OnWindowDeleted(Id window_id) override;
+ void OnWindowVisibilityChanged(Id window_id, bool visible) override;
+ void OnWindowOpacityChanged(Id window_id,
+ float old_opacity,
+ float new_opacity) override;
+ void OnWindowParentDrawnStateChanged(Id window_id, bool drawn) override;
+ void OnWindowSharedPropertyChanged(Id window_id,
+ const mojo::String& name,
+ mojo::Array<uint8_t> new_data) override;
+ void OnWindowInputEvent(uint32_t event_id,
+ Id window_id,
+ std::unique_ptr<ui::Event> event,
+ uint32_t event_observer_id) override;
+ void OnEventObserved(std::unique_ptr<ui::Event> event,
+ uint32_t event_observer_id) override;
+ void OnWindowFocused(Id focused_window_id) override;
+ void OnWindowPredefinedCursorChanged(Id window_id,
+ mojom::Cursor cursor) override;
+ void OnChangeCompleted(uint32_t change_id, bool success) override;
+ void RequestClose(uint32_t window_id) override;
+ void GetWindowManager(
+ mojo::AssociatedInterfaceRequest<WindowManager> internal) override;
+
+ // Overridden from WindowManager:
+ void OnConnect(ClientSpecificId client_id) override;
+ void WmNewDisplayAdded(mojom::DisplayPtr display,
+ mojom::WindowDataPtr root_data,
+ bool parent_drawn) override;
+ void WmSetBounds(uint32_t change_id,
+ Id window_id,
+ const gfx::Rect& transit_bounds) override;
+ void WmSetProperty(uint32_t change_id,
+ Id window_id,
+ const mojo::String& name,
+ mojo::Array<uint8_t> transit_data) override;
+ void WmCreateTopLevelWindow(uint32_t change_id,
+ ClientSpecificId requesting_client_id,
+ mojo::Map<mojo::String, mojo::Array<uint8_t>>
+ transport_properties) override;
+ void WmClientJankinessChanged(ClientSpecificId client_id,
+ bool janky) override;
+ void OnAccelerator(uint32_t id, std::unique_ptr<ui::Event> event) override;
+
+ // Overridden from WindowManagerClient:
+ void SetFrameDecorationValues(
+ mojom::FrameDecorationValuesPtr values) override;
+ void SetNonClientCursor(Window* window,
+ mus::mojom::Cursor cursor_id) override;
+ void AddAccelerator(uint32_t id,
+ mojom::EventMatcherPtr event_matcher,
+ const base::Callback<void(bool)>& callback) override;
+ void RemoveAccelerator(uint32_t id) override;
+ void AddActivationParent(Window* window) override;
+ void RemoveActivationParent(Window* window) override;
+ void ActivateNextWindow() override;
+ void SetUnderlaySurfaceOffsetAndExtendedHitArea(
+ Window* window,
+ const gfx::Vector2d& offset,
+ const gfx::Insets& hit_area) override;
+
+ // The one int in |cursor_location_mapping_|. When we read from this
+ // location, we must always read from it atomically.
+ base::subtle::Atomic32* cursor_location_memory() {
+ return reinterpret_cast<base::subtle::Atomic32*>(
+ cursor_location_mapping_.get());
+ }
+
+ // This is set once and only once when we get OnEmbed(). It gives the unique
+ // id for this client.
+ ClientSpecificId client_id_;
+
+ // Id assigned to the next window created.
+ ClientSpecificId next_window_id_;
+
+ // Id used for the next change id supplied to the server.
+ uint32_t next_change_id_;
+ InFlightMap in_flight_map_;
+
+ WindowTreeClientDelegate* delegate_;
+
+ WindowManagerDelegate* window_manager_delegate_;
+
+ std::set<Window*> roots_;
+
+ IdToWindowMap windows_;
+ std::map<ClientSpecificId, std::set<Window*>> embedded_windows_;
+
+ Window* capture_window_;
+
+ Window* focused_window_;
+
+ mojo::Binding<mojom::WindowTreeClient> binding_;
+ mojom::WindowTreePtr tree_ptr_;
+ // Typically this is the value contained in |tree_ptr_|, but tests may
+ // directly set this.
+ mojom::WindowTree* tree_;
+
+ bool delete_on_no_roots_;
+
+ bool in_destructor_;
+
+ // A mapping to shared memory that is one 32 bit integer long. The window
+ // server uses this to let us synchronously read the cursor location.
+ mojo::ScopedSharedBufferMapping cursor_location_mapping_;
+
+ base::ObserverList<WindowTreeClientObserver> observers_;
+
+ std::unique_ptr<mojo::AssociatedBinding<mojom::WindowManager>>
+ window_manager_internal_;
+ mojom::WindowManagerClientAssociatedPtr window_manager_internal_client_;
+
+ bool has_event_observer_ = false;
+
+ // Monotonically increasing ID for event observers.
+ uint32_t event_observer_id_ = 0u;
+
+ base::WeakPtrFactory<WindowTreeClient> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowTreeClient);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_PUBLIC_CPP_WINDOW_TREE_CLIENT_H_
diff --git a/chromium/components/mus/public/cpp/window_tree_client_delegate.h b/chromium/components/mus/public/cpp/window_tree_client_delegate.h
new file mode 100644
index 00000000000..41f09dfdbf0
--- /dev/null
+++ b/chromium/components/mus/public/cpp/window_tree_client_delegate.h
@@ -0,0 +1,53 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_PUBLIC_CPP_WINDOW_TREE_CLIENT_DELEGATE_H_
+#define COMPONENTS_MUS_PUBLIC_CPP_WINDOW_TREE_CLIENT_DELEGATE_H_
+
+#include <string>
+
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "services/shell/public/interfaces/interface_provider.mojom.h"
+
+namespace ui {
+class Event;
+}
+
+namespace mus {
+
+class Window;
+class WindowTreeClient;
+
+// Interface implemented by an application using mus.
+class WindowTreeClientDelegate {
+ public:
+ // Called when the application implementing this interface is embedded at
+ // |root|.
+ // NOTE: this is only invoked if the WindowTreeClient is created with an
+ // InterfaceRequest.
+ virtual void OnEmbed(Window* root) = 0;
+
+ // Sent when another app is embedded in |root| (one of the roots of the
+ // connection). Afer this call |root| is deleted. If |root| is the only root
+ // and the connection is configured to delete when there are no roots (the
+ // default), then after |root| is destroyed the connection is destroyed as
+ // well.
+ virtual void OnUnembed(Window* root);
+
+ // Called from the destructor of WindowTreeClient after all the Windows have
+ // been destroyed. |client| is no longer valid after this call.
+ virtual void OnWindowTreeClientDestroyed(WindowTreeClient* client) = 0;
+
+ // Called when the WindowTreeClient receives an input event observed via
+ // SetEventObserver(). |target| may be null for events that were sent to
+ // windows owned by other processes.
+ virtual void OnEventObserved(const ui::Event& event, Window* target) = 0;
+
+ protected:
+ virtual ~WindowTreeClientDelegate() {}
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_PUBLIC_CPP_WINDOW_TREE_CLIENT_DELEGATE_H_
diff --git a/chromium/components/mus/public/cpp/window_tree_client_observer.h b/chromium/components/mus/public/cpp/window_tree_client_observer.h
new file mode 100644
index 00000000000..394a5cea1e1
--- /dev/null
+++ b/chromium/components/mus/public/cpp/window_tree_client_observer.h
@@ -0,0 +1,27 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_PUBLIC_CPP_WINDOW_TREE_CLIENT_OBSERVER_H_
+#define COMPONENTS_MUS_PUBLIC_CPP_WINDOW_TREE_CLIENT_OBSERVER_H_
+
+namespace mus {
+
+class Window;
+class WindowTreeClient;
+
+class WindowTreeClientObserver {
+ public:
+ virtual void OnWindowTreeFocusChanged(Window* gained_focus,
+ Window* lost_focus) {}
+
+ // Called right before the client is destroyed.
+ virtual void OnWillDestroyClient(WindowTreeClient* client) {}
+
+ protected:
+ virtual ~WindowTreeClientObserver() {}
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_PUBLIC_CPP_WINDOW_TREE_CLIENT_OBSERVER_H_
diff --git a/chromium/components/mus/public/cpp/window_tree_host_factory.h b/chromium/components/mus/public/cpp/window_tree_host_factory.h
new file mode 100644
index 00000000000..75661af89d6
--- /dev/null
+++ b/chromium/components/mus/public/cpp/window_tree_host_factory.h
@@ -0,0 +1,37 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_PUBLIC_CPP_WINDOW_TREE_HOST_FACTORY_H_
+#define COMPONENTS_MUS_PUBLIC_CPP_WINDOW_TREE_HOST_FACTORY_H_
+
+#include <memory>
+
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "components/mus/public/interfaces/window_tree_host.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace shell {
+class Connector;
+}
+
+namespace mus {
+
+class WindowManagerDelegate;
+class WindowTreeClientDelegate;
+
+// The following create a new window tree host. Supply a |factory| if you have
+// already connected to mus, otherwise supply |shell|, which contacts mus and
+// obtains a WindowTreeHostFactory.
+void CreateWindowTreeHost(mojom::WindowTreeHostFactory* factory,
+ WindowTreeClientDelegate* delegate,
+ mojom::WindowTreeHostPtr* host,
+ WindowManagerDelegate* window_manager_delegate);
+void CreateWindowTreeHost(shell::Connector* connector,
+ WindowTreeClientDelegate* delegate,
+ mojom::WindowTreeHostPtr* host,
+ WindowManagerDelegate* window_manager_delegate);
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_PUBLIC_CPP_WINDOW_TREE_HOST_FACTORY_H_
diff --git a/chromium/components/mus/public/interfaces/OWNERS b/chromium/components/mus/public/interfaces/OWNERS
new file mode 100644
index 00000000000..08850f42120
--- /dev/null
+++ b/chromium/components/mus/public/interfaces/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chromium/components/mus/public/interfaces/gpu/OWNERS b/chromium/components/mus/public/interfaces/gpu/OWNERS
new file mode 100644
index 00000000000..08850f42120
--- /dev/null
+++ b/chromium/components/mus/public/interfaces/gpu/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chromium/components/mus/surfaces/DEPS b/chromium/components/mus/surfaces/DEPS
new file mode 100644
index 00000000000..e21114ea1d7
--- /dev/null
+++ b/chromium/components/mus/surfaces/DEPS
@@ -0,0 +1,10 @@
+include_rules = [
+ "+cc",
+ "+components/display_compositor",
+ "+components/gpu",
+ "+gpu",
+ "+services/shell",
+ "+mojo/common",
+ "+mojo/converters",
+ "+mojo/public",
+]
diff --git a/chromium/components/mus/surfaces/OWNERS b/chromium/components/mus/surfaces/OWNERS
new file mode 100644
index 00000000000..1f1af6bcc54
--- /dev/null
+++ b/chromium/components/mus/surfaces/OWNERS
@@ -0,0 +1,2 @@
+fsamuel@chromium.org
+rjkroege@chromium.org
diff --git a/chromium/components/mus/surfaces/direct_output_surface.cc b/chromium/components/mus/surfaces/direct_output_surface.cc
new file mode 100644
index 00000000000..7c7bc55f4c9
--- /dev/null
+++ b/chromium/components/mus/surfaces/direct_output_surface.cc
@@ -0,0 +1,81 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/surfaces/direct_output_surface.h"
+
+#include <stdint.h>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/output/context_provider.h"
+#include "cc/output/output_surface_client.h"
+#include "cc/scheduler/begin_frame_source.h"
+#include "gpu/command_buffer/client/context_support.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+
+namespace mus {
+
+DirectOutputSurface::DirectOutputSurface(
+ scoped_refptr<SurfacesContextProvider> context_provider,
+ cc::SyntheticBeginFrameSource* synthetic_begin_frame_source)
+ : cc::OutputSurface(context_provider, nullptr, nullptr),
+ synthetic_begin_frame_source_(synthetic_begin_frame_source),
+ weak_ptr_factory_(this) {
+ context_provider->SetDelegate(this);
+}
+
+DirectOutputSurface::~DirectOutputSurface() = default;
+
+bool DirectOutputSurface::BindToClient(cc::OutputSurfaceClient* client) {
+ if (!cc::OutputSurface::BindToClient(client))
+ return false;
+
+ if (capabilities_.uses_default_gl_framebuffer) {
+ capabilities_.flipped_output_surface =
+ context_provider()->ContextCapabilities().flips_vertically;
+ }
+ return true;
+}
+
+void DirectOutputSurface::OnVSyncParametersUpdated(
+ const base::TimeTicks& timebase,
+ const base::TimeDelta& interval) {
+ // TODO(brianderson): We should not be receiving 0 intervals.
+ synthetic_begin_frame_source_->OnUpdateVSyncParameters(
+ timebase,
+ interval.is_zero() ? cc::BeginFrameArgs::DefaultInterval() : interval);
+}
+
+void DirectOutputSurface::SwapBuffers(cc::CompositorFrame frame) {
+ DCHECK(context_provider_);
+ DCHECK(frame.gl_frame_data);
+ if (frame.gl_frame_data->sub_buffer_rect ==
+ gfx::Rect(frame.gl_frame_data->size)) {
+ context_provider_->ContextSupport()->Swap();
+ } else {
+ context_provider_->ContextSupport()->PartialSwapBuffers(
+ frame.gl_frame_data->sub_buffer_rect);
+ }
+
+ gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
+ const GLuint64 fence_sync = gl->InsertFenceSyncCHROMIUM();
+ gl->ShallowFlushCHROMIUM();
+
+ gpu::SyncToken sync_token;
+ gl->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
+
+ context_provider_->ContextSupport()->SignalSyncToken(
+ sync_token, base::Bind(&OutputSurface::OnSwapBuffersComplete,
+ weak_ptr_factory_.GetWeakPtr()));
+ client_->DidSwapBuffers();
+}
+
+uint32_t DirectOutputSurface::GetFramebufferCopyTextureFormat() {
+ // TODO(danakj): What attributes are used for the default framebuffer here?
+ // Can it have alpha? SurfacesContextProvider doesn't take any attributes.
+ return GL_RGB;
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/surfaces/direct_output_surface.h b/chromium/components/mus/surfaces/direct_output_surface.h
new file mode 100644
index 00000000000..6e08485cfbb
--- /dev/null
+++ b/chromium/components/mus/surfaces/direct_output_surface.h
@@ -0,0 +1,47 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_SURFACES_DIRECT_OUTPUT_SURFACE_H_
+#define COMPONENTS_MUS_SURFACES_DIRECT_OUTPUT_SURFACE_H_
+
+#include <memory>
+
+#include "cc/output/output_surface.h"
+#include "components/mus/surfaces/surfaces_context_provider.h"
+#include "components/mus/surfaces/surfaces_context_provider_delegate.h"
+
+namespace cc {
+class CompositorFrame;
+class SyntheticBeginFrameSource;
+}
+
+namespace mus {
+
+// An OutputSurface implementation that directly draws and
+// swaps to an actual GL surface.
+class DirectOutputSurface : public cc::OutputSurface,
+ public SurfacesContextProviderDelegate {
+ public:
+ explicit DirectOutputSurface(
+ scoped_refptr<SurfacesContextProvider> context_provider,
+ cc::SyntheticBeginFrameSource* synthetic_begin_frame_source);
+ ~DirectOutputSurface() override;
+
+ // cc::OutputSurface implementation
+ bool BindToClient(cc::OutputSurfaceClient* client) override;
+ void SwapBuffers(cc::CompositorFrame frame) override;
+ uint32_t GetFramebufferCopyTextureFormat() override;
+
+ // SurfacesContextProviderDelegate implementation
+ void OnVSyncParametersUpdated(const base::TimeTicks& timebase,
+ const base::TimeDelta& interval) override;
+
+ private:
+ cc::SyntheticBeginFrameSource* const synthetic_begin_frame_source_;
+ base::WeakPtrFactory<DirectOutputSurface> weak_ptr_factory_;
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_SURFACES_DIRECT_OUTPUT_SURFACE_H_
diff --git a/chromium/components/mus/surfaces/direct_output_surface_ozone.cc b/chromium/components/mus/surfaces/direct_output_surface_ozone.cc
new file mode 100644
index 00000000000..5148336078a
--- /dev/null
+++ b/chromium/components/mus/surfaces/direct_output_surface_ozone.cc
@@ -0,0 +1,166 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/surfaces/direct_output_surface_ozone.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/output/context_provider.h"
+#include "cc/output/output_surface_client.h"
+#include "cc/scheduler/begin_frame_source.h"
+#include "components/display_compositor/buffer_queue.h"
+#include "components/mus/common/gpu_service.h"
+#include "components/mus/common/mojo_gpu_memory_buffer_manager.h"
+#include "components/mus/gpu/mus_gpu_memory_buffer_manager.h"
+#include "components/mus/surfaces/surfaces_context_provider.h"
+#include "gpu/command_buffer/client/context_support.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+
+using display_compositor::BufferQueue;
+
+namespace mus {
+
+DirectOutputSurfaceOzone::DirectOutputSurfaceOzone(
+ scoped_refptr<SurfacesContextProvider> context_provider,
+ gfx::AcceleratedWidget widget,
+ cc::SyntheticBeginFrameSource* synthetic_begin_frame_source,
+ uint32_t target,
+ uint32_t internalformat)
+ : cc::OutputSurface(context_provider, nullptr, nullptr),
+ gl_helper_(context_provider->ContextGL(),
+ context_provider->ContextSupport()),
+ synthetic_begin_frame_source_(synthetic_begin_frame_source),
+ weak_ptr_factory_(this) {
+ if (!GpuService::UseChromeGpuCommandBuffer()) {
+ ozone_gpu_memory_buffer_manager_.reset(new OzoneGpuMemoryBufferManager());
+ buffer_queue_.reset(new BufferQueue(
+ context_provider->ContextGL(), target, internalformat, &gl_helper_,
+ ozone_gpu_memory_buffer_manager_.get(), widget));
+ } else {
+ buffer_queue_.reset(new BufferQueue(
+ context_provider->ContextGL(), target, internalformat, &gl_helper_,
+ MusGpuMemoryBufferManager::current(), widget));
+ }
+
+ capabilities_.uses_default_gl_framebuffer = false;
+ capabilities_.flipped_output_surface = true;
+ // Set |max_frames_pending| to 2 for surfaceless, which aligns scheduling
+ // more closely with the previous surfaced behavior.
+ // With a surface, swap buffer ack used to return early, before actually
+ // presenting the back buffer, enabling the browser compositor to run ahead.
+ // Surfaceless implementation acks at the time of actual buffer swap, which
+ // shifts the start of the new frame forward relative to the old
+ // implementation.
+ capabilities_.max_frames_pending = 2;
+
+ buffer_queue_->Initialize();
+
+ context_provider->SetSwapBuffersCompletionCallback(
+ base::Bind(&DirectOutputSurfaceOzone::OnGpuSwapBuffersCompleted,
+ base::Unretained(this)));
+}
+
+DirectOutputSurfaceOzone::~DirectOutputSurfaceOzone() {
+ // TODO(rjkroege): Support cleanup.
+}
+
+bool DirectOutputSurfaceOzone::IsDisplayedAsOverlayPlane() const {
+ // TODO(rjkroege): implement remaining overlay functionality.
+ return true;
+}
+
+unsigned DirectOutputSurfaceOzone::GetOverlayTextureId() const {
+ DCHECK(buffer_queue_);
+ return buffer_queue_->current_texture_id();
+}
+
+void DirectOutputSurfaceOzone::SwapBuffers(cc::CompositorFrame frame) {
+ DCHECK(buffer_queue_);
+ DCHECK(frame.gl_frame_data);
+
+ buffer_queue_->SwapBuffers(frame.gl_frame_data->sub_buffer_rect);
+
+ // Code combining GpuBrowserCompositorOutputSurface + DirectOutputSurface
+ if (frame.gl_frame_data->sub_buffer_rect ==
+ gfx::Rect(frame.gl_frame_data->size)) {
+ context_provider_->ContextSupport()->Swap();
+ } else {
+ context_provider_->ContextSupport()->PartialSwapBuffers(
+ frame.gl_frame_data->sub_buffer_rect);
+ }
+
+ gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
+ const GLuint64 fence_sync = gl->InsertFenceSyncCHROMIUM();
+ gl->ShallowFlushCHROMIUM();
+
+ gpu::SyncToken sync_token;
+ gl->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
+
+ client_->DidSwapBuffers();
+}
+
+bool DirectOutputSurfaceOzone::BindToClient(cc::OutputSurfaceClient* client) {
+ if (!cc::OutputSurface::BindToClient(client))
+ return false;
+
+ if (capabilities_.uses_default_gl_framebuffer) {
+ capabilities_.flipped_output_surface =
+ context_provider()->ContextCapabilities().flips_vertically;
+ }
+ return true;
+}
+
+void DirectOutputSurfaceOzone::OnUpdateVSyncParametersFromGpu(
+ base::TimeTicks timebase,
+ base::TimeDelta interval) {
+ DCHECK(HasClient());
+ synthetic_begin_frame_source_->OnUpdateVSyncParameters(timebase, interval);
+}
+
+void DirectOutputSurfaceOzone::OnGpuSwapBuffersCompleted(
+ gfx::SwapResult result) {
+ DCHECK(buffer_queue_);
+ bool force_swap = false;
+ if (result == gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS) {
+ // Even through the swap failed, this is a fixable error so we can pretend
+ // it succeeded to the rest of the system.
+ result = gfx::SwapResult::SWAP_ACK;
+ buffer_queue_->RecreateBuffers();
+ force_swap = true;
+ }
+
+ buffer_queue_->PageFlipComplete();
+ OnSwapBuffersComplete();
+
+ if (force_swap)
+ client_->SetNeedsRedrawRect(gfx::Rect(SurfaceSize()));
+}
+
+void DirectOutputSurfaceOzone::BindFramebuffer() {
+ DCHECK(buffer_queue_);
+ buffer_queue_->BindFramebuffer();
+}
+
+uint32_t DirectOutputSurfaceOzone::GetFramebufferCopyTextureFormat() {
+ return buffer_queue_->internal_format();
+}
+
+// We call this on every frame but changing the size once we've allocated
+// backing NativePixmapOzone instances will cause a DCHECK because
+// Chrome never Reshape(s) after the first one from (0,0). NB: this implies
+// that screen size changes need to be plumbed differently. In particular, we
+// must create the native window in the size that the hardware reports.
+void DirectOutputSurfaceOzone::Reshape(const gfx::Size& size,
+ float scale_factor,
+ const gfx::ColorSpace& color_space,
+ bool alpha) {
+ OutputSurface::Reshape(size, scale_factor, color_space, alpha);
+ DCHECK(buffer_queue_);
+ buffer_queue_->Reshape(SurfaceSize(), scale_factor);
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/surfaces/direct_output_surface_ozone.h b/chromium/components/mus/surfaces/direct_output_surface_ozone.h
new file mode 100644
index 00000000000..8f6f4e4bb76
--- /dev/null
+++ b/chromium/components/mus/surfaces/direct_output_surface_ozone.h
@@ -0,0 +1,84 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_SURFACES_DIRECT_OUTPUT_SURFACE_OZONE_H_
+#define COMPONENTS_MUS_SURFACES_DIRECT_OUTPUT_SURFACE_OZONE_H_
+
+#include <memory>
+
+#include "base/memory/weak_ptr.h"
+#include "cc/output/context_provider.h"
+#include "cc/output/output_surface.h"
+#include "components/display_compositor/gl_helper.h"
+#include "components/mus/surfaces/ozone_gpu_memory_buffer_manager.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/swap_result.h"
+#include "ui/gl/gl_surface.h"
+
+namespace display_compositor {
+class BufferQueue;
+}
+
+namespace ui {
+class LatencyInfo;
+} // namespace ui
+
+namespace cc {
+class CompositorFrame;
+class SyntheticBeginFrameSource;
+} // namespace cc
+
+namespace mus {
+
+class SurfacesContextProvider;
+
+// An OutputSurface implementation that directly draws and swap to a GL
+// "surfaceless" surface (aka one backed by a buffer managed explicitly in
+// mus/ozone. This class is adapted from
+// GpuSurfacelessBrowserCompositorOutputSurface.
+class DirectOutputSurfaceOzone : public cc::OutputSurface {
+ public:
+ DirectOutputSurfaceOzone(
+ scoped_refptr<SurfacesContextProvider> context_provider,
+ gfx::AcceleratedWidget widget,
+ cc::SyntheticBeginFrameSource* synthetic_begin_frame_source,
+ uint32_t target,
+ uint32_t internalformat);
+
+ ~DirectOutputSurfaceOzone() override;
+
+ // TODO(rjkroege): Implement the equivalent of Reflector.
+
+ private:
+ // cc::OutputSurface implementation.
+ void SwapBuffers(cc::CompositorFrame frame) override;
+ void BindFramebuffer() override;
+ uint32_t GetFramebufferCopyTextureFormat() override;
+ void Reshape(const gfx::Size& size,
+ float scale_factor,
+ const gfx::ColorSpace& color_space,
+ bool alpha) override;
+ bool IsDisplayedAsOverlayPlane() const override;
+ unsigned GetOverlayTextureId() const override;
+ bool BindToClient(cc::OutputSurfaceClient* client) override;
+
+ // Taken from BrowserCompositor specific API.
+ void OnUpdateVSyncParametersFromGpu(base::TimeTicks timebase,
+ base::TimeDelta interval);
+
+ // Called when a swap completion is sent from the GPU process.
+ void OnGpuSwapBuffersCompleted(gfx::SwapResult result);
+
+ display_compositor::GLHelper gl_helper_;
+ std::unique_ptr<OzoneGpuMemoryBufferManager> ozone_gpu_memory_buffer_manager_;
+ std::unique_ptr<display_compositor::BufferQueue> buffer_queue_;
+ cc::SyntheticBeginFrameSource* const synthetic_begin_frame_source_;
+
+ base::WeakPtrFactory<DirectOutputSurfaceOzone> weak_ptr_factory_;
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_SURFACES_DIRECT_OUTPUT_SURFACE_OZONE_H_
diff --git a/chromium/components/mus/surfaces/display_compositor.cc b/chromium/components/mus/surfaces/display_compositor.cc
new file mode 100644
index 00000000000..8ffa0b86276
--- /dev/null
+++ b/chromium/components/mus/surfaces/display_compositor.cc
@@ -0,0 +1,124 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/surfaces/display_compositor.h"
+
+#include "cc/output/copy_output_request.h"
+#include "cc/output/output_surface.h"
+#include "cc/output/renderer_settings.h"
+#include "cc/output/texture_mailbox_deleter.h"
+#include "cc/scheduler/begin_frame_source.h"
+#include "cc/scheduler/delay_based_time_source.h"
+#include "cc/surfaces/display.h"
+#include "cc/surfaces/display_scheduler.h"
+#include "components/mus/surfaces/direct_output_surface.h"
+#include "components/mus/surfaces/surfaces_context_provider.h"
+
+#if defined(USE_OZONE)
+#include "components/mus/surfaces/direct_output_surface_ozone.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#endif
+
+namespace mus {
+
+DisplayCompositor::DisplayCompositor(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ gfx::AcceleratedWidget widget,
+ const scoped_refptr<GpuState>& gpu_state,
+ const scoped_refptr<SurfacesState>& surfaces_state)
+ : task_runner_(task_runner),
+ surfaces_state_(surfaces_state),
+ factory_(surfaces_state->manager(), this),
+ allocator_(surfaces_state->next_id_namespace()) {
+ allocator_.RegisterSurfaceIdNamespace(surfaces_state_->manager());
+ surfaces_state_->manager()->RegisterSurfaceFactoryClient(
+ allocator_.id_namespace(), this);
+
+ scoped_refptr<SurfacesContextProvider> surfaces_context_provider(
+ new SurfacesContextProvider(widget, gpu_state));
+ // TODO(rjkroege): If there is something better to do than CHECK, add it.
+ CHECK(surfaces_context_provider->BindToCurrentThread());
+
+ std::unique_ptr<cc::SyntheticBeginFrameSource> synthetic_begin_frame_source(
+ new cc::DelayBasedBeginFrameSource(
+ base::MakeUnique<cc::DelayBasedTimeSource>(task_runner_.get())));
+
+ std::unique_ptr<cc::OutputSurface> display_output_surface;
+ if (surfaces_context_provider->ContextCapabilities().surfaceless) {
+#if defined(USE_OZONE)
+ display_output_surface = base::WrapUnique(new DirectOutputSurfaceOzone(
+ surfaces_context_provider, widget, synthetic_begin_frame_source.get(),
+ GL_TEXTURE_2D, GL_RGB));
+#else
+ NOTREACHED();
+#endif
+ } else {
+ display_output_surface = base::WrapUnique(new DirectOutputSurface(
+ surfaces_context_provider, synthetic_begin_frame_source.get()));
+ }
+
+ int max_frames_pending =
+ display_output_surface->capabilities().max_frames_pending;
+ DCHECK_GT(max_frames_pending, 0);
+
+ std::unique_ptr<cc::DisplayScheduler> scheduler(
+ new cc::DisplayScheduler(synthetic_begin_frame_source.get(),
+ task_runner_.get(), max_frames_pending));
+
+ display_.reset(new cc::Display(
+ surfaces_state_->manager(), nullptr /* bitmap_manager */,
+ nullptr /* gpu_memory_buffer_manager */, cc::RendererSettings(),
+ allocator_.id_namespace(), std::move(synthetic_begin_frame_source),
+ std::move(display_output_surface), std::move(scheduler),
+ base::MakeUnique<cc::TextureMailboxDeleter>(task_runner_.get())));
+ display_->Initialize(this);
+}
+
+DisplayCompositor::~DisplayCompositor() {
+ surfaces_state_->manager()->UnregisterSurfaceFactoryClient(
+ allocator_.id_namespace());
+}
+
+void DisplayCompositor::SubmitCompositorFrame(
+ cc::CompositorFrame frame,
+ const base::Callback<void(cc::SurfaceDrawStatus)>& callback) {
+ gfx::Size frame_size =
+ frame.delegated_frame_data->render_pass_list.back()->output_rect.size();
+ if (frame_size.IsEmpty() || frame_size != display_size_) {
+ if (!surface_id_.is_null())
+ factory_.Destroy(surface_id_);
+ surface_id_ = allocator_.GenerateId();
+ factory_.Create(surface_id_);
+ display_size_ = frame_size;
+ display_->Resize(display_size_);
+ }
+ display_->SetSurfaceId(surface_id_, frame.metadata.device_scale_factor);
+ factory_.SubmitCompositorFrame(surface_id_, std::move(frame), callback);
+}
+
+void DisplayCompositor::RequestCopyOfOutput(
+ std::unique_ptr<cc::CopyOutputRequest> output_request) {
+ factory_.RequestCopyOfSurface(surface_id_, std::move(output_request));
+}
+
+void DisplayCompositor::ReturnResources(
+ const cc::ReturnedResourceArray& resources) {
+ // TODO(fsamuel): Implement this.
+}
+
+void DisplayCompositor::SetBeginFrameSource(
+ cc::BeginFrameSource* begin_frame_source) {
+ // TODO(fsamuel): Implement this.
+}
+
+void DisplayCompositor::DisplayOutputSurfaceLost() {
+ // TODO(fsamuel): This looks like it would crash if a frame was in flight and
+ // will be submitted.
+ display_.reset();
+}
+
+void DisplayCompositor::DisplaySetMemoryPolicy(
+ const cc::ManagedMemoryPolicy& policy) {}
+
+} // namespace mus
diff --git a/chromium/components/mus/surfaces/display_compositor.h b/chromium/components/mus/surfaces/display_compositor.h
new file mode 100644
index 00000000000..27d1eebf3cf
--- /dev/null
+++ b/chromium/components/mus/surfaces/display_compositor.h
@@ -0,0 +1,80 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_SURFACES_DISPLAY_COMPOSITOR_H_
+#define COMPONENTS_MUS_SURFACES_DISPLAY_COMPOSITOR_H_
+
+#include "cc/surfaces/display_client.h"
+#include "cc/surfaces/surface.h"
+#include "cc/surfaces/surface_factory.h"
+#include "cc/surfaces/surface_factory_client.h"
+#include "cc/surfaces/surface_id_allocator.h"
+#include "components/mus/gles2/gpu_state.h"
+#include "components/mus/surfaces/surfaces_state.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace cc {
+class Display;
+}
+
+namespace mus {
+
+// TODO(fsamuel): This should become a mojo interface for the mus-gpu split.
+// TODO(fsamuel): This should not be a SurfaceFactoryClient.
+// The DisplayCompositor receives CompositorFrames from all sources,
+// creates a top-level CompositorFrame once per tick, and generates graphical
+// output.
+class DisplayCompositor : public cc::SurfaceFactoryClient,
+ public cc::DisplayClient {
+ public:
+ DisplayCompositor(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ gfx::AcceleratedWidget widget,
+ const scoped_refptr<GpuState>& gpu_state,
+ const scoped_refptr<SurfacesState>& surfaces_state);
+ ~DisplayCompositor() override;
+
+ // DisplayCompositor embedders submit a CompositorFrame when content on the
+ // display should be changed. A well-behaving embedder should only submit
+ // a CompositorFrame once per BeginFrame tick. The callback is called the
+ // first time this frame is used to draw, or if the frame is discarded.
+ void SubmitCompositorFrame(
+ cc::CompositorFrame frame,
+ const base::Callback<void(cc::SurfaceDrawStatus)>& callback);
+
+ // TODO(fsamuel): This is used for surface hittesting and should not be
+ // exposed outside of DisplayCompositor.
+ const cc::SurfaceId& surface_id() const { return surface_id_; }
+
+ // This requests the display CompositorFrame be rendered and given to the
+ // callback within CopyOutputRequest.
+ void RequestCopyOfOutput(
+ std::unique_ptr<cc::CopyOutputRequest> output_request);
+
+ // TODO(fsamuel): Invent an async way to create a SurfaceNamespace
+ // A SurfaceNamespace can create CompositorFrameSinks where the client can
+ // make up the ID.
+
+ private:
+ // SurfaceFactoryClient implementation.
+ void ReturnResources(const cc::ReturnedResourceArray& resources) override;
+ void SetBeginFrameSource(cc::BeginFrameSource* begin_frame_source) override;
+
+ // DisplayClient implementation.
+ void DisplayOutputSurfaceLost() override;
+ void DisplaySetMemoryPolicy(const cc::ManagedMemoryPolicy& policy) override;
+
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ scoped_refptr<SurfacesState> surfaces_state_;
+ cc::SurfaceFactory factory_;
+ cc::SurfaceIdAllocator allocator_;
+ cc::SurfaceId surface_id_;
+
+ gfx::Size display_size_;
+ std::unique_ptr<cc::Display> display_;
+ DISALLOW_COPY_AND_ASSIGN(DisplayCompositor);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_SURFACES_DISPLAY_COMPOSITOR_H_
diff --git a/chromium/components/mus/surfaces/ozone_gpu_memory_buffer_manager.cc b/chromium/components/mus/surfaces/ozone_gpu_memory_buffer_manager.cc
new file mode 100644
index 00000000000..b14cd1394fe
--- /dev/null
+++ b/chromium/components/mus/surfaces/ozone_gpu_memory_buffer_manager.cc
@@ -0,0 +1,49 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/surfaces/ozone_gpu_memory_buffer_manager.h"
+
+#include "components/mus/gles2/ozone_gpu_memory_buffer.h"
+#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
+#include "ui/gfx/buffer_types.h"
+
+namespace mus {
+
+OzoneGpuMemoryBufferManager::OzoneGpuMemoryBufferManager() {}
+
+OzoneGpuMemoryBufferManager::~OzoneGpuMemoryBufferManager() {}
+
+std::unique_ptr<gfx::GpuMemoryBuffer>
+OzoneGpuMemoryBufferManager::AllocateGpuMemoryBuffer(
+ const gfx::Size& size,
+ gfx::BufferFormat format,
+ gfx::BufferUsage usage,
+ gpu::SurfaceHandle surface_handle) {
+ return OzoneGpuMemoryBuffer::CreateOzoneGpuMemoryBuffer(
+ size, format, gfx::BufferUsage::SCANOUT, surface_handle);
+}
+
+std::unique_ptr<gfx::GpuMemoryBuffer>
+OzoneGpuMemoryBufferManager::CreateGpuMemoryBufferFromHandle(
+ const gfx::GpuMemoryBufferHandle& handle,
+ const gfx::Size& size,
+ gfx::BufferFormat format) {
+ NOTIMPLEMENTED();
+ return nullptr;
+}
+
+gfx::GpuMemoryBuffer*
+OzoneGpuMemoryBufferManager::GpuMemoryBufferFromClientBuffer(
+ ClientBuffer buffer) {
+ NOTIMPLEMENTED();
+ return nullptr;
+}
+
+void OzoneGpuMemoryBufferManager::SetDestructionSyncToken(
+ gfx::GpuMemoryBuffer* buffer,
+ const gpu::SyncToken& sync_token) {
+ NOTIMPLEMENTED();
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/surfaces/ozone_gpu_memory_buffer_manager.h b/chromium/components/mus/surfaces/ozone_gpu_memory_buffer_manager.h
new file mode 100644
index 00000000000..d6cf15f55fe
--- /dev/null
+++ b/chromium/components/mus/surfaces/ozone_gpu_memory_buffer_manager.h
@@ -0,0 +1,39 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_SURFACES_DIRECT_OUTPUT_SURFACES_OZONE_H_
+#define COMPONENTS_MUS_SURFACES_DIRECT_OUTPUT_SURFACES_OZONE_H_
+
+#include "base/macros.h"
+#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
+
+namespace mus {
+
+class OzoneGpuMemoryBufferManager : public gpu::GpuMemoryBufferManager {
+ public:
+ OzoneGpuMemoryBufferManager();
+ ~OzoneGpuMemoryBufferManager() override;
+
+ // gpu::GpuMemoryBufferManager:
+ std::unique_ptr<gfx::GpuMemoryBuffer> AllocateGpuMemoryBuffer(
+ const gfx::Size& size,
+ gfx::BufferFormat format,
+ gfx::BufferUsage usage,
+ gpu::SurfaceHandle surface_handle) override;
+ std::unique_ptr<gfx::GpuMemoryBuffer> CreateGpuMemoryBufferFromHandle(
+ const gfx::GpuMemoryBufferHandle& handle,
+ const gfx::Size& size,
+ gfx::BufferFormat format) override;
+ gfx::GpuMemoryBuffer* GpuMemoryBufferFromClientBuffer(
+ ClientBuffer buffer) override;
+ void SetDestructionSyncToken(gfx::GpuMemoryBuffer* buffer,
+ const gpu::SyncToken& sync_token) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(OzoneGpuMemoryBufferManager);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_SURFACES_DIRECT_OUTPUT_SURFACES_OZONE_H_
diff --git a/chromium/components/mus/surfaces/surfaces_context_provider.cc b/chromium/components/mus/surfaces/surfaces_context_provider.cc
new file mode 100644
index 00000000000..291a4402eb5
--- /dev/null
+++ b/chromium/components/mus/surfaces/surfaces_context_provider.cc
@@ -0,0 +1,206 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/surfaces/surfaces_context_provider.h"
+
+#include <stddef.h>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
+#include "components/mus/common/switches.h"
+#include "components/mus/gles2/command_buffer_driver.h"
+#include "components/mus/gles2/command_buffer_impl.h"
+#include "components/mus/gles2/command_buffer_local.h"
+#include "components/mus/gles2/gpu_state.h"
+#include "components/mus/gpu/gpu_service_mus.h"
+#include "components/mus/surfaces/surfaces_context_provider_delegate.h"
+#include "gpu/command_buffer/client/gles2_cmd_helper.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
+#include "gpu/command_buffer/client/shared_memory_limits.h"
+#include "gpu/command_buffer/client/transfer_buffer.h"
+#include "gpu/ipc/client/command_buffer_proxy_impl.h"
+#include "ui/gl/gpu_preference.h"
+
+namespace mus {
+
+SurfacesContextProvider::SurfacesContextProvider(
+ gfx::AcceleratedWidget widget,
+ const scoped_refptr<GpuState>& state)
+ : use_chrome_gpu_command_buffer_(false),
+ delegate_(nullptr),
+ widget_(widget),
+ command_buffer_local_(nullptr) {
+// TODO(penghuang): Kludge: Use mojo command buffer when running on Windows
+// since Chrome command buffer breaks unit tests
+#if defined(OS_WIN)
+ use_chrome_gpu_command_buffer_ = false;
+#else
+ use_chrome_gpu_command_buffer_ =
+ !base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kUseMojoGpuCommandBufferInMus);
+#endif
+ if (!use_chrome_gpu_command_buffer_) {
+ command_buffer_local_ = new CommandBufferLocal(this, widget_, state);
+ } else {
+ GpuServiceMus* service = GpuServiceMus::GetInstance();
+ gpu::CommandBufferProxyImpl* shared_command_buffer = nullptr;
+ gpu::GpuStreamId stream_id = gpu::GpuStreamId::GPU_STREAM_DEFAULT;
+ gpu::GpuStreamPriority stream_priority = gpu::GpuStreamPriority::NORMAL;
+ gpu::gles2::ContextCreationAttribHelper attributes;
+ attributes.alpha_size = -1;
+ attributes.depth_size = 0;
+ attributes.stencil_size = 0;
+ attributes.samples = 0;
+ attributes.sample_buffers = 0;
+ attributes.bind_generates_resource = false;
+ attributes.lose_context_when_out_of_memory = true;
+ GURL active_url;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+ base::ThreadTaskRunnerHandle::Get();
+ command_buffer_proxy_impl_ = gpu::CommandBufferProxyImpl::Create(
+ service->gpu_channel_local(), widget, shared_command_buffer, stream_id,
+ stream_priority, attributes, active_url, task_runner);
+ command_buffer_proxy_impl_->SetSwapBuffersCompletionCallback(
+ base::Bind(&SurfacesContextProvider::OnGpuSwapBuffersCompleted,
+ base::Unretained(this)));
+ command_buffer_proxy_impl_->SetUpdateVSyncParametersCallback(
+ base::Bind(&SurfacesContextProvider::OnUpdateVSyncParameters,
+ base::Unretained(this)));
+ }
+}
+
+void SurfacesContextProvider::SetDelegate(
+ SurfacesContextProviderDelegate* delegate) {
+ DCHECK(!delegate_);
+ delegate_ = delegate;
+}
+
+// This routine needs to be safe to call more than once.
+// This is called when we have an accelerated widget.
+bool SurfacesContextProvider::BindToCurrentThread() {
+ if (implementation_)
+ return true;
+
+ // SurfacesContextProvider should always live on the same thread as the
+ // Window Manager.
+ DCHECK(CalledOnValidThread());
+ gpu::GpuControl* gpu_control = nullptr;
+ gpu::CommandBuffer* command_buffer = nullptr;
+ if (!use_chrome_gpu_command_buffer_) {
+ if (!command_buffer_local_->Initialize())
+ return false;
+ gpu_control = command_buffer_local_;
+ command_buffer = command_buffer_local_;
+ } else {
+ if (!command_buffer_proxy_impl_)
+ return false;
+ gpu_control = command_buffer_proxy_impl_.get();
+ command_buffer = command_buffer_proxy_impl_.get();
+ }
+
+ gles2_helper_.reset(new gpu::gles2::GLES2CmdHelper(command_buffer));
+ constexpr gpu::SharedMemoryLimits default_limits;
+ if (!gles2_helper_->Initialize(default_limits.command_buffer_size))
+ return false;
+ gles2_helper_->SetAutomaticFlushes(false);
+ transfer_buffer_.reset(new gpu::TransferBuffer(gles2_helper_.get()));
+ capabilities_ = gpu_control->GetCapabilities();
+ bool bind_generates_resource =
+ !!capabilities_.bind_generates_resource_chromium;
+ // TODO(piman): Some contexts (such as compositor) want this to be true, so
+ // this needs to be a public parameter.
+ bool lose_context_when_out_of_memory = false;
+ bool support_client_side_arrays = false;
+ implementation_.reset(new gpu::gles2::GLES2Implementation(
+ gles2_helper_.get(), NULL, transfer_buffer_.get(),
+ bind_generates_resource, lose_context_when_out_of_memory,
+ support_client_side_arrays, gpu_control));
+ return implementation_->Initialize(
+ default_limits.start_transfer_buffer_size,
+ default_limits.min_transfer_buffer_size,
+ default_limits.max_transfer_buffer_size,
+ default_limits.mapped_memory_reclaim_limit);
+}
+
+gpu::gles2::GLES2Interface* SurfacesContextProvider::ContextGL() {
+ DCHECK(implementation_);
+ return implementation_.get();
+}
+
+gpu::ContextSupport* SurfacesContextProvider::ContextSupport() {
+ return implementation_.get();
+}
+
+class GrContext* SurfacesContextProvider::GrContext() {
+ return NULL;
+}
+
+void SurfacesContextProvider::InvalidateGrContext(uint32_t state) {}
+
+gpu::Capabilities SurfacesContextProvider::ContextCapabilities() {
+ return capabilities_;
+}
+
+base::Lock* SurfacesContextProvider::GetLock() {
+ // This context provider is not used on multiple threads.
+ NOTREACHED();
+ return nullptr;
+}
+
+void SurfacesContextProvider::SetLostContextCallback(
+ const LostContextCallback& lost_context_callback) {
+ implementation_->SetLostContextCallback(lost_context_callback);
+}
+
+SurfacesContextProvider::~SurfacesContextProvider() {
+ implementation_->Flush();
+ implementation_.reset();
+ transfer_buffer_.reset();
+ gles2_helper_.reset();
+ command_buffer_proxy_impl_.reset();
+ if (command_buffer_local_) {
+ command_buffer_local_->Destroy();
+ command_buffer_local_ = nullptr;
+ }
+}
+
+void SurfacesContextProvider::UpdateVSyncParameters(
+ const base::TimeTicks& timebase,
+ const base::TimeDelta& interval) {
+ if (delegate_)
+ delegate_->OnVSyncParametersUpdated(timebase, interval);
+}
+
+void SurfacesContextProvider::GpuCompletedSwapBuffers(gfx::SwapResult result) {
+ if (!swap_buffers_completion_callback_.is_null()) {
+ swap_buffers_completion_callback_.Run(result);
+ }
+}
+
+void SurfacesContextProvider::OnGpuSwapBuffersCompleted(
+ const std::vector<ui::LatencyInfo>& latency_info,
+ gfx::SwapResult result,
+ const gpu::GpuProcessHostedCALayerTreeParamsMac* params_mac) {
+ if (!swap_buffers_completion_callback_.is_null()) {
+ swap_buffers_completion_callback_.Run(result);
+ }
+}
+
+void SurfacesContextProvider::OnUpdateVSyncParameters(
+ base::TimeTicks timebase,
+ base::TimeDelta interval) {
+ if (delegate_)
+ delegate_->OnVSyncParametersUpdated(timebase, interval);
+}
+
+void SurfacesContextProvider::SetSwapBuffersCompletionCallback(
+ gl::GLSurface::SwapCompletionCallback callback) {
+ swap_buffers_completion_callback_ = callback;
+}
+
+} // namespace mus
diff --git a/chromium/components/mus/surfaces/surfaces_context_provider.h b/chromium/components/mus/surfaces/surfaces_context_provider.h
new file mode 100644
index 00000000000..8dab0bcb9d6
--- /dev/null
+++ b/chromium/components/mus/surfaces/surfaces_context_provider.h
@@ -0,0 +1,109 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_SURFACES_SURFACES_CONTEXT_PROVIDER_H_
+#define COMPONENTS_MUS_SURFACES_SURFACES_CONTEXT_PROVIDER_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/threading/non_thread_safe.h"
+#include "cc/output/context_provider.h"
+#include "components/mus/gles2/command_buffer_local_client.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gl/gl_surface.h"
+
+namespace gpu {
+
+class CommandBufferProxyImpl;
+struct GpuProcessHostedCALayerTreeParamsMac;
+class TransferBuffer;
+
+namespace gles2 {
+class GLES2CmdHelper;
+class GLES2Implementation;
+}
+
+} // namespace gpu
+
+namespace ui {
+class LatencyInfo;
+}
+
+namespace mus {
+
+class CommandBufferDriver;
+class CommandBufferImpl;
+class CommandBufferLocal;
+class GpuState;
+class SurfacesContextProviderDelegate;
+
+class SurfacesContextProvider : public cc::ContextProvider,
+ public CommandBufferLocalClient,
+ public base::NonThreadSafe {
+ public:
+ SurfacesContextProvider(gfx::AcceleratedWidget widget,
+ const scoped_refptr<GpuState>& state);
+
+ void SetDelegate(SurfacesContextProviderDelegate* delegate);
+
+ // cc::ContextProvider implementation.
+ bool BindToCurrentThread() override;
+ gpu::gles2::GLES2Interface* ContextGL() override;
+ gpu::ContextSupport* ContextSupport() override;
+ class GrContext* GrContext() override;
+ void InvalidateGrContext(uint32_t state) override;
+ gpu::Capabilities ContextCapabilities() override;
+ void DeleteCachedResources() override {}
+ void SetLostContextCallback(
+ const LostContextCallback& lost_context_callback) override;
+ base::Lock* GetLock() override;
+
+ // SurfacesContextProvider API.
+ void SetSwapBuffersCompletionCallback(
+ gl::GLSurface::SwapCompletionCallback callback);
+
+ protected:
+ friend class base::RefCountedThreadSafe<SurfacesContextProvider>;
+ ~SurfacesContextProvider() override;
+
+ private:
+ // CommandBufferLocalClient:
+ void UpdateVSyncParameters(const base::TimeTicks& timebase,
+ const base::TimeDelta& interval) override;
+ void GpuCompletedSwapBuffers(gfx::SwapResult result) override;
+
+ // Callbacks for CommandBufferProxyImpl:
+ void OnGpuSwapBuffersCompleted(
+ const std::vector<ui::LatencyInfo>& latency_info,
+ gfx::SwapResult result,
+ const gpu::GpuProcessHostedCALayerTreeParamsMac* params_mac);
+ void OnUpdateVSyncParameters(base::TimeTicks timebase,
+ base::TimeDelta interval);
+
+ bool use_chrome_gpu_command_buffer_;
+
+ // From GLES2Context:
+ // Initialized in BindToCurrentThread.
+ std::unique_ptr<gpu::gles2::GLES2CmdHelper> gles2_helper_;
+ std::unique_ptr<gpu::TransferBuffer> transfer_buffer_;
+ std::unique_ptr<gpu::gles2::GLES2Implementation> implementation_;
+
+ gpu::Capabilities capabilities_;
+ LostContextCallback lost_context_callback_;
+
+ SurfacesContextProviderDelegate* delegate_;
+ gfx::AcceleratedWidget widget_;
+ CommandBufferLocal* command_buffer_local_;
+ std::unique_ptr<gpu::CommandBufferProxyImpl> command_buffer_proxy_impl_;
+ gl::GLSurface::SwapCompletionCallback swap_buffers_completion_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(SurfacesContextProvider);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_SURFACES_SURFACES_CONTEXT_PROVIDER_H_
diff --git a/chromium/components/mus/surfaces/surfaces_context_provider_delegate.h b/chromium/components/mus/surfaces/surfaces_context_provider_delegate.h
new file mode 100644
index 00000000000..170190a923f
--- /dev/null
+++ b/chromium/components/mus/surfaces/surfaces_context_provider_delegate.h
@@ -0,0 +1,28 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_SURFACES_SURFACES_CONTEXT_PROVIDER_DELEGATE_H_
+#define COMPONENTS_MUS_SURFACES_SURFACES_CONTEXT_PROVIDER_DELEGATE_H_
+
+#include <stdint.h>
+
+namespace base {
+class TimeDelta;
+class TimeTicks;
+}
+
+namespace mus {
+
+class SurfacesContextProviderDelegate {
+ public:
+ virtual void OnVSyncParametersUpdated(const base::TimeTicks& timebase,
+ const base::TimeDelta& interval) = 0;
+
+ protected:
+ virtual ~SurfacesContextProviderDelegate() {}
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_SURFACES_SURFACES_CONTEXT_PROVIDER_DELEGATE_H_
diff --git a/chromium/components/mus/surfaces/surfaces_state.cc b/chromium/components/mus/surfaces/surfaces_state.cc
new file mode 100644
index 00000000000..e3dfeb8d85d
--- /dev/null
+++ b/chromium/components/mus/surfaces/surfaces_state.cc
@@ -0,0 +1,13 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/surfaces/surfaces_state.h"
+
+namespace mus {
+
+SurfacesState::SurfacesState() : next_id_namespace_(1u) {}
+
+SurfacesState::~SurfacesState() {}
+
+} // namespace mus
diff --git a/chromium/components/mus/surfaces/surfaces_state.h b/chromium/components/mus/surfaces/surfaces_state.h
new file mode 100644
index 00000000000..daaf5587ab6
--- /dev/null
+++ b/chromium/components/mus/surfaces/surfaces_state.h
@@ -0,0 +1,49 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_SURFACES_SURFACES_STATE_H_
+#define COMPONENTS_MUS_SURFACES_SURFACES_STATE_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "cc/surfaces/surface_manager.h"
+
+namespace cc {
+class SurfaceHittest;
+class SurfaceManager;
+} // namespace cc
+
+namespace mus {
+
+// The SurfacesState object is an object global to the Window Manager app that
+// holds the SurfaceManager and allocates new Surfaces namespaces.
+// This object lives on the main thread of the Window Manager.
+// TODO(rjkroege, fsamuel): This object will need to change to support multiple
+// displays.
+class SurfacesState : public base::RefCounted<SurfacesState> {
+ public:
+ SurfacesState();
+
+ uint32_t next_id_namespace() { return next_id_namespace_++; }
+
+ cc::SurfaceManager* manager() { return &manager_; }
+
+ private:
+ friend class base::RefCounted<SurfacesState>;
+ ~SurfacesState();
+
+ // A Surface ID is an unsigned 64-bit int where the high 32-bits are generated
+ // by the Surfaces service, and the low 32-bits are generated by the process
+ // that requested the Surface.
+ uint32_t next_id_namespace_;
+ cc::SurfaceManager manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(SurfacesState);
+};
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_SURFACES_SURFACES_STATE_H_
diff --git a/chromium/components/mus/test_wm/manifest.json b/chromium/components/mus/test_wm/manifest.json
new file mode 100644
index 00000000000..67f2fa96671
--- /dev/null
+++ b/chromium/components/mus/test_wm/manifest.json
@@ -0,0 +1,10 @@
+{
+ "manifest_version": 1,
+ "name": "mojo:test_wm",
+ "display_name": "Test Window Manager",
+ "capabilities": {
+ "required": {
+ "mojo:mus": { "interfaces": [ "mus::mojom::WindowManagerWindowTreeFactory" ] }
+ }
+ }
+}
diff --git a/chromium/components/mus/test_wm/test_wm.cc b/chromium/components/mus/test_wm/test_wm.cc
new file mode 100644
index 00000000000..39ad3759839
--- /dev/null
+++ b/chromium/components/mus/test_wm/test_wm.cc
@@ -0,0 +1,107 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/public/cpp/window.h"
+#include "components/mus/public/cpp/window_manager_delegate.h"
+#include "components/mus/public/cpp/window_tree_client.h"
+#include "components/mus/public/cpp/window_tree_client_delegate.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/shell/public/cpp/application_runner.h"
+#include "services/shell/public/cpp/connector.h"
+#include "services/shell/public/cpp/shell_client.h"
+#include "ui/display/display.h"
+#include "ui/display/mojo/display_type_converters.h"
+
+namespace mus {
+namespace test {
+
+class TestWM : public shell::ShellClient,
+ public mus::WindowTreeClientDelegate,
+ public mus::WindowManagerDelegate {
+ public:
+ TestWM() {}
+ ~TestWM() override { delete window_tree_client_; }
+
+ private:
+ // shell::ShellClient:
+ void Initialize(shell::Connector* connector,
+ const shell::Identity& identity,
+ uint32_t id) override {
+ window_tree_client_ = new mus::WindowTreeClient(this, this, nullptr);
+ window_tree_client_->ConnectAsWindowManager(connector);
+ }
+ bool AcceptConnection(shell::Connection* connection) override {
+ return true;
+ }
+
+ // mus::WindowTreeClientDelegate:
+ void OnEmbed(mus::Window* root) override {
+ // WindowTreeClients configured as the window manager should never get
+ // OnEmbed().
+ NOTREACHED();
+ }
+ void OnWindowTreeClientDestroyed(mus::WindowTreeClient* client) override {
+ window_tree_client_ = nullptr;
+ }
+ void OnEventObserved(const ui::Event& event, mus::Window* target) override {
+ // Don't care.
+ }
+
+ // mus::WindowManagerDelegate:
+ void SetWindowManagerClient(mus::WindowManagerClient* client) override {
+ window_manager_client_ = client;
+ }
+ bool OnWmSetBounds(mus::Window* window, gfx::Rect* bounds) override {
+ return true;
+ }
+ bool OnWmSetProperty(
+ mus::Window* window,
+ const std::string& name,
+ std::unique_ptr<std::vector<uint8_t>>* new_data) override {
+ return true;
+ }
+ mus::Window* OnWmCreateTopLevelWindow(
+ std::map<std::string, std::vector<uint8_t>>* properties) override {
+ mus::Window* window = root_->window_tree()->NewWindow(properties);
+ window->SetBounds(gfx::Rect(10, 10, 500, 500));
+ root_->AddChild(window);
+ return window;
+ }
+ void OnWmClientJankinessChanged(const std::set<Window*>& client_windows,
+ bool janky) override {
+ // Don't care.
+ }
+ void OnWmNewDisplay(Window* window,
+ const display::Display& display) override {
+ // Only handles a single root.
+ DCHECK(!root_);
+ root_ = window;
+ DCHECK(window_manager_client_);
+ window_manager_client_->AddActivationParent(root_);
+ mus::mojom::FrameDecorationValuesPtr frame_decoration_values =
+ mus::mojom::FrameDecorationValues::New();
+ frame_decoration_values->max_title_bar_button_width = 0;
+ window_manager_client_->SetFrameDecorationValues(
+ std::move(frame_decoration_values));
+ }
+ void OnAccelerator(uint32_t id, const ui::Event& event) override {
+ // Don't care.
+ }
+
+ mus::Window* root_ = nullptr;
+ mus::WindowManagerClient* window_manager_client_ = nullptr;
+ // See WindowTreeClient for details on ownership.
+ mus::WindowTreeClient* window_tree_client_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(TestWM);
+};
+
+} // namespace test
+} // namespace mus
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+ shell::ApplicationRunner runner(new mus::test::TestWM);
+ return runner.Run(shell_handle);
+}
diff --git a/chromium/components/mus/ws/accelerator.cc b/chromium/components/mus/ws/accelerator.cc
new file mode 100644
index 00000000000..fd8cde39668
--- /dev/null
+++ b/chromium/components/mus/ws/accelerator.cc
@@ -0,0 +1,33 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/accelerator.h"
+
+namespace mus {
+namespace ws {
+
+Accelerator::Accelerator(uint32_t id, const mojom::EventMatcher& matcher)
+ : id_(id),
+ accelerator_phase_(matcher.accelerator_phase),
+ event_matcher_(matcher),
+ weak_factory_(this) {}
+
+Accelerator::~Accelerator() {}
+
+bool Accelerator::MatchesEvent(const ui::Event& event,
+ const ui::mojom::AcceleratorPhase phase) const {
+ if (accelerator_phase_ != phase)
+ return false;
+ if (!event_matcher_.MatchesEvent(event))
+ return false;
+ return true;
+}
+
+bool Accelerator::EqualEventMatcher(const Accelerator* other) const {
+ return accelerator_phase_ == other->accelerator_phase_ &&
+ event_matcher_.Equals(other->event_matcher_);
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/accelerator.h b/chromium/components/mus/ws/accelerator.h
new file mode 100644
index 00000000000..2113ad1ec9b
--- /dev/null
+++ b/chromium/components/mus/ws/accelerator.h
@@ -0,0 +1,57 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_ACCELERATOR_H_
+#define COMPONENTS_MUS_WS_ACCELERATOR_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/mus/public/interfaces/event_matcher.mojom.h"
+#include "components/mus/ws/event_matcher.h"
+
+namespace ui {
+class Event;
+}
+
+namespace mus {
+namespace ws {
+
+// An Accelerator encompasses an id defined by the client, along with a unique
+// mojom::EventMatcher. See WindowManagerClient.
+//
+// This provides a WeakPtr, as the client might delete the accelerator between
+// an event having been matched and the dispatch of the accelerator to the
+// client.
+class Accelerator {
+ public:
+ Accelerator(uint32_t id, const mojom::EventMatcher& matcher);
+ ~Accelerator();
+
+ // Returns true if |event| and |phase | matches the definition in the
+ // mojom::EventMatcher used for initialization.
+ bool MatchesEvent(const ui::Event& event,
+ const ui::mojom::AcceleratorPhase phase) const;
+
+ // Returns true if |other| was created with an identical mojom::EventMatcher.
+ bool EqualEventMatcher(const Accelerator* other) const;
+
+ base::WeakPtr<Accelerator> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
+
+ uint32_t id() const { return id_; }
+
+ private:
+ uint32_t id_;
+ ui::mojom::AcceleratorPhase accelerator_phase_;
+ EventMatcher event_matcher_;
+ base::WeakPtrFactory<Accelerator> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(Accelerator);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_ACCELERATOR_H_
diff --git a/chromium/components/mus/ws/access_policy.h b/chromium/components/mus/ws/access_policy.h
new file mode 100644
index 00000000000..2f0cb366cc5
--- /dev/null
+++ b/chromium/components/mus/ws/access_policy.h
@@ -0,0 +1,84 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_ACCESS_POLICY_H_
+#define COMPONENTS_MUS_WS_ACCESS_POLICY_H_
+
+#include <stdint.h>
+
+#include "components/mus/public/interfaces/mus_constants.mojom.h"
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "components/mus/ws/ids.h"
+
+namespace mus {
+namespace ws {
+
+class AccessPolicyDelegate;
+class ServerWindow;
+
+// AccessPolicy is used by WindowTree to determine what the WindowTree is
+// allowed to do.
+class AccessPolicy {
+ public:
+ virtual ~AccessPolicy() {}
+
+ virtual void Init(ClientSpecificId client_id,
+ AccessPolicyDelegate* delegate) = 0;
+
+ // Unless otherwise mentioned all arguments have been validated. That is the
+ // |window| arguments are non-null unless otherwise stated (eg CanSetWindow()
+ // is allowed to take a NULL window).
+ virtual bool CanRemoveWindowFromParent(const ServerWindow* window) const = 0;
+ virtual bool CanAddWindow(const ServerWindow* parent,
+ const ServerWindow* child) const = 0;
+ virtual bool CanAddTransientWindow(const ServerWindow* parent,
+ const ServerWindow* child) const = 0;
+ virtual bool CanRemoveTransientWindowFromParent(
+ const ServerWindow* window) const = 0;
+ virtual bool CanSetModal(const ServerWindow* window) const = 0;
+ virtual bool CanReorderWindow(const ServerWindow* window,
+ const ServerWindow* relative_window,
+ mojom::OrderDirection direction) const = 0;
+ virtual bool CanDeleteWindow(const ServerWindow* window) const = 0;
+ virtual bool CanGetWindowTree(const ServerWindow* window) const = 0;
+ // Used when building a window tree (GetWindowTree()) to decide if we should
+ // descend into |window|.
+ virtual bool CanDescendIntoWindowForWindowTree(
+ const ServerWindow* window) const = 0;
+ virtual bool CanEmbed(const ServerWindow* window) const = 0;
+ virtual bool CanChangeWindowVisibility(const ServerWindow* window) const = 0;
+ virtual bool CanChangeWindowOpacity(const ServerWindow* window) const = 0;
+ virtual bool CanSetWindowSurface(const ServerWindow* window,
+ mojom::SurfaceType surface_type) const = 0;
+ virtual bool CanSetWindowBounds(const ServerWindow* window) const = 0;
+ virtual bool CanSetWindowProperties(const ServerWindow* window) const = 0;
+ virtual bool CanSetWindowTextInputState(const ServerWindow* window) const = 0;
+ virtual bool CanSetCapture(const ServerWindow* window) const = 0;
+ virtual bool CanSetFocus(const ServerWindow* window) const = 0;
+ virtual bool CanSetClientArea(const ServerWindow* window) const = 0;
+ virtual bool CanSetHitTestMask(const ServerWindow* window) const = 0;
+ // Used for all client controllable cursor properties; which cursor should be
+ // displayed, visibility, locking, etc.
+ virtual bool CanSetCursorProperties(const ServerWindow* window) const = 0;
+
+ // Returns whether the client should notify on a hierarchy change.
+ // |new_parent| and |old_parent| are initially set to the new and old parents
+ // but may be altered so that the client only sees a certain set of windows.
+ virtual bool ShouldNotifyOnHierarchyChange(
+ const ServerWindow* window,
+ const ServerWindow** new_parent,
+ const ServerWindow** old_parent) const = 0;
+ virtual bool CanSetWindowManager() const = 0;
+
+ // Returns the window to supply to the client when focus changes to |focused|.
+ virtual const ServerWindow* GetWindowForFocusChange(
+ const ServerWindow* focused) = 0;
+
+ virtual bool IsValidIdForNewWindow(const ClientWindowId& id) const = 0;
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_ACCESS_POLICY_H_
diff --git a/chromium/components/mus/ws/access_policy_delegate.h b/chromium/components/mus/ws/access_policy_delegate.h
new file mode 100644
index 00000000000..3e33de55c46
--- /dev/null
+++ b/chromium/components/mus/ws/access_policy_delegate.h
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_ACCESS_POLICY_DELEGATE_H_
+#define COMPONENTS_MUS_WS_ACCESS_POLICY_DELEGATE_H_
+
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "components/mus/ws/ids.h"
+
+namespace mus {
+
+namespace ws {
+
+class ServerWindow;
+
+// Delegate used by the AccessPolicy implementations to get state.
+class AccessPolicyDelegate {
+ public:
+ // Returns true if the tree has |window| as one of its roots.
+ virtual bool HasRootForAccessPolicy(const ServerWindow* window) const = 0;
+
+ // Returns true if |window| has been exposed to the client.
+ virtual bool IsWindowKnownForAccessPolicy(
+ const ServerWindow* window) const = 0;
+
+ // Returns true if Embed(window) has been invoked on |window|.
+ virtual bool IsWindowRootOfAnotherTreeForAccessPolicy(
+ const ServerWindow* window) const = 0;
+
+ protected:
+ virtual ~AccessPolicyDelegate() {}
+};
+
+} // namespace ws
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_ACCESS_POLICY_DELEGATE_H_
diff --git a/chromium/components/mus/ws/animation_runner.cc b/chromium/components/mus/ws/animation_runner.cc
new file mode 100644
index 00000000000..080fb2c0f45
--- /dev/null
+++ b/chromium/components/mus/ws/animation_runner.cc
@@ -0,0 +1,163 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/animation_runner.h"
+
+#include "base/memory/scoped_vector.h"
+#include "components/mus/ws/animation_runner_observer.h"
+#include "components/mus/ws/scheduled_animation_group.h"
+#include "components/mus/ws/server_window.h"
+
+namespace mus {
+namespace ws {
+namespace {
+
+bool ConvertWindowAndAnimationPairsToScheduledAnimationGroups(
+ const std::vector<AnimationRunner::WindowAndAnimationPair>& pairs,
+ AnimationRunner::AnimationId id,
+ base::TimeTicks now,
+ std::vector<std::unique_ptr<ScheduledAnimationGroup>>* groups) {
+ for (const auto& window_animation_pair : pairs) {
+ DCHECK(window_animation_pair.first);
+ DCHECK(window_animation_pair.second);
+ std::unique_ptr<ScheduledAnimationGroup> group(
+ ScheduledAnimationGroup::Create(window_animation_pair.first, now, id,
+ *(window_animation_pair.second)));
+ if (!group.get())
+ return false;
+ groups->push_back(std::move(group));
+ }
+ return true;
+}
+
+} // namespace
+
+AnimationRunner::AnimationRunner(base::TimeTicks now)
+ : next_id_(1), last_tick_time_(now) {}
+
+AnimationRunner::~AnimationRunner() {}
+
+void AnimationRunner::AddObserver(AnimationRunnerObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void AnimationRunner::RemoveObserver(AnimationRunnerObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+AnimationRunner::AnimationId AnimationRunner::Schedule(
+ const std::vector<WindowAndAnimationPair>& pairs,
+ base::TimeTicks now) {
+ DCHECK_GE(now, last_tick_time_);
+
+ const AnimationId animation_id = next_id_++;
+ std::vector<std::unique_ptr<ScheduledAnimationGroup>> groups;
+ if (!ConvertWindowAndAnimationPairsToScheduledAnimationGroups(
+ pairs, animation_id, now, &groups)) {
+ return 0;
+ }
+
+ // Cancel any animations for the affected windows. If the cancelled animations
+ // also animated properties that are not animated by the new group - instantly
+ // set those to the target value.
+ for (auto& group : groups) {
+ ScheduledAnimationGroup* current_group =
+ window_to_animation_map_[group->window()].get();
+ if (current_group)
+ current_group->SetValuesToTargetValuesForPropertiesNotIn(*group.get());
+
+ CancelAnimationForWindowImpl(group->window(), CANCEL_SOURCE_SCHEDULE);
+ }
+
+ // Update internal maps
+ for (auto& group : groups) {
+ group->ObtainStartValues();
+ ServerWindow* window = group->window();
+ window_to_animation_map_[window] = std::move(group);
+ DCHECK(!id_to_windows_map_[animation_id].count(window));
+ id_to_windows_map_[animation_id].insert(window);
+ }
+
+ FOR_EACH_OBSERVER(AnimationRunnerObserver, observers_,
+ OnAnimationScheduled(animation_id));
+ return animation_id;
+}
+
+void AnimationRunner::CancelAnimation(AnimationId id) {
+ if (id_to_windows_map_.count(id) == 0)
+ return;
+
+ std::set<ServerWindow*> windows(id_to_windows_map_[id]);
+ for (ServerWindow* window : windows)
+ CancelAnimationForWindow(window);
+}
+
+void AnimationRunner::CancelAnimationForWindow(ServerWindow* window) {
+ CancelAnimationForWindowImpl(window, CANCEL_SOURCE_CANCEL);
+}
+
+void AnimationRunner::Tick(base::TimeTicks time) {
+ DCHECK(time >= last_tick_time_);
+ last_tick_time_ = time;
+ if (window_to_animation_map_.empty())
+ return;
+
+ // The animation ids of any windows whose animation completes are added here.
+ // We notify after processing all windows so that if an observer mutates us in
+ // some way we're aren't left in a weird state.
+ std::set<AnimationId> animations_completed;
+ for (WindowToAnimationMap::iterator i = window_to_animation_map_.begin();
+ i != window_to_animation_map_.end();) {
+ bool animation_done = i->second->Tick(time);
+ if (animation_done) {
+ const AnimationId animation_id = i->second->id();
+ ServerWindow* window = i->first;
+ ++i;
+ bool animation_completed = RemoveWindowFromMaps(window);
+ if (animation_completed)
+ animations_completed.insert(animation_id);
+ } else {
+ ++i;
+ }
+ }
+ for (const AnimationId& id : animations_completed) {
+ FOR_EACH_OBSERVER(AnimationRunnerObserver, observers_, OnAnimationDone(id));
+ }
+}
+
+void AnimationRunner::CancelAnimationForWindowImpl(ServerWindow* window,
+ CancelSource source) {
+ if (!window_to_animation_map_[window])
+ return;
+
+ const AnimationId animation_id = window_to_animation_map_[window]->id();
+ if (RemoveWindowFromMaps(window)) {
+ // This was the last window in the group.
+ if (source == CANCEL_SOURCE_CANCEL) {
+ FOR_EACH_OBSERVER(AnimationRunnerObserver, observers_,
+ OnAnimationCanceled(animation_id));
+ } else {
+ FOR_EACH_OBSERVER(AnimationRunnerObserver, observers_,
+ OnAnimationInterrupted(animation_id));
+ }
+ }
+}
+
+bool AnimationRunner::RemoveWindowFromMaps(ServerWindow* window) {
+ DCHECK(window_to_animation_map_[window]);
+
+ const AnimationId animation_id = window_to_animation_map_[window]->id();
+ window_to_animation_map_.erase(window);
+
+ DCHECK(id_to_windows_map_.count(animation_id));
+ id_to_windows_map_[animation_id].erase(window);
+ if (!id_to_windows_map_[animation_id].empty())
+ return false;
+
+ id_to_windows_map_.erase(animation_id);
+ return true;
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/animation_runner.h b/chromium/components/mus/ws/animation_runner.h
new file mode 100644
index 00000000000..5a7d80c4774
--- /dev/null
+++ b/chromium/components/mus/ws/animation_runner.h
@@ -0,0 +1,119 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_ANIMATION_RUNNER_H_
+#define COMPONENTS_MUS_WS_ANIMATION_RUNNER_H_
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+#include "base/observer_list.h"
+#include "base/time/time.h"
+
+namespace mus {
+namespace mojom {
+class AnimationGroup;
+}
+
+namespace ws {
+
+class AnimationRunnerObserver;
+class ScheduledAnimationGroup;
+class ServerWindow;
+
+// AnimationRunner is responsible for maintaining and running a set of
+// animations. The animations are represented as a set of AnimationGroups. New
+// animations are scheduled by way of Schedule(). A |window| may only have one
+// animation running at a time. Schedule()ing a new animation for a window
+// already animating implicitly cancels the current animation for the window.
+// Animations progress by way of the Tick() function.
+class AnimationRunner {
+ public:
+ using AnimationId = uint32_t;
+ using WindowAndAnimationPair =
+ std::pair<ServerWindow*, const mojom::AnimationGroup*>;
+
+ explicit AnimationRunner(base::TimeTicks now);
+ ~AnimationRunner();
+
+ void AddObserver(AnimationRunnerObserver* observer);
+ void RemoveObserver(AnimationRunnerObserver* observer);
+
+ // Schedules animations. If any of the groups are not valid no animations are
+ // scheuled and 0 is returned. If there is an existing animation in progress
+ // for any of the windows it is canceled and any properties that were
+ // animating but are no longer animating are set to their target value.
+ AnimationId Schedule(const std::vector<WindowAndAnimationPair>& groups,
+ base::TimeTicks now);
+
+ // Cancels an animation scheduled by an id that was previously returned from
+ // Schedule().
+ void CancelAnimation(AnimationId id);
+
+ // Cancels the animation scheduled for |window|. Does nothing if there is no
+ // animation scheduled for |window|. This does not change |window|. That is,
+ // any in progress animations are stopped.
+ void CancelAnimationForWindow(ServerWindow* window);
+
+ // Advance the animations updating values appropriately.
+ void Tick(base::TimeTicks time);
+
+ // Returns true if there are animations currently scheduled.
+ bool HasAnimations() const { return !window_to_animation_map_.empty(); }
+
+ // Returns true if the animation identified by |id| is valid and animating.
+ bool IsAnimating(AnimationId id) const {
+ return id_to_windows_map_.count(id) > 0;
+ }
+
+ // Returns the windows that are currently animating for |id|. Returns an empty
+ // set if |id| does not identify a valid animation.
+ std::set<ServerWindow*> GetWindowsAnimating(AnimationId id) {
+ return IsAnimating(id) ? id_to_windows_map_.find(id)->second
+ : std::set<ServerWindow*>();
+ }
+
+ private:
+ enum CancelSource {
+ // Cancel is the result of scheduling another animation for the window.
+ CANCEL_SOURCE_SCHEDULE,
+
+ // Cancel originates from an explicit call to cancel.
+ CANCEL_SOURCE_CANCEL,
+ };
+
+ using WindowToAnimationMap =
+ std::unordered_map<ServerWindow*,
+ std::unique_ptr<ScheduledAnimationGroup>>;
+ using IdToWindowsMap = std::map<AnimationId, std::set<ServerWindow*>>;
+
+ void CancelAnimationForWindowImpl(ServerWindow* window, CancelSource source);
+
+ // Removes |window| from both |window_to_animation_map_| and
+ // |id_to_windows_map_|.
+ // Returns true if there are no more windows animating with the animation id
+ // the window is associated with.
+ bool RemoveWindowFromMaps(ServerWindow* window);
+
+ AnimationId next_id_;
+
+ base::TimeTicks last_tick_time_;
+
+ base::ObserverList<AnimationRunnerObserver> observers_;
+
+ WindowToAnimationMap window_to_animation_map_;
+
+ IdToWindowsMap id_to_windows_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(AnimationRunner);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_ANIMATION_RUNNER_H_
diff --git a/chromium/components/mus/ws/animation_runner_observer.h b/chromium/components/mus/ws/animation_runner_observer.h
new file mode 100644
index 00000000000..4c73e6cdbae
--- /dev/null
+++ b/chromium/components/mus/ws/animation_runner_observer.h
@@ -0,0 +1,25 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_ANIMATION_RUNNER_OBSERVER_H_
+#define COMPONENTS_MUS_WS_ANIMATION_RUNNER_OBSERVER_H_
+
+namespace mus {
+namespace ws {
+
+class AnimationRunnerObserver {
+ public:
+ virtual void OnAnimationScheduled(uint32_t id) = 0;
+ virtual void OnAnimationDone(uint32_t id) = 0;
+ virtual void OnAnimationInterrupted(uint32_t id) = 0;
+ virtual void OnAnimationCanceled(uint32_t id) = 0;
+
+ protected:
+ virtual ~AnimationRunnerObserver() {}
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_ANIMATION_RUNNER_OBSERVER_H_
diff --git a/chromium/components/mus/ws/animation_runner_unittest.cc b/chromium/components/mus/ws/animation_runner_unittest.cc
new file mode 100644
index 00000000000..660a920b313
--- /dev/null
+++ b/chromium/components/mus/ws/animation_runner_unittest.cc
@@ -0,0 +1,639 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/animation_runner.h"
+
+#include "base/strings/stringprintf.h"
+#include "components/mus/public/interfaces/window_manager_constants.mojom.h"
+#include "components/mus/ws/animation_runner_observer.h"
+#include "components/mus/ws/scheduled_animation_group.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/test_server_window_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::TimeDelta;
+
+namespace mus {
+using mojom::AnimationProperty;
+using mojom::AnimationTweenType;
+using mojom::AnimationElement;
+using mojom::AnimationGroup;
+using mojom::AnimationProperty;
+using mojom::AnimationSequence;
+using mojom::AnimationTweenType;
+using mojom::AnimationValue;
+using mojom::AnimationValuePtr;
+
+namespace ws {
+namespace {
+
+class TestAnimationRunnerObserver : public AnimationRunnerObserver {
+ public:
+ TestAnimationRunnerObserver() {}
+ ~TestAnimationRunnerObserver() override {}
+
+ std::vector<std::string>* changes() { return &changes_; }
+ std::vector<uint32_t>* change_ids() { return &change_ids_; }
+
+ void clear_changes() {
+ changes_.clear();
+ change_ids_.clear();
+ }
+
+ // AnimationRunnerDelgate:
+ void OnAnimationScheduled(uint32_t id) override {
+ change_ids_.push_back(id);
+ changes_.push_back("scheduled");
+ }
+ void OnAnimationDone(uint32_t id) override {
+ change_ids_.push_back(id);
+ changes_.push_back("done");
+ }
+ void OnAnimationInterrupted(uint32_t id) override {
+ change_ids_.push_back(id);
+ changes_.push_back("interrupted");
+ }
+ void OnAnimationCanceled(uint32_t id) override {
+ change_ids_.push_back(id);
+ changes_.push_back("canceled");
+ }
+
+ private:
+ std::vector<uint32_t> change_ids_;
+ std::vector<std::string> changes_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestAnimationRunnerObserver);
+};
+
+// Creates an AnimationValuePtr from the specified float value.
+AnimationValuePtr FloatAnimationValue(float float_value) {
+ AnimationValuePtr value(AnimationValue::New());
+ value->float_value = float_value;
+ return value;
+}
+
+// Creates an AnimationValuePtr from the specified transform.
+AnimationValuePtr TransformAnimationValue(const gfx::Transform& transform) {
+ AnimationValuePtr value(AnimationValue::New());
+ value->transform = transform;
+ return value;
+}
+
+// Adds an AnimationElement to |group|s last sequence with the specified value.
+void AddElement(AnimationGroup* group,
+ TimeDelta time,
+ AnimationValuePtr start_value,
+ AnimationValuePtr target_value,
+ AnimationProperty property,
+ AnimationTweenType tween_type) {
+ AnimationSequence& sequence =
+ *(group->sequences[group->sequences.size() - 1]);
+ sequence.elements.push_back(AnimationElement::New());
+ AnimationElement& element =
+ *(sequence.elements[sequence.elements.size() - 1]);
+ element.property = property;
+ element.duration = time.InMicroseconds();
+ element.tween_type = tween_type;
+ element.start_value = std::move(start_value);
+ element.target_value = std::move(target_value);
+}
+
+void AddOpacityElement(AnimationGroup* group,
+ TimeDelta time,
+ AnimationValuePtr start_value,
+ AnimationValuePtr target_value) {
+ AddElement(group, time, std::move(start_value), std::move(target_value),
+ AnimationProperty::OPACITY, AnimationTweenType::LINEAR);
+}
+
+void AddTransformElement(AnimationGroup* group,
+ TimeDelta time,
+ AnimationValuePtr start_value,
+ AnimationValuePtr target_value) {
+ AddElement(group, time, std::move(start_value), std::move(target_value),
+ AnimationProperty::TRANSFORM, AnimationTweenType::LINEAR);
+}
+
+void AddPauseElement(AnimationGroup* group, TimeDelta time) {
+ AddElement(group, time, AnimationValuePtr(), AnimationValuePtr(),
+ AnimationProperty::NONE, AnimationTweenType::LINEAR);
+}
+
+void InitGroupForWindow(AnimationGroup* group,
+ const WindowId& id,
+ int cycle_count) {
+ group->window_id = WindowIdToTransportId(id);
+ group->sequences.push_back(AnimationSequence::New());
+ group->sequences[group->sequences.size() - 1]->cycle_count = cycle_count;
+}
+
+} // namespace
+
+class AnimationRunnerTest : public testing::Test {
+ public:
+ AnimationRunnerTest()
+ : initial_time_(base::TimeTicks::Now()), runner_(initial_time_) {
+ runner_.AddObserver(&runner_observer_);
+ }
+ ~AnimationRunnerTest() override { runner_.RemoveObserver(&runner_observer_); }
+
+ protected:
+ // Convenience to schedule an animation for a single window/group pair.
+ AnimationRunner::AnimationId ScheduleForSingleWindow(
+ ServerWindow* window,
+ const AnimationGroup* group,
+ base::TimeTicks now) {
+ std::vector<AnimationRunner::WindowAndAnimationPair> pairs;
+ pairs.push_back(std::make_pair(window, group));
+ return runner_.Schedule(pairs, now);
+ }
+
+ // If |id| is valid and there is only one window schedule against the
+ // animation it is returned; otherwise returns null.
+ ServerWindow* GetSingleWindowAnimating(AnimationRunner::AnimationId id) {
+ std::set<ServerWindow*> windows(runner_.GetWindowsAnimating(id));
+ return windows.size() == 1 ? *windows.begin() : nullptr;
+ }
+
+ const base::TimeTicks initial_time_;
+ TestAnimationRunnerObserver runner_observer_;
+ AnimationRunner runner_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AnimationRunnerTest);
+};
+
+// Opacity from 1 to .5 over 1000.
+TEST_F(AnimationRunnerTest, SingleProperty) {
+ TestServerWindowDelegate window_delegate;
+ ServerWindow window(&window_delegate, WindowId());
+
+ AnimationGroup group;
+ InitGroupForWindow(&group, window.id(), 1);
+ AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
+ AnimationValuePtr(), FloatAnimationValue(.5));
+
+ const uint32_t animation_id =
+ ScheduleForSingleWindow(&window, &group, initial_time_);
+
+ ASSERT_EQ(1u, runner_observer_.changes()->size());
+ EXPECT_EQ("scheduled", runner_observer_.changes()->at(0));
+ EXPECT_EQ(animation_id, runner_observer_.change_ids()->at(0));
+ runner_observer_.clear_changes();
+
+ EXPECT_TRUE(runner_.HasAnimations());
+
+ // Opacity should still be 1 (the initial value).
+ EXPECT_EQ(1.f, window.opacity());
+
+ // Animate half way.
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(500));
+
+ EXPECT_EQ(.75f, window.opacity());
+ EXPECT_TRUE(runner_observer_.changes()->empty());
+
+ // Run well past the end. Value should progress to end and delegate should
+ // be notified.
+ runner_.Tick(initial_time_ + TimeDelta::FromSeconds(10));
+ EXPECT_EQ(.5f, window.opacity());
+
+ ASSERT_EQ(1u, runner_observer_.changes()->size());
+ EXPECT_EQ("done", runner_observer_.changes()->at(0));
+ EXPECT_EQ(animation_id, runner_observer_.change_ids()->at(0));
+
+ EXPECT_FALSE(runner_.HasAnimations());
+}
+
+// Opacity from 1 to .5, followed by transform from identity to 2x,3x.
+TEST_F(AnimationRunnerTest, TwoPropertiesInSequence) {
+ TestServerWindowDelegate window_delegate;
+ ServerWindow window(&window_delegate, WindowId());
+
+ AnimationGroup group;
+ InitGroupForWindow(&group, window.id(), 1);
+ AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
+ AnimationValuePtr(), FloatAnimationValue(.5f));
+
+ gfx::Transform done_transform;
+ done_transform.Scale(2, 4);
+ AddTransformElement(&group, TimeDelta::FromMicroseconds(2000),
+ AnimationValuePtr(),
+ TransformAnimationValue(done_transform));
+
+ const uint32_t animation_id =
+ ScheduleForSingleWindow(&window, &group, initial_time_);
+ runner_observer_.clear_changes();
+
+ // Nothing in the window should have changed yet.
+ EXPECT_EQ(1.f, window.opacity());
+ EXPECT_TRUE(window.transform().IsIdentity());
+
+ // Animate half way from through opacity animation.
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(500));
+
+ EXPECT_EQ(.75f, window.opacity());
+ EXPECT_TRUE(window.transform().IsIdentity());
+
+ // Finish first element (opacity).
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(1000));
+ EXPECT_EQ(.5f, window.opacity());
+ EXPECT_TRUE(window.transform().IsIdentity());
+
+ // Half way through second (transform).
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(2000));
+ EXPECT_EQ(.5f, window.opacity());
+ gfx::Transform half_way_transform;
+ half_way_transform.Scale(1.5, 2.5);
+ EXPECT_EQ(half_way_transform, window.transform());
+
+ EXPECT_TRUE(runner_observer_.changes()->empty());
+
+ // To end.
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(3500));
+ EXPECT_EQ(.5f, window.opacity());
+ EXPECT_EQ(done_transform, window.transform());
+
+ ASSERT_EQ(1u, runner_observer_.changes()->size());
+ EXPECT_EQ("done", runner_observer_.changes()->at(0));
+ EXPECT_EQ(animation_id, runner_observer_.change_ids()->at(0));
+}
+
+// Opacity from .5 to 1 over 1000, transform to 2x,4x over 500.
+TEST_F(AnimationRunnerTest, TwoPropertiesInParallel) {
+ TestServerWindowDelegate window_delegate;
+ ServerWindow window(&window_delegate, WindowId(1, 1));
+
+ AnimationGroup group;
+ InitGroupForWindow(&group, window.id(), 1);
+ AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
+ FloatAnimationValue(.5f), FloatAnimationValue(1));
+
+ group.sequences.push_back(AnimationSequence::New());
+ group.sequences[1]->cycle_count = 1;
+ gfx::Transform done_transform;
+ done_transform.Scale(2, 4);
+ AddTransformElement(&group, TimeDelta::FromMicroseconds(500),
+ AnimationValuePtr(),
+ TransformAnimationValue(done_transform));
+
+ const uint32_t animation_id =
+ ScheduleForSingleWindow(&window, &group, initial_time_);
+
+ runner_observer_.clear_changes();
+
+ // Nothing in the window should have changed yet.
+ EXPECT_EQ(1.f, window.opacity());
+ EXPECT_TRUE(window.transform().IsIdentity());
+
+ // Animate to 250, which is 1/4 way through opacity and half way through
+ // transform.
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(250));
+
+ EXPECT_EQ(.625f, window.opacity());
+ gfx::Transform half_way_transform;
+ half_way_transform.Scale(1.5, 2.5);
+ EXPECT_EQ(half_way_transform, window.transform());
+
+ // Animate to 500, which is 1/2 way through opacity and transform done.
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(500));
+ EXPECT_EQ(.75f, window.opacity());
+ EXPECT_EQ(done_transform, window.transform());
+
+ // Animate to 750, which is 3/4 way through opacity and transform done.
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(750));
+ EXPECT_EQ(.875f, window.opacity());
+ EXPECT_EQ(done_transform, window.transform());
+
+ EXPECT_TRUE(runner_observer_.changes()->empty());
+
+ // To end.
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(3500));
+ EXPECT_EQ(1.f, window.opacity());
+ EXPECT_EQ(done_transform, window.transform());
+
+ ASSERT_EQ(1u, runner_observer_.changes()->size());
+ EXPECT_EQ("done", runner_observer_.changes()->at(0));
+ EXPECT_EQ(animation_id, runner_observer_.change_ids()->at(0));
+}
+
+// Opacity from .5 to 1 over 1000, pause for 500, 1 to .5 over 500, with a cycle
+// count of 3.
+TEST_F(AnimationRunnerTest, Cycles) {
+ TestServerWindowDelegate window_delegate;
+ ServerWindow window(&window_delegate, WindowId(1, 2));
+
+ window.SetOpacity(.5f);
+
+ AnimationGroup group;
+ InitGroupForWindow(&group, window.id(), 3);
+ AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
+ AnimationValuePtr(), FloatAnimationValue(1));
+ AddPauseElement(&group, TimeDelta::FromMicroseconds(500));
+ AddOpacityElement(&group, TimeDelta::FromMicroseconds(500),
+ AnimationValuePtr(), FloatAnimationValue(.5));
+
+ ScheduleForSingleWindow(&window, &group, initial_time_);
+ runner_observer_.clear_changes();
+
+ // Nothing in the window should have changed yet.
+ EXPECT_EQ(.5f, window.opacity());
+
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(500));
+ EXPECT_EQ(.75f, window.opacity());
+
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(1250));
+ EXPECT_EQ(1.f, window.opacity());
+
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(1750));
+ EXPECT_EQ(.75f, window.opacity());
+
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(2500));
+ EXPECT_EQ(.75f, window.opacity());
+
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(3250));
+ EXPECT_EQ(1.f, window.opacity());
+
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(3750));
+ EXPECT_EQ(.75f, window.opacity());
+
+ // Animate to the end.
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(6500));
+ EXPECT_EQ(.5f, window.opacity());
+
+ ASSERT_EQ(1u, runner_observer_.changes()->size());
+ EXPECT_EQ("done", runner_observer_.changes()->at(0));
+}
+
+// Verifies scheduling the same window twice sends an interrupt.
+TEST_F(AnimationRunnerTest, ScheduleTwice) {
+ TestServerWindowDelegate window_delegate;
+ ServerWindow window(&window_delegate, WindowId(1, 2));
+
+ AnimationGroup group;
+ InitGroupForWindow(&group, window.id(), 1);
+ AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
+ AnimationValuePtr(), FloatAnimationValue(.5));
+
+ const uint32_t animation_id =
+ ScheduleForSingleWindow(&window, &group, initial_time_);
+ runner_observer_.clear_changes();
+
+ // Animate half way.
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(500));
+
+ EXPECT_EQ(.75f, window.opacity());
+ EXPECT_TRUE(runner_observer_.changes()->empty());
+
+ // Schedule again. We should get an interrupt, but opacity shouldn't change.
+ const uint32_t animation2_id = ScheduleForSingleWindow(
+ &window, &group, initial_time_ + TimeDelta::FromMicroseconds(500));
+
+ // Id should have changed.
+ EXPECT_NE(animation_id, animation2_id);
+
+ EXPECT_FALSE(runner_.IsAnimating(animation_id));
+ EXPECT_EQ(&window, GetSingleWindowAnimating(animation2_id));
+
+ EXPECT_EQ(.75f, window.opacity());
+ EXPECT_EQ(2u, runner_observer_.changes()->size());
+ EXPECT_EQ("interrupted", runner_observer_.changes()->at(0));
+ EXPECT_EQ(animation_id, runner_observer_.change_ids()->at(0));
+ EXPECT_EQ("scheduled", runner_observer_.changes()->at(1));
+ EXPECT_EQ(animation2_id, runner_observer_.change_ids()->at(1));
+ runner_observer_.clear_changes();
+
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(1000));
+ EXPECT_EQ(.625f, window.opacity());
+ EXPECT_TRUE(runner_observer_.changes()->empty());
+
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(2000));
+ EXPECT_EQ(.5f, window.opacity());
+ EXPECT_EQ(1u, runner_observer_.changes()->size());
+ EXPECT_EQ("done", runner_observer_.changes()->at(0));
+ EXPECT_EQ(animation2_id, runner_observer_.change_ids()->at(0));
+}
+
+// Verifies Remove() works.
+TEST_F(AnimationRunnerTest, CancelAnimationForWindow) {
+ // Create an animation and advance it part way.
+ TestServerWindowDelegate window_delegate;
+ ServerWindow window(&window_delegate, WindowId());
+ AnimationGroup group;
+ InitGroupForWindow(&group, window.id(), 1);
+ AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
+ AnimationValuePtr(), FloatAnimationValue(.5));
+
+ const uint32_t animation_id =
+ ScheduleForSingleWindow(&window, &group, initial_time_);
+ runner_observer_.clear_changes();
+ EXPECT_EQ(&window, GetSingleWindowAnimating(animation_id));
+
+ EXPECT_TRUE(runner_.HasAnimations());
+
+ // Animate half way.
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(500));
+ EXPECT_EQ(.75f, window.opacity());
+ EXPECT_TRUE(runner_observer_.changes()->empty());
+
+ // Cancel the animation.
+ runner_.CancelAnimationForWindow(&window);
+
+ EXPECT_FALSE(runner_.HasAnimations());
+ EXPECT_EQ(nullptr, GetSingleWindowAnimating(animation_id));
+
+ EXPECT_EQ(.75f, window.opacity());
+
+ EXPECT_EQ(1u, runner_observer_.changes()->size());
+ EXPECT_EQ("canceled", runner_observer_.changes()->at(0));
+ EXPECT_EQ(animation_id, runner_observer_.change_ids()->at(0));
+}
+
+// Verifies a tick with a very large delta and a sequence that repeats forever
+// doesn't take a long time.
+TEST_F(AnimationRunnerTest, InfiniteRepeatWithHugeGap) {
+ TestServerWindowDelegate window_delegate;
+ ServerWindow window(&window_delegate, WindowId(1, 2));
+
+ window.SetOpacity(.5f);
+
+ AnimationGroup group;
+ InitGroupForWindow(&group, window.id(), 0);
+ AddOpacityElement(&group, TimeDelta::FromMicroseconds(500),
+ AnimationValuePtr(), FloatAnimationValue(1));
+ AddOpacityElement(&group, TimeDelta::FromMicroseconds(500),
+ AnimationValuePtr(), FloatAnimationValue(.5));
+
+ ScheduleForSingleWindow(&window, &group, initial_time_);
+ runner_observer_.clear_changes();
+
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(1000000000750));
+
+ EXPECT_EQ(.75f, window.opacity());
+
+ ASSERT_EQ(0u, runner_observer_.changes()->size());
+}
+
+// Verifies a second schedule sets any properties that are no longer animating
+// to their final value.
+TEST_F(AnimationRunnerTest, RescheduleSetsPropertiesToFinalValue) {
+ TestServerWindowDelegate window_delegate;
+ ServerWindow window(&window_delegate, WindowId());
+
+ AnimationGroup group;
+ InitGroupForWindow(&group, window.id(), 1);
+ AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
+ AnimationValuePtr(), FloatAnimationValue(.5));
+
+ gfx::Transform done_transform;
+ done_transform.Scale(2, 4);
+ AddTransformElement(&group, TimeDelta::FromMicroseconds(500),
+ AnimationValuePtr(),
+ TransformAnimationValue(done_transform));
+
+ ScheduleForSingleWindow(&window, &group, initial_time_);
+
+ // Schedule() again, this time without animating opacity.
+ group.sequences[0]->elements[0]->property = AnimationProperty::NONE;
+ ScheduleForSingleWindow(&window, &group, initial_time_);
+
+ // Opacity should go to final value.
+ EXPECT_EQ(.5f, window.opacity());
+ // Transform shouldn't have changed since newly scheduled animation also has
+ // transform in it.
+ EXPECT_TRUE(window.transform().IsIdentity());
+}
+
+// Opacity from 1 to .5 over 1000 of v1 and v2 transform to 2x,4x over 500.
+TEST_F(AnimationRunnerTest, TwoWindows) {
+ TestServerWindowDelegate window_delegate;
+ ServerWindow window1(&window_delegate, WindowId());
+ ServerWindow window2(&window_delegate, WindowId(1, 2));
+
+ AnimationGroup group1;
+ InitGroupForWindow(&group1, window1.id(), 1);
+ AddOpacityElement(&group1, TimeDelta::FromMicroseconds(1000),
+ AnimationValuePtr(), FloatAnimationValue(.5));
+
+ AnimationGroup group2;
+ InitGroupForWindow(&group2, window2.id(), 1);
+ gfx::Transform done_transform;
+ done_transform.Scale(2, 4);
+ AddTransformElement(&group2, TimeDelta::FromMicroseconds(500),
+ AnimationValuePtr(),
+ TransformAnimationValue(done_transform));
+
+ std::vector<AnimationRunner::WindowAndAnimationPair> pairs;
+ pairs.push_back(std::make_pair(&window1, &group1));
+ pairs.push_back(std::make_pair(&window2, &group2));
+
+ const uint32_t animation_id = runner_.Schedule(pairs, initial_time_);
+
+ ASSERT_EQ(1u, runner_observer_.changes()->size());
+ EXPECT_EQ("scheduled", runner_observer_.changes()->at(0));
+ EXPECT_EQ(animation_id, runner_observer_.change_ids()->at(0));
+ runner_observer_.clear_changes();
+
+ EXPECT_TRUE(runner_.HasAnimations());
+ EXPECT_TRUE(runner_.IsAnimating(animation_id));
+
+ // Properties should be at the initial value.
+ EXPECT_EQ(1.f, window1.opacity());
+ EXPECT_TRUE(window2.transform().IsIdentity());
+
+ // Animate 250ms in, which is quarter way for opacity and half way for
+ // transform.
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(250));
+ EXPECT_EQ(.875f, window1.opacity());
+ gfx::Transform half_way_transform;
+ half_way_transform.Scale(1.5, 2.5);
+ EXPECT_EQ(half_way_transform, window2.transform());
+ std::set<ServerWindow*> windows_animating(
+ runner_.GetWindowsAnimating(animation_id));
+ EXPECT_EQ(2u, windows_animating.size());
+ EXPECT_EQ(1u, windows_animating.count(&window1));
+ EXPECT_EQ(1u, windows_animating.count(&window2));
+
+ // Animate 750ms in, window1 should be done 3/4 done, and window2 done.
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(750));
+ EXPECT_EQ(.625, window1.opacity());
+ EXPECT_EQ(done_transform, window2.transform());
+ windows_animating = runner_.GetWindowsAnimating(animation_id);
+ EXPECT_EQ(1u, windows_animating.size());
+ EXPECT_EQ(1u, windows_animating.count(&window1));
+ EXPECT_TRUE(runner_.HasAnimations());
+ EXPECT_TRUE(runner_.IsAnimating(animation_id));
+
+ // Animate to end.
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(1750));
+ EXPECT_EQ(.5, window1.opacity());
+ EXPECT_EQ(done_transform, window2.transform());
+ windows_animating = runner_.GetWindowsAnimating(animation_id);
+ EXPECT_TRUE(windows_animating.empty());
+ EXPECT_FALSE(runner_.HasAnimations());
+ EXPECT_FALSE(runner_.IsAnimating(animation_id));
+}
+
+TEST_F(AnimationRunnerTest, Reschedule) {
+ TestServerWindowDelegate window_delegate;
+ ServerWindow window(&window_delegate, WindowId());
+
+ // Animation from 1-0 over 1ms and in parallel transform to 2x,4x over 1ms.
+ AnimationGroup group;
+ InitGroupForWindow(&group, window.id(), 1);
+ AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
+ AnimationValuePtr(), FloatAnimationValue(0));
+ group.sequences.push_back(AnimationSequence::New());
+ group.sequences[1]->cycle_count = 1;
+ gfx::Transform done_transform;
+ done_transform.Scale(2, 4);
+ AddTransformElement(&group, TimeDelta::FromMicroseconds(1000),
+ AnimationValuePtr(),
+ TransformAnimationValue(done_transform));
+ const uint32_t animation_id1 =
+ ScheduleForSingleWindow(&window, &group, initial_time_);
+
+ // Animate half way in.
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(500));
+ EXPECT_EQ(.5f, window.opacity());
+ gfx::Transform half_way_transform;
+ half_way_transform.Scale(1.5, 2.5);
+ EXPECT_EQ(half_way_transform, window.transform());
+
+ runner_observer_.clear_changes();
+
+ // Schedule the same window animating opacity to 1.
+ AnimationGroup group2;
+ InitGroupForWindow(&group2, window.id(), 1);
+ AddOpacityElement(&group2, TimeDelta::FromMicroseconds(1000),
+ AnimationValuePtr(), FloatAnimationValue(1));
+ const uint32_t animation_id2 = ScheduleForSingleWindow(
+ &window, &group2, initial_time_ + TimeDelta::FromMicroseconds(500));
+
+ // Opacity should remain at .5, but transform should go to end state.
+ EXPECT_EQ(.5f, window.opacity());
+ EXPECT_EQ(done_transform, window.transform());
+
+ ASSERT_EQ(2u, runner_observer_.changes()->size());
+ EXPECT_EQ("interrupted", runner_observer_.changes()->at(0));
+ EXPECT_EQ(animation_id1, runner_observer_.change_ids()->at(0));
+ EXPECT_EQ("scheduled", runner_observer_.changes()->at(1));
+ EXPECT_EQ(animation_id2, runner_observer_.change_ids()->at(1));
+ runner_observer_.clear_changes();
+
+ // Animate half way through new sequence. Opacity should be the only thing
+ // changing.
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(1000));
+ EXPECT_EQ(.75f, window.opacity());
+ EXPECT_EQ(done_transform, window.transform());
+ ASSERT_EQ(0u, runner_observer_.changes()->size());
+
+ // Animate to end.
+ runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(2000));
+ ASSERT_EQ(1u, runner_observer_.changes()->size());
+ EXPECT_EQ("done", runner_observer_.changes()->at(0));
+ EXPECT_EQ(animation_id2, runner_observer_.change_ids()->at(0));
+}
+
+} // ws
+} // mus
diff --git a/chromium/components/mus/ws/cursor_unittest.cc b/chromium/components/mus/ws/cursor_unittest.cc
new file mode 100644
index 00000000000..995318e85f0
--- /dev/null
+++ b/chromium/components/mus/ws/cursor_unittest.cc
@@ -0,0 +1,198 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "components/mus/common/types.h"
+#include "components/mus/common/util.h"
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "components/mus/surfaces/surfaces_state.h"
+#include "components/mus/ws/display_manager.h"
+#include "components/mus/ws/ids.h"
+#include "components/mus/ws/platform_display.h"
+#include "components/mus/ws/platform_display_factory.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/server_window_surface_manager_test_api.h"
+#include "components/mus/ws/test_utils.h"
+#include "components/mus/ws/window_manager_display_root.h"
+#include "components/mus/ws/window_manager_state.h"
+#include "components/mus/ws/window_server.h"
+#include "components/mus/ws/window_server_delegate.h"
+#include "components/mus/ws/window_tree.h"
+#include "components/mus/ws/window_tree_binding.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/event.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace mus {
+namespace ws {
+namespace test {
+
+const UserId kTestId1 = "20";
+
+class CursorTest : public testing::Test {
+ public:
+ CursorTest() : cursor_id_(-1), platform_display_factory_(&cursor_id_) {}
+ ~CursorTest() override {}
+
+ protected:
+ // testing::Test:
+ void SetUp() override {
+ PlatformDisplay::set_factory_for_testing(&platform_display_factory_);
+ window_server_.reset(
+ new WindowServer(&window_server_delegate_,
+ scoped_refptr<SurfacesState>(new SurfacesState)));
+ window_server_delegate_.set_window_server(window_server_.get());
+
+ window_server_delegate_.set_num_displays_to_create(1);
+
+ // As a side effect, this allocates Displays.
+ WindowManagerWindowTreeFactorySetTestApi(
+ window_server_->window_manager_window_tree_factory_set())
+ .Add(kTestId1);
+ window_server_->user_id_tracker()->AddUserId(kTestId1);
+ window_server_->user_id_tracker()->SetActiveUserId(kTestId1);
+ }
+
+ ServerWindow* GetRoot() {
+ DisplayManager* display_manager = window_server_->display_manager();
+ // ASSERT_EQ(1u, display_manager->displays().size());
+ Display* display = *display_manager->displays().begin();
+ return display->GetWindowManagerDisplayRootForUser(kTestId1)->root();
+ }
+
+ // Create a 30x30 window where the outer 10 pixels is non-client.
+ ServerWindow* BuildServerWindow() {
+ DisplayManager* display_manager = window_server_->display_manager();
+ Display* display = *display_manager->displays().begin();
+ WindowManagerDisplayRoot* active_display_root =
+ display->GetActiveWindowManagerDisplayRoot();
+ WindowTree* tree =
+ active_display_root->window_manager_state()->window_tree();
+ ClientWindowId child_window_id;
+ if (!NewWindowInTree(tree, &child_window_id))
+ return nullptr;
+
+ ServerWindow* w = tree->GetWindowByClientId(child_window_id);
+ w->SetBounds(gfx::Rect(10, 10, 30, 30));
+ w->SetClientArea(gfx::Insets(10, 10), std::vector<gfx::Rect>());
+ w->SetVisible(true);
+
+ ServerWindowSurfaceManagerTestApi test_api(w->GetOrCreateSurfaceManager());
+ test_api.CreateEmptyDefaultSurface();
+
+ return w;
+ }
+
+ void MoveCursorTo(const gfx::Point& p) {
+ DisplayManager* display_manager = window_server_->display_manager();
+ ASSERT_EQ(1u, display_manager->displays().size());
+ Display* display = *display_manager->displays().begin();
+ WindowManagerDisplayRoot* active_display_root =
+ display->GetActiveWindowManagerDisplayRoot();
+ ASSERT_TRUE(active_display_root);
+ static_cast<PlatformDisplayDelegate*>(display)->OnEvent(ui::PointerEvent(
+ ui::MouseEvent(ui::ET_MOUSE_MOVED, p, p, base::TimeTicks(), 0, 0)));
+ WindowManagerState* wms = active_display_root->window_manager_state();
+ wms->OnEventAck(wms->window_tree(), mojom::EventResult::HANDLED);
+ }
+
+ protected:
+ int32_t cursor_id_;
+ TestPlatformDisplayFactory platform_display_factory_;
+ TestWindowServerDelegate window_server_delegate_;
+ std::unique_ptr<WindowServer> window_server_;
+ base::MessageLoop message_loop_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CursorTest);
+};
+
+TEST_F(CursorTest, ChangeByMouseMove) {
+ ServerWindow* win = BuildServerWindow();
+ win->SetPredefinedCursor(mojom::Cursor::IBEAM);
+ EXPECT_EQ(mojom::Cursor::IBEAM, mojom::Cursor(win->cursor()));
+ win->SetNonClientCursor(mojom::Cursor::EAST_RESIZE);
+ EXPECT_EQ(mojom::Cursor::EAST_RESIZE,
+ mojom::Cursor(win->non_client_cursor()));
+
+ // Non client area
+ MoveCursorTo(gfx::Point(15, 15));
+ EXPECT_EQ(mojom::Cursor::EAST_RESIZE, mojom::Cursor(cursor_id_));
+
+ // Client area
+ MoveCursorTo(gfx::Point(25, 25));
+ EXPECT_EQ(mojom::Cursor::IBEAM, mojom::Cursor(cursor_id_));
+}
+
+TEST_F(CursorTest, ChangeByClientAreaChange) {
+ ServerWindow* win = BuildServerWindow();
+ win->SetPredefinedCursor(mojom::Cursor::IBEAM);
+ EXPECT_EQ(mojom::Cursor::IBEAM, mojom::Cursor(win->cursor()));
+ win->SetNonClientCursor(mojom::Cursor::EAST_RESIZE);
+ EXPECT_EQ(mojom::Cursor::EAST_RESIZE,
+ mojom::Cursor(win->non_client_cursor()));
+
+ // Non client area before we move.
+ MoveCursorTo(gfx::Point(15, 15));
+ EXPECT_EQ(mojom::Cursor::EAST_RESIZE, mojom::Cursor(cursor_id_));
+
+ // Changing the client area should cause a change.
+ win->SetClientArea(gfx::Insets(1, 1), std::vector<gfx::Rect>());
+ EXPECT_EQ(mojom::Cursor::IBEAM, mojom::Cursor(cursor_id_));
+}
+
+TEST_F(CursorTest, NonClientCursorChange) {
+ ServerWindow* win = BuildServerWindow();
+ win->SetPredefinedCursor(mojom::Cursor::IBEAM);
+ EXPECT_EQ(mojom::Cursor::IBEAM, mojom::Cursor(win->cursor()));
+ win->SetNonClientCursor(mojom::Cursor::EAST_RESIZE);
+ EXPECT_EQ(mojom::Cursor::EAST_RESIZE,
+ mojom::Cursor(win->non_client_cursor()));
+
+ MoveCursorTo(gfx::Point(15, 15));
+ EXPECT_EQ(mojom::Cursor::EAST_RESIZE, mojom::Cursor(cursor_id_));
+
+ win->SetNonClientCursor(mojom::Cursor::WEST_RESIZE);
+ EXPECT_EQ(mojom::Cursor::WEST_RESIZE, mojom::Cursor(cursor_id_));
+}
+
+TEST_F(CursorTest, IgnoreClientCursorChangeInNonClientArea) {
+ ServerWindow* win = BuildServerWindow();
+ win->SetPredefinedCursor(mojom::Cursor::IBEAM);
+ EXPECT_EQ(mojom::Cursor::IBEAM, mojom::Cursor(win->cursor()));
+ win->SetNonClientCursor(mojom::Cursor::EAST_RESIZE);
+ EXPECT_EQ(mojom::Cursor::EAST_RESIZE,
+ mojom::Cursor(win->non_client_cursor()));
+
+ MoveCursorTo(gfx::Point(15, 15));
+ EXPECT_EQ(mojom::Cursor::EAST_RESIZE, mojom::Cursor(cursor_id_));
+
+ win->SetPredefinedCursor(mojom::Cursor::HELP);
+ EXPECT_EQ(mojom::Cursor::EAST_RESIZE, mojom::Cursor(cursor_id_));
+}
+
+TEST_F(CursorTest, NonClientToClientByBoundsChange) {
+ ServerWindow* win = BuildServerWindow();
+ win->SetPredefinedCursor(mojom::Cursor::IBEAM);
+ EXPECT_EQ(mojom::Cursor::IBEAM, mojom::Cursor(win->cursor()));
+ win->SetNonClientCursor(mojom::Cursor::EAST_RESIZE);
+ EXPECT_EQ(mojom::Cursor::EAST_RESIZE,
+ mojom::Cursor(win->non_client_cursor()));
+
+ // Non client area before we move.
+ MoveCursorTo(gfx::Point(15, 15));
+ EXPECT_EQ(mojom::Cursor::EAST_RESIZE, mojom::Cursor(cursor_id_));
+
+ win->SetBounds(gfx::Rect(0, 0, 30, 30));
+ EXPECT_EQ(mojom::Cursor::IBEAM, mojom::Cursor(cursor_id_));
+}
+
+} // namespace test
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/default_access_policy.cc b/chromium/components/mus/ws/default_access_policy.cc
new file mode 100644
index 00000000000..1930dc05925
--- /dev/null
+++ b/chromium/components/mus/ws/default_access_policy.cc
@@ -0,0 +1,203 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/default_access_policy.h"
+
+#include "components/mus/ws/access_policy_delegate.h"
+#include "components/mus/ws/server_window.h"
+
+namespace mus {
+namespace ws {
+
+DefaultAccessPolicy::DefaultAccessPolicy() {}
+
+DefaultAccessPolicy::~DefaultAccessPolicy() {}
+
+void DefaultAccessPolicy::Init(ClientSpecificId client_id,
+ AccessPolicyDelegate* delegate) {
+ client_id_ = client_id;
+ delegate_ = delegate;
+}
+
+bool DefaultAccessPolicy::CanRemoveWindowFromParent(
+ const ServerWindow* window) const {
+ if (!WasCreatedByThisClient(window))
+ return false; // Can only unparent windows we created.
+
+ return delegate_->HasRootForAccessPolicy(window->parent()) ||
+ WasCreatedByThisClient(window->parent());
+}
+
+bool DefaultAccessPolicy::CanAddWindow(const ServerWindow* parent,
+ const ServerWindow* child) const {
+ return WasCreatedByThisClient(child) &&
+ (delegate_->HasRootForAccessPolicy(parent) ||
+ (WasCreatedByThisClient(parent) &&
+ !delegate_->IsWindowRootOfAnotherTreeForAccessPolicy(parent)));
+}
+
+bool DefaultAccessPolicy::CanAddTransientWindow(
+ const ServerWindow* parent,
+ const ServerWindow* child) const {
+ return (delegate_->HasRootForAccessPolicy(child) ||
+ WasCreatedByThisClient(child)) &&
+ (delegate_->HasRootForAccessPolicy(parent) ||
+ WasCreatedByThisClient(parent));
+}
+
+bool DefaultAccessPolicy::CanRemoveTransientWindowFromParent(
+ const ServerWindow* window) const {
+ return (delegate_->HasRootForAccessPolicy(window) ||
+ WasCreatedByThisClient(window)) &&
+ (delegate_->HasRootForAccessPolicy(window->transient_parent()) ||
+ WasCreatedByThisClient(window->transient_parent()));
+}
+
+bool DefaultAccessPolicy::CanSetModal(const ServerWindow* window) const {
+ return delegate_->HasRootForAccessPolicy(window) ||
+ WasCreatedByThisClient(window);
+}
+
+bool DefaultAccessPolicy::CanReorderWindow(
+ const ServerWindow* window,
+ const ServerWindow* relative_window,
+ mojom::OrderDirection direction) const {
+ return WasCreatedByThisClient(window) &&
+ WasCreatedByThisClient(relative_window);
+}
+
+bool DefaultAccessPolicy::CanDeleteWindow(const ServerWindow* window) const {
+ return WasCreatedByThisClient(window);
+}
+
+bool DefaultAccessPolicy::CanGetWindowTree(const ServerWindow* window) const {
+ return WasCreatedByThisClient(window) ||
+ delegate_->HasRootForAccessPolicy(window);
+}
+
+bool DefaultAccessPolicy::CanDescendIntoWindowForWindowTree(
+ const ServerWindow* window) const {
+ return (WasCreatedByThisClient(window) &&
+ !delegate_->IsWindowRootOfAnotherTreeForAccessPolicy(window)) ||
+ delegate_->HasRootForAccessPolicy(window);
+}
+
+bool DefaultAccessPolicy::CanEmbed(const ServerWindow* window) const {
+ return WasCreatedByThisClient(window);
+}
+
+bool DefaultAccessPolicy::CanChangeWindowVisibility(
+ const ServerWindow* window) const {
+ return WasCreatedByThisClient(window) ||
+ delegate_->HasRootForAccessPolicy(window);
+}
+
+bool DefaultAccessPolicy::CanChangeWindowOpacity(
+ const ServerWindow* window) const {
+ return WasCreatedByThisClient(window) ||
+ delegate_->HasRootForAccessPolicy(window);
+}
+
+bool DefaultAccessPolicy::CanSetWindowSurface(
+ const ServerWindow* window,
+ mojom::SurfaceType surface_type) const {
+ if (surface_type == mojom::SurfaceType::UNDERLAY)
+ return WasCreatedByThisClient(window);
+
+ // Once a window embeds another app, the embedder app is no longer able to
+ // call SetWindowSurfaceId() - this ability is transferred to the embedded
+ // app.
+ if (delegate_->IsWindowRootOfAnotherTreeForAccessPolicy(window))
+ return false;
+ return WasCreatedByThisClient(window) ||
+ delegate_->HasRootForAccessPolicy(window);
+}
+
+bool DefaultAccessPolicy::CanSetWindowBounds(const ServerWindow* window) const {
+ return WasCreatedByThisClient(window);
+}
+
+bool DefaultAccessPolicy::CanSetWindowProperties(
+ const ServerWindow* window) const {
+ return WasCreatedByThisClient(window);
+}
+
+bool DefaultAccessPolicy::CanSetWindowTextInputState(
+ const ServerWindow* window) const {
+ return WasCreatedByThisClient(window) ||
+ delegate_->HasRootForAccessPolicy(window);
+}
+
+bool DefaultAccessPolicy::CanSetCapture(const ServerWindow* window) const {
+ return WasCreatedByThisClient(window) ||
+ delegate_->HasRootForAccessPolicy(window);
+}
+
+bool DefaultAccessPolicy::CanSetFocus(const ServerWindow* window) const {
+ return !window || WasCreatedByThisClient(window) ||
+ delegate_->HasRootForAccessPolicy(window);
+}
+
+bool DefaultAccessPolicy::CanSetClientArea(const ServerWindow* window) const {
+ return WasCreatedByThisClient(window) ||
+ delegate_->HasRootForAccessPolicy(window);
+}
+
+bool DefaultAccessPolicy::CanSetHitTestMask(const ServerWindow* window) const {
+ return WasCreatedByThisClient(window) ||
+ delegate_->HasRootForAccessPolicy(window);
+}
+
+bool DefaultAccessPolicy::CanSetCursorProperties(
+ const ServerWindow* window) const {
+ return WasCreatedByThisClient(window) ||
+ delegate_->HasRootForAccessPolicy(window);
+}
+
+bool DefaultAccessPolicy::ShouldNotifyOnHierarchyChange(
+ const ServerWindow* window,
+ const ServerWindow** new_parent,
+ const ServerWindow** old_parent) const {
+ if (!WasCreatedByThisClient(window))
+ return false;
+
+ if (*new_parent && !WasCreatedByThisClient(*new_parent) &&
+ !delegate_->HasRootForAccessPolicy((*new_parent))) {
+ *new_parent = nullptr;
+ }
+
+ if (*old_parent && !WasCreatedByThisClient(*old_parent) &&
+ !delegate_->HasRootForAccessPolicy((*old_parent))) {
+ *old_parent = nullptr;
+ }
+ return true;
+}
+
+const ServerWindow* DefaultAccessPolicy::GetWindowForFocusChange(
+ const ServerWindow* focused) {
+ if (WasCreatedByThisClient(focused) ||
+ delegate_->HasRootForAccessPolicy(focused))
+ return focused;
+ return nullptr;
+}
+
+bool DefaultAccessPolicy::CanSetWindowManager() const {
+ return false;
+}
+
+bool DefaultAccessPolicy::WasCreatedByThisClient(
+ const ServerWindow* window) const {
+ return window->id().client_id == client_id_;
+}
+
+bool DefaultAccessPolicy::IsValidIdForNewWindow(
+ const ClientWindowId& id) const {
+ // Clients using DefaultAccessPolicy only see windows they have created (for
+ // the embed point they choose the id), so it's ok for clients to use whatever
+ // id they want.
+ return true;
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/default_access_policy.h b/chromium/components/mus/ws/default_access_policy.h
new file mode 100644
index 00000000000..9135fb9901d
--- /dev/null
+++ b/chromium/components/mus/ws/default_access_policy.h
@@ -0,0 +1,76 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_DEFAULT_ACCESS_POLICY_H_
+#define COMPONENTS_MUS_WS_DEFAULT_ACCESS_POLICY_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "components/mus/ws/access_policy.h"
+
+namespace mus {
+namespace ws {
+
+class AccessPolicyDelegate;
+
+// AccessPolicy for all clients, except the window manager.
+class DefaultAccessPolicy : public AccessPolicy {
+ public:
+ DefaultAccessPolicy();
+ ~DefaultAccessPolicy() override;
+
+ // AccessPolicy:
+ void Init(ClientSpecificId client_id,
+ AccessPolicyDelegate* delegate) override;
+ bool CanRemoveWindowFromParent(const ServerWindow* window) const override;
+ bool CanAddWindow(const ServerWindow* parent,
+ const ServerWindow* child) const override;
+ bool CanAddTransientWindow(const ServerWindow* parent,
+ const ServerWindow* child) const override;
+ bool CanRemoveTransientWindowFromParent(
+ const ServerWindow* window) const override;
+ bool CanSetModal(const ServerWindow* window) const override;
+ bool CanReorderWindow(const ServerWindow* window,
+ const ServerWindow* relative_window,
+ mojom::OrderDirection direction) const override;
+ bool CanDeleteWindow(const ServerWindow* window) const override;
+ bool CanGetWindowTree(const ServerWindow* window) const override;
+ bool CanDescendIntoWindowForWindowTree(
+ const ServerWindow* window) const override;
+ bool CanEmbed(const ServerWindow* window) const override;
+ bool CanChangeWindowVisibility(const ServerWindow* window) const override;
+ bool CanChangeWindowOpacity(const ServerWindow* window) const override;
+ bool CanSetWindowSurface(const ServerWindow* window,
+ mus::mojom::SurfaceType surface_type) const override;
+ bool CanSetWindowBounds(const ServerWindow* window) const override;
+ bool CanSetWindowProperties(const ServerWindow* window) const override;
+ bool CanSetWindowTextInputState(const ServerWindow* window) const override;
+ bool CanSetCapture(const ServerWindow* window) const override;
+ bool CanSetFocus(const ServerWindow* window) const override;
+ bool CanSetClientArea(const ServerWindow* window) const override;
+ bool CanSetHitTestMask(const ServerWindow* window) const override;
+ bool CanSetCursorProperties(const ServerWindow* window) const override;
+ bool ShouldNotifyOnHierarchyChange(
+ const ServerWindow* window,
+ const ServerWindow** new_parent,
+ const ServerWindow** old_parent) const override;
+ const ServerWindow* GetWindowForFocusChange(
+ const ServerWindow* focused) override;
+ bool CanSetWindowManager() const override;
+ bool IsValidIdForNewWindow(const ClientWindowId& id) const override;
+
+ private:
+ bool WasCreatedByThisClient(const ServerWindow* window) const;
+
+ ClientSpecificId client_id_ = 0u;
+ AccessPolicyDelegate* delegate_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(DefaultAccessPolicy);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_DEFAULT_ACCESS_POLICY_H_
diff --git a/chromium/components/mus/ws/display.cc b/chromium/components/mus/ws/display.cc
new file mode 100644
index 00000000000..84a4162de05
--- /dev/null
+++ b/chromium/components/mus/ws/display.cc
@@ -0,0 +1,419 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/display.h"
+
+#include <set>
+#include <vector>
+
+#include "base/debug/debugger.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/mus/common/types.h"
+#include "components/mus/ws/display_binding.h"
+#include "components/mus/ws/display_manager.h"
+#include "components/mus/ws/focus_controller.h"
+#include "components/mus/ws/platform_display.h"
+#include "components/mus/ws/platform_display_init_params.h"
+#include "components/mus/ws/user_activity_monitor.h"
+#include "components/mus/ws/window_manager_display_root.h"
+#include "components/mus/ws/window_manager_state.h"
+#include "components/mus/ws/window_manager_window_tree_factory.h"
+#include "components/mus/ws/window_server.h"
+#include "components/mus/ws/window_server_delegate.h"
+#include "components/mus/ws/window_tree.h"
+#include "components/mus/ws/window_tree_binding.h"
+#include "mojo/common/common_type_converters.h"
+#include "services/shell/public/interfaces/connector.mojom.h"
+#include "ui/base/cursor/cursor.h"
+
+namespace mus {
+namespace ws {
+
+Display::Display(WindowServer* window_server,
+ const PlatformDisplayInitParams& platform_display_init_params)
+ : id_(window_server->display_manager()->GetAndAdvanceNextDisplayId()),
+ window_server_(window_server),
+ platform_display_(PlatformDisplay::Create(platform_display_init_params)),
+ last_cursor_(ui::kCursorNone) {
+ platform_display_->Init(this);
+
+ window_server_->window_manager_window_tree_factory_set()->AddObserver(this);
+ window_server_->user_id_tracker()->AddObserver(this);
+}
+
+Display::~Display() {
+ window_server_->user_id_tracker()->RemoveObserver(this);
+
+ window_server_->window_manager_window_tree_factory_set()->RemoveObserver(
+ this);
+
+ if (!focus_controller_) {
+ focus_controller_->RemoveObserver(this);
+ focus_controller_.reset();
+ }
+
+ for (ServerWindow* window : windows_needing_frame_destruction_)
+ window->RemoveObserver(this);
+
+ // If there is a |binding_| then the tree was created specifically for this
+ // display (which corresponds to a WindowTreeHost).
+ if (binding_ && !window_manager_display_root_map_.empty()) {
+ window_server_->DestroyTree(window_manager_display_root_map_.begin()
+ ->second->window_manager_state()
+ ->window_tree());
+ }
+}
+
+void Display::Init(std::unique_ptr<DisplayBinding> binding) {
+ init_called_ = true;
+ binding_ = std::move(binding);
+ display_manager()->AddDisplay(this);
+ InitWindowManagerDisplayRootsIfNecessary();
+}
+
+DisplayManager* Display::display_manager() {
+ return window_server_->display_manager();
+}
+
+const DisplayManager* Display::display_manager() const {
+ return window_server_->display_manager();
+}
+
+mojom::DisplayPtr Display::ToMojomDisplay() const {
+ mojom::DisplayPtr display_ptr = mojom::Display::New();
+ display_ptr = mojom::Display::New();
+ display_ptr->id = id_;
+ // TODO(sky): Display should know it's origin.
+ display_ptr->bounds.SetRect(0, 0, root_->bounds().size().width(),
+ root_->bounds().size().height());
+ // TODO(sky): window manager needs an API to set the work area.
+ display_ptr->work_area = display_ptr->bounds;
+ display_ptr->device_pixel_ratio = platform_display_->GetDeviceScaleFactor();
+ display_ptr->rotation = platform_display_->GetRotation();
+ // TODO(sky): make this real.
+ display_ptr->is_primary = true;
+ // TODO(sky): make this real.
+ display_ptr->touch_support = mojom::TouchSupport::UNKNOWN;
+ display_ptr->frame_decoration_values = mojom::FrameDecorationValues::New();
+ return display_ptr;
+}
+
+void Display::SchedulePaint(const ServerWindow* window,
+ const gfx::Rect& bounds) {
+ DCHECK(root_->Contains(window));
+ platform_display_->SchedulePaint(window, bounds);
+}
+
+void Display::ScheduleSurfaceDestruction(ServerWindow* window) {
+ if (!platform_display_->IsFramePending()) {
+ window->DestroySurfacesScheduledForDestruction();
+ return;
+ }
+ if (windows_needing_frame_destruction_.count(window))
+ return;
+ windows_needing_frame_destruction_.insert(window);
+ window->AddObserver(this);
+}
+
+mojom::Rotation Display::GetRotation() const {
+ return platform_display_->GetRotation();
+}
+
+gfx::Size Display::GetSize() const {
+ return root_->bounds().size();
+}
+
+int64_t Display::GetPlatformDisplayId() const {
+ return platform_display_->GetDisplayId();
+}
+
+ServerWindow* Display::GetRootWithId(const WindowId& id) {
+ if (id == root_->id())
+ return root_.get();
+ for (auto& pair : window_manager_display_root_map_) {
+ if (pair.second->root()->id() == id)
+ return pair.second->root();
+ }
+ return nullptr;
+}
+
+WindowManagerDisplayRoot* Display::GetWindowManagerDisplayRootWithRoot(
+ const ServerWindow* window) {
+ for (auto& pair : window_manager_display_root_map_) {
+ if (pair.second->root() == window)
+ return pair.second.get();
+ }
+ return nullptr;
+}
+
+const WindowManagerDisplayRoot* Display::GetWindowManagerDisplayRootForUser(
+ const UserId& user_id) const {
+ auto iter = window_manager_display_root_map_.find(user_id);
+ return iter == window_manager_display_root_map_.end() ? nullptr
+ : iter->second.get();
+}
+
+const WindowManagerDisplayRoot* Display::GetActiveWindowManagerDisplayRoot()
+ const {
+ return GetWindowManagerDisplayRootForUser(
+ window_server_->user_id_tracker()->active_id());
+}
+
+bool Display::SetFocusedWindow(ServerWindow* new_focused_window) {
+ ServerWindow* old_focused_window = focus_controller_->GetFocusedWindow();
+ if (old_focused_window == new_focused_window)
+ return true;
+ DCHECK(!new_focused_window || root_window()->Contains(new_focused_window));
+ return focus_controller_->SetFocusedWindow(new_focused_window);
+}
+
+ServerWindow* Display::GetFocusedWindow() {
+ return focus_controller_->GetFocusedWindow();
+}
+
+void Display::ActivateNextWindow() {
+ // TODO(sky): this is wrong, needs to figure out the next window to activate
+ // and then route setting through WindowServer.
+ focus_controller_->ActivateNextWindow();
+}
+
+void Display::AddActivationParent(ServerWindow* window) {
+ activation_parents_.Add(window);
+}
+
+void Display::RemoveActivationParent(ServerWindow* window) {
+ activation_parents_.Remove(window);
+}
+
+void Display::UpdateTextInputState(ServerWindow* window,
+ const ui::TextInputState& state) {
+ // Do not need to update text input for unfocused windows.
+ if (!platform_display_ || focus_controller_->GetFocusedWindow() != window)
+ return;
+ platform_display_->UpdateTextInputState(state);
+}
+
+void Display::SetImeVisibility(ServerWindow* window, bool visible) {
+ // Do not need to show or hide IME for unfocused window.
+ if (focus_controller_->GetFocusedWindow() != window)
+ return;
+ platform_display_->SetImeVisibility(visible);
+}
+
+void Display::OnWillDestroyTree(WindowTree* tree) {
+ for (auto it = window_manager_display_root_map_.begin();
+ it != window_manager_display_root_map_.end(); ++it) {
+ if (it->second->window_manager_state()->window_tree() == tree) {
+ window_manager_display_root_map_.erase(it);
+ break;
+ }
+ }
+}
+
+void Display::UpdateNativeCursor(int32_t cursor_id) {
+ if (cursor_id != last_cursor_) {
+ platform_display_->SetCursorById(cursor_id);
+ last_cursor_ = cursor_id;
+ }
+}
+
+void Display::SetSize(const gfx::Size& size) {
+ platform_display_->SetViewportSize(size);
+}
+
+void Display::SetTitle(const mojo::String& title) {
+ platform_display_->SetTitle(title.To<base::string16>());
+}
+
+void Display::InitWindowManagerDisplayRootsIfNecessary() {
+ if (!init_called_ || !root_)
+ return;
+
+ display_manager()->OnDisplayAcceleratedWidgetAvailable(this);
+ if (binding_) {
+ std::unique_ptr<WindowManagerDisplayRoot> display_root_ptr(
+ new WindowManagerDisplayRoot(this));
+ WindowManagerDisplayRoot* display_root = display_root_ptr.get();
+ // For this case we never create additional displays roots, so any
+ // id works.
+ window_manager_display_root_map_[shell::mojom::kRootUserID] =
+ std::move(display_root_ptr);
+ WindowTree* window_tree = binding_->CreateWindowTree(display_root->root());
+ display_root->window_manager_state_ = window_tree->window_manager_state();
+ } else {
+ CreateWindowManagerDisplayRootsFromFactories();
+ }
+}
+
+void Display::CreateWindowManagerDisplayRootsFromFactories() {
+ std::vector<WindowManagerWindowTreeFactory*> factories =
+ window_server_->window_manager_window_tree_factory_set()->GetFactories();
+ for (WindowManagerWindowTreeFactory* factory : factories) {
+ if (factory->window_tree())
+ CreateWindowManagerDisplayRootFromFactory(factory);
+ }
+}
+
+void Display::CreateWindowManagerDisplayRootFromFactory(
+ WindowManagerWindowTreeFactory* factory) {
+ std::unique_ptr<WindowManagerDisplayRoot> display_root_ptr(
+ new WindowManagerDisplayRoot(this));
+ WindowManagerDisplayRoot* display_root = display_root_ptr.get();
+ window_manager_display_root_map_[factory->user_id()] =
+ std::move(display_root_ptr);
+ display_root->window_manager_state_ =
+ factory->window_tree()->window_manager_state();
+ const bool is_active =
+ factory->user_id() == window_server_->user_id_tracker()->active_id();
+ display_root->root()->SetVisible(is_active);
+ display_root->window_manager_state()->window_tree()->AddRootForWindowManager(
+ display_root->root());
+}
+
+ServerWindow* Display::GetRootWindow() {
+ return root_.get();
+}
+
+void Display::OnEvent(const ui::Event& event) {
+ WindowManagerDisplayRoot* display_root = GetActiveWindowManagerDisplayRoot();
+ if (display_root)
+ display_root->window_manager_state()->ProcessEvent(event);
+ window_server_
+ ->GetUserActivityMonitorForUser(
+ window_server_->user_id_tracker()->active_id())
+ ->OnUserActivity();
+}
+
+void Display::OnNativeCaptureLost() {
+ WindowManagerDisplayRoot* display_root = GetActiveWindowManagerDisplayRoot();
+ if (display_root)
+ display_root->window_manager_state()->SetCapture(nullptr, kInvalidClientId);
+}
+
+void Display::OnDisplayClosed() {
+ display_manager()->DestroyDisplay(this);
+}
+
+void Display::OnViewportMetricsChanged(const ViewportMetrics& old_metrics,
+ const ViewportMetrics& new_metrics) {
+ if (!root_) {
+ root_.reset(window_server_->CreateServerWindow(
+ display_manager()->GetAndAdvanceNextRootId(),
+ ServerWindow::Properties()));
+ root_->SetBounds(gfx::Rect(new_metrics.size_in_pixels));
+ root_->SetVisible(true);
+ focus_controller_.reset(new FocusController(this, root_.get()));
+ focus_controller_->AddObserver(this);
+ InitWindowManagerDisplayRootsIfNecessary();
+ } else {
+ root_->SetBounds(gfx::Rect(new_metrics.size_in_pixels));
+ const gfx::Rect wm_bounds(root_->bounds().size());
+ for (auto& pair : window_manager_display_root_map_)
+ pair.second->root()->SetBounds(wm_bounds);
+ }
+ display_manager()->OnDisplayUpdate(this);
+}
+
+void Display::OnCompositorFrameDrawn() {
+ std::set<ServerWindow*> windows;
+ windows.swap(windows_needing_frame_destruction_);
+ for (ServerWindow* window : windows) {
+ window->RemoveObserver(this);
+ window->DestroySurfacesScheduledForDestruction();
+ }
+}
+
+bool Display::CanHaveActiveChildren(ServerWindow* window) const {
+ return window && activation_parents_.Contains(window);
+}
+
+void Display::OnActivationChanged(ServerWindow* old_active_window,
+ ServerWindow* new_active_window) {
+ DCHECK_NE(new_active_window, old_active_window);
+ if (new_active_window && new_active_window->parent()) {
+ // Raise the new active window.
+ // TODO(sad): Let the WM dictate whether to raise the window or not?
+ new_active_window->parent()->StackChildAtTop(new_active_window);
+ }
+}
+
+void Display::OnFocusChanged(FocusControllerChangeSource change_source,
+ ServerWindow* old_focused_window,
+ ServerWindow* new_focused_window) {
+ // TODO(sky): focus is global, not per windowtreehost. Move.
+
+ // There are up to four clients that need to be notified:
+ // . the client containing |old_focused_window|.
+ // . the client with |old_focused_window| as its root.
+ // . the client containing |new_focused_window|.
+ // . the client with |new_focused_window| as its root.
+ // Some of these client may be the same. The following takes care to notify
+ // each only once.
+ WindowTree* owning_tree_old = nullptr;
+ WindowTree* embedded_tree_old = nullptr;
+
+ if (old_focused_window) {
+ owning_tree_old =
+ window_server_->GetTreeWithId(old_focused_window->id().client_id);
+ if (owning_tree_old) {
+ owning_tree_old->ProcessFocusChanged(old_focused_window,
+ new_focused_window);
+ }
+ embedded_tree_old = window_server_->GetTreeWithRoot(old_focused_window);
+ if (embedded_tree_old) {
+ DCHECK_NE(owning_tree_old, embedded_tree_old);
+ embedded_tree_old->ProcessFocusChanged(old_focused_window,
+ new_focused_window);
+ }
+ }
+ WindowTree* owning_tree_new = nullptr;
+ WindowTree* embedded_tree_new = nullptr;
+ if (new_focused_window) {
+ owning_tree_new =
+ window_server_->GetTreeWithId(new_focused_window->id().client_id);
+ if (owning_tree_new && owning_tree_new != owning_tree_old &&
+ owning_tree_new != embedded_tree_old) {
+ owning_tree_new->ProcessFocusChanged(old_focused_window,
+ new_focused_window);
+ }
+ embedded_tree_new = window_server_->GetTreeWithRoot(new_focused_window);
+ if (embedded_tree_new && embedded_tree_new != owning_tree_old &&
+ embedded_tree_new != embedded_tree_old) {
+ DCHECK_NE(owning_tree_new, embedded_tree_new);
+ embedded_tree_new->ProcessFocusChanged(old_focused_window,
+ new_focused_window);
+ }
+ }
+
+ // WindowManagers are always notified of focus changes.
+ WindowManagerDisplayRoot* display_root = GetActiveWindowManagerDisplayRoot();
+ if (display_root) {
+ WindowTree* wm_tree = display_root->window_manager_state()->window_tree();
+ if (wm_tree != owning_tree_old && wm_tree != embedded_tree_old &&
+ wm_tree != owning_tree_new && wm_tree != embedded_tree_new) {
+ wm_tree->ProcessFocusChanged(old_focused_window, new_focused_window);
+ }
+ }
+
+ UpdateTextInputState(new_focused_window,
+ new_focused_window->text_input_state());
+}
+
+void Display::OnWindowDestroyed(ServerWindow* window) {
+ windows_needing_frame_destruction_.erase(window);
+ window->RemoveObserver(this);
+}
+
+void Display::OnUserIdRemoved(const UserId& id) {
+ window_manager_display_root_map_.erase(id);
+}
+
+void Display::OnWindowManagerWindowTreeFactoryReady(
+ WindowManagerWindowTreeFactory* factory) {
+ if (!binding_)
+ CreateWindowManagerDisplayRootFromFactory(factory);
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/display.h b/chromium/components/mus/ws/display.h
new file mode 100644
index 00000000000..419b1909c03
--- /dev/null
+++ b/chromium/components/mus/ws/display.h
@@ -0,0 +1,224 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_DISPLAY_H_
+#define COMPONENTS_MUS_WS_DISPLAY_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <queue>
+#include <set>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/mus/common/types.h"
+#include "components/mus/public/interfaces/window_manager_constants.mojom.h"
+#include "components/mus/public/interfaces/window_tree_host.mojom.h"
+#include "components/mus/ws/focus_controller_delegate.h"
+#include "components/mus/ws/focus_controller_observer.h"
+#include "components/mus/ws/platform_display.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/server_window_observer.h"
+#include "components/mus/ws/server_window_tracker.h"
+#include "components/mus/ws/user_id_tracker_observer.h"
+#include "components/mus/ws/window_manager_window_tree_factory_set_observer.h"
+
+namespace mus {
+namespace ws {
+
+class DisplayBinding;
+class DisplayManager;
+class FocusController;
+struct PlatformDisplayInitParams;
+class WindowManagerDisplayRoot;
+class WindowServer;
+class WindowTree;
+
+namespace test {
+class DisplayTestApi;
+}
+
+// Displays manages the state associated with a single display. Display has a
+// single root window whose children are the roots for a per-user
+// WindowManager. Display is configured in two distinct
+// ways:
+// . with a DisplayBinding. In this mode there is only ever one WindowManager
+// for the display, which comes from the client that created the
+// Display.
+// . without a DisplayBinding. In this mode a WindowManager is automatically
+// created per user.
+class Display : public PlatformDisplayDelegate,
+ public mojom::WindowTreeHost,
+ public FocusControllerObserver,
+ public FocusControllerDelegate,
+ public ServerWindowObserver,
+ public UserIdTrackerObserver,
+ public WindowManagerWindowTreeFactorySetObserver {
+ public:
+ Display(WindowServer* window_server,
+ const PlatformDisplayInitParams& platform_display_init_params);
+ ~Display() override;
+
+ // Initializes state that depends on the existence of a Display.
+ void Init(std::unique_ptr<DisplayBinding> binding);
+
+ uint32_t id() const { return id_; }
+
+ DisplayManager* display_manager();
+ const DisplayManager* display_manager() const;
+
+ PlatformDisplay* platform_display() { return platform_display_.get(); }
+
+ // Returns a mojom::Display for the specified display. WindowManager specific
+ // values are not set.
+ mojom::DisplayPtr ToMojomDisplay() const;
+
+ // Schedules a paint for the specified region in the coordinates of |window|.
+ void SchedulePaint(const ServerWindow* window, const gfx::Rect& bounds);
+
+ // Schedules destruction of surfaces in |window|. If a frame has been
+ // scheduled but not drawn surface destruction is delayed until the frame is
+ // drawn, otherwise destruction is immediate.
+ void ScheduleSurfaceDestruction(ServerWindow* window);
+
+ mojom::Rotation GetRotation() const;
+ gfx::Size GetSize() const;
+
+ // Returns the id for the corresponding id.
+ int64_t GetPlatformDisplayId() const;
+
+ WindowServer* window_server() { return window_server_; }
+
+ // Returns the root of the Display. The root's children are the roots
+ // of the corresponding WindowManagers.
+ ServerWindow* root_window() { return root_.get(); }
+ const ServerWindow* root_window() const { return root_.get(); }
+
+ // Returns the ServerWindow whose id is |id|. This does not do a search over
+ // all windows, rather just the display and window manager root windows.
+ //
+ // In general you shouldn't use this, rather use WindowServer::GetWindow(),
+ // which calls this as necessary.
+ ServerWindow* GetRootWithId(const WindowId& id);
+
+ WindowManagerDisplayRoot* GetWindowManagerDisplayRootWithRoot(
+ const ServerWindow* window);
+ WindowManagerDisplayRoot* GetWindowManagerDisplayRootForUser(
+ const UserId& user_id) {
+ return const_cast<WindowManagerDisplayRoot*>(
+ const_cast<const Display*>(this)->GetWindowManagerDisplayRootForUser(
+ user_id));
+ }
+ const WindowManagerDisplayRoot* GetWindowManagerDisplayRootForUser(
+ const UserId& user_id) const;
+ WindowManagerDisplayRoot* GetActiveWindowManagerDisplayRoot() {
+ return const_cast<WindowManagerDisplayRoot*>(
+ const_cast<const Display*>(this)->GetActiveWindowManagerDisplayRoot());
+ }
+ const WindowManagerDisplayRoot* GetActiveWindowManagerDisplayRoot() const;
+ size_t num_window_manger_states() const {
+ return window_manager_display_root_map_.size();
+ }
+
+ // TODO(sky): this should only be called by WindowServer, move to interface
+ // used by WindowServer.
+ // See description of WindowServer::SetFocusedWindow() for details on return
+ // value.
+ bool SetFocusedWindow(ServerWindow* window);
+ // NOTE: this returns the focused window only if the focused window is in this
+ // display. If this returns null focus may be in another display.
+ ServerWindow* GetFocusedWindow();
+
+ void ActivateNextWindow();
+
+ void AddActivationParent(ServerWindow* window);
+ void RemoveActivationParent(ServerWindow* window);
+
+ void UpdateTextInputState(ServerWindow* window,
+ const ui::TextInputState& state);
+ void SetImeVisibility(ServerWindow* window, bool visible);
+
+ // Called just before |tree| is destroyed.
+ void OnWillDestroyTree(WindowTree* tree);
+
+ void UpdateNativeCursor(int32_t cursor_id);
+
+ // mojom::WindowTreeHost:
+ void SetSize(const gfx::Size& size) override;
+ void SetTitle(const mojo::String& title) override;
+
+ private:
+ friend class test::DisplayTestApi;
+
+ using WindowManagerDisplayRootMap =
+ std::map<UserId, std::unique_ptr<WindowManagerDisplayRoot>>;
+
+ // Inits the necessary state once the display is ready.
+ void InitWindowManagerDisplayRootsIfNecessary();
+
+ // Creates the set of WindowManagerDisplayRoots from the
+ // WindowManagerWindowTreeFactorySet.
+ void CreateWindowManagerDisplayRootsFromFactories();
+
+ void CreateWindowManagerDisplayRootFromFactory(
+ WindowManagerWindowTreeFactory* factory);
+
+ // PlatformDisplayDelegate:
+ ServerWindow* GetRootWindow() override;
+ void OnEvent(const ui::Event& event) override;
+ void OnNativeCaptureLost() override;
+ void OnDisplayClosed() override;
+ void OnViewportMetricsChanged(const ViewportMetrics& old_metrics,
+ const ViewportMetrics& new_metrics) override;
+ void OnCompositorFrameDrawn() override;
+
+ // FocusControllerDelegate:
+ bool CanHaveActiveChildren(ServerWindow* window) const override;
+
+ // FocusControllerObserver:
+ void OnActivationChanged(ServerWindow* old_active_window,
+ ServerWindow* new_active_window) override;
+ void OnFocusChanged(FocusControllerChangeSource change_source,
+ ServerWindow* old_focused_window,
+ ServerWindow* new_focused_window) override;
+
+ // ServerWindowObserver:
+ void OnWindowDestroyed(ServerWindow* window) override;
+
+ // UserIdTrackerObserver:
+ void OnUserIdRemoved(const UserId& id) override;
+
+ // WindowManagerWindowTreeFactorySetObserver:
+ void OnWindowManagerWindowTreeFactoryReady(
+ WindowManagerWindowTreeFactory* factory) override;
+
+ const uint32_t id_;
+ std::unique_ptr<DisplayBinding> binding_;
+ // Set once Init() has been called.
+ bool init_called_ = false;
+ WindowServer* const window_server_;
+ std::unique_ptr<ServerWindow> root_;
+ std::unique_ptr<PlatformDisplay> platform_display_;
+ std::unique_ptr<FocusController> focus_controller_;
+
+ // The last cursor set. Used to track whether we need to change the cursor.
+ int32_t last_cursor_;
+
+ ServerWindowTracker activation_parents_;
+
+ // Set of windows with surfaces that need to be destroyed once the frame
+ // draws.
+ std::set<ServerWindow*> windows_needing_frame_destruction_;
+
+ WindowManagerDisplayRootMap window_manager_display_root_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(Display);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_DISPLAY_H_
diff --git a/chromium/components/mus/ws/display_binding.cc b/chromium/components/mus/ws/display_binding.cc
new file mode 100644
index 00000000000..519bef4a2f8
--- /dev/null
+++ b/chromium/components/mus/ws/display_binding.cc
@@ -0,0 +1,39 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/display_binding.h"
+
+#include "base/memory/ptr_util.h"
+#include "components/mus/ws/display.h"
+#include "components/mus/ws/window_manager_access_policy.h"
+#include "components/mus/ws/window_server.h"
+#include "components/mus/ws/window_tree.h"
+#include "services/shell/public/interfaces/connector.mojom.h"
+
+namespace mus {
+namespace ws {
+
+DisplayBindingImpl::DisplayBindingImpl(mojom::WindowTreeHostRequest request,
+ Display* display,
+ const UserId& user_id,
+ mojom::WindowTreeClientPtr client,
+ WindowServer* window_server)
+ : window_server_(window_server),
+ user_id_(user_id),
+ binding_(display, std::move(request)),
+ client_(std::move(client)) {}
+
+DisplayBindingImpl::~DisplayBindingImpl() {}
+
+WindowTree* DisplayBindingImpl::CreateWindowTree(ServerWindow* root) {
+ const uint32_t embed_flags = 0;
+ WindowTree* tree = window_server_->EmbedAtWindow(
+ root, user_id_, std::move(client_), embed_flags,
+ base::WrapUnique(new WindowManagerAccessPolicy));
+ tree->ConfigureWindowManager();
+ return tree;
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/display_binding.h b/chromium/components/mus/ws/display_binding.h
new file mode 100644
index 00000000000..e1f46124870
--- /dev/null
+++ b/chromium/components/mus/ws/display_binding.h
@@ -0,0 +1,60 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_DISPLAY_BINDING_H_
+#define COMPONENTS_MUS_WS_DISPLAY_BINDING_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "components/mus/public/interfaces/window_tree_host.mojom.h"
+#include "components/mus/ws/display.h"
+#include "components/mus/ws/user_id.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace mus {
+namespace ws {
+
+class ServerWindow;
+class WindowServer;
+class WindowTree;
+
+// DisplayBinding manages the binding between a Display and it's mojo clients.
+// DisplayBinding is used when a Display is created via a
+// WindowTreeHostFactory.
+//
+// DisplayBinding is owned by Display.
+class DisplayBinding {
+ public:
+ virtual ~DisplayBinding() {}
+
+ virtual WindowTree* CreateWindowTree(ServerWindow* root) = 0;
+};
+
+// Live implementation of DisplayBinding.
+class DisplayBindingImpl : public DisplayBinding {
+ public:
+ DisplayBindingImpl(mojom::WindowTreeHostRequest request,
+ Display* display,
+ const UserId& user_id,
+ mojom::WindowTreeClientPtr client,
+ WindowServer* window_server);
+ ~DisplayBindingImpl() override;
+
+ private:
+ // DisplayBinding:
+ WindowTree* CreateWindowTree(ServerWindow* root) override;
+
+ WindowServer* window_server_;
+ const UserId user_id_;
+ mojo::Binding<mojom::WindowTreeHost> binding_;
+ mojom::WindowTreeClientPtr client_;
+
+ DISALLOW_COPY_AND_ASSIGN(DisplayBindingImpl);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_DISPLAY_BINDING_H_
diff --git a/chromium/components/mus/ws/display_manager.cc b/chromium/components/mus/ws/display_manager.cc
new file mode 100644
index 00000000000..d5554093ec1
--- /dev/null
+++ b/chromium/components/mus/ws/display_manager.cc
@@ -0,0 +1,167 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/display_manager.h"
+
+#include "base/memory/ptr_util.h"
+#include "components/mus/ws/display.h"
+#include "components/mus/ws/display_manager_delegate.h"
+#include "components/mus/ws/event_dispatcher.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/user_display_manager.h"
+#include "components/mus/ws/user_id_tracker.h"
+#include "components/mus/ws/window_manager_state.h"
+
+namespace mus {
+namespace ws {
+
+DisplayManager::DisplayManager(DisplayManagerDelegate* delegate,
+ UserIdTracker* user_id_tracker)
+ // |next_root_id_| is used as the lower bits, so that starting at 0 is
+ // fine. |next_display_id_| is used by itself, so we start at 1 to reserve
+ // 0 as invalid.
+ : delegate_(delegate),
+ user_id_tracker_(user_id_tracker),
+ next_root_id_(0),
+ next_display_id_(1) {
+ user_id_tracker_->AddObserver(this);
+}
+
+DisplayManager::~DisplayManager() {
+ user_id_tracker_->RemoveObserver(this);
+ DestroyAllDisplays();
+}
+
+UserDisplayManager* DisplayManager::GetUserDisplayManager(
+ const UserId& user_id) {
+ if (!user_display_managers_.count(user_id)) {
+ user_display_managers_[user_id] =
+ base::WrapUnique(new UserDisplayManager(this, delegate_, user_id));
+ }
+ return user_display_managers_[user_id].get();
+}
+
+void DisplayManager::AddDisplay(Display* display) {
+ DCHECK_EQ(0u, pending_displays_.count(display));
+ pending_displays_.insert(display);
+}
+
+void DisplayManager::DestroyDisplay(Display* display) {
+ if (pending_displays_.count(display)) {
+ pending_displays_.erase(display);
+ } else {
+ for (const auto& pair : user_display_managers_)
+ pair.second->OnWillDestroyDisplay(display);
+
+ DCHECK(displays_.count(display));
+ displays_.erase(display);
+ }
+ delete display;
+
+ // If we have no more roots left, let the app know so it can terminate.
+ // TODO(sky): move to delegate/observer.
+ if (!displays_.size() && !pending_displays_.size())
+ delegate_->OnNoMoreDisplays();
+}
+
+void DisplayManager::DestroyAllDisplays() {
+ while (!pending_displays_.empty())
+ DestroyDisplay(*pending_displays_.begin());
+ DCHECK(pending_displays_.empty());
+
+ while (!displays_.empty())
+ DestroyDisplay(*displays_.begin());
+ DCHECK(displays_.empty());
+}
+
+std::set<const Display*> DisplayManager::displays() const {
+ std::set<const Display*> ret_value(displays_.begin(), displays_.end());
+ return ret_value;
+}
+
+void DisplayManager::OnDisplayUpdate(Display* display) {
+ for (const auto& pair : user_display_managers_)
+ pair.second->OnDisplayUpdate(display);
+}
+
+Display* DisplayManager::GetDisplayContaining(ServerWindow* window) {
+ return const_cast<Display*>(
+ static_cast<const DisplayManager*>(this)->GetDisplayContaining(window));
+}
+
+const Display* DisplayManager::GetDisplayContaining(
+ const ServerWindow* window) const {
+ while (window && window->parent())
+ window = window->parent();
+ for (Display* display : displays_) {
+ if (window == display->root_window())
+ return display;
+ }
+ return nullptr;
+}
+
+const WindowManagerDisplayRoot* DisplayManager::GetWindowManagerDisplayRoot(
+ const ServerWindow* window) const {
+ const ServerWindow* last = window;
+ while (window && window->parent()) {
+ last = window;
+ window = window->parent();
+ }
+ for (Display* display : displays_) {
+ if (window == display->root_window())
+ return display->GetWindowManagerDisplayRootWithRoot(last);
+ }
+ return nullptr;
+}
+
+WindowManagerDisplayRoot* DisplayManager::GetWindowManagerDisplayRoot(
+ const ServerWindow* window) {
+ return const_cast<WindowManagerDisplayRoot*>(
+ const_cast<const DisplayManager*>(this)->GetWindowManagerDisplayRoot(
+ window));
+}
+
+WindowId DisplayManager::GetAndAdvanceNextRootId() {
+ // TODO(sky): handle wrapping!
+ const uint16_t id = next_root_id_++;
+ DCHECK_LT(id, next_root_id_);
+ return RootWindowId(id);
+}
+
+uint32_t DisplayManager::GetAndAdvanceNextDisplayId() {
+ // TODO(sky): handle wrapping!
+ const uint32_t id = next_display_id_++;
+ DCHECK_LT(id, next_display_id_);
+ return id;
+}
+
+void DisplayManager::OnDisplayAcceleratedWidgetAvailable(Display* display) {
+ DCHECK_NE(0u, pending_displays_.count(display));
+ DCHECK_EQ(0u, displays_.count(display));
+ const bool is_first_display = displays_.empty();
+ displays_.insert(display);
+ pending_displays_.erase(display);
+ if (is_first_display)
+ delegate_->OnFirstDisplayReady();
+}
+
+void DisplayManager::OnActiveUserIdChanged(const UserId& previously_active_id,
+ const UserId& active_id) {
+ WindowManagerState* previous_window_manager_state =
+ delegate_->GetWindowManagerStateForUser(previously_active_id);
+ gfx::Point mouse_location_on_screen;
+ if (previous_window_manager_state) {
+ mouse_location_on_screen = previous_window_manager_state->event_dispatcher()
+ ->mouse_pointer_last_location();
+ previous_window_manager_state->Deactivate();
+ }
+
+ WindowManagerState* current_window_manager_state =
+ delegate_->GetWindowManagerStateForUser(active_id);
+ if (current_window_manager_state)
+ current_window_manager_state->Activate(mouse_location_on_screen);
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/display_manager.h b/chromium/components/mus/ws/display_manager.h
new file mode 100644
index 00000000000..0c01f217e31
--- /dev/null
+++ b/chromium/components/mus/ws/display_manager.h
@@ -0,0 +1,102 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_DISPLAY_MANAGER_H_
+#define COMPONENTS_MUS_WS_DISPLAY_MANAGER_H_
+
+#include <map>
+#include <memory>
+#include <set>
+
+#include "base/macros.h"
+#include "components/mus/ws/ids.h"
+#include "components/mus/ws/user_id.h"
+#include "components/mus/ws/user_id_tracker_observer.h"
+
+namespace mus {
+namespace ws {
+
+class Display;
+class DisplayManagerDelegate;
+class ServerWindow;
+class UserDisplayManager;
+class UserIdTracker;
+class WindowManagerDisplayRoot;
+
+// DisplayManager manages the set of Displays. DisplayManager distinguishes
+// between displays that do yet have an accelerated widget (pending), vs
+// those that do.
+class DisplayManager : public UserIdTrackerObserver {
+ public:
+ DisplayManager(DisplayManagerDelegate* delegate,
+ UserIdTracker* user_id_tracker);
+ ~DisplayManager() override;
+
+ // Returns the UserDisplayManager for |user_id|. DisplayManager owns the
+ // return value.
+ UserDisplayManager* GetUserDisplayManager(const UserId& user_id);
+
+ // Adds/removes a Display. DisplayManager owns the Displays.
+ // TODO(sky): make add take a scoped_ptr.
+ void AddDisplay(Display* display);
+ void DestroyDisplay(Display* display);
+ void DestroyAllDisplays();
+ const std::set<Display*>& displays() { return displays_; }
+ std::set<const Display*> displays() const;
+
+ // Notifies when something about the Display changes.
+ void OnDisplayUpdate(Display* display);
+
+ // Returns the Display that contains |window|, or null if |window| is not
+ // attached to a display.
+ Display* GetDisplayContaining(ServerWindow* window);
+ const Display* GetDisplayContaining(const ServerWindow* window) const;
+
+ const WindowManagerDisplayRoot* GetWindowManagerDisplayRoot(
+ const ServerWindow* window) const;
+ // TODO(sky): constness here is wrong! fix!
+ WindowManagerDisplayRoot* GetWindowManagerDisplayRoot(
+ const ServerWindow* window);
+
+ bool has_displays() const { return !displays_.empty(); }
+ bool has_active_or_pending_displays() const {
+ return !displays_.empty() || !pending_displays_.empty();
+ }
+
+ // Returns the id for the next root window (both for the root of a Display
+ // as well as the root of WindowManagers).
+ WindowId GetAndAdvanceNextRootId();
+
+ uint32_t GetAndAdvanceNextDisplayId();
+
+ // Called when the AcceleratedWidget is available for |display|.
+ void OnDisplayAcceleratedWidgetAvailable(Display* display);
+
+ private:
+ // UserIdTrackerObserver:
+ void OnActiveUserIdChanged(const UserId& previously_active_id,
+ const UserId& active_id) override;
+
+ DisplayManagerDelegate* delegate_;
+ UserIdTracker* user_id_tracker_;
+
+ // Displays are initially added to |pending_displays_|. When the display is
+ // initialized it is moved to |displays_|. WindowServer owns the Displays.
+ std::set<Display*> pending_displays_;
+ std::set<Display*> displays_;
+
+ std::map<UserId, std::unique_ptr<UserDisplayManager>> user_display_managers_;
+
+ // ID to use for next root node.
+ ClientSpecificId next_root_id_;
+
+ uint32_t next_display_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(DisplayManager);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_DISPLAY_MANAGER_H_
diff --git a/chromium/components/mus/ws/display_manager_delegate.h b/chromium/components/mus/ws/display_manager_delegate.h
new file mode 100644
index 00000000000..9d59e2fba8b
--- /dev/null
+++ b/chromium/components/mus/ws/display_manager_delegate.h
@@ -0,0 +1,38 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_DISPLAY_MANAGER_DELEGATE_H_
+#define COMPONENTS_MUS_WS_DISPLAY_MANAGER_DELEGATE_H_
+
+#include "components/mus/public/interfaces/display.mojom.h"
+#include "components/mus/ws/user_id.h"
+
+namespace mus {
+namespace ws {
+
+class Display;
+class WindowManagerState;
+
+class DisplayManagerDelegate {
+ public:
+ virtual void OnFirstDisplayReady() = 0;
+ virtual void OnNoMoreDisplays() = 0;
+
+ // Gets the frame decorations for the specified user. Returns true if the
+ // decorations have been set, false otherwise. |values| may be null.
+ virtual bool GetFrameDecorationsForUser(
+ const UserId& user_id,
+ mojom::FrameDecorationValuesPtr* values) = 0;
+
+ virtual WindowManagerState* GetWindowManagerStateForUser(
+ const UserId& user_id) = 0;
+
+ protected:
+ virtual ~DisplayManagerDelegate() {}
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_DISPLAY_MANAGER_DELEGATE_H_
diff --git a/chromium/components/mus/ws/display_unittest.cc b/chromium/components/mus/ws/display_unittest.cc
new file mode 100644
index 00000000000..dfc24ef25a8
--- /dev/null
+++ b/chromium/components/mus/ws/display_unittest.cc
@@ -0,0 +1,308 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "components/mus/common/types.h"
+#include "components/mus/common/util.h"
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "components/mus/surfaces/surfaces_state.h"
+#include "components/mus/ws/display_manager.h"
+#include "components/mus/ws/ids.h"
+#include "components/mus/ws/platform_display.h"
+#include "components/mus/ws/platform_display_factory.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/test_utils.h"
+#include "components/mus/ws/window_manager_display_root.h"
+#include "components/mus/ws/window_manager_state.h"
+#include "components/mus/ws/window_server.h"
+#include "components/mus/ws/window_server_delegate.h"
+#include "components/mus/ws/window_tree.h"
+#include "components/mus/ws/window_tree_binding.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/event.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace mus {
+namespace ws {
+namespace test {
+namespace {
+
+const UserId kTestId1 = "2";
+const UserId kTestId2 = "21";
+
+ClientWindowId ClientWindowIdForFirstRoot(WindowTree* tree) {
+ if (tree->roots().empty())
+ return ClientWindowId();
+ return ClientWindowIdForWindow(tree, *tree->roots().begin());
+}
+
+WindowManagerState* GetWindowManagerStateForUser(Display* display,
+ const UserId& user_id) {
+ WindowManagerDisplayRoot* display_root =
+ display->GetWindowManagerDisplayRootForUser(user_id);
+ return display_root ? display_root->window_manager_state() : nullptr;
+}
+
+} // namespace
+
+// -----------------------------------------------------------------------------
+
+class DisplayTest : public testing::Test {
+ public:
+ DisplayTest() : cursor_id_(0), platform_display_factory_(&cursor_id_) {}
+ ~DisplayTest() override {}
+
+ protected:
+ // testing::Test:
+ void SetUp() override {
+ PlatformDisplay::set_factory_for_testing(&platform_display_factory_);
+ window_server_.reset(new WindowServer(&window_server_delegate_,
+ scoped_refptr<SurfacesState>()));
+ window_server_delegate_.set_window_server(window_server_.get());
+ window_server_->user_id_tracker()->AddUserId(kTestId1);
+ window_server_->user_id_tracker()->AddUserId(kTestId2);
+ }
+
+ protected:
+ int32_t cursor_id_;
+ TestPlatformDisplayFactory platform_display_factory_;
+ TestWindowServerDelegate window_server_delegate_;
+ std::unique_ptr<WindowServer> window_server_;
+ base::MessageLoop message_loop_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DisplayTest);
+};
+
+TEST_F(DisplayTest, CallsCreateDefaultDisplays) {
+ const int kNumHostsToCreate = 2;
+ window_server_delegate_.set_num_displays_to_create(kNumHostsToCreate);
+
+ DisplayManager* display_manager = window_server_->display_manager();
+ WindowManagerWindowTreeFactorySetTestApi(
+ window_server_->window_manager_window_tree_factory_set())
+ .Add(kTestId1);
+ // The first register should trigger creation of the default
+ // Displays. There should be kNumHostsToCreate Displays.
+ EXPECT_EQ(static_cast<size_t>(kNumHostsToCreate),
+ display_manager->displays().size());
+
+ // Each host should have a WindowManagerState for kTestId1.
+ for (Display* display : display_manager->displays()) {
+ EXPECT_EQ(1u, display->num_window_manger_states());
+ EXPECT_TRUE(GetWindowManagerStateForUser(display, kTestId1));
+ EXPECT_FALSE(GetWindowManagerStateForUser(display, kTestId2));
+ }
+
+ // Add another registry, should trigger creation of another wm.
+ WindowManagerWindowTreeFactorySetTestApi(
+ window_server_->window_manager_window_tree_factory_set())
+ .Add(kTestId2);
+ for (Display* display : display_manager->displays()) {
+ ASSERT_EQ(2u, display->num_window_manger_states());
+ WindowManagerDisplayRoot* root1 =
+ display->GetWindowManagerDisplayRootForUser(kTestId1);
+ ASSERT_TRUE(root1);
+ WindowManagerDisplayRoot* root2 =
+ display->GetWindowManagerDisplayRootForUser(kTestId2);
+ ASSERT_TRUE(root2);
+ // Verify the two states have different roots.
+ EXPECT_NE(root1, root2);
+ EXPECT_NE(root1->root(), root2->root());
+ }
+}
+
+TEST_F(DisplayTest, Destruction) {
+ window_server_delegate_.set_num_displays_to_create(1);
+
+ WindowManagerWindowTreeFactorySetTestApi(
+ window_server_->window_manager_window_tree_factory_set())
+ .Add(kTestId1);
+
+ // Add another registry, should trigger creation of another wm.
+ DisplayManager* display_manager = window_server_->display_manager();
+ WindowManagerWindowTreeFactorySetTestApi(
+ window_server_->window_manager_window_tree_factory_set())
+ .Add(kTestId2);
+ ASSERT_EQ(1u, display_manager->displays().size());
+ Display* display = *display_manager->displays().begin();
+ ASSERT_EQ(2u, display->num_window_manger_states());
+ // There should be two trees, one for each windowmanager.
+ EXPECT_EQ(2u, window_server_->num_trees());
+
+ {
+ WindowManagerState* state = GetWindowManagerStateForUser(display, kTestId1);
+ // Destroy the tree associated with |state|. Should result in deleting
+ // |state|.
+ window_server_->DestroyTree(state->window_tree());
+ ASSERT_EQ(1u, display->num_window_manger_states());
+ EXPECT_FALSE(GetWindowManagerStateForUser(display, kTestId1));
+ EXPECT_EQ(1u, display_manager->displays().size());
+ EXPECT_EQ(1u, window_server_->num_trees());
+ }
+
+ EXPECT_FALSE(window_server_delegate_.got_on_no_more_displays());
+ window_server_->display_manager()->DestroyDisplay(display);
+ // There is still one tree left.
+ EXPECT_EQ(1u, window_server_->num_trees());
+ EXPECT_TRUE(window_server_delegate_.got_on_no_more_displays());
+}
+
+TEST_F(DisplayTest, EventStateResetOnUserSwitch) {
+ window_server_delegate_.set_num_displays_to_create(1);
+
+ WindowManagerWindowTreeFactorySetTestApi(
+ window_server_->window_manager_window_tree_factory_set())
+ .Add(kTestId1);
+ WindowManagerWindowTreeFactorySetTestApi(
+ window_server_->window_manager_window_tree_factory_set())
+ .Add(kTestId2);
+
+ window_server_->user_id_tracker()->SetActiveUserId(kTestId1);
+
+ DisplayManager* display_manager = window_server_->display_manager();
+ ASSERT_EQ(1u, display_manager->displays().size());
+ Display* display = *display_manager->displays().begin();
+ WindowManagerState* active_wms =
+ display->GetActiveWindowManagerDisplayRoot()->window_manager_state();
+ ASSERT_TRUE(active_wms);
+ EXPECT_EQ(kTestId1, active_wms->user_id());
+
+ static_cast<PlatformDisplayDelegate*>(display)->OnEvent(ui::PointerEvent(
+ ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(20, 25),
+ gfx::Point(20, 25), base::TimeTicks(),
+ ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)));
+
+ EXPECT_TRUE(EventDispatcherTestApi(active_wms->event_dispatcher())
+ .AreAnyPointersDown());
+ EXPECT_EQ(gfx::Point(20, 25),
+ active_wms->event_dispatcher()->mouse_pointer_last_location());
+
+ // Switch the user. Should trigger resetting state in old event dispatcher
+ // and update state in new event dispatcher.
+ window_server_->user_id_tracker()->SetActiveUserId(kTestId2);
+ EXPECT_NE(
+ active_wms,
+ display->GetActiveWindowManagerDisplayRoot()->window_manager_state());
+ EXPECT_FALSE(EventDispatcherTestApi(active_wms->event_dispatcher())
+ .AreAnyPointersDown());
+ active_wms =
+ display->GetActiveWindowManagerDisplayRoot()->window_manager_state();
+ EXPECT_EQ(kTestId2, active_wms->user_id());
+ EXPECT_EQ(gfx::Point(20, 25),
+ active_wms->event_dispatcher()->mouse_pointer_last_location());
+ EXPECT_FALSE(EventDispatcherTestApi(active_wms->event_dispatcher())
+ .AreAnyPointersDown());
+}
+
+// Verifies capture fails when wm is inactive and succeeds when wm is active.
+TEST_F(DisplayTest, SetCaptureFromWindowManager) {
+ window_server_delegate_.set_num_displays_to_create(1);
+ WindowManagerWindowTreeFactorySetTestApi(
+ window_server_->window_manager_window_tree_factory_set())
+ .Add(kTestId1);
+ WindowManagerWindowTreeFactorySetTestApi(
+ window_server_->window_manager_window_tree_factory_set())
+ .Add(kTestId2);
+ window_server_->user_id_tracker()->SetActiveUserId(kTestId1);
+ DisplayManager* display_manager = window_server_->display_manager();
+ ASSERT_EQ(1u, display_manager->displays().size());
+ Display* display = *display_manager->displays().begin();
+ WindowManagerState* wms_for_id2 =
+ GetWindowManagerStateForUser(display, kTestId2);
+ ASSERT_TRUE(wms_for_id2);
+ EXPECT_FALSE(wms_for_id2->IsActive());
+
+ // Create a child of the root that we can set capture on.
+ WindowTree* tree = wms_for_id2->window_tree();
+ ClientWindowId child_window_id;
+ ASSERT_TRUE(NewWindowInTree(tree, &child_window_id));
+
+ WindowTreeTestApi(tree).EnableCapture();
+
+ // SetCapture() should fail for user id2 as it is inactive.
+ EXPECT_FALSE(tree->SetCapture(child_window_id));
+
+ // Make the second user active and verify capture works.
+ window_server_->user_id_tracker()->SetActiveUserId(kTestId2);
+ EXPECT_TRUE(wms_for_id2->IsActive());
+ EXPECT_TRUE(tree->SetCapture(child_window_id));
+}
+
+TEST_F(DisplayTest, FocusFailsForInactiveUser) {
+ window_server_delegate_.set_num_displays_to_create(1);
+ WindowManagerWindowTreeFactorySetTestApi(
+ window_server_->window_manager_window_tree_factory_set())
+ .Add(kTestId1);
+ TestWindowTreeClient* window_tree_client1 =
+ window_server_delegate_.last_client();
+ ASSERT_TRUE(window_tree_client1);
+ WindowManagerWindowTreeFactorySetTestApi(
+ window_server_->window_manager_window_tree_factory_set())
+ .Add(kTestId2);
+ window_server_->user_id_tracker()->SetActiveUserId(kTestId1);
+ DisplayManager* display_manager = window_server_->display_manager();
+ ASSERT_EQ(1u, display_manager->displays().size());
+ Display* display = *display_manager->displays().begin();
+ WindowManagerState* wms_for_id2 =
+ GetWindowManagerStateForUser(display, kTestId2);
+ wms_for_id2->window_tree()->AddActivationParent(
+ ClientWindowIdForFirstRoot(wms_for_id2->window_tree()));
+ ASSERT_TRUE(wms_for_id2);
+ EXPECT_FALSE(wms_for_id2->IsActive());
+ ClientWindowId child2_id;
+ NewWindowInTree(wms_for_id2->window_tree(), &child2_id);
+
+ // Focus should fail for windows in inactive window managers.
+ EXPECT_FALSE(wms_for_id2->window_tree()->SetFocus(child2_id));
+
+ // Focus should succeed for the active window manager.
+ WindowManagerState* wms_for_id1 =
+ GetWindowManagerStateForUser(display, kTestId1);
+ ASSERT_TRUE(wms_for_id1);
+ wms_for_id1->window_tree()->AddActivationParent(
+ ClientWindowIdForFirstRoot(wms_for_id1->window_tree()));
+ ClientWindowId child1_id;
+ NewWindowInTree(wms_for_id1->window_tree(), &child1_id);
+ EXPECT_TRUE(wms_for_id1->IsActive());
+ EXPECT_TRUE(wms_for_id1->window_tree()->SetFocus(child1_id));
+}
+
+// Verifies a single tree is used for multiple displays.
+TEST_F(DisplayTest, MultipleDisplays) {
+ window_server_delegate_.set_num_displays_to_create(2);
+ WindowManagerWindowTreeFactorySetTestApi(
+ window_server_->window_manager_window_tree_factory_set())
+ .Add(kTestId1);
+ window_server_->user_id_tracker()->SetActiveUserId(kTestId1);
+ ASSERT_EQ(1u, window_server_delegate_.bindings()->size());
+ TestWindowTreeBinding* window_tree_binding =
+ (*window_server_delegate_.bindings())[0];
+ WindowTree* tree = window_tree_binding->tree();
+ ASSERT_EQ(2u, tree->roots().size());
+ std::set<const ServerWindow*> roots = tree->roots();
+ auto it = roots.begin();
+ ServerWindow* root1 = tree->GetWindow((*it)->id());
+ ++it;
+ ServerWindow* root2 = tree->GetWindow((*it)->id());
+ ASSERT_NE(root1, root2);
+ Display* display1 = tree->GetDisplay(root1);
+ WindowManagerState* display1_wms =
+ display1->GetWindowManagerDisplayRootForUser(kTestId1)
+ ->window_manager_state();
+ Display* display2 = tree->GetDisplay(root2);
+ WindowManagerState* display2_wms =
+ display2->GetWindowManagerDisplayRootForUser(kTestId1)
+ ->window_manager_state();
+ EXPECT_EQ(display1_wms->window_tree(), display2_wms->window_tree());
+}
+
+} // namespace test
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/event_dispatcher.cc b/chromium/components/mus/ws/event_dispatcher.cc
new file mode 100644
index 00000000000..23874c3abc4
--- /dev/null
+++ b/chromium/components/mus/ws/event_dispatcher.cc
@@ -0,0 +1,559 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/event_dispatcher.h"
+
+#include <algorithm>
+
+#include "base/time/time.h"
+#include "components/mus/ws/accelerator.h"
+#include "components/mus/ws/display.h"
+#include "components/mus/ws/event_dispatcher_delegate.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/server_window_delegate.h"
+#include "components/mus/ws/window_coordinate_conversions.h"
+#include "components/mus/ws/window_finder.h"
+#include "ui/events/event_utils.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/point_conversions.h"
+
+namespace mus {
+namespace ws {
+
+using Entry = std::pair<uint32_t, std::unique_ptr<Accelerator>>;
+
+namespace {
+
+bool IsOnlyOneMouseButtonDown(int flags) {
+ const uint32_t button_only_flags =
+ flags & (ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON |
+ ui::EF_RIGHT_MOUSE_BUTTON);
+ return button_only_flags == ui::EF_LEFT_MOUSE_BUTTON ||
+ button_only_flags == ui::EF_MIDDLE_MOUSE_BUTTON ||
+ button_only_flags == ui::EF_RIGHT_MOUSE_BUTTON;
+}
+
+bool IsLocationInNonclientArea(const ServerWindow* target,
+ const gfx::Point& location) {
+ if (!target->parent())
+ return false;
+
+ gfx::Rect client_area(target->bounds().size());
+ client_area.Inset(target->client_area());
+ if (client_area.Contains(location))
+ return false;
+
+ for (const auto& rect : target->additional_client_areas()) {
+ if (rect.Contains(location))
+ return false;
+ }
+
+ return true;
+}
+
+uint32_t PointerId(const ui::LocatedEvent& event) {
+ if (event.IsPointerEvent())
+ return event.AsPointerEvent()->pointer_id();
+ if (event.IsMouseWheelEvent())
+ return ui::PointerEvent::kMousePointerId;
+
+ NOTREACHED();
+ return 0;
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+
+EventDispatcher::EventDispatcher(EventDispatcherDelegate* delegate)
+ : delegate_(delegate),
+ capture_window_(nullptr),
+ capture_window_client_id_(kInvalidClientId),
+ modal_window_controller_(this),
+ mouse_button_down_(false),
+ mouse_cursor_source_window_(nullptr),
+ mouse_cursor_in_non_client_area_(false) {}
+
+EventDispatcher::~EventDispatcher() {
+ if (capture_window_) {
+ UnobserveWindow(capture_window_);
+ capture_window_ = nullptr;
+ capture_window_client_id_ = kInvalidClientId;
+ }
+ for (const auto& pair : pointer_targets_) {
+ if (pair.second.window)
+ UnobserveWindow(pair.second.window);
+ }
+ pointer_targets_.clear();
+}
+
+void EventDispatcher::Reset() {
+ if (capture_window_) {
+ CancelPointerEventsToTarget(capture_window_);
+ DCHECK(capture_window_ == nullptr);
+ }
+
+ while (!pointer_targets_.empty())
+ StopTrackingPointer(pointer_targets_.begin()->first);
+
+ mouse_button_down_ = false;
+}
+
+void EventDispatcher::SetMousePointerScreenLocation(
+ const gfx::Point& screen_location) {
+ DCHECK(pointer_targets_.empty());
+ mouse_pointer_last_location_ = screen_location;
+ UpdateCursorProviderByLastKnownLocation();
+ // Write our initial location back to our shared screen coordinate. This
+ // shouldn't cause problems because we already read the cursor before we
+ // process any events in views during window construction.
+ delegate_->OnMouseCursorLocationChanged(screen_location);
+}
+
+bool EventDispatcher::GetCurrentMouseCursor(int32_t* cursor_out) {
+ if (!mouse_cursor_source_window_)
+ return false;
+
+ *cursor_out = mouse_cursor_in_non_client_area_
+ ? mouse_cursor_source_window_->non_client_cursor()
+ : mouse_cursor_source_window_->cursor();
+ return true;
+}
+
+bool EventDispatcher::SetCaptureWindow(ServerWindow* window,
+ ClientSpecificId client_id) {
+ if (!window)
+ client_id = kInvalidClientId;
+
+ if (window == capture_window_ && client_id == capture_window_client_id_)
+ return true;
+
+ // A window that is blocked by a modal window cannot gain capture.
+ if (window && modal_window_controller_.IsWindowBlocked(window))
+ return false;
+
+ if (capture_window_) {
+ // Stop observing old capture window. |pointer_targets_| are cleared on
+ // initial setting of a capture window.
+ delegate_->OnServerWindowCaptureLost(capture_window_);
+ UnobserveWindow(capture_window_);
+ } else {
+ // Cancel implicit capture to all other windows.
+ for (const auto& pair : pointer_targets_) {
+ ServerWindow* target = pair.second.window;
+ if (!target)
+ continue;
+ UnobserveWindow(target);
+ if (target == window)
+ continue;
+
+ ui::EventType event_type = pair.second.is_mouse_event
+ ? ui::ET_POINTER_EXITED
+ : ui::ET_POINTER_CANCELLED;
+ ui::EventPointerType pointer_type =
+ pair.second.is_mouse_event ? ui::EventPointerType::POINTER_TYPE_MOUSE
+ : ui::EventPointerType::POINTER_TYPE_TOUCH;
+ // TODO(jonross): Track previous location in PointerTarget for sending
+ // cancels.
+ ui::PointerEvent event(
+ event_type, gfx::Point(), gfx::Point(), ui::EF_NONE, pair.first,
+ ui::PointerDetails(pointer_type), ui::EventTimeForNow());
+ DispatchToPointerTarget(pair.second, event);
+ }
+ pointer_targets_.clear();
+ }
+
+ // Set the capture before changing native capture; otherwise, the callback
+ // from native platform might try to set the capture again.
+ bool had_capture_window = capture_window_ != nullptr;
+ capture_window_ = window;
+ capture_window_client_id_ = client_id;
+
+ // Begin tracking the capture window if it is not yet being observed.
+ if (window) {
+ ObserveWindow(window);
+ // TODO(sky): this conditional is problematic for the case of capture moving
+ // to a different display.
+ if (!had_capture_window)
+ delegate_->SetNativeCapture(window);
+ } else {
+ delegate_->ReleaseNativeCapture();
+ if (!mouse_button_down_)
+ UpdateCursorProviderByLastKnownLocation();
+ }
+
+ return true;
+}
+
+void EventDispatcher::AddSystemModalWindow(ServerWindow* window) {
+ modal_window_controller_.AddSystemModalWindow(window);
+}
+
+void EventDispatcher::ReleaseCaptureBlockedByModalWindow(
+ const ServerWindow* modal_window) {
+ if (!capture_window_)
+ return;
+
+ if (modal_window_controller_.IsWindowBlockedBy(capture_window_,
+ modal_window)) {
+ SetCaptureWindow(nullptr, kInvalidClientId);
+ }
+}
+
+void EventDispatcher::ReleaseCaptureBlockedByAnyModalWindow() {
+ if (!capture_window_)
+ return;
+
+ if (modal_window_controller_.IsWindowBlocked(capture_window_))
+ SetCaptureWindow(nullptr, kInvalidClientId);
+}
+
+void EventDispatcher::UpdateNonClientAreaForCurrentWindow() {
+ if (mouse_cursor_source_window_) {
+ gfx::Point location = mouse_pointer_last_location_;
+ ServerWindow* target = FindDeepestVisibleWindowForEvents(&location);
+ if (target == mouse_cursor_source_window_) {
+ mouse_cursor_in_non_client_area_ =
+ mouse_cursor_source_window_
+ ? IsLocationInNonclientArea(mouse_cursor_source_window_, location)
+ : false;
+ }
+ }
+}
+
+void EventDispatcher::UpdateCursorProviderByLastKnownLocation() {
+ if (!mouse_button_down_) {
+ gfx::Point location = mouse_pointer_last_location_;
+ mouse_cursor_source_window_ = FindDeepestVisibleWindowForEvents(&location);
+
+ mouse_cursor_in_non_client_area_ =
+ mouse_cursor_source_window_
+ ? IsLocationInNonclientArea(mouse_cursor_source_window_, location)
+ : false;
+ }
+}
+
+bool EventDispatcher::AddAccelerator(uint32_t id,
+ mojom::EventMatcherPtr event_matcher) {
+ std::unique_ptr<Accelerator> accelerator(new Accelerator(id, *event_matcher));
+ // If an accelerator with the same id or matcher already exists, then abort.
+ for (const auto& pair : accelerators_) {
+ if (pair.first == id || accelerator->EqualEventMatcher(pair.second.get()))
+ return false;
+ }
+ accelerators_.insert(Entry(id, std::move(accelerator)));
+ return true;
+}
+
+void EventDispatcher::RemoveAccelerator(uint32_t id) {
+ auto it = accelerators_.find(id);
+ // Clients may pass bogus ids.
+ if (it != accelerators_.end())
+ accelerators_.erase(it);
+}
+
+void EventDispatcher::ProcessEvent(const ui::Event& event) {
+ if (event.IsKeyEvent()) {
+ const ui::KeyEvent* key_event = event.AsKeyEvent();
+ if (event.type() == ui::ET_KEY_PRESSED && !key_event->is_char()) {
+ Accelerator* pre_target =
+ FindAccelerator(*key_event, ui::mojom::AcceleratorPhase::PRE_TARGET);
+ if (pre_target) {
+ delegate_->OnAccelerator(pre_target->id(), event);
+ return;
+ }
+ }
+ ProcessKeyEvent(*key_event);
+ return;
+ }
+
+ if (event.IsPointerEvent() || event.IsMouseWheelEvent()) {
+ ProcessLocatedEvent(*event.AsLocatedEvent());
+ return;
+ }
+
+ NOTREACHED();
+}
+
+void EventDispatcher::ProcessKeyEvent(const ui::KeyEvent& event) {
+ Accelerator* post_target =
+ FindAccelerator(event, ui::mojom::AcceleratorPhase::POST_TARGET);
+ ServerWindow* focused_window =
+ delegate_->GetFocusedWindowForEventDispatcher();
+ if (focused_window) {
+ // Assume key events are for the client area.
+ const bool in_nonclient_area = false;
+ const ClientSpecificId client_id =
+ delegate_->GetEventTargetClientId(focused_window, in_nonclient_area);
+ delegate_->DispatchInputEventToWindow(focused_window, client_id, event,
+ post_target);
+ return;
+ }
+ delegate_->OnEventTargetNotFound(event);
+ if (post_target)
+ delegate_->OnAccelerator(post_target->id(), event);
+}
+
+void EventDispatcher::ProcessLocatedEvent(const ui::LocatedEvent& event) {
+ DCHECK(event.IsPointerEvent() || event.IsMouseWheelEvent());
+ const bool is_mouse_event =
+ event.IsMousePointerEvent() || event.IsMouseWheelEvent();
+
+ if (is_mouse_event) {
+ mouse_pointer_last_location_ = event.location();
+ delegate_->OnMouseCursorLocationChanged(event.root_location());
+ }
+
+ // Release capture on pointer up. For mouse we only release if there are
+ // no buttons down.
+ const bool is_pointer_going_up =
+ (event.type() == ui::ET_POINTER_UP ||
+ event.type() == ui::ET_POINTER_CANCELLED) &&
+ (!is_mouse_event || IsOnlyOneMouseButtonDown(event.flags()));
+
+ // Update mouse down state upon events which change it.
+ if (is_mouse_event) {
+ if (event.type() == ui::ET_POINTER_DOWN)
+ mouse_button_down_ = true;
+ else if (is_pointer_going_up)
+ mouse_button_down_ = false;
+ }
+
+ if (capture_window_) {
+ mouse_cursor_source_window_ = capture_window_;
+ DispatchToClient(capture_window_, capture_window_client_id_, event);
+ return;
+ }
+
+ const int32_t pointer_id = PointerId(event);
+ if (!IsTrackingPointer(pointer_id) ||
+ !pointer_targets_[pointer_id].is_pointer_down) {
+ const bool any_pointers_down = AreAnyPointersDown();
+ UpdateTargetForPointer(pointer_id, event);
+ if (is_mouse_event)
+ mouse_cursor_source_window_ = pointer_targets_[pointer_id].window;
+
+ PointerTarget& pointer_target = pointer_targets_[pointer_id];
+ if (pointer_target.is_pointer_down) {
+ if (is_mouse_event)
+ mouse_cursor_source_window_ = pointer_target.window;
+ if (!any_pointers_down) {
+ delegate_->SetFocusedWindowFromEventDispatcher(pointer_target.window);
+ delegate_->SetNativeCapture(pointer_target.window);
+ }
+ }
+ }
+
+ // When we release the mouse button, we want the cursor to be sourced from
+ // the window under the mouse pointer, even though we're sending the button
+ // up event to the window that had implicit capture. We have to set this
+ // before we perform dispatch because the Delegate is going to read this
+ // information from us.
+ if (is_pointer_going_up && is_mouse_event)
+ UpdateCursorProviderByLastKnownLocation();
+
+ DispatchToPointerTarget(pointer_targets_[pointer_id], event);
+
+ if (is_pointer_going_up) {
+ if (is_mouse_event)
+ pointer_targets_[pointer_id].is_pointer_down = false;
+ else
+ StopTrackingPointer(pointer_id);
+ if (!AreAnyPointersDown())
+ delegate_->ReleaseNativeCapture();
+ }
+}
+
+void EventDispatcher::StartTrackingPointer(
+ int32_t pointer_id,
+ const PointerTarget& pointer_target) {
+ DCHECK(!IsTrackingPointer(pointer_id));
+ ObserveWindow(pointer_target.window);
+ pointer_targets_[pointer_id] = pointer_target;
+}
+
+void EventDispatcher::StopTrackingPointer(int32_t pointer_id) {
+ DCHECK(IsTrackingPointer(pointer_id));
+ ServerWindow* window = pointer_targets_[pointer_id].window;
+ pointer_targets_.erase(pointer_id);
+ if (window)
+ UnobserveWindow(window);
+}
+
+void EventDispatcher::UpdateTargetForPointer(int32_t pointer_id,
+ const ui::LocatedEvent& event) {
+ if (!IsTrackingPointer(pointer_id)) {
+ StartTrackingPointer(pointer_id, PointerTargetForEvent(event));
+ return;
+ }
+
+ const PointerTarget pointer_target = PointerTargetForEvent(event);
+ if (pointer_target.window == pointer_targets_[pointer_id].window &&
+ pointer_target.in_nonclient_area ==
+ pointer_targets_[pointer_id].in_nonclient_area) {
+ // The targets are the same, only set the down state to true if necessary.
+ // Down going to up is handled by ProcessLocatedEvent().
+ if (pointer_target.is_pointer_down)
+ pointer_targets_[pointer_id].is_pointer_down = true;
+ return;
+ }
+
+ // The targets are changing. Send an exit if appropriate.
+ if (event.IsMousePointerEvent()) {
+ ui::PointerEvent exit_event(
+ ui::ET_POINTER_EXITED, event.location(), event.root_location(),
+ event.flags(), ui::PointerEvent::kMousePointerId,
+ ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_MOUSE),
+ event.time_stamp());
+ DispatchToPointerTarget(pointer_targets_[pointer_id], exit_event);
+ }
+
+ // Technically we're updating in place, but calling start then stop makes for
+ // simpler code.
+ StopTrackingPointer(pointer_id);
+ StartTrackingPointer(pointer_id, pointer_target);
+}
+
+EventDispatcher::PointerTarget EventDispatcher::PointerTargetForEvent(
+ const ui::LocatedEvent& event) {
+ PointerTarget pointer_target;
+ gfx::Point location(event.location());
+ ServerWindow* target_window = FindDeepestVisibleWindowForEvents(&location);
+ pointer_target.window =
+ modal_window_controller_.GetTargetForWindow(target_window);
+ pointer_target.is_mouse_event = event.IsMousePointerEvent();
+ pointer_target.in_nonclient_area =
+ target_window != pointer_target.window ||
+ IsLocationInNonclientArea(pointer_target.window, location);
+ pointer_target.is_pointer_down = event.type() == ui::ET_POINTER_DOWN;
+ return pointer_target;
+}
+
+bool EventDispatcher::AreAnyPointersDown() const {
+ for (const auto& pair : pointer_targets_) {
+ if (pair.second.is_pointer_down)
+ return true;
+ }
+ return false;
+}
+
+void EventDispatcher::DispatchToPointerTarget(const PointerTarget& target,
+ const ui::LocatedEvent& event) {
+ if (!target.window) {
+ delegate_->OnEventTargetNotFound(event);
+ return;
+ }
+
+ if (target.is_mouse_event)
+ mouse_cursor_in_non_client_area_ = target.in_nonclient_area;
+
+ DispatchToClient(target.window, delegate_->GetEventTargetClientId(
+ target.window, target.in_nonclient_area),
+ event);
+}
+
+void EventDispatcher::DispatchToClient(ServerWindow* window,
+ ClientSpecificId client_id,
+ const ui::LocatedEvent& event) {
+ gfx::Point location(event.location());
+ gfx::Transform transform(GetTransformToWindow(window));
+ transform.TransformPoint(&location);
+ std::unique_ptr<ui::Event> clone = ui::Event::Clone(event);
+ clone->AsLocatedEvent()->set_location(location);
+ // TODO(jonross): add post-target accelerator support once accelerators
+ // support pointer events.
+ delegate_->DispatchInputEventToWindow(window, client_id, *clone, nullptr);
+}
+
+void EventDispatcher::CancelPointerEventsToTarget(ServerWindow* window) {
+ if (capture_window_ == window) {
+ UnobserveWindow(window);
+ capture_window_ = nullptr;
+ capture_window_client_id_ = kInvalidClientId;
+ mouse_button_down_ = false;
+ // A window only cares to be informed that it lost capture if it explicitly
+ // requested capture. A window can lose capture if another window gains
+ // explicit capture.
+ delegate_->OnServerWindowCaptureLost(window);
+ delegate_->ReleaseNativeCapture();
+ UpdateCursorProviderByLastKnownLocation();
+ return;
+ }
+
+ for (auto& pair : pointer_targets_) {
+ if (pair.second.window == window) {
+ UnobserveWindow(window);
+ pair.second.window = nullptr;
+ }
+ }
+}
+
+void EventDispatcher::ObserveWindow(ServerWindow* window) {
+ auto res = observed_windows_.insert(std::make_pair(window, 0u));
+ res.first->second++;
+ if (res.second)
+ window->AddObserver(this);
+}
+
+void EventDispatcher::UnobserveWindow(ServerWindow* window) {
+ auto it = observed_windows_.find(window);
+ DCHECK(it != observed_windows_.end());
+ DCHECK_LT(0u, it->second);
+ it->second--;
+ if (!it->second) {
+ window->RemoveObserver(this);
+ observed_windows_.erase(it);
+ }
+}
+
+Accelerator* EventDispatcher::FindAccelerator(
+ const ui::KeyEvent& event,
+ const ui::mojom::AcceleratorPhase phase) {
+ for (const auto& pair : accelerators_) {
+ if (pair.second->MatchesEvent(event, phase)) {
+ return pair.second.get();
+ }
+ }
+ return nullptr;
+}
+
+ServerWindow* EventDispatcher::FindDeepestVisibleWindowForEvents(
+ gfx::Point* location) {
+ ServerWindow* root = delegate_->GetRootWindowContaining(*location);
+ if (!root)
+ return nullptr;
+
+ return mus::ws::FindDeepestVisibleWindowForEvents(root, location);
+}
+
+void EventDispatcher::OnWillChangeWindowHierarchy(ServerWindow* window,
+ ServerWindow* new_parent,
+ ServerWindow* old_parent) {
+ // TODO(sky): moving to a different root likely needs to transfer capture.
+ // TODO(sky): this isn't quite right, I think the logic should be (assuming
+ // moving in same root and still drawn):
+ // . if there is capture and window is still in the same root, continue
+ // sending to it.
+ // . if there isn't capture, then reevaluate each of the pointer targets
+ // sending exit as necessary.
+ // http://crbug.com/613646 .
+ if (!new_parent || !new_parent->IsDrawn() ||
+ new_parent->GetRoot() != old_parent->GetRoot()) {
+ CancelPointerEventsToTarget(window);
+ }
+}
+
+void EventDispatcher::OnWindowVisibilityChanged(ServerWindow* window) {
+ CancelPointerEventsToTarget(window);
+}
+
+void EventDispatcher::OnWindowDestroyed(ServerWindow* window) {
+ CancelPointerEventsToTarget(window);
+
+ if (mouse_cursor_source_window_ == window)
+ mouse_cursor_source_window_ = nullptr;
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/event_dispatcher.h b/chromium/components/mus/ws/event_dispatcher.h
new file mode 100644
index 00000000000..a6178073981
--- /dev/null
+++ b/chromium/components/mus/ws/event_dispatcher.h
@@ -0,0 +1,236 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_EVENT_DISPATCHER_H_
+#define COMPONENTS_MUS_WS_EVENT_DISPATCHER_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <utility>
+
+#include "base/macros.h"
+#include "components/mus/common/types.h"
+#include "components/mus/public/interfaces/event_matcher.mojom.h"
+#include "components/mus/ws/modal_window_controller.h"
+#include "components/mus/ws/server_window_observer.h"
+#include "ui/gfx/geometry/rect_f.h"
+
+namespace ui {
+class Event;
+class KeyEvent;
+class LocatedEvent;
+}
+
+namespace mus {
+namespace ws {
+
+class Accelerator;
+class EventDispatcherDelegate;
+class ServerWindow;
+
+namespace test {
+class EventDispatcherTestApi;
+}
+
+// Handles dispatching events to the right location as well as updating focus.
+class EventDispatcher : public ServerWindowObserver {
+ public:
+ explicit EventDispatcher(EventDispatcherDelegate* delegate);
+ ~EventDispatcher() override;
+
+ // Cancels capture and stops tracking any pointer events. This does not send
+ // any events to the delegate.
+ void Reset();
+
+ void SetMousePointerScreenLocation(const gfx::Point& screen_location);
+ const gfx::Point& mouse_pointer_last_location() const {
+ return mouse_pointer_last_location_;
+ }
+
+ // If we still have the window of the last mouse move, returns true and sets
+ // the current cursor to use to |cursor_out|.
+ bool GetCurrentMouseCursor(int32_t* cursor_out);
+
+ // |capture_window_| will receive all input. See window_tree.mojom for
+ // details.
+ ServerWindow* capture_window() { return capture_window_; }
+ const ServerWindow* capture_window() const { return capture_window_; }
+ // Setting capture can fail if the window is blocked by a modal window
+ // (indicated by returning |false|).
+ bool SetCaptureWindow(ServerWindow* capture_window,
+ ClientSpecificId client_id);
+
+ // Id of the client that capture events are sent to.
+ ClientSpecificId capture_window_client_id() const {
+ return capture_window_client_id_;
+ }
+
+ // Adds a system modal window. The window remains modal to system until it is
+ // destroyed. There can exist multiple system modal windows, in which case the
+ // one that is visible and added most recently or shown most recently would be
+ // the active one.
+ void AddSystemModalWindow(ServerWindow* window);
+
+ // Checks if |modal_window| is a visible modal window that blocks current
+ // capture window and if that's the case, releases the capture.
+ void ReleaseCaptureBlockedByModalWindow(const ServerWindow* modal_window);
+
+ // Checks if the current capture window is blocked by any visible modal window
+ // and if that's the case, releases the capture.
+ void ReleaseCaptureBlockedByAnyModalWindow();
+
+ // Retrieves the ServerWindow of the last mouse move.
+ ServerWindow* mouse_cursor_source_window() const {
+ return mouse_cursor_source_window_;
+ }
+
+ // If the mouse cursor is still over |mouse_cursor_source_window_|, updates
+ // whether we are in the non-client area. Used when
+ // |mouse_cursor_source_window_| has changed its properties.
+ void UpdateNonClientAreaForCurrentWindow();
+
+ // Possibly updates the cursor. If we aren't in an implicit capture, we take
+ // the last known location of the mouse pointer, and look for the
+ // ServerWindow* under it.
+ void UpdateCursorProviderByLastKnownLocation();
+
+ // Adds an accelerator with the given id and event-matcher. If an accelerator
+ // already exists with the same id or the same matcher, then the accelerator
+ // is not added. Returns whether adding the accelerator was successful or not.
+ bool AddAccelerator(uint32_t id, mojom::EventMatcherPtr event_matcher);
+ void RemoveAccelerator(uint32_t id);
+
+ // Processes the supplied event, informing the delegate as approriate. This
+ // may result in generating any number of events.
+ void ProcessEvent(const ui::Event& event);
+
+ private:
+ friend class test::EventDispatcherTestApi;
+
+ // Keeps track of state associated with an active pointer.
+ struct PointerTarget {
+ PointerTarget()
+ : window(nullptr),
+ is_mouse_event(false),
+ in_nonclient_area(false),
+ is_pointer_down(false) {}
+
+ // NOTE: this is set to null if the window is destroyed before a
+ // corresponding release/cancel.
+ ServerWindow* window;
+
+ bool is_mouse_event;
+
+ // Did the pointer event start in the non-client area.
+ bool in_nonclient_area;
+
+ bool is_pointer_down;
+ };
+
+ void ProcessKeyEvent(const ui::KeyEvent& event);
+
+ bool IsTrackingPointer(int32_t pointer_id) const {
+ return pointer_targets_.count(pointer_id) > 0;
+ }
+
+ // EventDispatcher provides the following logic for pointer events:
+ // . wheel events go to the current target of the associated pointer. If
+ // there is no target, they go to the deepest window.
+ // . move (not drag) events go to the deepest window.
+ // . when a pointer goes down all events until the corresponding up or
+ // cancel go to the deepest target. For mouse events the up only occurs
+ // when no buttons on the mouse are down.
+ // This also generates exit events as appropriate. For example, if the mouse
+ // moves between one window to another an exit is generated on the first.
+ void ProcessLocatedEvent(const ui::LocatedEvent& event);
+
+ // Adds |pointer_target| to |pointer_targets_|.
+ void StartTrackingPointer(int32_t pointer_id,
+ const PointerTarget& pointer_target);
+
+ // Removes a PointerTarget from |pointer_targets_|.
+ void StopTrackingPointer(int32_t pointer_id);
+
+ // Starts tracking the pointer for |event|, or if already tracking the
+ // pointer sends the appropriate event to the delegate and updates the
+ // currently tracked PointerTarget appropriately.
+ void UpdateTargetForPointer(int32_t pointer_id,
+ const ui::LocatedEvent& event);
+
+ // Returns a PointerTarget from the supplied event.
+ PointerTarget PointerTargetForEvent(const ui::LocatedEvent& event);
+
+ // Returns true if any pointers are in the pressed/down state.
+ bool AreAnyPointersDown() const;
+
+ // If |target->window| is valid, then passes the event to the delegate.
+ void DispatchToPointerTarget(const PointerTarget& target,
+ const ui::LocatedEvent& event);
+
+ // Dispatch |event| to the delegate.
+ void DispatchToClient(ServerWindow* window,
+ ClientSpecificId client_id,
+ const ui::LocatedEvent& event);
+
+ // Stops sending pointer events to |window|. This does not remove the entry
+ // for |window| from |pointer_targets_|, rather it nulls out the window. This
+ // way we continue to eat events until the up/cancel is received.
+ void CancelPointerEventsToTarget(ServerWindow* window);
+
+ // Used to observe a window. Can be called multiple times on a window. To
+ // unobserve a window, UnobserveWindow() should be called the same number of
+ // times.
+ void ObserveWindow(ServerWindow* winodw);
+ void UnobserveWindow(ServerWindow* winodw);
+
+ // Returns an Accelerator bound to the specified code/flags, and of the
+ // matching |phase|. Otherwise returns null.
+ Accelerator* FindAccelerator(const ui::KeyEvent& event,
+ const ui::mojom::AcceleratorPhase phase);
+
+ ServerWindow* FindDeepestVisibleWindowForEvents(gfx::Point* location);
+
+ // ServerWindowObserver:
+ void OnWillChangeWindowHierarchy(ServerWindow* window,
+ ServerWindow* new_parent,
+ ServerWindow* old_parent) override;
+ void OnWindowVisibilityChanged(ServerWindow* window) override;
+ void OnWindowDestroyed(ServerWindow* window) override;
+
+ EventDispatcherDelegate* delegate_;
+
+ ServerWindow* capture_window_;
+ ClientSpecificId capture_window_client_id_;
+
+ ModalWindowController modal_window_controller_;
+
+ bool mouse_button_down_;
+ ServerWindow* mouse_cursor_source_window_;
+ bool mouse_cursor_in_non_client_area_;
+
+ // The on screen location of the mouse pointer. This can be outside the
+ // bounds of |mouse_cursor_source_window_|, which can capture the cursor.
+ gfx::Point mouse_pointer_last_location_;
+
+ std::map<uint32_t, std::unique_ptr<Accelerator>> accelerators_;
+
+ using PointerIdToTargetMap = std::map<int32_t, PointerTarget>;
+ // |pointer_targets_| contains the active pointers. For a mouse based pointer
+ // a PointerTarget is always active (and present in |pointer_targets_|). For
+ // touch based pointers the pointer is active while down and removed on
+ // cancel or up.
+ PointerIdToTargetMap pointer_targets_;
+
+ // Keeps track of number of observe requests for each observed window.
+ std::map<const ServerWindow*, uint8_t> observed_windows_;
+
+ DISALLOW_COPY_AND_ASSIGN(EventDispatcher);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_EVENT_DISPATCHER_H_
diff --git a/chromium/components/mus/ws/event_dispatcher_delegate.h b/chromium/components/mus/ws/event_dispatcher_delegate.h
new file mode 100644
index 00000000000..1d31fb99d50
--- /dev/null
+++ b/chromium/components/mus/ws/event_dispatcher_delegate.h
@@ -0,0 +1,73 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_EVENT_DISPATCHER_DELEGATE_H_
+#define COMPONENTS_MUS_WS_EVENT_DISPATCHER_DELEGATE_H_
+
+#include <stdint.h>
+
+#include "components/mus/common/types.h"
+
+namespace gfx {
+class Point;
+}
+
+namespace ui {
+class Event;
+}
+
+namespace mus {
+namespace ws {
+
+class Accelerator;
+class ServerWindow;
+
+// Used by EventDispatcher for mocking in tests.
+class EventDispatcherDelegate {
+ public:
+ virtual void OnAccelerator(uint32_t accelerator, const ui::Event& event) = 0;
+
+ virtual void SetFocusedWindowFromEventDispatcher(ServerWindow* window) = 0;
+ virtual ServerWindow* GetFocusedWindowForEventDispatcher() = 0;
+
+ // Called when capture should be set on the native display. |window| is the
+ // window capture is being set on.
+ virtual void SetNativeCapture(ServerWindow* window) = 0;
+ // Called when the native display is having capture released. There is no
+ // longer a ServerWindow holding capture.
+ virtual void ReleaseNativeCapture() = 0;
+ // Called when |window| has lost capture. The native display may still be
+ // holding capture. The delegate should not change native display capture.
+ // ReleaseNativeCapture() is invoked if appropriate.
+ virtual void OnServerWindowCaptureLost(ServerWindow* window) = 0;
+
+ virtual void OnMouseCursorLocationChanged(const gfx::Point& point) = 0;
+
+ // Dispatches an event to the specific client.
+ virtual void DispatchInputEventToWindow(ServerWindow* target,
+ ClientSpecificId client_id,
+ const ui::Event& event,
+ Accelerator* accelerator) = 0;
+
+ // Returns the id of the client to send events to. |in_nonclient_area| is
+ // true if the event occurred in the non-client area of the window.
+ virtual ClientSpecificId GetEventTargetClientId(const ServerWindow* window,
+ bool in_nonclient_area) = 0;
+
+ // Returns the window to start searching from at the specified location, or
+ // null if there is a no window containing |location|.
+ virtual ServerWindow* GetRootWindowContaining(const gfx::Point& location) = 0;
+
+ // Called when event dispatch could not find a target. OnAccelerator may still
+ // be called.
+ virtual void OnEventTargetNotFound(const ui::Event& event) = 0;
+
+ protected:
+ virtual ~EventDispatcherDelegate() {}
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_EVENT_DISPATCHER_DELEGATE_H_
diff --git a/chromium/components/mus/ws/event_dispatcher_unittest.cc b/chromium/components/mus/ws/event_dispatcher_unittest.cc
new file mode 100644
index 00000000000..e47e961aff4
--- /dev/null
+++ b/chromium/components/mus/ws/event_dispatcher_unittest.cc
@@ -0,0 +1,1614 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/event_dispatcher.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <queue>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/mus/common/event_matcher_util.h"
+#include "components/mus/ws/accelerator.h"
+#include "components/mus/ws/event_dispatcher_delegate.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/server_window_surface_manager_test_api.h"
+#include "components/mus/ws/test_server_window_delegate.h"
+#include "components/mus/ws/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/event.h"
+
+namespace mus {
+namespace ws {
+namespace test {
+namespace {
+
+// Client ids used to indicate the client area and non-client area.
+const ClientSpecificId kClientAreaId = 11;
+const ClientSpecificId kNonclientAreaId = 111;
+
+// Identifies a generated event.
+struct DispatchedEventDetails {
+ DispatchedEventDetails()
+ : window(nullptr), client_id(kInvalidClientId), accelerator(nullptr) {}
+
+ bool IsNonclientArea() const { return client_id == kNonclientAreaId; }
+ bool IsClientArea() const { return client_id == kClientAreaId; }
+
+ ServerWindow* window;
+ ClientSpecificId client_id;
+ std::unique_ptr<ui::Event> event;
+ Accelerator* accelerator;
+};
+
+class TestEventDispatcherDelegate : public EventDispatcherDelegate {
+ public:
+ // Delegate interface used by this class to release capture on event
+ // dispatcher.
+ class Delegate {
+ public:
+ virtual void ReleaseCapture() = 0;
+ };
+
+ explicit TestEventDispatcherDelegate(Delegate* delegate)
+ : delegate_(delegate),
+ focused_window_(nullptr),
+ lost_capture_window_(nullptr),
+ last_accelerator_(0) {}
+ ~TestEventDispatcherDelegate() override {}
+
+ ui::Event* last_event_target_not_found() {
+ return last_event_target_not_found_.get();
+ }
+
+ uint32_t GetAndClearLastAccelerator() {
+ uint32_t return_value = last_accelerator_;
+ last_accelerator_ = 0;
+ return return_value;
+ }
+
+ void set_root(ServerWindow* root) { root_ = root; }
+
+ // Returns the last dispatched event, or null if there are no more.
+ std::unique_ptr<DispatchedEventDetails>
+ GetAndAdvanceDispatchedEventDetails() {
+ if (dispatched_event_queue_.empty())
+ return nullptr;
+
+ std::unique_ptr<DispatchedEventDetails> details =
+ std::move(dispatched_event_queue_.front());
+ dispatched_event_queue_.pop();
+ return details;
+ }
+
+ ServerWindow* GetAndClearLastFocusedWindow() {
+ ServerWindow* result = focused_window_;
+ focused_window_ = nullptr;
+ return result;
+ }
+
+ bool has_queued_events() const { return !dispatched_event_queue_.empty(); }
+ ServerWindow* lost_capture_window() { return lost_capture_window_; }
+
+ // EventDispatcherDelegate:
+ void SetFocusedWindowFromEventDispatcher(ServerWindow* window) override {
+ focused_window_ = window;
+ }
+
+ private:
+ // EventDispatcherDelegate:
+ void OnAccelerator(uint32_t accelerator, const ui::Event& event) override {
+ EXPECT_EQ(0u, last_accelerator_);
+ last_accelerator_ = accelerator;
+ }
+ ServerWindow* GetFocusedWindowForEventDispatcher() override {
+ return focused_window_;
+ }
+ void SetNativeCapture(ServerWindow* window) override {}
+ void ReleaseNativeCapture() override {
+ if (delegate_)
+ delegate_->ReleaseCapture();
+ }
+ void OnServerWindowCaptureLost(ServerWindow* window) override {
+ lost_capture_window_ = window;
+ }
+ void OnMouseCursorLocationChanged(const gfx::Point& point) override {}
+ void DispatchInputEventToWindow(ServerWindow* target,
+ ClientSpecificId client_id,
+ const ui::Event& event,
+ Accelerator* accelerator) override {
+ std::unique_ptr<DispatchedEventDetails> details(new DispatchedEventDetails);
+ details->window = target;
+ details->client_id = client_id;
+ details->event = ui::Event::Clone(event);
+ details->accelerator = accelerator;
+ dispatched_event_queue_.push(std::move(details));
+ }
+ ClientSpecificId GetEventTargetClientId(const ServerWindow* window,
+ bool in_nonclient_area) override {
+ return in_nonclient_area ? kNonclientAreaId : kClientAreaId;
+ }
+ ServerWindow* GetRootWindowContaining(const gfx::Point& location) override {
+ return root_;
+ }
+ void OnEventTargetNotFound(const ui::Event& event) override {
+ last_event_target_not_found_ = ui::Event::Clone(event);
+ }
+
+ Delegate* delegate_;
+ ServerWindow* focused_window_;
+ ServerWindow* lost_capture_window_;
+ uint32_t last_accelerator_;
+ std::queue<std::unique_ptr<DispatchedEventDetails>> dispatched_event_queue_;
+ ServerWindow* root_ = nullptr;
+ std::unique_ptr<ui::Event> last_event_target_not_found_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestEventDispatcherDelegate);
+};
+
+// Used by RunMouseEventTests(). Can identify up to two generated events. The
+// first ServerWindow and two points identify the first event, the second
+// ServerWindow and points identify the second event. If only one event is
+// generated set the second window to null.
+struct MouseEventTest {
+ ui::MouseEvent input_event;
+ ServerWindow* expected_target_window1;
+ gfx::Point expected_root_location1;
+ gfx::Point expected_location1;
+ ServerWindow* expected_target_window2;
+ gfx::Point expected_root_location2;
+ gfx::Point expected_location2;
+};
+
+// Verifies |details| matches the supplied ServerWindow and points.
+void ExpectDispatchedEventDetailsMatches(const DispatchedEventDetails* details,
+ ServerWindow* target,
+ const gfx::Point& root_location,
+ const gfx::Point& location) {
+ if (!target) {
+ ASSERT_FALSE(details);
+ return;
+ }
+
+ ASSERT_EQ(target, details->window);
+ ASSERT_TRUE(details->event);
+ ASSERT_TRUE(details->event->IsLocatedEvent());
+ ASSERT_TRUE(details->IsClientArea());
+ ASSERT_EQ(root_location, details->event->AsLocatedEvent()->root_location());
+ ASSERT_EQ(location, details->event->AsLocatedEvent()->location());
+}
+
+void RunMouseEventTests(EventDispatcher* dispatcher,
+ TestEventDispatcherDelegate* dispatcher_delegate,
+ MouseEventTest* tests,
+ size_t test_count) {
+ for (size_t i = 0; i < test_count; ++i) {
+ const MouseEventTest& test = tests[i];
+ ASSERT_FALSE(dispatcher_delegate->has_queued_events())
+ << " unexpected queued events before running " << i;
+ if (test.input_event.IsMouseWheelEvent())
+ dispatcher->ProcessEvent(test.input_event);
+ else
+ dispatcher->ProcessEvent(ui::PointerEvent(test.input_event));
+
+ std::unique_ptr<DispatchedEventDetails> details =
+ dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ ASSERT_NO_FATAL_FAILURE(ExpectDispatchedEventDetailsMatches(
+ details.get(), test.expected_target_window1,
+ test.expected_root_location1, test.expected_location1))
+ << " details don't match " << i;
+ details = dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ ASSERT_NO_FATAL_FAILURE(ExpectDispatchedEventDetailsMatches(
+ details.get(), test.expected_target_window2,
+ test.expected_root_location2, test.expected_location2))
+ << " details2 don't match " << i;
+ ASSERT_FALSE(dispatcher_delegate->has_queued_events())
+ << " unexpected queued events after running " << i;
+ }
+}
+
+} // namespace
+
+// Test fixture for EventDispatcher with friend access to verify the internal
+// state. Setup creates a TestServerWindowDelegate, a visible root ServerWindow,
+// a TestEventDispatcher and the EventDispatcher for testing.
+class EventDispatcherTest : public testing::Test,
+ public TestEventDispatcherDelegate::Delegate {
+ public:
+ EventDispatcherTest() {}
+ ~EventDispatcherTest() override {}
+
+ ServerWindow* root_window() { return root_window_.get(); }
+ TestEventDispatcherDelegate* test_event_dispatcher_delegate() {
+ return test_event_dispatcher_delegate_.get();
+ }
+ EventDispatcher* event_dispatcher() { return event_dispatcher_.get(); }
+
+ bool AreAnyPointersDown() const;
+ // Deletes everything created during SetUp()
+ void ClearSetup();
+ std::unique_ptr<ServerWindow> CreateChildWindowWithParent(
+ const WindowId& id,
+ ServerWindow* parent);
+ // Creates a window which is a child of |root_window_|.
+ std::unique_ptr<ServerWindow> CreateChildWindow(const WindowId& id);
+ bool IsMouseButtonDown() const;
+ bool IsWindowPointerTarget(const ServerWindow* window) const;
+ int NumberPointerTargetsForWindow(ServerWindow* window) const;
+ ServerWindow* GetActiveSystemModalWindow() const;
+
+ protected:
+ // testing::Test:
+ void SetUp() override;
+
+ private:
+ // TestEventDispatcherDelegate::Delegate:
+ void ReleaseCapture() override {
+ event_dispatcher_->SetCaptureWindow(nullptr, kInvalidClientId);
+ }
+
+ std::unique_ptr<TestServerWindowDelegate> window_delegate_;
+ std::unique_ptr<ServerWindow> root_window_;
+ std::unique_ptr<TestEventDispatcherDelegate> test_event_dispatcher_delegate_;
+ std::unique_ptr<EventDispatcher> event_dispatcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(EventDispatcherTest);
+};
+
+bool EventDispatcherTest::AreAnyPointersDown() const {
+ return EventDispatcherTestApi(event_dispatcher_.get()).AreAnyPointersDown();
+}
+
+void EventDispatcherTest::ClearSetup() {
+ window_delegate_.reset();
+ root_window_.reset();
+ test_event_dispatcher_delegate_.reset();
+ event_dispatcher_.reset();
+}
+
+std::unique_ptr<ServerWindow> EventDispatcherTest::CreateChildWindowWithParent(
+ const WindowId& id,
+ ServerWindow* parent) {
+ std::unique_ptr<ServerWindow> child(
+ new ServerWindow(window_delegate_.get(), id));
+ parent->Add(child.get());
+ child->SetVisible(true);
+ EnableHitTest(child.get());
+ return child;
+}
+
+std::unique_ptr<ServerWindow> EventDispatcherTest::CreateChildWindow(
+ const WindowId& id) {
+ return CreateChildWindowWithParent(id, root_window_.get());
+}
+
+bool EventDispatcherTest::IsMouseButtonDown() const {
+ return EventDispatcherTestApi(event_dispatcher_.get()).is_mouse_button_down();
+}
+
+bool EventDispatcherTest::IsWindowPointerTarget(
+ const ServerWindow* window) const {
+ return EventDispatcherTestApi(event_dispatcher_.get())
+ .IsWindowPointerTarget(window);
+}
+
+int EventDispatcherTest::NumberPointerTargetsForWindow(
+ ServerWindow* window) const {
+ return EventDispatcherTestApi(event_dispatcher_.get())
+ .NumberPointerTargetsForWindow(window);
+}
+
+ServerWindow* EventDispatcherTest::GetActiveSystemModalWindow() const {
+ ModalWindowController* mwc =
+ EventDispatcherTestApi(event_dispatcher_.get()).modal_window_controller();
+ return ModalWindowControllerTestApi(mwc).GetActiveSystemModalWindow();
+}
+
+void EventDispatcherTest::SetUp() {
+ testing::Test::SetUp();
+
+ window_delegate_.reset(new TestServerWindowDelegate());
+ root_window_.reset(new ServerWindow(window_delegate_.get(), WindowId(1, 2)));
+ window_delegate_->set_root_window(root_window_.get());
+ root_window_->SetVisible(true);
+
+ test_event_dispatcher_delegate_.reset(new TestEventDispatcherDelegate(this));
+ event_dispatcher_.reset(
+ new EventDispatcher(test_event_dispatcher_delegate_.get()));
+ test_event_dispatcher_delegate_->set_root(root_window_.get());
+}
+
+TEST_F(EventDispatcherTest, ProcessEvent) {
+ std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3));
+
+ root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ child->SetBounds(gfx::Rect(10, 10, 20, 20));
+
+ // Send event that is over child.
+ const ui::PointerEvent ui_event(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(20, 25), gfx::Point(20, 25),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ event_dispatcher()->ProcessEvent(ui_event);
+
+ std::unique_ptr<DispatchedEventDetails> details =
+ test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
+ ASSERT_TRUE(details);
+ ASSERT_EQ(child.get(), details->window);
+
+ ASSERT_TRUE(details->event);
+ ASSERT_TRUE(details->event->IsPointerEvent());
+
+ ui::PointerEvent* dispatched_event = details->event->AsPointerEvent();
+ EXPECT_EQ(gfx::Point(20, 25), dispatched_event->root_location());
+ EXPECT_EQ(gfx::Point(10, 15), dispatched_event->location());
+}
+
+TEST_F(EventDispatcherTest, ProcessEventNoTarget) {
+ // Send event without a target.
+ ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE);
+ event_dispatcher()->ProcessEvent(key);
+
+ // Event wasn't dispatched to a target.
+ std::unique_ptr<DispatchedEventDetails> details =
+ test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_FALSE(details);
+
+ // Delegate was informed that there wasn't a target.
+ ui::Event* event_out =
+ test_event_dispatcher_delegate()->last_event_target_not_found();
+ ASSERT_TRUE(event_out);
+ EXPECT_TRUE(event_out->IsKeyEvent());
+ EXPECT_EQ(ui::VKEY_A, event_out->AsKeyEvent()->key_code());
+}
+
+TEST_F(EventDispatcherTest, AcceleratorBasic) {
+ ClearSetup();
+ TestEventDispatcherDelegate event_dispatcher_delegate(nullptr);
+ EventDispatcher dispatcher(&event_dispatcher_delegate);
+
+ uint32_t accelerator_1 = 1;
+ mojom::EventMatcherPtr matcher = mus::CreateKeyMatcher(
+ ui::mojom::KeyboardCode::W, ui::mojom::kEventFlagControlDown);
+ EXPECT_TRUE(dispatcher.AddAccelerator(accelerator_1, std::move(matcher)));
+
+ uint32_t accelerator_2 = 2;
+ matcher = mus::CreateKeyMatcher(ui::mojom::KeyboardCode::N,
+ ui::mojom::kEventFlagNone);
+ EXPECT_TRUE(dispatcher.AddAccelerator(accelerator_2, std::move(matcher)));
+
+ // Attempting to add a new accelerator with the same id should fail.
+ matcher = mus::CreateKeyMatcher(ui::mojom::KeyboardCode::T,
+ ui::mojom::kEventFlagNone);
+ EXPECT_FALSE(dispatcher.AddAccelerator(accelerator_2, std::move(matcher)));
+
+ // Adding the accelerator with the same id should succeed once the existing
+ // accelerator is removed.
+ dispatcher.RemoveAccelerator(accelerator_2);
+ matcher = mus::CreateKeyMatcher(ui::mojom::KeyboardCode::T,
+ ui::mojom::kEventFlagNone);
+ EXPECT_TRUE(dispatcher.AddAccelerator(accelerator_2, std::move(matcher)));
+
+ // Attempting to add an accelerator with the same matcher should fail.
+ uint32_t accelerator_3 = 3;
+ matcher = mus::CreateKeyMatcher(ui::mojom::KeyboardCode::T,
+ ui::mojom::kEventFlagNone);
+ EXPECT_FALSE(dispatcher.AddAccelerator(accelerator_3, std::move(matcher)));
+
+ matcher = mus::CreateKeyMatcher(ui::mojom::KeyboardCode::T,
+ ui::mojom::kEventFlagControlDown);
+ EXPECT_TRUE(dispatcher.AddAccelerator(accelerator_3, std::move(matcher)));
+}
+
+TEST_F(EventDispatcherTest, EventMatching) {
+ TestEventDispatcherDelegate* event_dispatcher_delegate =
+ test_event_dispatcher_delegate();
+ EventDispatcher* dispatcher = event_dispatcher();
+
+ mojom::EventMatcherPtr matcher = mus::CreateKeyMatcher(
+ ui::mojom::KeyboardCode::W, ui::mojom::kEventFlagControlDown);
+ uint32_t accelerator_1 = 1;
+ dispatcher->AddAccelerator(accelerator_1, std::move(matcher));
+
+ ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN);
+ dispatcher->ProcessEvent(key);
+ EXPECT_EQ(accelerator_1,
+ event_dispatcher_delegate->GetAndClearLastAccelerator());
+
+ // EF_NUM_LOCK_ON should be ignored since CreateKeyMatcher defaults to
+ // ignoring.
+ key = ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_W,
+ ui::EF_CONTROL_DOWN | ui::EF_NUM_LOCK_ON);
+ dispatcher->ProcessEvent(key);
+ EXPECT_EQ(accelerator_1,
+ event_dispatcher_delegate->GetAndClearLastAccelerator());
+
+ key = ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_NONE);
+ dispatcher->ProcessEvent(key);
+ EXPECT_EQ(0u, event_dispatcher_delegate->GetAndClearLastAccelerator());
+
+ uint32_t accelerator_2 = 2;
+ matcher = mus::CreateKeyMatcher(ui::mojom::KeyboardCode::W,
+ ui::mojom::kEventFlagNone);
+ dispatcher->AddAccelerator(accelerator_2, std::move(matcher));
+ dispatcher->ProcessEvent(key);
+ EXPECT_EQ(accelerator_2,
+ event_dispatcher_delegate->GetAndClearLastAccelerator());
+
+ dispatcher->RemoveAccelerator(accelerator_2);
+ dispatcher->ProcessEvent(key);
+ EXPECT_EQ(0u, event_dispatcher_delegate->GetAndClearLastAccelerator());
+}
+
+// Tests that a post-target accelerator is not triggered by ProcessEvent.
+TEST_F(EventDispatcherTest, PostTargetAccelerator) {
+ TestEventDispatcherDelegate* event_dispatcher_delegate =
+ test_event_dispatcher_delegate();
+ EventDispatcher* dispatcher = event_dispatcher();
+
+ mojom::EventMatcherPtr matcher = mus::CreateKeyMatcher(
+ ui::mojom::KeyboardCode::W, ui::mojom::kEventFlagControlDown);
+ matcher->accelerator_phase = ui::mojom::AcceleratorPhase::POST_TARGET;
+ uint32_t accelerator_1 = 1;
+ dispatcher->AddAccelerator(accelerator_1, std::move(matcher));
+
+ ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN);
+ // The post-target accelerator should be fired if there is no focused window.
+ dispatcher->ProcessEvent(key);
+ EXPECT_EQ(accelerator_1,
+ event_dispatcher_delegate->GetAndClearLastAccelerator());
+ std::unique_ptr<DispatchedEventDetails> details =
+ event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_FALSE(details);
+
+ // Set focused window for EventDispatcher dispatches key events.
+ std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3));
+ event_dispatcher_delegate->SetFocusedWindowFromEventDispatcher(child.get());
+
+ // With a focused window the event should be dispatched.
+ dispatcher->ProcessEvent(key);
+ EXPECT_EQ(0u, event_dispatcher_delegate->GetAndClearLastAccelerator());
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_TRUE(details);
+ EXPECT_TRUE(details->accelerator);
+
+ base::WeakPtr<Accelerator> accelerator_weak_ptr =
+ details->accelerator->GetWeakPtr();
+ dispatcher->RemoveAccelerator(accelerator_1);
+ EXPECT_FALSE(accelerator_weak_ptr);
+
+ // Post deletion there should be no accelerator
+ dispatcher->ProcessEvent(key);
+ EXPECT_EQ(0u, event_dispatcher_delegate->GetAndClearLastAccelerator());
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_TRUE(details);
+ EXPECT_FALSE(details->accelerator);
+}
+
+TEST_F(EventDispatcherTest, Capture) {
+ ServerWindow* root = root_window();
+ std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3));
+
+ root->SetBounds(gfx::Rect(0, 0, 100, 100));
+ child->SetBounds(gfx::Rect(10, 10, 20, 20));
+
+ MouseEventTest tests[] = {
+ // Send a mouse down event over child.
+ {ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(20, 25),
+ gfx::Point(20, 25), base::TimeTicks(),
+ ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON),
+ child.get(), gfx::Point(20, 25), gfx::Point(10, 15), nullptr,
+ gfx::Point(), gfx::Point()},
+
+ // Capture should be activated. Let's send a mouse move outside the bounds
+ // of the child.
+ {ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(50, 50),
+ gfx::Point(50, 50), base::TimeTicks(),
+ ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON),
+ child.get(), gfx::Point(50, 50), gfx::Point(40, 40), nullptr,
+ gfx::Point(), gfx::Point()},
+ // Release the mouse and verify that the mouse up event goes to the child.
+ {ui::MouseEvent(ui::ET_MOUSE_RELEASED, gfx::Point(50, 50),
+ gfx::Point(50, 50), base::TimeTicks(),
+ ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON),
+ child.get(), gfx::Point(50, 50), gfx::Point(40, 40), nullptr,
+ gfx::Point(), gfx::Point()},
+
+ // A mouse move at (50, 50) should now go to the root window. As the
+ // move crosses between |child| and |root| |child| gets an exit, and
+ // |root| the move.
+ {ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(50, 50),
+ gfx::Point(50, 50), base::TimeTicks(),
+ ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON),
+ child.get(), gfx::Point(50, 50), gfx::Point(40, 40), root,
+ gfx::Point(50, 50), gfx::Point(50, 50)},
+
+ };
+ RunMouseEventTests(event_dispatcher(), test_event_dispatcher_delegate(),
+ tests, arraysize(tests));
+}
+
+TEST_F(EventDispatcherTest, CaptureMultipleMouseButtons) {
+ std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3));
+
+ root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ child->SetBounds(gfx::Rect(10, 10, 20, 20));
+
+ MouseEventTest tests[] = {
+ // Send a mouse down event over child with a left mouse button
+ {ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(20, 25),
+ gfx::Point(20, 25), base::TimeTicks(),
+ ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON),
+ child.get(), gfx::Point(20, 25), gfx::Point(10, 15), nullptr,
+ gfx::Point(), gfx::Point()},
+
+ // Capture should be activated. Let's send a mouse move outside the bounds
+ // of the child and press the right mouse button too.
+ {ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(50, 50),
+ gfx::Point(50, 50), base::TimeTicks(),
+ ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON, 0),
+ child.get(), gfx::Point(50, 50), gfx::Point(40, 40), nullptr,
+ gfx::Point(), gfx::Point()},
+
+ // Release the left mouse button and verify that the mouse up event goes
+ // to the child.
+ {ui::MouseEvent(ui::ET_MOUSE_RELEASED, gfx::Point(50, 50),
+ gfx::Point(50, 50), base::TimeTicks(),
+ ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON,
+ ui::EF_RIGHT_MOUSE_BUTTON),
+ child.get(), gfx::Point(50, 50), gfx::Point(40, 40), nullptr,
+ gfx::Point(), gfx::Point()},
+
+ // A mouse move at (50, 50) should still go to the child.
+ {ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(50, 50),
+ gfx::Point(50, 50), base::TimeTicks(),
+ ui::EF_LEFT_MOUSE_BUTTON, 0),
+ child.get(), gfx::Point(50, 50), gfx::Point(40, 40), nullptr,
+ gfx::Point(), gfx::Point()},
+
+ };
+ RunMouseEventTests(event_dispatcher(), test_event_dispatcher_delegate(),
+ tests, arraysize(tests));
+}
+
+TEST_F(EventDispatcherTest, ClientAreaGoesToOwner) {
+ std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3));
+
+ root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ child->SetBounds(gfx::Rect(10, 10, 20, 20));
+
+ child->SetClientArea(gfx::Insets(5, 5, 5, 5), std::vector<gfx::Rect>());
+
+ TestEventDispatcherDelegate* event_dispatcher_delegate =
+ test_event_dispatcher_delegate();
+ EventDispatcher* dispatcher = event_dispatcher();
+
+ // Start move loop by sending mouse event over non-client area.
+ const ui::PointerEvent press_event(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(12, 12), gfx::Point(12, 12),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ dispatcher->ProcessEvent(press_event);
+
+ // Events should target child and be in the non-client area.
+ std::unique_ptr<DispatchedEventDetails> details =
+ event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_FALSE(event_dispatcher_delegate->has_queued_events());
+ ASSERT_TRUE(details);
+ ASSERT_EQ(child.get(), details->window);
+ EXPECT_TRUE(details->IsNonclientArea());
+
+ // Move the mouse 5,6 pixels and target is the same.
+ const ui::PointerEvent move_event(
+ ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(17, 18), gfx::Point(17, 18),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, 0));
+ dispatcher->ProcessEvent(move_event);
+
+ // Still same target.
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_FALSE(event_dispatcher_delegate->has_queued_events());
+ ASSERT_EQ(child.get(), details->window);
+ EXPECT_TRUE(details->IsNonclientArea());
+
+ // Release the mouse.
+ const ui::PointerEvent release_event(ui::MouseEvent(
+ ui::ET_MOUSE_RELEASED, gfx::Point(17, 18), gfx::Point(17, 18),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ dispatcher->ProcessEvent(release_event);
+
+ // The event should not have been dispatched to the delegate.
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_FALSE(event_dispatcher_delegate->has_queued_events());
+ ASSERT_EQ(child.get(), details->window);
+ EXPECT_TRUE(details->IsNonclientArea());
+
+ // Press in the client area and verify target/client area. The non-client area
+ // should get an exit first.
+ const ui::PointerEvent press_event2(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(21, 22), gfx::Point(21, 22),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ dispatcher->ProcessEvent(press_event2);
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_TRUE(event_dispatcher_delegate->has_queued_events());
+ ASSERT_EQ(child.get(), details->window);
+ EXPECT_TRUE(details->IsNonclientArea());
+ EXPECT_EQ(ui::ET_POINTER_EXITED, details->event->type());
+
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_FALSE(event_dispatcher_delegate->has_queued_events());
+ ASSERT_EQ(child.get(), details->window);
+ EXPECT_TRUE(details->IsClientArea());
+ EXPECT_EQ(ui::ET_POINTER_DOWN, details->event->type());
+}
+
+TEST_F(EventDispatcherTest, AdditionalClientArea) {
+ std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3));
+
+ root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ child->SetBounds(gfx::Rect(10, 10, 20, 20));
+
+ std::vector<gfx::Rect> additional_client_areas;
+ additional_client_areas.push_back(gfx::Rect(18, 0, 2, 2));
+ child->SetClientArea(gfx::Insets(5, 5, 5, 5), additional_client_areas);
+
+ TestEventDispatcherDelegate* event_dispatcher_delegate =
+ test_event_dispatcher_delegate();
+ // Press in the additional client area, it should go to the child.
+ const ui::PointerEvent press_event(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(28, 11), gfx::Point(28, 11),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ event_dispatcher()->ProcessEvent(press_event);
+
+ // Events should target child and be in the client area.
+ std::unique_ptr<DispatchedEventDetails> details =
+ event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_FALSE(event_dispatcher_delegate->has_queued_events());
+ ASSERT_EQ(child.get(), details->window);
+ EXPECT_TRUE(details->IsClientArea());
+}
+
+TEST_F(EventDispatcherTest, HitTestMask) {
+ std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3));
+
+ root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ child->SetBounds(gfx::Rect(10, 10, 20, 20));
+ child->SetHitTestMask(gfx::Rect(2, 2, 16, 16));
+
+ // Move in the masked area.
+ const ui::PointerEvent move1(ui::MouseEvent(
+ ui::ET_MOUSE_MOVED, gfx::Point(11, 11), gfx::Point(11, 11),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, 0));
+ event_dispatcher()->ProcessEvent(move1);
+
+ // Event went through the child window and hit the root.
+ std::unique_ptr<DispatchedEventDetails> details1 =
+ test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_EQ(root_window(), details1->window);
+ EXPECT_TRUE(details1->IsClientArea());
+
+ child->ClearHitTestMask();
+
+ // Move right in the same part of the window.
+ const ui::PointerEvent move2(ui::MouseEvent(
+ ui::ET_MOUSE_MOVED, gfx::Point(11, 12), gfx::Point(11, 12),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, 0));
+ event_dispatcher()->ProcessEvent(move2);
+
+ // Mouse exits the root.
+ std::unique_ptr<DispatchedEventDetails> details2 =
+ test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_EQ(ui::ET_POINTER_EXITED, details2->event->type());
+
+ // Mouse hits the child.
+ std::unique_ptr<DispatchedEventDetails> details3 =
+ test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_EQ(child.get(), details3->window);
+ EXPECT_TRUE(details3->IsClientArea());
+}
+
+TEST_F(EventDispatcherTest, DontFocusOnSecondDown) {
+ std::unique_ptr<ServerWindow> child1 = CreateChildWindow(WindowId(1, 3));
+ std::unique_ptr<ServerWindow> child2 = CreateChildWindow(WindowId(1, 4));
+
+ root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ child1->SetBounds(gfx::Rect(10, 10, 20, 20));
+ child2->SetBounds(gfx::Rect(50, 51, 11, 12));
+
+ TestEventDispatcherDelegate* event_dispatcher_delegate =
+ test_event_dispatcher_delegate();
+ EventDispatcher* dispatcher = event_dispatcher();
+
+ // Press on child1. First press event should change focus.
+ const ui::PointerEvent press_event(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(12, 12), gfx::Point(12, 12),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ dispatcher->ProcessEvent(press_event);
+ std::unique_ptr<DispatchedEventDetails> details =
+ event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_FALSE(event_dispatcher_delegate->has_queued_events());
+ EXPECT_EQ(child1.get(), details->window);
+ EXPECT_EQ(child1.get(),
+ event_dispatcher_delegate->GetAndClearLastFocusedWindow());
+
+ // Press (with a different pointer id) on child2. Event should go to child2,
+ // but focus should not change.
+ const ui::PointerEvent touch_event(ui::TouchEvent(
+ ui::ET_TOUCH_PRESSED, gfx::Point(53, 54), 2, base::TimeTicks()));
+ dispatcher->ProcessEvent(touch_event);
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_FALSE(event_dispatcher_delegate->has_queued_events());
+ EXPECT_EQ(child2.get(), details->window);
+ EXPECT_EQ(nullptr, event_dispatcher_delegate->GetAndClearLastFocusedWindow());
+}
+
+TEST_F(EventDispatcherTest, TwoPointersActive) {
+ std::unique_ptr<ServerWindow> child1 = CreateChildWindow(WindowId(1, 3));
+ std::unique_ptr<ServerWindow> child2 = CreateChildWindow(WindowId(1, 4));
+
+ root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ child1->SetBounds(gfx::Rect(10, 10, 20, 20));
+ child2->SetBounds(gfx::Rect(50, 51, 11, 12));
+
+ TestEventDispatcherDelegate* event_dispatcher_delegate =
+ test_event_dispatcher_delegate();
+ EventDispatcher* dispatcher = event_dispatcher();
+
+ // Press on child1.
+ const ui::PointerEvent touch_event1(ui::TouchEvent(
+ ui::ET_TOUCH_PRESSED, gfx::Point(12, 13), 1, base::TimeTicks()));
+ dispatcher->ProcessEvent(touch_event1);
+ std::unique_ptr<DispatchedEventDetails> details =
+ event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_EQ(child1.get(), details->window);
+
+ // Drag over child2, child1 should get the drag.
+ const ui::PointerEvent drag_event1(ui::TouchEvent(
+ ui::ET_TOUCH_MOVED, gfx::Point(53, 54), 1, base::TimeTicks()));
+ dispatcher->ProcessEvent(drag_event1);
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_EQ(child1.get(), details->window);
+
+ // Press on child2 with a different touch id.
+ const ui::PointerEvent touch_event2(ui::TouchEvent(
+ ui::ET_TOUCH_PRESSED, gfx::Point(54, 55), 2, base::TimeTicks()));
+ dispatcher->ProcessEvent(touch_event2);
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_EQ(child2.get(), details->window);
+
+ // Drag over child1 with id 2, child2 should continue to get the drag.
+ const ui::PointerEvent drag_event2(ui::TouchEvent(
+ ui::ET_TOUCH_MOVED, gfx::Point(13, 14), 2, base::TimeTicks()));
+ dispatcher->ProcessEvent(drag_event2);
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_EQ(child2.get(), details->window);
+
+ // Drag again with id 1, child1 should continue to get it.
+ dispatcher->ProcessEvent(drag_event1);
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_EQ(child1.get(), details->window);
+
+ // Release touch id 1, and click on 2. 2 should get it.
+ const ui::PointerEvent touch_release(ui::TouchEvent(
+ ui::ET_TOUCH_RELEASED, gfx::Point(54, 55), 1, base::TimeTicks()));
+ dispatcher->ProcessEvent(touch_release);
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_EQ(child1.get(), details->window);
+ const ui::PointerEvent touch_event3(ui::TouchEvent(
+ ui::ET_TOUCH_PRESSED, gfx::Point(54, 55), 2, base::TimeTicks()));
+ dispatcher->ProcessEvent(touch_event3);
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_EQ(child2.get(), details->window);
+}
+
+TEST_F(EventDispatcherTest, DestroyWindowWhileGettingEvents) {
+ std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3));
+
+ root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ child->SetBounds(gfx::Rect(10, 10, 20, 20));
+
+ TestEventDispatcherDelegate* event_dispatcher_delegate =
+ test_event_dispatcher_delegate();
+ EventDispatcher* dispatcher = event_dispatcher();
+
+ // Press on child.
+ const ui::PointerEvent touch_event1(ui::TouchEvent(
+ ui::ET_TOUCH_PRESSED, gfx::Point(12, 13), 1, base::TimeTicks()));
+ dispatcher->ProcessEvent(touch_event1);
+ std::unique_ptr<DispatchedEventDetails> details =
+ event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_FALSE(event_dispatcher_delegate->has_queued_events());
+ EXPECT_EQ(child.get(), details->window);
+
+ // Delete child, and continue the drag. Event should not be dispatched.
+ child.reset();
+
+ const ui::PointerEvent drag_event1(ui::TouchEvent(
+ ui::ET_TOUCH_MOVED, gfx::Point(53, 54), 1, base::TimeTicks()));
+ dispatcher->ProcessEvent(drag_event1);
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_EQ(nullptr, details.get());
+}
+
+TEST_F(EventDispatcherTest, MouseInExtendedHitTestRegion) {
+ ServerWindow* root = root_window();
+ std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3));
+
+ root->SetBounds(gfx::Rect(0, 0, 100, 100));
+ child->SetBounds(gfx::Rect(10, 10, 20, 20));
+
+ TestEventDispatcherDelegate* event_dispatcher_delegate =
+ test_event_dispatcher_delegate();
+ EventDispatcher* dispatcher = event_dispatcher();
+
+ // Send event that is not over child.
+ const ui::PointerEvent ui_event(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(8, 9), gfx::Point(8, 9),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ dispatcher->ProcessEvent(ui_event);
+ std::unique_ptr<DispatchedEventDetails> details =
+ event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ ASSERT_EQ(root, details->window);
+
+ // Release the mouse.
+ const ui::PointerEvent release_event(ui::MouseEvent(
+ ui::ET_MOUSE_RELEASED, gfx::Point(8, 9), gfx::Point(8, 9),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ dispatcher->ProcessEvent(release_event);
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_FALSE(event_dispatcher_delegate->has_queued_events());
+ ASSERT_EQ(root, details->window);
+ EXPECT_TRUE(details->IsClientArea());
+
+ // Change the extended hit test region and send event in extended hit test
+ // region. Should result in exit for root, followed by press for child.
+ child->set_extended_hit_test_region(gfx::Insets(5, 5, 5, 5));
+ dispatcher->ProcessEvent(ui_event);
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_EQ(root, details->window);
+ EXPECT_EQ(ui::ET_POINTER_EXITED, details->event->type());
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ ASSERT_TRUE(details);
+
+ EXPECT_FALSE(event_dispatcher_delegate->has_queued_events());
+ EXPECT_TRUE(details->IsNonclientArea());
+ ASSERT_EQ(child.get(), details->window);
+ EXPECT_EQ(ui::ET_POINTER_DOWN, details->event->type());
+ ASSERT_TRUE(details->event.get());
+ ASSERT_TRUE(details->event->IsPointerEvent());
+ EXPECT_EQ(gfx::Point(-2, -1), details->event->AsPointerEvent()->location());
+}
+
+TEST_F(EventDispatcherTest, WheelWhileDown) {
+ std::unique_ptr<ServerWindow> child1 = CreateChildWindow(WindowId(1, 3));
+ std::unique_ptr<ServerWindow> child2 = CreateChildWindow(WindowId(1, 4));
+
+ root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ child1->SetBounds(gfx::Rect(10, 10, 20, 20));
+ child2->SetBounds(gfx::Rect(50, 51, 11, 12));
+
+ MouseEventTest tests[] = {
+ // Send a mouse down event over child1.
+ {ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(15, 15),
+ gfx::Point(15, 15), base::TimeTicks(),
+ ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON),
+ child1.get(), gfx::Point(15, 15), gfx::Point(5, 5), nullptr,
+ gfx::Point(), gfx::Point()},
+ // Send mouse wheel over child2, should go to child1 as it has capture.
+ {ui::MouseWheelEvent(gfx::Vector2d(1, 0), gfx::Point(53, 54),
+ gfx::Point(53, 54), base::TimeTicks(), ui::EF_NONE,
+ ui::EF_NONE),
+ child1.get(), gfx::Point(53, 54), gfx::Point(43, 44), nullptr,
+ gfx::Point(), gfx::Point()},
+ };
+ RunMouseEventTests(event_dispatcher(), test_event_dispatcher_delegate(),
+ tests, arraysize(tests));
+}
+
+// Tests that when explicit capture has been set that all events go to the
+// designated window, and that when capture is cleared, events find the
+// appropriate target window.
+TEST_F(EventDispatcherTest, SetExplicitCapture) {
+ ServerWindow* root = root_window();
+ std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3));
+
+ root->SetBounds(gfx::Rect(0, 0, 100, 100));
+ child->SetBounds(gfx::Rect(10, 10, 20, 20));
+
+ TestEventDispatcherDelegate* event_dispatcher_delegate =
+ test_event_dispatcher_delegate();
+ EventDispatcher* dispatcher = event_dispatcher();
+
+ {
+ // Send all pointer events to the child.
+ dispatcher->SetCaptureWindow(child.get(), kClientAreaId);
+
+ // The mouse press should go to the child even though its outside its
+ // bounds.
+ const ui::PointerEvent left_press_event(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(5, 5), gfx::Point(5, 5),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ dispatcher->ProcessEvent(left_press_event);
+
+ // Events should target child.
+ std::unique_ptr<DispatchedEventDetails> details =
+ event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+
+ ASSERT_TRUE(details);
+ ASSERT_EQ(child.get(), details->window);
+ EXPECT_TRUE(details->IsClientArea());
+ EXPECT_TRUE(IsMouseButtonDown());
+
+ // The mouse down state should update while capture is set.
+ const ui::PointerEvent right_press_event(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(5, 5), gfx::Point(5, 5),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON,
+ ui::EF_RIGHT_MOUSE_BUTTON));
+ dispatcher->ProcessEvent(right_press_event);
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_TRUE(IsMouseButtonDown());
+
+ // One button released should not clear mouse down
+ const ui::PointerEvent left_release_event(ui::MouseEvent(
+ ui::ET_MOUSE_RELEASED, gfx::Point(5, 5), gfx::Point(5, 5),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON,
+ ui::EF_LEFT_MOUSE_BUTTON));
+ dispatcher->ProcessEvent(left_release_event);
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_TRUE(IsMouseButtonDown());
+
+ // Touch Event while mouse is down should not affect state.
+ const ui::PointerEvent touch_event(ui::TouchEvent(
+ ui::ET_TOUCH_PRESSED, gfx::Point(15, 15), 2, base::TimeTicks()));
+ dispatcher->ProcessEvent(touch_event);
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_TRUE(IsMouseButtonDown());
+
+ // Move event should not affect down
+ const ui::PointerEvent move_event(
+ ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(15, 5), gfx::Point(15, 5),
+ base::TimeTicks(), ui::EF_RIGHT_MOUSE_BUTTON,
+ ui::EF_RIGHT_MOUSE_BUTTON));
+ dispatcher->ProcessEvent(move_event);
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_TRUE(IsMouseButtonDown());
+
+ // All mouse buttons up should clear mouse down.
+ const ui::PointerEvent right_release_event(
+ ui::MouseEvent(ui::ET_MOUSE_RELEASED, gfx::Point(5, 5),
+ gfx::Point(5, 5), base::TimeTicks(),
+ ui::EF_RIGHT_MOUSE_BUTTON, ui::EF_RIGHT_MOUSE_BUTTON));
+ dispatcher->ProcessEvent(right_release_event);
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_FALSE(IsMouseButtonDown());
+ }
+
+ {
+ // Releasing capture and sending the same event will go to the root.
+ dispatcher->SetCaptureWindow(nullptr, kClientAreaId);
+ const ui::PointerEvent press_event(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(5, 5), gfx::Point(5, 5),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ dispatcher->ProcessEvent(press_event);
+
+ // Events should target the root.
+ std::unique_ptr<DispatchedEventDetails> details =
+ event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+
+ ASSERT_TRUE(details);
+ ASSERT_EQ(root, details->window);
+ }
+}
+
+// This test verifies that explicit capture overrides and resets implicit
+// capture.
+TEST_F(EventDispatcherTest, ExplicitCaptureOverridesImplicitCapture) {
+ ServerWindow* root = root_window();
+ std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3));
+
+ root->SetBounds(gfx::Rect(0, 0, 100, 100));
+ child->SetBounds(gfx::Rect(10, 10, 20, 20));
+
+ TestEventDispatcherDelegate* event_dispatcher_delegate =
+ test_event_dispatcher_delegate();
+ EventDispatcher* dispatcher = event_dispatcher();
+
+ // Run some implicit capture tests.
+ MouseEventTest tests[] = {
+ // Send a mouse down event over child with a left mouse button
+ {ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(20, 25),
+ gfx::Point(20, 25), base::TimeTicks(),
+ ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON),
+ child.get(), gfx::Point(20, 25), gfx::Point(10, 15)},
+ // Capture should be activated. Let's send a mouse move outside the bounds
+ // of the child and press the right mouse button too.
+ {ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(50, 50),
+ gfx::Point(50, 50), base::TimeTicks(),
+ ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON, 0),
+ child.get(), gfx::Point(50, 50), gfx::Point(40, 40)},
+ // Release the left mouse button and verify that the mouse up event goes
+ // to the child.
+ {ui::MouseEvent(ui::ET_MOUSE_RELEASED, gfx::Point(50, 50),
+ gfx::Point(50, 50), base::TimeTicks(),
+ ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON,
+ ui::EF_RIGHT_MOUSE_BUTTON),
+ child.get(), gfx::Point(50, 50), gfx::Point(40, 40)},
+ // A mouse move at (50, 50) should still go to the child.
+ {ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(50, 50),
+ gfx::Point(50, 50), base::TimeTicks(),
+ ui::EF_LEFT_MOUSE_BUTTON, 0),
+ child.get(), gfx::Point(50, 50), gfx::Point(40, 40)},
+
+ };
+ RunMouseEventTests(dispatcher, event_dispatcher_delegate, tests,
+ arraysize(tests));
+
+ // Add a second pointer target to the child.
+ {
+ const ui::PointerEvent touch_event(ui::TouchEvent(
+ ui::ET_TOUCH_PRESSED, gfx::Point(12, 13), 1, base::TimeTicks()));
+ dispatcher->ProcessEvent(touch_event);
+ }
+
+ std::unique_ptr<DispatchedEventDetails> details =
+ event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_FALSE(event_dispatcher_delegate->has_queued_events());
+ EXPECT_EQ(child.get(), details->window);
+
+ // Verify that no window has explicit capture and hence we did indeed do
+ // implicit capture.
+ ASSERT_EQ(nullptr, dispatcher->capture_window());
+
+ // Give the root window explicit capture and verify input events over the
+ // child go to the root instead.
+ dispatcher->SetCaptureWindow(root, kNonclientAreaId);
+
+ // The implicit target should receive a cancel event for each pointer target.
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ ASSERT_TRUE(details);
+ EXPECT_TRUE(event_dispatcher_delegate->has_queued_events());
+ EXPECT_EQ(child.get(), details->window);
+ EXPECT_EQ(ui::ET_POINTER_CANCELLED, details->event->type());
+
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ ASSERT_TRUE(details);
+ EXPECT_FALSE(event_dispatcher_delegate->has_queued_events());
+ EXPECT_EQ(child.get(), details->window);
+ EXPECT_EQ(ui::ET_POINTER_EXITED, details->event->type());
+
+ const ui::PointerEvent press_event(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(15, 15), gfx::Point(15, 15),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ dispatcher->ProcessEvent(press_event);
+
+ // Events should target the root.
+ details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ ASSERT_TRUE(details);
+ ASSERT_EQ(root, details->window);
+ ASSERT_TRUE(details->IsNonclientArea());
+}
+
+// Tests that setting capture does delete active pointer targets for the capture
+// window.
+TEST_F(EventDispatcherTest, CaptureUpdatesActivePointerTargets) {
+ ServerWindow* root = root_window();
+ root->SetBounds(gfx::Rect(0, 0, 100, 100));
+
+ EventDispatcher* dispatcher = event_dispatcher();
+ {
+ const ui::PointerEvent press_event(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(5, 5), gfx::Point(5, 5),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ dispatcher->ProcessEvent(press_event);
+
+ std::unique_ptr<DispatchedEventDetails> details =
+ test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
+ ASSERT_TRUE(details);
+ ASSERT_EQ(root, details->window);
+ }
+ {
+ const ui::PointerEvent touch_event(ui::TouchEvent(
+ ui::ET_TOUCH_PRESSED, gfx::Point(12, 13), 1, base::TimeTicks()));
+ dispatcher->ProcessEvent(touch_event);
+ }
+
+ ASSERT_TRUE(AreAnyPointersDown());
+ ASSERT_TRUE(IsWindowPointerTarget(root));
+ EXPECT_EQ(2, NumberPointerTargetsForWindow(root));
+
+ // Setting the capture should clear the implicit pointers for the specified
+ // window.
+ dispatcher->SetCaptureWindow(root, kNonclientAreaId);
+ EXPECT_FALSE(AreAnyPointersDown());
+ EXPECT_FALSE(IsWindowPointerTarget(root));
+}
+
+// Tests that when explicit capture is changed, that the previous window with
+// capture is no longer being observed.
+TEST_F(EventDispatcherTest, UpdatingCaptureStopsObservingPreviousCapture) {
+ std::unique_ptr<ServerWindow> child1 = CreateChildWindow(WindowId(1, 3));
+ std::unique_ptr<ServerWindow> child2 = CreateChildWindow(WindowId(1, 4));
+
+ root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ child1->SetBounds(gfx::Rect(10, 10, 20, 20));
+ child2->SetBounds(gfx::Rect(50, 51, 11, 12));
+
+ EventDispatcher* dispatcher = event_dispatcher();
+ ASSERT_FALSE(AreAnyPointersDown());
+ ASSERT_FALSE(IsWindowPointerTarget(child1.get()));
+ ASSERT_FALSE(IsWindowPointerTarget(child2.get()));
+ dispatcher->SetCaptureWindow(child1.get(), kClientAreaId);
+ dispatcher->SetCaptureWindow(child2.get(), kClientAreaId);
+ EXPECT_EQ(child1.get(),
+ test_event_dispatcher_delegate()->lost_capture_window());
+
+ // If observing does not stop during the capture update this crashes.
+ child1->AddObserver(dispatcher);
+}
+
+// Tests that destroying a window with explicit capture clears the capture
+// state.
+TEST_F(EventDispatcherTest, DestroyingCaptureWindowRemovesExplicitCapture) {
+ std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3));
+ child->SetBounds(gfx::Rect(10, 10, 20, 20));
+
+ EventDispatcher* dispatcher = event_dispatcher();
+ dispatcher->SetCaptureWindow(child.get(), kClientAreaId);
+ EXPECT_EQ(child.get(), dispatcher->capture_window());
+
+ ServerWindow* lost_capture_window = child.get();
+ child.reset();
+ EXPECT_EQ(nullptr, dispatcher->capture_window());
+ EXPECT_EQ(lost_capture_window,
+ test_event_dispatcher_delegate()->lost_capture_window());
+}
+
+// Tests that when |client_id| is set for a window performing capture, that this
+// preference is used regardless of whether an event targets the client region.
+TEST_F(EventDispatcherTest, CaptureInNonClientAreaOverridesActualPoint) {
+ ServerWindow* root = root_window();
+ root->SetBounds(gfx::Rect(0, 0, 100, 100));
+
+ root->SetClientArea(gfx::Insets(5, 5, 5, 5), std::vector<gfx::Rect>());
+ EventDispatcher* dispatcher = event_dispatcher();
+ dispatcher->SetCaptureWindow(root, kNonclientAreaId);
+
+ TestEventDispatcherDelegate* event_dispatcher_delegate =
+ test_event_dispatcher_delegate();
+ // Press in the client area, it should be marked as non client.
+ const ui::PointerEvent press_event(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(6, 6), gfx::Point(6, 6),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ event_dispatcher()->ProcessEvent(press_event);
+
+ // Events should target child and be in the client area.
+ std::unique_ptr<DispatchedEventDetails> details =
+ event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
+ EXPECT_FALSE(event_dispatcher_delegate->has_queued_events());
+ ASSERT_EQ(root, details->window);
+ EXPECT_TRUE(details->IsNonclientArea());
+}
+
+TEST_F(EventDispatcherTest, ProcessPointerEvents) {
+ std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3));
+
+ root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ child->SetBounds(gfx::Rect(10, 10, 20, 20));
+
+ {
+ const ui::PointerEvent pointer_event(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(20, 25), gfx::Point(20, 25),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ event_dispatcher()->ProcessEvent(pointer_event);
+
+ std::unique_ptr<DispatchedEventDetails> details =
+ test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
+ ASSERT_TRUE(details);
+ ASSERT_EQ(child.get(), details->window);
+
+ ASSERT_TRUE(details->event);
+ ASSERT_TRUE(details->event->IsPointerEvent());
+
+ ui::PointerEvent* dispatched_event = details->event->AsPointerEvent();
+ EXPECT_EQ(gfx::Point(20, 25), dispatched_event->root_location());
+ EXPECT_EQ(gfx::Point(10, 15), dispatched_event->location());
+ }
+
+ {
+ const int touch_id = 3;
+ const ui::PointerEvent pointer_event(
+ ui::TouchEvent(ui::ET_TOUCH_RELEASED, gfx::Point(25, 20), touch_id,
+ base::TimeTicks()));
+ event_dispatcher()->ProcessEvent(pointer_event);
+
+ std::unique_ptr<DispatchedEventDetails> details =
+ test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
+ ASSERT_TRUE(details);
+ ASSERT_EQ(child.get(), details->window);
+
+ ASSERT_TRUE(details->event);
+ ASSERT_TRUE(details->event->IsPointerEvent());
+
+ ui::PointerEvent* dispatched_event = details->event->AsPointerEvent();
+ EXPECT_EQ(gfx::Point(25, 20), dispatched_event->root_location());
+ EXPECT_EQ(gfx::Point(15, 10), dispatched_event->location());
+ EXPECT_EQ(touch_id, dispatched_event->pointer_id());
+ }
+}
+
+TEST_F(EventDispatcherTest, ResetClearsPointerDown) {
+ std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3));
+
+ root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ child->SetBounds(gfx::Rect(10, 10, 20, 20));
+
+ // Send event that is over child.
+ const ui::PointerEvent ui_event(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(20, 25), gfx::Point(20, 25),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ event_dispatcher()->ProcessEvent(ui_event);
+
+ std::unique_ptr<DispatchedEventDetails> details =
+ test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
+ ASSERT_TRUE(details);
+ ASSERT_EQ(child.get(), details->window);
+
+ EXPECT_TRUE(AreAnyPointersDown());
+
+ event_dispatcher()->Reset();
+ EXPECT_FALSE(test_event_dispatcher_delegate()->has_queued_events());
+ EXPECT_FALSE(AreAnyPointersDown());
+}
+
+TEST_F(EventDispatcherTest, ResetClearsCapture) {
+ ServerWindow* root = root_window();
+ root->SetBounds(gfx::Rect(0, 0, 100, 100));
+
+ root->SetClientArea(gfx::Insets(5, 5, 5, 5), std::vector<gfx::Rect>());
+ EventDispatcher* dispatcher = event_dispatcher();
+ dispatcher->SetCaptureWindow(root, kNonclientAreaId);
+
+ event_dispatcher()->Reset();
+ EXPECT_FALSE(test_event_dispatcher_delegate()->has_queued_events());
+ EXPECT_EQ(nullptr, event_dispatcher()->capture_window());
+}
+
+// Tests that events on a modal parent target the modal child.
+TEST_F(EventDispatcherTest, ModalWindowEventOnModalParent) {
+ std::unique_ptr<ServerWindow> w1 = CreateChildWindow(WindowId(1, 3));
+ std::unique_ptr<ServerWindow> w2 = CreateChildWindow(WindowId(1, 5));
+
+ root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ w1->SetBounds(gfx::Rect(10, 10, 30, 30));
+ w2->SetBounds(gfx::Rect(50, 10, 10, 10));
+
+ w1->AddTransientWindow(w2.get());
+ w2->SetModal();
+
+ // Send event that is over |w1|.
+ const ui::PointerEvent mouse_pressed(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(15, 15), gfx::Point(15, 15),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ event_dispatcher()->ProcessEvent(mouse_pressed);
+
+ std::unique_ptr<DispatchedEventDetails> details =
+ test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
+ ASSERT_TRUE(details);
+ EXPECT_EQ(w2.get(), details->window);
+ EXPECT_TRUE(details->IsNonclientArea());
+
+ ASSERT_TRUE(details->event);
+ ASSERT_TRUE(details->event->IsPointerEvent());
+
+ ui::PointerEvent* dispatched_event = details->event->AsPointerEvent();
+ EXPECT_EQ(gfx::Point(15, 15), dispatched_event->root_location());
+ EXPECT_EQ(gfx::Point(-35, 5), dispatched_event->location());
+}
+
+// Tests that events on a modal child target the modal child itself.
+TEST_F(EventDispatcherTest, ModalWindowEventOnModalChild) {
+ std::unique_ptr<ServerWindow> w1 = CreateChildWindow(WindowId(1, 3));
+ std::unique_ptr<ServerWindow> w2 = CreateChildWindow(WindowId(1, 5));
+
+ root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ w1->SetBounds(gfx::Rect(10, 10, 30, 30));
+ w2->SetBounds(gfx::Rect(50, 10, 10, 10));
+
+ w1->AddTransientWindow(w2.get());
+ w2->SetModal();
+
+ // Send event that is over |w2|.
+ const ui::PointerEvent mouse_pressed(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(55, 15), gfx::Point(55, 15),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ event_dispatcher()->ProcessEvent(mouse_pressed);
+
+ std::unique_ptr<DispatchedEventDetails> details =
+ test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
+ ASSERT_TRUE(details);
+ EXPECT_EQ(w2.get(), details->window);
+ EXPECT_TRUE(details->IsClientArea());
+
+ ASSERT_TRUE(details->event);
+ ASSERT_TRUE(details->event->IsPointerEvent());
+
+ ui::PointerEvent* dispatched_event = details->event->AsPointerEvent();
+ EXPECT_EQ(gfx::Point(55, 15), dispatched_event->root_location());
+ EXPECT_EQ(gfx::Point(5, 5), dispatched_event->location());
+}
+
+// Tests that events on an unrelated window are not affected by the modal
+// window.
+TEST_F(EventDispatcherTest, ModalWindowEventOnUnrelatedWindow) {
+ std::unique_ptr<ServerWindow> w1 = CreateChildWindow(WindowId(1, 3));
+ std::unique_ptr<ServerWindow> w2 = CreateChildWindow(WindowId(1, 5));
+ std::unique_ptr<ServerWindow> w3 = CreateChildWindow(WindowId(1, 6));
+
+ root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ w1->SetBounds(gfx::Rect(10, 10, 30, 30));
+ w2->SetBounds(gfx::Rect(50, 10, 10, 10));
+ w3->SetBounds(gfx::Rect(70, 10, 10, 10));
+
+ w1->AddTransientWindow(w2.get());
+ w2->SetModal();
+
+ // Send event that is over |w3|.
+ const ui::PointerEvent mouse_pressed(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(75, 15), gfx::Point(75, 15),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ event_dispatcher()->ProcessEvent(mouse_pressed);
+
+ std::unique_ptr<DispatchedEventDetails> details =
+ test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
+ ASSERT_TRUE(details);
+ EXPECT_EQ(w3.get(), details->window);
+ EXPECT_TRUE(details->IsClientArea());
+
+ ASSERT_TRUE(details->event);
+ ASSERT_TRUE(details->event->IsPointerEvent());
+
+ ui::PointerEvent* dispatched_event = details->event->AsPointerEvent();
+ EXPECT_EQ(gfx::Point(75, 15), dispatched_event->root_location());
+ EXPECT_EQ(gfx::Point(5, 5), dispatched_event->location());
+}
+
+// Tests that events events on a descendant of a modal parent target the modal
+// child.
+TEST_F(EventDispatcherTest, ModalWindowEventOnDescendantOfModalParent) {
+ std::unique_ptr<ServerWindow> w1 = CreateChildWindow(WindowId(1, 3));
+ std::unique_ptr<ServerWindow> w11 =
+ CreateChildWindowWithParent(WindowId(1, 4), w1.get());
+ std::unique_ptr<ServerWindow> w2 = CreateChildWindow(WindowId(1, 5));
+
+ root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ w1->SetBounds(gfx::Rect(10, 10, 30, 30));
+ w11->SetBounds(gfx::Rect(10, 10, 10, 10));
+ w2->SetBounds(gfx::Rect(50, 10, 10, 10));
+
+ w1->AddTransientWindow(w2.get());
+ w2->SetModal();
+
+ // Send event that is over |w11|.
+ const ui::PointerEvent mouse_pressed(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(25, 25), gfx::Point(25, 25),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ event_dispatcher()->ProcessEvent(mouse_pressed);
+
+ std::unique_ptr<DispatchedEventDetails> details =
+ test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
+ ASSERT_TRUE(details);
+ EXPECT_EQ(w2.get(), details->window);
+ EXPECT_TRUE(details->IsNonclientArea());
+
+ ASSERT_TRUE(details->event);
+ ASSERT_TRUE(details->event->IsPointerEvent());
+
+ ui::PointerEvent* dispatched_event = details->event->AsPointerEvent();
+ EXPECT_EQ(gfx::Point(25, 25), dispatched_event->root_location());
+ EXPECT_EQ(gfx::Point(-25, 15), dispatched_event->location());
+}
+
+// Tests that events on a system modal window target the modal window itself.
+TEST_F(EventDispatcherTest, ModalWindowEventOnSystemModal) {
+ std::unique_ptr<ServerWindow> w1 = CreateChildWindow(WindowId(1, 3));
+
+ root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ w1->SetBounds(gfx::Rect(10, 10, 30, 30));
+ w1->SetModal();
+
+ // Send event that is over |w1|.
+ const ui::PointerEvent mouse_pressed(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(15, 15), gfx::Point(15, 15),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ event_dispatcher()->ProcessEvent(mouse_pressed);
+
+ std::unique_ptr<DispatchedEventDetails> details =
+ test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
+ ASSERT_TRUE(details);
+ EXPECT_EQ(w1.get(), details->window);
+ EXPECT_TRUE(details->IsClientArea());
+
+ ASSERT_TRUE(details->event);
+ ASSERT_TRUE(details->event->IsPointerEvent());
+
+ ui::PointerEvent* dispatched_event = details->event->AsPointerEvent();
+ EXPECT_EQ(gfx::Point(15, 15), dispatched_event->root_location());
+ EXPECT_EQ(gfx::Point(5, 5), dispatched_event->location());
+}
+
+// Tests that events outside of system modal window target the modal window.
+TEST_F(EventDispatcherTest, ModalWindowEventOutsideSystemModal) {
+ std::unique_ptr<ServerWindow> w1 = CreateChildWindow(WindowId(1, 3));
+
+ root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ w1->SetBounds(gfx::Rect(10, 10, 30, 30));
+ w1->SetModal();
+ event_dispatcher()->AddSystemModalWindow(w1.get());
+
+ // Send event that is over |w1|.
+ const ui::PointerEvent mouse_pressed(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(45, 15), gfx::Point(45, 15),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ event_dispatcher()->ProcessEvent(mouse_pressed);
+
+ std::unique_ptr<DispatchedEventDetails> details =
+ test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
+ ASSERT_TRUE(details);
+ EXPECT_EQ(w1.get(), details->window);
+ EXPECT_TRUE(details->IsNonclientArea());
+
+ ASSERT_TRUE(details->event);
+ ASSERT_TRUE(details->event->IsPointerEvent());
+
+ ui::PointerEvent* dispatched_event = details->event->AsPointerEvent();
+ EXPECT_EQ(gfx::Point(45, 15), dispatched_event->root_location());
+ EXPECT_EQ(gfx::Point(35, 5), dispatched_event->location());
+}
+
+// Tests that setting capture to a descendant of a modal parent fails.
+TEST_F(EventDispatcherTest, ModalWindowSetCaptureDescendantOfModalParent) {
+ std::unique_ptr<ServerWindow> w1 = CreateChildWindow(WindowId(1, 3));
+ std::unique_ptr<ServerWindow> w11 =
+ CreateChildWindowWithParent(WindowId(1, 4), w1.get());
+ std::unique_ptr<ServerWindow> w2 = CreateChildWindow(WindowId(1, 5));
+
+ root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ w1->SetBounds(gfx::Rect(10, 10, 30, 30));
+ w11->SetBounds(gfx::Rect(10, 10, 10, 10));
+ w2->SetBounds(gfx::Rect(50, 10, 10, 10));
+
+ w1->AddTransientWindow(w2.get());
+ w2->SetModal();
+
+ EXPECT_FALSE(event_dispatcher()->SetCaptureWindow(w11.get(), kClientAreaId));
+ EXPECT_EQ(nullptr, event_dispatcher()->capture_window());
+}
+
+// Tests that setting capture to a window unrelated to a modal parent works.
+TEST_F(EventDispatcherTest, ModalWindowSetCaptureUnrelatedWindow) {
+ std::unique_ptr<ServerWindow> w1 = CreateChildWindow(WindowId(1, 3));
+ std::unique_ptr<ServerWindow> w2 = CreateChildWindow(WindowId(1, 4));
+ std::unique_ptr<ServerWindow> w3 = CreateChildWindow(WindowId(1, 5));
+
+ root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ w1->SetBounds(gfx::Rect(10, 10, 30, 30));
+ w2->SetBounds(gfx::Rect(50, 10, 10, 10));
+ w3->SetBounds(gfx::Rect(70, 10, 10, 10));
+
+ w1->AddTransientWindow(w2.get());
+ w2->SetModal();
+
+ EXPECT_TRUE(event_dispatcher()->SetCaptureWindow(w3.get(), kClientAreaId));
+ EXPECT_EQ(w3.get(), event_dispatcher()->capture_window());
+}
+
+// Tests that setting capture fails when there is a system modal window.
+TEST_F(EventDispatcherTest, ModalWindowSystemSetCapture) {
+ std::unique_ptr<ServerWindow> w1 = CreateChildWindow(WindowId(1, 3));
+ std::unique_ptr<ServerWindow> w2 = CreateChildWindow(WindowId(1, 4));
+
+ root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ w1->SetBounds(gfx::Rect(10, 10, 30, 30));
+ w2->SetBounds(gfx::Rect(50, 10, 10, 10));
+
+ event_dispatcher()->AddSystemModalWindow(w2.get());
+
+ EXPECT_FALSE(event_dispatcher()->SetCaptureWindow(w1.get(), kClientAreaId));
+ EXPECT_EQ(nullptr, event_dispatcher()->capture_window());
+}
+
+// Tests having multiple system modal windows.
+TEST_F(EventDispatcherTest, ModalWindowMultipleSystemModals) {
+ std::unique_ptr<ServerWindow> w1 = CreateChildWindow(WindowId(1, 3));
+ std::unique_ptr<ServerWindow> w2 = CreateChildWindow(WindowId(1, 4));
+ std::unique_ptr<ServerWindow> w3 = CreateChildWindow(WindowId(1, 5));
+
+ w2->SetVisible(false);
+
+ // In the beginning, there should be no active system modal window.
+ EXPECT_EQ(nullptr, GetActiveSystemModalWindow());
+
+ // Add a visible system modal window. It should become the active one.
+ event_dispatcher()->AddSystemModalWindow(w1.get());
+ EXPECT_EQ(w1.get(), GetActiveSystemModalWindow());
+
+ // Add an invisible system modal window. It should not change the active one.
+ event_dispatcher()->AddSystemModalWindow(w2.get());
+ EXPECT_EQ(w1.get(), GetActiveSystemModalWindow());
+
+ // Add another visible system modal window. It should become the active one.
+ event_dispatcher()->AddSystemModalWindow(w3.get());
+ EXPECT_EQ(w3.get(), GetActiveSystemModalWindow());
+
+ // Make an existing system modal window visible. It should become the active
+ // one.
+ w2->SetVisible(true);
+ EXPECT_EQ(w2.get(), GetActiveSystemModalWindow());
+
+ // Remove the active system modal window. Next one should become active.
+ w2.reset();
+ EXPECT_EQ(w3.get(), GetActiveSystemModalWindow());
+
+ // Remove an inactive system modal window. It should not change the active
+ // one.
+ w1.reset();
+ EXPECT_EQ(w3.get(), GetActiveSystemModalWindow());
+
+ // Remove the last remaining system modal window. There should be no active
+ // one anymore.
+ w3.reset();
+ EXPECT_EQ(nullptr, GetActiveSystemModalWindow());
+}
+
+TEST_F(EventDispatcherTest, CaptureNotResetOnParentChange) {
+ std::unique_ptr<ServerWindow> w1 = CreateChildWindow(WindowId(1, 3));
+ DisableHitTest(w1.get());
+ std::unique_ptr<ServerWindow> w11 =
+ CreateChildWindowWithParent(WindowId(1, 4), w1.get());
+ std::unique_ptr<ServerWindow> w2 = CreateChildWindow(WindowId(1, 5));
+ DisableHitTest(w2.get());
+
+ root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ w1->SetBounds(gfx::Rect(0, 0, 100, 100));
+ w11->SetBounds(gfx::Rect(10, 10, 10, 10));
+ w2->SetBounds(gfx::Rect(0, 0, 100, 100));
+
+ // Send event that is over |w11|.
+ const ui::PointerEvent mouse_pressed(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(15, 15), gfx::Point(15, 15),
+ base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ event_dispatcher()->ProcessEvent(mouse_pressed);
+ event_dispatcher()->SetCaptureWindow(w11.get(), kClientAreaId);
+
+ std::unique_ptr<DispatchedEventDetails> details =
+ test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
+ ASSERT_TRUE(details);
+ EXPECT_EQ(w11.get(), details->window);
+ EXPECT_TRUE(details->IsClientArea());
+
+ // Move |w11| to |w2| and verify the mouse is still down, and |w11| has
+ // capture.
+ w2->Add(w11.get());
+ EXPECT_TRUE(IsMouseButtonDown());
+ EXPECT_EQ(w11.get(),
+ EventDispatcherTestApi(event_dispatcher()).capture_window());
+}
+
+TEST_F(EventDispatcherTest, ChangeCaptureFromClientToNonclient) {
+ std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3));
+ event_dispatcher()->SetCaptureWindow(child.get(), kNonclientAreaId);
+ EXPECT_EQ(kNonclientAreaId,
+ event_dispatcher()->capture_window_client_id());
+ EXPECT_EQ(nullptr, test_event_dispatcher_delegate()->lost_capture_window());
+ event_dispatcher()->SetCaptureWindow(child.get(), kClientAreaId);
+ // Changing capture from client to non-client should notify the delegate.
+ // The delegate can decide if it really wants to forward the event or not.
+ EXPECT_EQ(child.get(),
+ test_event_dispatcher_delegate()->lost_capture_window());
+ EXPECT_EQ(child.get(), event_dispatcher()->capture_window());
+ EXPECT_EQ(kClientAreaId, event_dispatcher()->capture_window_client_id());
+}
+
+} // namespace test
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/event_matcher.cc b/chromium/components/mus/ws/event_matcher.cc
new file mode 100644
index 00000000000..da45554d2ea
--- /dev/null
+++ b/chromium/components/mus/ws/event_matcher.cc
@@ -0,0 +1,113 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/event_matcher.h"
+
+namespace mus {
+namespace ws {
+
+EventMatcher::EventMatcher(const mojom::EventMatcher& matcher)
+ : fields_to_match_(NONE),
+ event_type_(ui::ET_UNKNOWN),
+ event_flags_(ui::EF_NONE),
+ ignore_event_flags_(ui::EF_NONE),
+ keyboard_code_(ui::VKEY_UNKNOWN),
+ pointer_type_(ui::EventPointerType::POINTER_TYPE_UNKNOWN) {
+ if (matcher.type_matcher) {
+ fields_to_match_ |= TYPE;
+ switch (matcher.type_matcher->type) {
+ case ui::mojom::EventType::POINTER_DOWN:
+ event_type_ = ui::ET_POINTER_DOWN;
+ break;
+ case ui::mojom::EventType::POINTER_MOVE:
+ event_type_ = ui::ET_POINTER_MOVED;
+ break;
+ case ui::mojom::EventType::MOUSE_EXIT:
+ event_type_ = ui::ET_POINTER_EXITED;
+ break;
+ case ui::mojom::EventType::POINTER_UP:
+ event_type_ = ui::ET_POINTER_UP;
+ break;
+ case ui::mojom::EventType::POINTER_CANCEL:
+ event_type_ = ui::ET_POINTER_CANCELLED;
+ break;
+ case ui::mojom::EventType::KEY_PRESSED:
+ event_type_ = ui::ET_KEY_PRESSED;
+ break;
+ case ui::mojom::EventType::KEY_RELEASED:
+ event_type_ = ui::ET_KEY_RELEASED;
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+ if (matcher.flags_matcher) {
+ fields_to_match_ |= FLAGS;
+ event_flags_ = matcher.flags_matcher->flags;
+ if (matcher.ignore_flags_matcher)
+ ignore_event_flags_ = matcher.ignore_flags_matcher->flags;
+ }
+ if (matcher.key_matcher) {
+ fields_to_match_ |= KEYBOARD_CODE;
+ keyboard_code_ = static_cast<uint16_t>(matcher.key_matcher->keyboard_code);
+ }
+ if (matcher.pointer_kind_matcher) {
+ fields_to_match_ |= POINTER_KIND;
+ switch (matcher.pointer_kind_matcher->pointer_kind) {
+ case ui::mojom::PointerKind::MOUSE:
+ pointer_type_ = ui::EventPointerType::POINTER_TYPE_MOUSE;
+ break;
+ case ui::mojom::PointerKind::TOUCH:
+ pointer_type_ = ui::EventPointerType::POINTER_TYPE_TOUCH;
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+ if (matcher.pointer_location_matcher) {
+ fields_to_match_ |= POINTER_LOCATION;
+ pointer_region_ = matcher.pointer_location_matcher->region;
+ }
+}
+
+EventMatcher::~EventMatcher() {}
+
+bool EventMatcher::MatchesEvent(const ui::Event& event) const {
+ if ((fields_to_match_ & TYPE) && event.type() != event_type_)
+ return false;
+ int flags = event.flags() & ~ignore_event_flags_;
+ if ((fields_to_match_ & FLAGS) && flags != event_flags_)
+ return false;
+ if (fields_to_match_ & KEYBOARD_CODE) {
+ if (!event.IsKeyEvent())
+ return false;
+ if (keyboard_code_ != event.AsKeyEvent()->GetConflatedWindowsKeyCode())
+ return false;
+ }
+ if (fields_to_match_ & POINTER_KIND) {
+ if (!event.IsPointerEvent() ||
+ pointer_type_ != event.AsPointerEvent()->pointer_details().pointer_type)
+ return false;
+ }
+ if (fields_to_match_ & POINTER_LOCATION) {
+ // TODO(sad): The tricky part here is to make sure the same coord-space is
+ // used for the location-region and the event-location.
+ NOTIMPLEMENTED();
+ return false;
+ }
+ return true;
+}
+
+bool EventMatcher::Equals(const EventMatcher& other) const {
+ return fields_to_match_ == other.fields_to_match_ &&
+ event_type_ == other.event_type_ &&
+ event_flags_ == other.event_flags_ &&
+ ignore_event_flags_ == other.ignore_event_flags_ &&
+ keyboard_code_ == other.keyboard_code_ &&
+ pointer_type_ == other.pointer_type_ &&
+ pointer_region_ == other.pointer_region_;
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/event_matcher.h b/chromium/components/mus/ws/event_matcher.h
new file mode 100644
index 00000000000..ec9231b57a4
--- /dev/null
+++ b/chromium/components/mus/ws/event_matcher.h
@@ -0,0 +1,56 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_EVENT_MATCHER_H_
+#define COMPONENTS_MUS_WS_EVENT_MATCHER_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "components/mus/public/interfaces/event_matcher.mojom.h"
+#include "ui/events/event.h"
+#include "ui/events/mojo/event_constants.mojom.h"
+#include "ui/events/mojo/keyboard_codes.mojom.h"
+#include "ui/gfx/geometry/rect_f.h"
+
+namespace mus {
+namespace ws {
+
+// Wraps a mojom::EventMatcher and allows events to be tested against it.
+class EventMatcher {
+ public:
+ explicit EventMatcher(const mojom::EventMatcher& matcher);
+ ~EventMatcher();
+
+ bool MatchesEvent(const ui::Event& event) const;
+
+ bool Equals(const EventMatcher& other) const;
+
+ private:
+ enum MatchFields {
+ NONE = 0,
+ TYPE = 1 << 0,
+ FLAGS = 1 << 1,
+ KEYBOARD_CODE = 1 << 2,
+ POINTER_KIND = 1 << 3,
+ POINTER_LOCATION = 1 << 4,
+ };
+
+ uint32_t fields_to_match_;
+ ui::EventType event_type_;
+ // Bitfields of kEventFlag* and kMouseEventFlag* values in
+ // input_event_constants.mojom.
+ int event_flags_;
+ int ignore_event_flags_;
+ uint16_t keyboard_code_;
+ ui::EventPointerType pointer_type_;
+ gfx::RectF pointer_region_;
+
+ DISALLOW_COPY_AND_ASSIGN(EventMatcher);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_EVENT_MATCHER_H_
diff --git a/chromium/components/mus/ws/event_matcher_unittest.cc b/chromium/components/mus/ws/event_matcher_unittest.cc
new file mode 100644
index 00000000000..04c011fb459
--- /dev/null
+++ b/chromium/components/mus/ws/event_matcher_unittest.cc
@@ -0,0 +1,71 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/event_matcher.h"
+
+#include "base/time/time.h"
+#include "components/mus/public/interfaces/event_matcher.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/event.h"
+#include "ui/events/mojo/event_constants.mojom.h"
+#include "ui/gfx/geometry/point.h"
+
+namespace mus {
+namespace ws {
+
+// NOTE: Most of the matching functionality is exercised by tests of Accelerator
+// handling in the EventDispatcher and WindowTree tests. These are just basic
+// sanity checks.
+
+using EventTesterTest = testing::Test;
+
+TEST_F(EventTesterTest, MatchesEventByType) {
+ mojom::EventMatcherPtr matcher = mojom::EventMatcher::New();
+ matcher->type_matcher = mojom::EventTypeMatcher::New();
+ matcher->type_matcher->type = ui::mojom::EventType::POINTER_DOWN;
+ EventMatcher pointer_down_matcher(*matcher);
+
+ ui::PointerEvent pointer_down(
+ ui::TouchEvent(ui::ET_TOUCH_PRESSED, gfx::Point(), 1, base::TimeTicks()));
+ EXPECT_TRUE(pointer_down_matcher.MatchesEvent(pointer_down));
+
+ ui::PointerEvent pointer_up(ui::TouchEvent(
+ ui::ET_TOUCH_RELEASED, gfx::Point(), 1, base::TimeTicks()));
+ EXPECT_FALSE(pointer_down_matcher.MatchesEvent(pointer_up));
+}
+
+TEST_F(EventTesterTest, MatchesEventByKeyCode) {
+ mojom::EventMatcherPtr matcher(mojom::EventMatcher::New());
+ matcher->type_matcher = mojom::EventTypeMatcher::New();
+ matcher->type_matcher->type = ui::mojom::EventType::KEY_PRESSED;
+ matcher->key_matcher = mojom::KeyEventMatcher::New();
+ matcher->key_matcher->keyboard_code = ui::mojom::KeyboardCode::Z;
+ EventMatcher z_matcher(*matcher);
+
+ ui::KeyEvent z_key(ui::ET_KEY_PRESSED, ui::VKEY_Z, ui::EF_NONE);
+ EXPECT_TRUE(z_matcher.MatchesEvent(z_key));
+
+ ui::KeyEvent y_key(ui::ET_KEY_PRESSED, ui::VKEY_Y, ui::EF_NONE);
+ EXPECT_FALSE(z_matcher.MatchesEvent(y_key));
+}
+
+TEST_F(EventTesterTest, MatchesEventByKeyFlags) {
+ mojom::EventMatcherPtr matcher(mojom::EventMatcher::New());
+ matcher->type_matcher = mojom::EventTypeMatcher::New();
+ matcher->type_matcher->type = ui::mojom::EventType::KEY_PRESSED;
+ matcher->flags_matcher = mojom::EventFlagsMatcher::New();
+ matcher->flags_matcher->flags = ui::mojom::kEventFlagControlDown;
+ matcher->key_matcher = mojom::KeyEventMatcher::New();
+ matcher->key_matcher->keyboard_code = ui::mojom::KeyboardCode::N;
+ EventMatcher control_n_matcher(*matcher);
+
+ ui::KeyEvent control_n(ui::ET_KEY_PRESSED, ui::VKEY_N, ui::EF_CONTROL_DOWN);
+ EXPECT_TRUE(control_n_matcher.MatchesEvent(control_n));
+
+ ui::KeyEvent shift_n(ui::ET_KEY_PRESSED, ui::VKEY_N, ui::EF_SHIFT_DOWN);
+ EXPECT_FALSE(control_n_matcher.MatchesEvent(shift_n));
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/focus_controller.cc b/chromium/components/mus/ws/focus_controller.cc
new file mode 100644
index 00000000000..5728acbe154
--- /dev/null
+++ b/chromium/components/mus/ws/focus_controller.cc
@@ -0,0 +1,311 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/focus_controller.h"
+
+#include "base/macros.h"
+#include "components/mus/public/interfaces/window_manager.mojom.h"
+#include "components/mus/ws/focus_controller_delegate.h"
+#include "components/mus/ws/focus_controller_observer.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/server_window_drawn_tracker.h"
+
+namespace mus {
+namespace ws {
+
+namespace {
+
+ServerWindow* GetDeepestLastDescendant(ServerWindow* window) {
+ while (!window->children().empty())
+ window = window->children().back();
+ return window;
+}
+
+// This can be used to iterate over each node in a rooted tree for the purpose
+// of shifting focus/activation.
+class WindowTreeIterator {
+ public:
+ explicit WindowTreeIterator(ServerWindow* root) : root_(root) {}
+ ~WindowTreeIterator() {}
+
+ ServerWindow* GetNext(ServerWindow* window) const {
+ if (window == root_ || window == nullptr)
+ return GetDeepestLastDescendant(root_);
+
+ // Return the next sibling.
+ ServerWindow* parent = window->parent();
+ if (parent) {
+ const ServerWindow::Windows& siblings = parent->children();
+ ServerWindow::Windows::const_reverse_iterator iter =
+ std::find(siblings.rbegin(), siblings.rend(), window);
+ DCHECK(iter != siblings.rend());
+ ++iter;
+ if (iter != siblings.rend())
+ return GetDeepestLastDescendant(*iter);
+ }
+
+ // All children and siblings have been explored. Next is the parent.
+ return parent;
+ }
+
+ private:
+ ServerWindow* root_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowTreeIterator);
+};
+
+} // namespace
+
+FocusController::FocusController(FocusControllerDelegate* delegate,
+ ServerWindow* root)
+ : delegate_(delegate),
+ root_(root),
+ focused_window_(nullptr),
+ active_window_(nullptr),
+ activation_reason_(ActivationChangeReason::UNKNONW) {
+ DCHECK(delegate_);
+ DCHECK(root_);
+}
+
+FocusController::~FocusController() {
+}
+
+bool FocusController::SetFocusedWindow(ServerWindow* window) {
+ if (GetFocusedWindow() == window)
+ return true;
+
+ return SetFocusedWindowImpl(FocusControllerChangeSource::EXPLICIT, window);
+}
+
+ServerWindow* FocusController::GetFocusedWindow() {
+ return focused_window_;
+}
+
+void FocusController::ActivateNextWindow() {
+ WindowTreeIterator iter(root_);
+ ServerWindow* activate = active_window_;
+ while (true) {
+ activate = iter.GetNext(activate);
+ if (activation_reason_ == ActivationChangeReason::CYCLE) {
+ if (activate == active_window_) {
+ // We have cycled over all the activatable windows. Remove the oldest
+ // window that was cycled.
+ if (!cycle_windows_->windows().empty()) {
+ cycle_windows_->Remove(cycle_windows_->windows().front());
+ continue;
+ }
+ } else if (cycle_windows_->Contains(activate)) {
+ // We are cycling between activated windows, and this window has already
+ // been through the cycle. So skip over it.
+ continue;
+ }
+ }
+ if (activate == active_window_ || CanBeActivated(activate))
+ break;
+ }
+ SetActiveWindow(activate, ActivationChangeReason::CYCLE);
+
+ if (active_window_) {
+ // Do not shift focus if the focused window already lives in the active
+ // window.
+ if (focused_window_ && active_window_->Contains(focused_window_))
+ return;
+ // Focus the first focusable window in the tree.
+ WindowTreeIterator iter(active_window_);
+ ServerWindow* focus = nullptr;
+ do {
+ focus = iter.GetNext(focus);
+ } while (focus != active_window_ && !CanBeFocused(focus));
+ SetFocusedWindow(focus);
+ } else {
+ SetFocusedWindow(nullptr);
+ }
+}
+
+void FocusController::AddObserver(FocusControllerObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void FocusController::RemoveObserver(FocusControllerObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void FocusController::SetActiveWindow(ServerWindow* window,
+ ActivationChangeReason reason) {
+ DCHECK(!window || CanBeActivated(window));
+ if (active_window_ == window)
+ return;
+ if (reason != ActivationChangeReason::CYCLE) {
+ cycle_windows_.reset();
+ } else if (activation_reason_ != ActivationChangeReason::CYCLE) {
+ DCHECK(!cycle_windows_);
+ cycle_windows_.reset(new ServerWindowTracker());
+ if (active_window_)
+ cycle_windows_->Add(active_window_);
+ }
+
+ ServerWindow* old_active = active_window_;
+ active_window_ = window;
+ activation_reason_ = reason;
+ FOR_EACH_OBSERVER(FocusControllerObserver, observers_,
+ OnActivationChanged(old_active, active_window_));
+ if (active_window_ && activation_reason_ == ActivationChangeReason::CYCLE)
+ cycle_windows_->Add(active_window_);
+}
+
+bool FocusController::CanBeFocused(ServerWindow* window) const {
+ // All ancestors of |window| must be drawn, and be focusable.
+ for (ServerWindow* w = window; w; w = w->parent()) {
+ if (!w->IsDrawn())
+ return false;
+ if (!w->can_focus())
+ return false;
+ }
+
+ // |window| must be a descendent of an activatable window.
+ return GetActivatableAncestorOf(window) != nullptr;
+}
+
+bool FocusController::CanBeActivated(ServerWindow* window) const {
+ DCHECK(window);
+ // A detached window cannot be activated.
+ if (!root_->Contains(window))
+ return false;
+
+ // The parent window must be allowed to have active children.
+ if (!delegate_->CanHaveActiveChildren(window->parent()))
+ return false;
+
+ if (!window->can_focus())
+ return false;
+
+ // The window must be drawn, or if it's not drawn, it must be minimized.
+ if (!window->IsDrawn()) {
+ bool is_minimized = false;
+ const ServerWindow::Properties& props = window->properties();
+ if (props.count(mojom::WindowManager::kShowState_Property)) {
+ is_minimized =
+ props.find(mojom::WindowManager::kShowState_Property)->second[0] ==
+ static_cast<int>(mus::mojom::ShowState::MINIMIZED);
+ }
+ if (!is_minimized)
+ return false;
+ }
+
+ // TODO(sad): If there's a transient modal window, then this cannot be
+ // activated.
+ return true;
+}
+
+ServerWindow* FocusController::GetActivatableAncestorOf(
+ ServerWindow* window) const {
+ for (ServerWindow* w = window; w; w = w->parent()) {
+ if (CanBeActivated(w))
+ return w;
+ }
+ return nullptr;
+}
+
+bool FocusController::SetFocusedWindowImpl(
+ FocusControllerChangeSource change_source,
+ ServerWindow* window) {
+ if (window && !CanBeFocused(window))
+ return false;
+
+ ServerWindow* old_focused = GetFocusedWindow();
+
+ DCHECK(!window || window->IsDrawn());
+
+ // Activate the closest activatable ancestor window.
+ // TODO(sad): The window to activate doesn't necessarily have to be a direct
+ // ancestor (e.g. could be a transient parent).
+ SetActiveWindow(GetActivatableAncestorOf(window),
+ ActivationChangeReason::FOCUS);
+
+ FOR_EACH_OBSERVER(FocusControllerObserver, observers_,
+ OnFocusChanged(change_source, old_focused, window));
+
+ focused_window_ = window;
+ // We can currently use only a single ServerWindowDrawnTracker since focused
+ // window is expected to be a direct descendant of the active window.
+ if (focused_window_ && active_window_) {
+ DCHECK(active_window_->Contains(focused_window_));
+ }
+ ServerWindow* track_window = focused_window_;
+ if (!track_window)
+ track_window = active_window_;
+ if (track_window)
+ drawn_tracker_.reset(new ServerWindowDrawnTracker(track_window, this));
+ else
+ drawn_tracker_.reset();
+ return true;
+}
+
+void FocusController::OnDrawnStateWillChange(ServerWindow* ancestor,
+ ServerWindow* window,
+ bool is_drawn) {
+ DCHECK(!is_drawn);
+ DCHECK_NE(ancestor, window);
+ DCHECK(root_->Contains(window));
+ drawn_tracker_.reset();
+
+ // Find the window that triggered this state-change notification.
+ ServerWindow* trigger = window;
+ while (trigger->parent() != ancestor)
+ trigger = trigger->parent();
+ DCHECK(trigger);
+ auto will_be_hidden = [trigger](ServerWindow* w) {
+ return trigger->Contains(w);
+ };
+
+ // If |window| is |active_window_|, then activate the next activatable window
+ // that does not belong to the subtree which is getting hidden.
+ if (window == active_window_) {
+ WindowTreeIterator iter(root_);
+ ServerWindow* activate = active_window_;
+ do {
+ activate = iter.GetNext(activate);
+ } while (activate != active_window_ &&
+ (will_be_hidden(activate) || !CanBeActivated(activate)));
+ if (activate == window)
+ activate = nullptr;
+ SetActiveWindow(activate, ActivationChangeReason::DRAWN_STATE_CHANGED);
+
+ // Now make sure focus is in the active window.
+ ServerWindow* focus = nullptr;
+ if (active_window_) {
+ WindowTreeIterator iter(active_window_);
+ focus = nullptr;
+ do {
+ focus = iter.GetNext(focus);
+ } while (focus != active_window_ &&
+ (will_be_hidden(focus) || !CanBeFocused(focus)));
+ DCHECK(focus && CanBeFocused(focus));
+ }
+ SetFocusedWindowImpl(FocusControllerChangeSource::DRAWN_STATE_CHANGED,
+ focus);
+ return;
+ }
+
+ // Move focus to the next focusable window in |active_window_|.
+ DCHECK_EQ(focused_window_, window);
+ WindowTreeIterator iter(active_window_);
+ ServerWindow* focus = focused_window_;
+ do {
+ focus = iter.GetNext(focus);
+ } while (focus != focused_window_ &&
+ (will_be_hidden(focus) || !CanBeFocused(focus)));
+ if (focus == window)
+ focus = nullptr;
+ SetFocusedWindowImpl(FocusControllerChangeSource::DRAWN_STATE_CHANGED, focus);
+}
+
+void FocusController::OnDrawnStateChanged(ServerWindow* ancestor,
+ ServerWindow* window,
+ bool is_drawn) {
+ // DCHECK(false); TODO(sadrul):
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/focus_controller.h b/chromium/components/mus/ws/focus_controller.h
new file mode 100644
index 00000000000..96a08609ad0
--- /dev/null
+++ b/chromium/components/mus/ws/focus_controller.h
@@ -0,0 +1,104 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_FOCUS_CONTROLLER_H_
+#define COMPONENTS_MUS_WS_FOCUS_CONTROLLER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "components/mus/ws/server_window_drawn_tracker_observer.h"
+#include "components/mus/ws/server_window_tracker.h"
+
+namespace mus {
+
+namespace ws {
+
+class FocusControllerDelegate;
+class FocusControllerObserver;
+class ServerWindow;
+class ServerWindowDrawnTracker;
+struct WindowId;
+
+// Describes the source of the change.
+enum class FocusControllerChangeSource {
+ EXPLICIT,
+ DRAWN_STATE_CHANGED,
+};
+
+// Tracks the focused window. Focus is moved to another window when the drawn
+// state of the focused window changes.
+class FocusController : public ServerWindowDrawnTrackerObserver {
+ public:
+ FocusController(FocusControllerDelegate* delegate, ServerWindow* root);
+ ~FocusController() override;
+
+ // Sets the focused window. Does nothing if |window| is currently focused.
+ // This does not notify the delegate. See ServerWindow::SetFocusedWindow()
+ // for details on return value.
+ bool SetFocusedWindow(ServerWindow* window);
+ ServerWindow* GetFocusedWindow();
+
+ // Moves activation to the next activatable window.
+ void ActivateNextWindow();
+
+ void AddObserver(FocusControllerObserver* observer);
+ void RemoveObserver(FocusControllerObserver* observer);
+
+ private:
+ enum class ActivationChangeReason {
+ UNKNONW,
+ CYCLE, // Activation changed because of ActivateNextWindow().
+ FOCUS, // Focus change required a different window to be activated.
+ DRAWN_STATE_CHANGED, // Active window was hidden or destroyed.
+ };
+ void SetActiveWindow(ServerWindow* window, ActivationChangeReason reason);
+
+ // Returns whether |window| can be focused or activated.
+ bool CanBeFocused(ServerWindow* window) const;
+ bool CanBeActivated(ServerWindow* window) const;
+
+ // Returns the closest activatable ancestor of |window|. Returns nullptr if
+ // there is no such ancestor.
+ ServerWindow* GetActivatableAncestorOf(ServerWindow* window) const;
+
+ // Implementation of SetFocusedWindow().
+ bool SetFocusedWindowImpl(FocusControllerChangeSource change_source,
+ ServerWindow* window);
+
+ // ServerWindowDrawnTrackerObserver:
+ void OnDrawnStateWillChange(ServerWindow* ancestor,
+ ServerWindow* window,
+ bool is_drawn) override;
+ void OnDrawnStateChanged(ServerWindow* ancestor,
+ ServerWindow* window,
+ bool is_drawn) override;
+
+ FocusControllerDelegate* delegate_;
+
+ ServerWindow* root_;
+ ServerWindow* focused_window_;
+ ServerWindow* active_window_;
+ // Tracks what caused |active_window_| to be activated.
+ ActivationChangeReason activation_reason_;
+
+ // Keeps track of the list of windows that have already been visited during a
+ // window cycle. This is only active when |activation_reason_| is set to
+ // CYCLE.
+ std::unique_ptr<ServerWindowTracker> cycle_windows_;
+
+ base::ObserverList<FocusControllerObserver> observers_;
+
+ // Keeps track of the visibility of the focused and active window.
+ std::unique_ptr<ServerWindowDrawnTracker> drawn_tracker_;
+
+ DISALLOW_COPY_AND_ASSIGN(FocusController);
+};
+
+} // namespace ws
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_FOCUS_CONTROLLER_H_
diff --git a/chromium/components/mus/ws/focus_controller_delegate.h b/chromium/components/mus/ws/focus_controller_delegate.h
new file mode 100644
index 00000000000..6eca41cabbb
--- /dev/null
+++ b/chromium/components/mus/ws/focus_controller_delegate.h
@@ -0,0 +1,26 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_FOCUS_CONTROLLER_DELEGATE_H_
+#define COMPONENTS_MUS_WS_FOCUS_CONTROLLER_DELEGATE_H_
+
+namespace mus {
+
+namespace ws {
+
+class ServerWindow;
+
+class FocusControllerDelegate {
+ public:
+ virtual bool CanHaveActiveChildren(ServerWindow* window) const = 0;
+
+ protected:
+ ~FocusControllerDelegate() {}
+};
+
+} // namespace ws
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_FOCUS_CONTROLLER_DELEGATE_H_
diff --git a/chromium/components/mus/ws/focus_controller_observer.h b/chromium/components/mus/ws/focus_controller_observer.h
new file mode 100644
index 00000000000..8edbae18c35
--- /dev/null
+++ b/chromium/components/mus/ws/focus_controller_observer.h
@@ -0,0 +1,29 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_FOCUS_CONTROLLER_OBSERVER_H_
+#define COMPONENTS_MUS_WS_FOCUS_CONTROLLER_OBSERVER_H_
+
+namespace mus {
+namespace ws {
+
+enum class FocusControllerChangeSource;
+class ServerWindow;
+
+class FocusControllerObserver {
+ public:
+ virtual void OnActivationChanged(ServerWindow* old_active_window,
+ ServerWindow* new_active_window) = 0;
+ virtual void OnFocusChanged(FocusControllerChangeSource change_source,
+ ServerWindow* old_focused_window,
+ ServerWindow* new_focused_window) = 0;
+
+ protected:
+ ~FocusControllerObserver() {}
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_FOCUS_CONTROLLER_OBSERVER_H_
diff --git a/chromium/components/mus/ws/focus_controller_unittest.cc b/chromium/components/mus/ws/focus_controller_unittest.cc
new file mode 100644
index 00000000000..563de06e875
--- /dev/null
+++ b/chromium/components/mus/ws/focus_controller_unittest.cc
@@ -0,0 +1,312 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/focus_controller.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "components/mus/ws/focus_controller_delegate.h"
+#include "components/mus/ws/focus_controller_observer.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/test_server_window_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mus {
+
+namespace ws {
+namespace {
+
+const char kDisallowActiveChildren[] = "disallow-active-children";
+
+class TestFocusControllerObserver : public FocusControllerObserver,
+ public FocusControllerDelegate {
+ public:
+ TestFocusControllerObserver()
+ : ignore_explicit_(true),
+ focus_change_count_(0u),
+ old_focused_window_(nullptr),
+ new_focused_window_(nullptr),
+ old_active_window_(nullptr),
+ new_active_window_(nullptr) {}
+
+ void ClearAll() {
+ focus_change_count_ = 0u;
+ old_focused_window_ = nullptr;
+ new_focused_window_ = nullptr;
+ old_active_window_ = nullptr;
+ new_active_window_ = nullptr;
+ }
+ size_t focus_change_count() const { return focus_change_count_; }
+ ServerWindow* old_focused_window() { return old_focused_window_; }
+ ServerWindow* new_focused_window() { return new_focused_window_; }
+
+ ServerWindow* old_active_window() { return old_active_window_; }
+ ServerWindow* new_active_window() { return new_active_window_; }
+
+ void set_ignore_explicit(bool ignore) { ignore_explicit_ = ignore; }
+
+ private:
+ // FocusControllerDelegate:
+ bool CanHaveActiveChildren(ServerWindow* window) const override {
+ return !window || window->properties().count(kDisallowActiveChildren) == 0;
+ }
+ // FocusControllerObserver:
+ void OnActivationChanged(ServerWindow* old_active_window,
+ ServerWindow* new_active_window) override {
+ old_active_window_ = old_active_window;
+ new_active_window_ = new_active_window;
+ }
+ void OnFocusChanged(FocusControllerChangeSource source,
+ ServerWindow* old_focused_window,
+ ServerWindow* new_focused_window) override {
+ if (ignore_explicit_ && source == FocusControllerChangeSource::EXPLICIT)
+ return;
+
+ focus_change_count_++;
+ old_focused_window_ = old_focused_window;
+ new_focused_window_ = new_focused_window;
+ }
+
+ bool ignore_explicit_;
+ size_t focus_change_count_;
+ ServerWindow* old_focused_window_;
+ ServerWindow* new_focused_window_;
+ ServerWindow* old_active_window_;
+ ServerWindow* new_active_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestFocusControllerObserver);
+};
+
+} // namespace
+
+TEST(FocusControllerTest, Basic) {
+ TestServerWindowDelegate server_window_delegate;
+ ServerWindow root(&server_window_delegate, WindowId());
+ server_window_delegate.set_root_window(&root);
+ root.SetVisible(true);
+ ServerWindow child(&server_window_delegate, WindowId());
+ child.SetVisible(true);
+ root.Add(&child);
+ ServerWindow child_child(&server_window_delegate, WindowId());
+ child_child.SetVisible(true);
+ child.Add(&child_child);
+
+ TestFocusControllerObserver focus_observer;
+ FocusController focus_controller(&focus_observer, &root);
+ focus_controller.AddObserver(&focus_observer);
+
+ focus_controller.SetFocusedWindow(&child_child);
+ EXPECT_EQ(0u, focus_observer.focus_change_count());
+
+ // Remove the ancestor of the focused window, focus should go to the |root|.
+ root.Remove(&child);
+ EXPECT_EQ(1u, focus_observer.focus_change_count());
+ EXPECT_EQ(&root, focus_observer.new_focused_window());
+ EXPECT_EQ(&child_child, focus_observer.old_focused_window());
+ focus_observer.ClearAll();
+
+ // Make the focused window invisible. Focus is lost in this case (as no one
+ // to give focus to).
+ root.SetVisible(false);
+ EXPECT_EQ(1u, focus_observer.focus_change_count());
+ EXPECT_EQ(nullptr, focus_observer.new_focused_window());
+ EXPECT_EQ(&root, focus_observer.old_focused_window());
+ focus_observer.ClearAll();
+
+ // Go back to initial state and focus |child_child|.
+ root.SetVisible(true);
+ root.Add(&child);
+ focus_controller.SetFocusedWindow(&child_child);
+ EXPECT_EQ(0u, focus_observer.focus_change_count());
+
+ // Hide the focused window, focus should go to parent.
+ child_child.SetVisible(false);
+ EXPECT_EQ(1u, focus_observer.focus_change_count());
+ EXPECT_EQ(&child, focus_observer.new_focused_window());
+ EXPECT_EQ(&child_child, focus_observer.old_focused_window());
+ focus_observer.ClearAll();
+
+ child_child.SetVisible(true);
+ focus_controller.SetFocusedWindow(&child_child);
+ EXPECT_EQ(0u, focus_observer.focus_change_count());
+
+ // Hide the parent of the focused window.
+ child.SetVisible(false);
+ EXPECT_EQ(1u, focus_observer.focus_change_count());
+ EXPECT_EQ(&root, focus_observer.new_focused_window());
+ EXPECT_EQ(&child_child, focus_observer.old_focused_window());
+ focus_observer.ClearAll();
+ focus_controller.RemoveObserver(&focus_observer);
+}
+
+// Tests that focus shifts correctly if the focused window is destroyed.
+TEST(FocusControllerTest, FocusShiftsOnDestroy) {
+ TestServerWindowDelegate server_window_delegate;
+ ServerWindow parent(&server_window_delegate, WindowId());
+ server_window_delegate.set_root_window(&parent);
+ parent.SetVisible(true);
+ ServerWindow child_first(&server_window_delegate, WindowId());
+ child_first.SetVisible(true);
+ parent.Add(&child_first);
+ std::unique_ptr<ServerWindow> child_second(
+ new ServerWindow(&server_window_delegate, WindowId()));
+ child_second->SetVisible(true);
+ parent.Add(child_second.get());
+ std::vector<uint8_t> dummy;
+ // Allow only |parent| to be activated.
+ parent.SetProperty(kDisallowActiveChildren, &dummy);
+
+ TestFocusControllerObserver focus_observer;
+ focus_observer.set_ignore_explicit(false);
+ FocusController focus_controller(&focus_observer, &parent);
+ focus_controller.AddObserver(&focus_observer);
+
+ focus_controller.ActivateNextWindow();
+ EXPECT_EQ(nullptr, focus_observer.old_active_window());
+ EXPECT_EQ(&parent, focus_observer.new_active_window());
+ EXPECT_EQ(nullptr, focus_observer.old_focused_window());
+ EXPECT_EQ(child_second.get(), focus_observer.new_focused_window());
+ focus_observer.ClearAll();
+
+ // Destroying |child_second| should move focus to |child_first|.
+ child_second.reset();
+ EXPECT_NE(nullptr, focus_observer.old_focused_window());
+ EXPECT_EQ(&child_first, focus_observer.new_focused_window());
+
+ focus_controller.RemoveObserver(&focus_observer);
+}
+
+TEST(FocusControllerTest, ActivationSkipsOverHiddenWindow) {
+ TestServerWindowDelegate server_window_delegate;
+ ServerWindow parent(&server_window_delegate, WindowId());
+ server_window_delegate.set_root_window(&parent);
+ parent.SetVisible(true);
+
+ ServerWindow child_first(&server_window_delegate, WindowId());
+ ServerWindow child_second(&server_window_delegate, WindowId());
+ ServerWindow child_third(&server_window_delegate, WindowId());
+
+ parent.Add(&child_first);
+ parent.Add(&child_second);
+ parent.Add(&child_third);
+
+ child_first.SetVisible(true);
+ child_second.SetVisible(false);
+ child_third.SetVisible(true);
+
+ TestFocusControllerObserver focus_observer;
+ focus_observer.set_ignore_explicit(false);
+ FocusController focus_controller(&focus_observer, &parent);
+ focus_controller.AddObserver(&focus_observer);
+
+ // Since |child_second| is invisible, activation should cycle from
+ // |child_third|, to |child_first|, to |parent|, back to |child_third|.
+ focus_controller.ActivateNextWindow();
+ EXPECT_EQ(nullptr, focus_observer.old_active_window());
+ EXPECT_EQ(&child_third, focus_observer.new_active_window());
+ focus_observer.ClearAll();
+
+ focus_controller.ActivateNextWindow();
+ EXPECT_EQ(&child_third, focus_observer.old_active_window());
+ EXPECT_EQ(&child_first, focus_observer.new_active_window());
+ focus_observer.ClearAll();
+
+ focus_controller.ActivateNextWindow();
+ EXPECT_EQ(&child_first, focus_observer.old_active_window());
+ EXPECT_EQ(&parent, focus_observer.new_active_window());
+ focus_observer.ClearAll();
+
+ focus_controller.ActivateNextWindow();
+ EXPECT_EQ(&parent, focus_observer.old_active_window());
+ EXPECT_EQ(&child_third, focus_observer.new_active_window());
+ focus_observer.ClearAll();
+
+ // Once |child_second| is made visible, activation should go from
+ // |child_third| to |child_second|.
+ child_second.SetVisible(true);
+ focus_controller.ActivateNextWindow();
+ EXPECT_EQ(&child_third, focus_observer.old_active_window());
+ EXPECT_EQ(&child_second, focus_observer.new_active_window());
+}
+
+TEST(FocusControllerTest, ActivationSkipsOverHiddenContainers) {
+ TestServerWindowDelegate server_window_delegate;
+ ServerWindow parent(&server_window_delegate, WindowId());
+ server_window_delegate.set_root_window(&parent);
+ parent.SetVisible(true);
+
+ ServerWindow child1(&server_window_delegate, WindowId());
+ ServerWindow child2(&server_window_delegate, WindowId());
+
+ parent.Add(&child1);
+ parent.Add(&child2);
+
+ child1.SetVisible(true);
+ child2.SetVisible(true);
+
+ ServerWindow child11(&server_window_delegate, WindowId());
+ ServerWindow child12(&server_window_delegate, WindowId());
+ ServerWindow child21(&server_window_delegate, WindowId());
+ ServerWindow child22(&server_window_delegate, WindowId());
+
+ child1.Add(&child11);
+ child1.Add(&child12);
+ child2.Add(&child21);
+ child2.Add(&child22);
+
+ child11.SetVisible(true);
+ child12.SetVisible(true);
+ child21.SetVisible(true);
+ child22.SetVisible(true);
+
+ TestFocusControllerObserver focus_observer;
+ FocusController focus_controller(&focus_observer, &parent);
+ focus_controller.AddObserver(&focus_observer);
+
+ focus_controller.ActivateNextWindow();
+ EXPECT_EQ(nullptr, focus_observer.old_active_window());
+ EXPECT_EQ(&child22, focus_observer.new_active_window());
+ focus_observer.ClearAll();
+
+ child2.SetVisible(false);
+ EXPECT_EQ(&child22, focus_observer.old_active_window());
+ EXPECT_EQ(&child12, focus_observer.new_active_window());
+}
+
+TEST(FocusControllerTest, NonFocusableWindowNotActivated) {
+ TestServerWindowDelegate server_window_delegate;
+ ServerWindow parent(&server_window_delegate, WindowId());
+ server_window_delegate.set_root_window(&parent);
+ parent.SetVisible(true);
+
+ ServerWindow child_first(&server_window_delegate, WindowId());
+ ServerWindow child_second(&server_window_delegate, WindowId());
+ parent.Add(&child_first);
+ parent.Add(&child_second);
+ child_first.SetVisible(true);
+ child_second.SetVisible(true);
+
+ TestFocusControllerObserver focus_observer;
+ focus_observer.set_ignore_explicit(false);
+ FocusController focus_controller(&focus_observer, &parent);
+ focus_controller.AddObserver(&focus_observer);
+
+ child_first.set_can_focus(false);
+
+ // |child_second| is activated first, but then activation skips over
+ // |child_first| and goes to |parent| instead.
+ focus_controller.ActivateNextWindow();
+ EXPECT_EQ(nullptr, focus_observer.old_active_window());
+ EXPECT_EQ(&child_second, focus_observer.new_active_window());
+ focus_observer.ClearAll();
+
+ focus_controller.ActivateNextWindow();
+ EXPECT_EQ(&child_second, focus_observer.old_active_window());
+ EXPECT_EQ(&parent, focus_observer.new_active_window());
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/ids.h b/chromium/components/mus/ws/ids.h
new file mode 100644
index 00000000000..3cdf4b29b87
--- /dev/null
+++ b/chromium/components/mus/ws/ids.h
@@ -0,0 +1,113 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_IDS_H_
+#define COMPONENTS_MUS_WS_IDS_H_
+
+#include <stdint.h>
+
+#include <tuple>
+
+#include "base/containers/hash_tables.h"
+#include "base/hash.h"
+#include "components/mus/common/types.h"
+#include "components/mus/common/util.h"
+
+namespace mus {
+namespace ws {
+
+// A client id used to indicate no client. That is, no WindowTree ever gets this
+// id.
+const ClientSpecificId kInvalidClientId = 0;
+
+// Every window has a unique id associated with it (WindowId). The id is a
+// combination of the id assigned to the client (the high order bits) and
+// a unique id for the window. Each client (WindowTree) refers to the window
+// by an id assigned by the client (ClientWindowId). To facilitate this
+// WindowTree maintains a mapping between WindowId and ClientWindowId.
+//
+// This model works when the client initiates creation of windows, which is
+// the typical use case. Embed roots and the WindowManager are special, they
+// get access to windows created by other clients. These clients see the
+// id assigned on the server. Such clients have to take care that they only
+// create windows using their client id. To do otherwise could result in
+// multiple windows having the same ClientWindowId. WindowTree enforces
+// that embed roots use the client id in creating the window id to avoid
+// possible conflicts.
+struct WindowId {
+ WindowId(ClientSpecificId client_id, ClientSpecificId window_id)
+ : client_id(client_id), window_id(window_id) {}
+ WindowId() : client_id(0), window_id(0) {}
+
+ bool operator==(const WindowId& other) const {
+ return other.client_id == client_id && other.window_id == window_id;
+ }
+
+ bool operator!=(const WindowId& other) const { return !(*this == other); }
+
+ bool operator<(const WindowId& other) const {
+ return std::tie(client_id, window_id) <
+ std::tie(other.client_id, other.window_id);
+ }
+
+ ClientSpecificId client_id;
+ ClientSpecificId window_id;
+};
+
+// Used for ids assigned by the client.
+struct ClientWindowId {
+ explicit ClientWindowId(Id id) : id(id) {}
+ ClientWindowId() : id(0u) {}
+
+ bool operator==(const ClientWindowId& other) const { return other.id == id; }
+
+ bool operator!=(const ClientWindowId& other) const {
+ return !(*this == other);
+ }
+
+ bool operator<(const ClientWindowId& other) const { return id < other.id; }
+
+ Id id;
+};
+
+inline WindowId WindowIdFromTransportId(Id id) {
+ return WindowId(HiWord(id), LoWord(id));
+}
+inline Id WindowIdToTransportId(const WindowId& id) {
+ return (id.client_id << 16) | id.window_id;
+}
+
+// Returns a WindowId that is reserved to indicate no window. That is, no window
+// will ever be created with this id.
+inline WindowId InvalidWindowId() {
+ return WindowId(kInvalidClientId, 0);
+}
+
+// Returns a root window id with a given index offset.
+inline WindowId RootWindowId(uint16_t index) {
+ return WindowId(kInvalidClientId, 2 + index);
+}
+
+} // namespace ws
+} // namespace mus
+
+namespace BASE_HASH_NAMESPACE {
+
+template <>
+struct hash<mus::ws::ClientWindowId> {
+ size_t operator()(const mus::ws::ClientWindowId& id) const {
+ return hash<mus::Id>()(id.id);
+ }
+};
+
+template <>
+struct hash<mus::ws::WindowId> {
+ size_t operator()(const mus::ws::WindowId& id) const {
+ return hash<mus::Id>()(WindowIdToTransportId(id));
+ }
+};
+
+} // namespace BASE_HASH_NAMESPACE
+
+#endif // COMPONENTS_MUS_WS_IDS_H_
diff --git a/chromium/components/mus/ws/modal_window_controller.cc b/chromium/components/mus/ws/modal_window_controller.cc
new file mode 100644
index 00000000000..89ec02e3720
--- /dev/null
+++ b/chromium/components/mus/ws/modal_window_controller.cc
@@ -0,0 +1,121 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/modal_window_controller.h"
+
+#include "base/stl_util.h"
+#include "components/mus/ws/event_dispatcher.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/server_window_drawn_tracker.h"
+
+namespace mus {
+namespace ws {
+namespace {
+
+const ServerWindow* GetModalChildForWindowAncestor(const ServerWindow* window) {
+ for (const ServerWindow* ancestor = window; ancestor;
+ ancestor = ancestor->parent()) {
+ for (const auto& transient_child : ancestor->transient_children()) {
+ if (transient_child->is_modal() && transient_child->IsDrawn())
+ return transient_child;
+ }
+ }
+ return nullptr;
+}
+
+const ServerWindow* GetWindowModalTargetForWindow(const ServerWindow* window) {
+ const ServerWindow* modal_window = GetModalChildForWindowAncestor(window);
+ if (!modal_window)
+ return window;
+ return GetWindowModalTargetForWindow(modal_window);
+}
+
+} // namespace
+
+ModalWindowController::ModalWindowController(EventDispatcher* event_dispatcher)
+ : event_dispatcher_(event_dispatcher) {}
+
+ModalWindowController::~ModalWindowController() {
+ for (auto it = system_modal_windows_.begin();
+ it != system_modal_windows_.end(); it++) {
+ (*it)->RemoveObserver(this);
+ }
+}
+
+void ModalWindowController::AddSystemModalWindow(ServerWindow* window) {
+ DCHECK(window);
+ DCHECK(!ContainsValue(system_modal_windows_, window));
+
+ window->SetModal();
+ system_modal_windows_.push_back(window);
+ window_drawn_trackers_.insert(make_pair(
+ window, base::WrapUnique(new ServerWindowDrawnTracker(window, this))));
+ window->AddObserver(this);
+
+ event_dispatcher_->ReleaseCaptureBlockedByModalWindow(window);
+}
+
+bool ModalWindowController::IsWindowBlockedBy(
+ const ServerWindow* window,
+ const ServerWindow* modal_window) const {
+ DCHECK(window);
+ DCHECK(modal_window);
+ if (!modal_window->is_modal() || !modal_window->IsDrawn())
+ return false;
+
+ if (modal_window->transient_parent() &&
+ !modal_window->transient_parent()->Contains(window)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool ModalWindowController::IsWindowBlocked(const ServerWindow* window) const {
+ DCHECK(window);
+ return GetActiveSystemModalWindow() || GetModalChildForWindowAncestor(window);
+}
+
+const ServerWindow* ModalWindowController::GetTargetForWindow(
+ const ServerWindow* window) const {
+ ServerWindow* system_modal_window = GetActiveSystemModalWindow();
+ return system_modal_window ? system_modal_window
+ : GetWindowModalTargetForWindow(window);
+}
+
+ServerWindow* ModalWindowController::GetActiveSystemModalWindow() const {
+ for (auto it = system_modal_windows_.rbegin();
+ it != system_modal_windows_.rend(); it++) {
+ ServerWindow* modal = *it;
+ if (modal->IsDrawn())
+ return modal;
+ }
+ return nullptr;
+}
+
+void ModalWindowController::OnWindowDestroyed(ServerWindow* window) {
+ window->RemoveObserver(this);
+ auto it = std::find(system_modal_windows_.begin(),
+ system_modal_windows_.end(), window);
+ DCHECK(it != system_modal_windows_.end());
+ system_modal_windows_.erase(it);
+ window_drawn_trackers_.erase(window);
+}
+
+void ModalWindowController::OnDrawnStateChanged(ServerWindow* ancestor,
+ ServerWindow* window,
+ bool is_drawn) {
+ if (!is_drawn)
+ return;
+
+ // Move the most recently shown window to the end of the list.
+ auto it = std::find(system_modal_windows_.begin(),
+ system_modal_windows_.end(), window);
+ DCHECK(it != system_modal_windows_.end());
+ system_modal_windows_.splice(system_modal_windows_.end(),
+ system_modal_windows_, it);
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/modal_window_controller.h b/chromium/components/mus/ws/modal_window_controller.h
new file mode 100644
index 00000000000..112fce771e6
--- /dev/null
+++ b/chromium/components/mus/ws/modal_window_controller.h
@@ -0,0 +1,87 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_MODAL_WINDOW_CONTROLLER_H_
+#define COMPONENTS_MUS_WS_MODAL_WINDOW_CONTROLLER_H_
+
+#include <list>
+#include <map>
+
+#include "components/mus/ws/server_window_drawn_tracker_observer.h"
+#include "components/mus/ws/server_window_observer.h"
+
+namespace mus {
+namespace ws {
+
+class EventDispatcher;
+class ServerWindow;
+class ServerWindowDrawnTracker;
+
+namespace test {
+class ModalWindowControllerTestApi;
+}
+
+// Used to keeps track of system modal windows and check whether windows are
+// blocked by modal windows or not to do appropriate retargetting of events.
+class ModalWindowController : public ServerWindowObserver,
+ public ServerWindowDrawnTrackerObserver {
+ public:
+ explicit ModalWindowController(EventDispatcher* event_dispatcher);
+ ~ModalWindowController() override;
+
+ // Adds a system modal window. The window remains modal to system until it is
+ // destroyed. There can exist multiple system modal windows, in which case the
+ // one that is visible and added most recently or shown most recently would be
+ // the active one.
+ void AddSystemModalWindow(ServerWindow* window);
+
+ // Checks whether |modal_window| is a visible modal window that blocks
+ // |window|.
+ bool IsWindowBlockedBy(const ServerWindow* window,
+ const ServerWindow* modal_window) const;
+
+ // Checks whether |window| is blocked by any visible modal window.
+ bool IsWindowBlocked(const ServerWindow* window) const;
+
+ // Returns the window that events targeted to |window| should be retargeted
+ // to; according to modal windows. If any modal window is blocking |window|
+ // (either system modal or window modal), returns the topmost one; otherwise,
+ // returns |window| itself.
+ const ServerWindow* GetTargetForWindow(const ServerWindow* window) const;
+ ServerWindow* GetTargetForWindow(ServerWindow* window) const {
+ return const_cast<ServerWindow*>(
+ GetTargetForWindow(static_cast<const ServerWindow*>(window)));
+ }
+
+ private:
+ friend class test::ModalWindowControllerTestApi;
+
+ // Returns the system modal window that is visible and added/shown most
+ // recently, if any.
+ ServerWindow* GetActiveSystemModalWindow() const;
+
+ // ServerWindowObserver:
+ void OnWindowDestroyed(ServerWindow* window) override;
+
+ // ServerWindowDrawnTrackerObserver:
+ void OnDrawnStateChanged(ServerWindow* ancestor,
+ ServerWindow* window,
+ bool is_drawn) override;
+
+ EventDispatcher* event_dispatcher_;
+
+ // List of system modal windows in order they are added/shown.
+ std::list<ServerWindow*> system_modal_windows_;
+
+ // Drawn trackers for system modal windows.
+ std::map<ServerWindow*, std::unique_ptr<ServerWindowDrawnTracker>>
+ window_drawn_trackers_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModalWindowController);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_MODAL_WINDOW_CONTROLLER_H_
diff --git a/chromium/components/mus/ws/mus_ws_unittests_app_manifest.json b/chromium/components/mus/ws/mus_ws_unittests_app_manifest.json
new file mode 100644
index 00000000000..5e3872e21ac
--- /dev/null
+++ b/chromium/components/mus/ws/mus_ws_unittests_app_manifest.json
@@ -0,0 +1,16 @@
+{
+ "manifest_version": 1,
+ "name": "mojo:mus_ws_unittests_app",
+ "display_name": "Mus Window Server Unittests",
+ "capabilities": {
+ "required": {
+ "*": { "classes": [ "app" ] },
+ "mojo:mus_ws_unittests_app": {
+ "interfaces": [ "mus::mojom::WindowTreeClient" ]
+ },
+ "mojo:mus": {
+ "interfaces": [ "mus::mojom::WindowTreeHostFactory" ]
+ }
+ }
+ }
+}
diff --git a/chromium/components/mus/ws/operation.cc b/chromium/components/mus/ws/operation.cc
new file mode 100644
index 00000000000..6c04bc34c01
--- /dev/null
+++ b/chromium/components/mus/ws/operation.cc
@@ -0,0 +1,30 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/operation.h"
+
+#include "components/mus/ws/window_server.h"
+#include "components/mus/ws/window_tree.h"
+
+namespace mus {
+namespace ws {
+
+Operation::Operation(WindowTree* tree,
+ WindowServer* window_server,
+ OperationType operation_type)
+ : window_server_(window_server),
+ source_tree_id_(tree->id()),
+ operation_type_(operation_type) {
+ DCHECK(operation_type != OperationType::NONE);
+ // Tell the window server about the operation currently in flight. The window
+ // server uses this information to suppress certain calls out to clients.
+ window_server_->PrepareForOperation(this);
+}
+
+Operation::~Operation() {
+ window_server_->FinishOperation();
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/operation.h b/chromium/components/mus/ws/operation.h
new file mode 100644
index 00000000000..15d0034fddc
--- /dev/null
+++ b/chromium/components/mus/ws/operation.h
@@ -0,0 +1,77 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_OPERATION_H_
+#define COMPONENTS_MUS_WS_OPERATION_H_
+
+#include <set>
+
+#include "base/macros.h"
+#include "components/mus/common/types.h"
+
+namespace mus {
+namespace ws {
+
+class WindowServer;
+class WindowTree;
+
+enum class OperationType {
+ NONE,
+ ADD_TRANSIENT_WINDOW,
+ ADD_WINDOW,
+ DELETE_WINDOW,
+ EMBED,
+ RELEASE_CAPTURE,
+ REMOVE_TRANSIENT_WINDOW_FROM_PARENT,
+ REMOVE_WINDOW_FROM_PARENT,
+ REORDER_WINDOW,
+ SET_CAPTURE,
+ SET_FOCUS,
+ SET_WINDOW_BOUNDS,
+ SET_WINDOW_OPACITY,
+ SET_WINDOW_PREDEFINED_CURSOR,
+ SET_WINDOW_PROPERTY,
+ SET_WINDOW_VISIBILITY,
+};
+
+// This class tracks the currently pending client-initiated operation.
+// This is typically used to suppress superfluous notifications generated
+// by suboperations in the window server.
+class Operation {
+ public:
+ Operation(WindowTree* tree,
+ WindowServer* window_server,
+ OperationType operation_type);
+ ~Operation();
+
+ ClientSpecificId source_tree_id() const { return source_tree_id_; }
+
+ const OperationType& type() const { return operation_type_; }
+
+ // Marks the tree with the specified id as having been sent a message
+ // during the course of |this| operation.
+ void MarkTreeAsMessaged(ClientSpecificId tree_id) {
+ message_ids_.insert(tree_id);
+ }
+
+ // Returns true if MarkTreeAsMessaged(tree_id) was invoked.
+ bool DidMessageTree(ClientSpecificId tree_id) const {
+ return message_ids_.count(tree_id) > 0;
+ }
+
+ private:
+ WindowServer* const window_server_;
+ const ClientSpecificId source_tree_id_;
+ const OperationType operation_type_;
+
+ // See description of MarkTreeAsMessaged/DidMessageTree.
+ std::set<ClientSpecificId> message_ids_;
+
+ DISALLOW_COPY_AND_ASSIGN(Operation);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_OPERATION_H_
diff --git a/chromium/components/mus/ws/platform_display.cc b/chromium/components/mus/ws/platform_display.cc
new file mode 100644
index 00000000000..84ca1fde8ba
--- /dev/null
+++ b/chromium/components/mus/ws/platform_display.cc
@@ -0,0 +1,414 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/platform_display.h"
+
+#include "base/numerics/safe_conversions.h"
+#include "build/build_config.h"
+#include "cc/ipc/quads.mojom.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/output/copy_output_request.h"
+#include "cc/output/delegated_frame_data.h"
+#include "cc/quads/render_pass.h"
+#include "cc/quads/shared_quad_state.h"
+#include "cc/quads/surface_draw_quad.h"
+#include "components/mus/gles2/gpu_state.h"
+#include "components/mus/public/interfaces/gpu.mojom.h"
+#include "components/mus/surfaces/display_compositor.h"
+#include "components/mus/surfaces/surfaces_state.h"
+#include "components/mus/ws/platform_display_factory.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/server_window_surface.h"
+#include "components/mus/ws/server_window_surface_manager.h"
+#include "components/mus/ws/window_coordinate_conversions.h"
+#include "services/shell/public/cpp/connection.h"
+#include "services/shell/public/cpp/connector.h"
+#include "third_party/skia/include/core/SkXfermode.h"
+#include "ui/base/cursor/cursor_loader.h"
+#include "ui/display/display.h"
+#include "ui/events/event.h"
+#include "ui/events/event_utils.h"
+#include "ui/platform_window/platform_ime_controller.h"
+#include "ui/platform_window/platform_window.h"
+
+#if defined(OS_WIN)
+#include "ui/platform_window/win/win_window.h"
+#elif defined(USE_X11)
+#include "ui/platform_window/x11/x11_window.h"
+#elif defined(OS_ANDROID)
+#include "ui/platform_window/android/platform_window_android.h"
+#elif defined(USE_OZONE)
+#include "ui/ozone/public/ozone_platform.h"
+#endif
+
+namespace mus {
+
+namespace ws {
+namespace {
+
+// DrawWindowTree recursively visits ServerWindows, creating a SurfaceDrawQuad
+// for each that lacks one.
+void DrawWindowTree(cc::RenderPass* pass,
+ ServerWindow* window,
+ const gfx::Vector2d& parent_to_root_origin_offset,
+ float opacity) {
+ if (!window->visible())
+ return;
+
+ ServerWindowSurface* default_surface =
+ window->surface_manager() ? window->surface_manager()->GetDefaultSurface()
+ : nullptr;
+
+ const gfx::Rect absolute_bounds =
+ window->bounds() + parent_to_root_origin_offset;
+ std::vector<ServerWindow*> children(window->GetChildren());
+ // TODO(rjkroege, fsamuel): Make sure we're handling alpha correctly.
+ const float combined_opacity = opacity * window->opacity();
+ for (auto it = children.rbegin(); it != children.rend(); ++it) {
+ DrawWindowTree(pass, *it, absolute_bounds.OffsetFromOrigin(),
+ combined_opacity);
+ }
+
+ if (!window->surface_manager() || !window->surface_manager()->ShouldDraw())
+ return;
+
+ ServerWindowSurface* underlay_surface =
+ window->surface_manager()->GetUnderlaySurface();
+ if (!default_surface && !underlay_surface)
+ return;
+
+ if (default_surface) {
+ gfx::Transform quad_to_target_transform;
+ quad_to_target_transform.Translate(absolute_bounds.x(),
+ absolute_bounds.y());
+
+ cc::SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState();
+
+ const gfx::Rect bounds_at_origin(window->bounds().size());
+ // TODO(fsamuel): These clipping and visible rects are incorrect. They need
+ // to be populated from CompositorFrame structs.
+ sqs->SetAll(quad_to_target_transform,
+ bounds_at_origin.size() /* layer_bounds */,
+ bounds_at_origin /* visible_layer_bounds */,
+ bounds_at_origin /* clip_rect */, false /* is_clipped */,
+ window->opacity(), SkXfermode::kSrcOver_Mode,
+ 0 /* sorting-context_id */);
+ auto quad = pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>();
+ quad->SetAll(sqs, bounds_at_origin /* rect */,
+ gfx::Rect() /* opaque_rect */,
+ bounds_at_origin /* visible_rect */, true /* needs_blending*/,
+ default_surface->id());
+ }
+ if (underlay_surface) {
+ const gfx::Rect underlay_absolute_bounds =
+ absolute_bounds - window->underlay_offset();
+ gfx::Transform quad_to_target_transform;
+ quad_to_target_transform.Translate(underlay_absolute_bounds.x(),
+ underlay_absolute_bounds.y());
+ cc::SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState();
+ const gfx::Rect bounds_at_origin(
+ underlay_surface->last_submitted_frame_size());
+ sqs->SetAll(quad_to_target_transform,
+ bounds_at_origin.size() /* layer_bounds */,
+ bounds_at_origin /* visible_layer_bounds */,
+ bounds_at_origin /* clip_rect */, false /* is_clipped */,
+ window->opacity(), SkXfermode::kSrcOver_Mode,
+ 0 /* sorting-context_id */);
+
+ auto quad = pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>();
+ quad->SetAll(sqs, bounds_at_origin /* rect */,
+ gfx::Rect() /* opaque_rect */,
+ bounds_at_origin /* visible_rect */, true /* needs_blending*/,
+ underlay_surface->id());
+ }
+}
+
+} // namespace
+
+// static
+PlatformDisplayFactory* PlatformDisplay::factory_ = nullptr;
+
+// static
+PlatformDisplay* PlatformDisplay::Create(
+ const PlatformDisplayInitParams& init_params) {
+ if (factory_)
+ return factory_->CreatePlatformDisplay();
+
+ return new DefaultPlatformDisplay(init_params);
+}
+
+DefaultPlatformDisplay::DefaultPlatformDisplay(
+ const PlatformDisplayInitParams& init_params)
+ : display_id_(init_params.display_id),
+ gpu_state_(init_params.gpu_state),
+ surfaces_state_(init_params.surfaces_state),
+ delegate_(nullptr),
+ draw_timer_(false, false),
+ frame_pending_(false),
+#if !defined(OS_ANDROID)
+ cursor_loader_(ui::CursorLoader::Create()),
+#endif
+ weak_factory_(this) {
+ metrics_.size_in_pixels = init_params.display_bounds.size();
+ // TODO(rjkroege): Preserve the display_id when Ozone platform can use it.
+}
+
+void DefaultPlatformDisplay::Init(PlatformDisplayDelegate* delegate) {
+ delegate_ = delegate;
+
+ gfx::Rect bounds(metrics_.size_in_pixels);
+#if defined(OS_WIN)
+ platform_window_.reset(new ui::WinWindow(this, bounds));
+#elif defined(USE_X11)
+ platform_window_.reset(new ui::X11Window(this));
+#elif defined(OS_ANDROID)
+ platform_window_.reset(new ui::PlatformWindowAndroid(this));
+#elif defined(USE_OZONE)
+ platform_window_ =
+ ui::OzonePlatform::GetInstance()->CreatePlatformWindow(this, bounds);
+#else
+ NOTREACHED() << "Unsupported platform";
+#endif
+ platform_window_->SetBounds(bounds);
+ platform_window_->Show();
+}
+
+DefaultPlatformDisplay::~DefaultPlatformDisplay() {
+ // Don't notify the delegate from the destructor.
+ delegate_ = nullptr;
+
+ // Invalidate WeakPtrs now to avoid callbacks back into the
+ // DefaultPlatformDisplay during destruction of |display_compositor_|.
+ weak_factory_.InvalidateWeakPtrs();
+ display_compositor_.reset();
+ // Destroy the PlatformWindow early on as it may call us back during
+ // destruction and we want to be in a known state. But destroy the surface
+ // first because it can still be using the platform window.
+ platform_window_.reset();
+}
+
+void DefaultPlatformDisplay::SchedulePaint(const ServerWindow* window,
+ const gfx::Rect& bounds) {
+ DCHECK(window);
+ if (!window->IsDrawn())
+ return;
+ const gfx::Rect root_relative_rect =
+ ConvertRectBetweenWindows(window, delegate_->GetRootWindow(), bounds);
+ if (root_relative_rect.IsEmpty())
+ return;
+ dirty_rect_.Union(root_relative_rect);
+ WantToDraw();
+}
+
+void DefaultPlatformDisplay::SetViewportSize(const gfx::Size& size) {
+ platform_window_->SetBounds(gfx::Rect(size));
+}
+
+void DefaultPlatformDisplay::SetTitle(const base::string16& title) {
+ platform_window_->SetTitle(title);
+}
+
+void DefaultPlatformDisplay::SetCapture() {
+ platform_window_->SetCapture();
+}
+
+void DefaultPlatformDisplay::ReleaseCapture() {
+ platform_window_->ReleaseCapture();
+}
+
+void DefaultPlatformDisplay::SetCursorById(int32_t cursor_id) {
+#if !defined(OS_ANDROID)
+ // TODO(erg): This still isn't sufficient, and will only use native cursors
+ // that chrome would use, not custom image cursors. For that, we should
+ // delegate to the window manager to load images from resource packs.
+ //
+ // We probably also need to deal with different DPIs.
+ ui::Cursor cursor(cursor_id);
+ cursor_loader_->SetPlatformCursor(&cursor);
+ platform_window_->SetCursor(cursor.platform());
+#endif
+}
+
+float DefaultPlatformDisplay::GetDeviceScaleFactor() {
+ return metrics_.device_scale_factor;
+}
+
+mojom::Rotation DefaultPlatformDisplay::GetRotation() {
+ // TODO(sky): implement me.
+ return mojom::Rotation::VALUE_0;
+}
+
+void DefaultPlatformDisplay::UpdateTextInputState(
+ const ui::TextInputState& state) {
+ ui::PlatformImeController* ime = platform_window_->GetPlatformImeController();
+ if (ime)
+ ime->UpdateTextInputState(state);
+}
+
+void DefaultPlatformDisplay::SetImeVisibility(bool visible) {
+ ui::PlatformImeController* ime = platform_window_->GetPlatformImeController();
+ if (ime)
+ ime->SetImeVisibility(visible);
+}
+
+void DefaultPlatformDisplay::Draw() {
+ if (!delegate_->GetRootWindow()->visible())
+ return;
+
+ // TODO(fsamuel): We should add a trace for generating a top level frame.
+ cc::CompositorFrame frame(GenerateCompositorFrame());
+ frame_pending_ = true;
+ if (display_compositor_) {
+ display_compositor_->SubmitCompositorFrame(
+ std::move(frame), base::Bind(&DefaultPlatformDisplay::DidDraw,
+ weak_factory_.GetWeakPtr()));
+ }
+ dirty_rect_ = gfx::Rect();
+}
+
+void DefaultPlatformDisplay::DidDraw(cc::SurfaceDrawStatus status) {
+ frame_pending_ = false;
+ delegate_->OnCompositorFrameDrawn();
+ if (!dirty_rect_.IsEmpty())
+ WantToDraw();
+}
+
+bool DefaultPlatformDisplay::IsFramePending() const {
+ return frame_pending_;
+}
+
+int64_t DefaultPlatformDisplay::GetDisplayId() const {
+ return display_id_;
+}
+
+void DefaultPlatformDisplay::WantToDraw() {
+ if (draw_timer_.IsRunning() || frame_pending_)
+ return;
+
+ // TODO(rjkroege): Use vblank to kick off Draw.
+ draw_timer_.Start(
+ FROM_HERE, base::TimeDelta(),
+ base::Bind(&DefaultPlatformDisplay::Draw, weak_factory_.GetWeakPtr()));
+}
+
+void DefaultPlatformDisplay::UpdateMetrics(const gfx::Size& size,
+ float device_scale_factor) {
+ if (display::Display::HasForceDeviceScaleFactor())
+ device_scale_factor = display::Display::GetForcedDeviceScaleFactor();
+ if (metrics_.size_in_pixels == size &&
+ metrics_.device_scale_factor == device_scale_factor)
+ return;
+
+ ViewportMetrics old_metrics = metrics_;
+ metrics_.size_in_pixels = size;
+ metrics_.device_scale_factor = device_scale_factor;
+ delegate_->OnViewportMetricsChanged(old_metrics, metrics_);
+}
+
+cc::CompositorFrame DefaultPlatformDisplay::GenerateCompositorFrame() {
+ std::unique_ptr<cc::RenderPass> render_pass = cc::RenderPass::Create();
+ render_pass->damage_rect = dirty_rect_;
+ render_pass->output_rect = gfx::Rect(metrics_.size_in_pixels);
+
+ DrawWindowTree(render_pass.get(), delegate_->GetRootWindow(), gfx::Vector2d(),
+ 1.0f);
+
+ std::unique_ptr<cc::DelegatedFrameData> frame_data(
+ new cc::DelegatedFrameData);
+ frame_data->render_pass_list.push_back(std::move(render_pass));
+
+ cc::CompositorFrame frame;
+ frame.delegated_frame_data = std::move(frame_data);
+ return frame;
+}
+
+void DefaultPlatformDisplay::OnBoundsChanged(const gfx::Rect& new_bounds) {
+ UpdateMetrics(new_bounds.size(), metrics_.device_scale_factor);
+}
+
+void DefaultPlatformDisplay::OnDamageRect(const gfx::Rect& damaged_region) {
+ dirty_rect_.Union(damaged_region);
+ WantToDraw();
+}
+
+void DefaultPlatformDisplay::DispatchEvent(ui::Event* event) {
+ if (event->IsScrollEvent()) {
+ // TODO(moshayedi): crbug.com/602859. Dispatch scroll events as
+ // they are once we have proper support for scroll events.
+ delegate_->OnEvent(ui::MouseWheelEvent(*event->AsScrollEvent()));
+ } else if (event->IsMouseEvent() && !event->IsMouseWheelEvent()) {
+ delegate_->OnEvent(ui::PointerEvent(*event->AsMouseEvent()));
+ } else if (event->IsTouchEvent()) {
+ delegate_->OnEvent(ui::PointerEvent(*event->AsTouchEvent()));
+ } else {
+ delegate_->OnEvent(*event);
+ }
+
+#if defined(USE_X11) || defined(USE_OZONE)
+ // We want to emulate the WM_CHAR generation behaviour of Windows.
+ //
+ // On Linux, we've previously inserted characters by having
+ // InputMethodAuraLinux take all key down events and send a character event
+ // to the TextInputClient. This causes a mismatch in code that has to be
+ // shared between Windows and Linux, including blink code. Now that we're
+ // trying to have one way of doing things, we need to standardize on and
+ // emulate Windows character events.
+ //
+ // This is equivalent to what we're doing in the current Linux port, but
+ // done once instead of done multiple times in different places.
+ if (event->type() == ui::ET_KEY_PRESSED) {
+ ui::KeyEvent* key_press_event = event->AsKeyEvent();
+ ui::KeyEvent char_event(key_press_event->GetCharacter(),
+ key_press_event->key_code(),
+ key_press_event->flags());
+ DCHECK_EQ(key_press_event->GetCharacter(), char_event.GetCharacter());
+ DCHECK_EQ(key_press_event->key_code(), char_event.key_code());
+ DCHECK_EQ(key_press_event->flags(), char_event.flags());
+ delegate_->OnEvent(char_event);
+ }
+#endif
+}
+
+void DefaultPlatformDisplay::OnCloseRequest() {
+ platform_window_->Close();
+}
+
+void DefaultPlatformDisplay::OnClosed() {
+ if (delegate_)
+ delegate_->OnDisplayClosed();
+}
+
+void DefaultPlatformDisplay::OnWindowStateChanged(
+ ui::PlatformWindowState new_state) {}
+
+void DefaultPlatformDisplay::OnLostCapture() {
+ delegate_->OnNativeCaptureLost();
+}
+
+void DefaultPlatformDisplay::OnAcceleratedWidgetAvailable(
+ gfx::AcceleratedWidget widget,
+ float device_scale_factor) {
+ if (widget != gfx::kNullAcceleratedWidget) {
+ display_compositor_.reset(
+ new DisplayCompositor(base::ThreadTaskRunnerHandle::Get(), widget,
+ gpu_state_, surfaces_state_));
+ }
+ UpdateMetrics(metrics_.size_in_pixels, device_scale_factor);
+}
+
+void DefaultPlatformDisplay::OnAcceleratedWidgetDestroyed() {
+ NOTREACHED();
+}
+
+void DefaultPlatformDisplay::OnActivationChanged(bool active) {}
+
+void DefaultPlatformDisplay::RequestCopyOfOutput(
+ std::unique_ptr<cc::CopyOutputRequest> output_request) {
+ if (display_compositor_)
+ display_compositor_->RequestCopyOfOutput(std::move(output_request));
+}
+
+} // namespace ws
+
+} // namespace mus
diff --git a/chromium/components/mus/ws/platform_display.h b/chromium/components/mus/ws/platform_display.h
new file mode 100644
index 00000000000..e16a46c1936
--- /dev/null
+++ b/chromium/components/mus/ws/platform_display.h
@@ -0,0 +1,195 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_PLATFORM_DISPLAY_H_
+#define COMPONENTS_MUS_WS_PLATFORM_DISPLAY_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
+#include "base/timer/timer.h"
+#include "build/build_config.h"
+#include "cc/surfaces/surface.h"
+#include "components/mus/public/interfaces/window_manager.mojom.h"
+#include "components/mus/public/interfaces/window_manager_constants.mojom.h"
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "components/mus/ws/platform_display_delegate.h"
+#include "components/mus/ws/platform_display_init_params.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/platform_window/platform_window_delegate.h"
+
+namespace cc {
+class CompositorFrame;
+class CopyOutputRequest;
+class SurfaceIdAllocator;
+class SurfaceManager;
+} // namespace cc
+
+namespace gles2 {
+class GpuState;
+} // namespace gles2
+
+namespace shell {
+class Connector;
+} // namespace shell
+
+namespace ui {
+class CursorLoader;
+class PlatformWindow;
+struct TextInputState;
+} // namespace ui
+
+namespace mus {
+
+class GpuState;
+class SurfacesState;
+class DisplayCompositor;
+
+namespace ws {
+
+class EventDispatcher;
+class PlatformDisplayFactory;
+class ServerWindow;
+
+struct ViewportMetrics {
+ gfx::Size size_in_pixels;
+ float device_scale_factor = 0.f;
+};
+
+// PlatformDisplay is used to connect the root ServerWindow to a display.
+class PlatformDisplay {
+ public:
+ virtual ~PlatformDisplay() {}
+
+ static PlatformDisplay* Create(const PlatformDisplayInitParams& init_params);
+
+ virtual void Init(PlatformDisplayDelegate* delegate) = 0;
+
+ // Schedules a paint for the specified region in the coordinates of |window|.
+ virtual void SchedulePaint(const ServerWindow* window,
+ const gfx::Rect& bounds) = 0;
+
+ virtual void SetViewportSize(const gfx::Size& size) = 0;
+
+ virtual void SetTitle(const base::string16& title) = 0;
+
+ virtual void SetCapture() = 0;
+
+ virtual void ReleaseCapture() = 0;
+
+ virtual void SetCursorById(int32_t cursor) = 0;
+
+ virtual mojom::Rotation GetRotation() = 0;
+
+ virtual float GetDeviceScaleFactor() = 0;
+
+ virtual void UpdateTextInputState(const ui::TextInputState& state) = 0;
+ virtual void SetImeVisibility(bool visible) = 0;
+
+ // Returns true if a compositor frame has been submitted but not drawn yet.
+ virtual bool IsFramePending() const = 0;
+
+ virtual void RequestCopyOfOutput(
+ std::unique_ptr<cc::CopyOutputRequest> output_request) = 0;
+
+ virtual int64_t GetDisplayId() const = 0;
+
+ // Overrides factory for testing. Default (NULL) value indicates regular
+ // (non-test) environment.
+ static void set_factory_for_testing(PlatformDisplayFactory* factory) {
+ PlatformDisplay::factory_ = factory;
+ }
+
+ private:
+ // Static factory instance (always NULL for non-test).
+ static PlatformDisplayFactory* factory_;
+};
+
+// PlatformDisplay implementation that connects to the services necessary to
+// actually display.
+class DefaultPlatformDisplay : public PlatformDisplay,
+ public ui::PlatformWindowDelegate {
+ public:
+ explicit DefaultPlatformDisplay(const PlatformDisplayInitParams& init_params);
+ ~DefaultPlatformDisplay() override;
+
+ // PlatformDisplay:
+ void Init(PlatformDisplayDelegate* delegate) override;
+ void SchedulePaint(const ServerWindow* window,
+ const gfx::Rect& bounds) override;
+ void SetViewportSize(const gfx::Size& size) override;
+ void SetTitle(const base::string16& title) override;
+ void SetCapture() override;
+ void ReleaseCapture() override;
+ void SetCursorById(int32_t cursor) override;
+ float GetDeviceScaleFactor() override;
+ mojom::Rotation GetRotation() override;
+ void UpdateTextInputState(const ui::TextInputState& state) override;
+ void SetImeVisibility(bool visible) override;
+ bool IsFramePending() const override;
+ void RequestCopyOfOutput(
+ std::unique_ptr<cc::CopyOutputRequest> output_request) override;
+ int64_t GetDisplayId() const override;
+
+ private:
+ void WantToDraw();
+
+ // This method initiates a top level redraw of the display.
+ // TODO(fsamuel): This should use vblank as a signal rather than a timer
+ // http://crbug.com/533042
+ void Draw();
+
+ // This is called after cc::Display has completed generating a new frame
+ // for the display. TODO(fsamuel): Idle time processing should happen here
+ // if there is budget for it.
+ void DidDraw(cc::SurfaceDrawStatus status);
+ void UpdateMetrics(const gfx::Size& size, float device_scale_factor);
+ cc::CompositorFrame GenerateCompositorFrame();
+
+ // ui::PlatformWindowDelegate:
+ void OnBoundsChanged(const gfx::Rect& new_bounds) override;
+ void OnDamageRect(const gfx::Rect& damaged_region) override;
+ void DispatchEvent(ui::Event* event) override;
+ void OnCloseRequest() override;
+ void OnClosed() override;
+ void OnWindowStateChanged(ui::PlatformWindowState new_state) override;
+ void OnLostCapture() override;
+ void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget,
+ float device_scale_factor) override;
+ void OnAcceleratedWidgetDestroyed() override;
+ void OnActivationChanged(bool active) override;
+
+ int64_t display_id_;
+
+ scoped_refptr<GpuState> gpu_state_;
+ scoped_refptr<SurfacesState> surfaces_state_;
+ PlatformDisplayDelegate* delegate_;
+
+ ViewportMetrics metrics_;
+ gfx::Rect dirty_rect_;
+ base::Timer draw_timer_;
+ bool frame_pending_;
+
+ std::unique_ptr<DisplayCompositor> display_compositor_;
+ std::unique_ptr<ui::PlatformWindow> platform_window_;
+
+#if !defined(OS_ANDROID)
+ std::unique_ptr<ui::CursorLoader> cursor_loader_;
+#endif
+
+ base::WeakPtrFactory<DefaultPlatformDisplay> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DefaultPlatformDisplay);
+};
+
+} // namespace ws
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_PLATFORM_DISPLAY_H_
diff --git a/chromium/components/mus/ws/platform_display_delegate.h b/chromium/components/mus/ws/platform_display_delegate.h
new file mode 100644
index 00000000000..95a9c761dc4
--- /dev/null
+++ b/chromium/components/mus/ws/platform_display_delegate.h
@@ -0,0 +1,53 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_PLATFORM_DISPLAY_DELEGATE_H_
+#define COMPONENTS_MUS_WS_PLATFORM_DISPLAY_DELEGATE_H_
+
+#include "components/mus/ws/ids.h"
+
+namespace ui {
+class Event;
+}
+
+namespace mus {
+
+namespace ws {
+
+class ServerWindow;
+struct ViewportMetrics;
+
+// PlatformDisplayDelegate is implemented by an object that manages the
+// lifetime of a PlatformDisplay, forwards events to the appropriate windows,
+/// and responses to changes in viewport size.
+class PlatformDisplayDelegate {
+ public:
+ // Returns the root window of this display.
+ virtual ServerWindow* GetRootWindow() = 0;
+
+ // Called when the window managed by the PlatformDisplay is closed.
+ virtual void OnDisplayClosed() = 0;
+
+ // Called when an event arrives.
+ virtual void OnEvent(const ui::Event& event) = 0;
+
+ // Called when the Display loses capture.
+ virtual void OnNativeCaptureLost() = 0;
+
+ // Signals that the metrics of this display's viewport has changed.
+ virtual void OnViewportMetricsChanged(const ViewportMetrics& old_metrics,
+ const ViewportMetrics& new_metrics) = 0;
+
+ // Called when a compositor frame is finished drawing.
+ virtual void OnCompositorFrameDrawn() = 0;
+
+ protected:
+ virtual ~PlatformDisplayDelegate() {}
+};
+
+} // namespace ws
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_PLATFORM_DISPLAY_DELEGATE_H_
diff --git a/chromium/components/mus/ws/platform_display_factory.h b/chromium/components/mus/ws/platform_display_factory.h
new file mode 100644
index 00000000000..1cad7dd8331
--- /dev/null
+++ b/chromium/components/mus/ws/platform_display_factory.h
@@ -0,0 +1,23 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_PLATFORM_DISPLAY_FACTORY_H_
+#define COMPONENTS_MUS_WS_PLATFORM_DISPLAY_FACTORY_H_
+
+namespace mus {
+namespace ws {
+
+class PlatformDisplay;
+
+// Abstract factory for PlatformDisplays. Used by tests to construct test
+// PlatformDisplays.
+class PlatformDisplayFactory {
+ public:
+ virtual PlatformDisplay* CreatePlatformDisplay() = 0;
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_PLATFORM_DISPLAY_FACTORY_H_
diff --git a/chromium/components/mus/ws/platform_display_init_params.cc b/chromium/components/mus/ws/platform_display_init_params.cc
new file mode 100644
index 00000000000..394b5711637
--- /dev/null
+++ b/chromium/components/mus/ws/platform_display_init_params.cc
@@ -0,0 +1,21 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/platform_display_init_params.h"
+
+#include "components/mus/gles2/gpu_state.h"
+#include "components/mus/surfaces/surfaces_state.h"
+
+namespace mus {
+namespace ws {
+
+PlatformDisplayInitParams::PlatformDisplayInitParams()
+ : display_bounds(gfx::Rect(0, 0, 1024, 768)), display_id(1) {}
+
+PlatformDisplayInitParams::PlatformDisplayInitParams(
+ const PlatformDisplayInitParams& other) = default;
+PlatformDisplayInitParams::~PlatformDisplayInitParams() {}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/platform_display_init_params.h b/chromium/components/mus/ws/platform_display_init_params.h
new file mode 100644
index 00000000000..e596834f49b
--- /dev/null
+++ b/chromium/components/mus/ws/platform_display_init_params.h
@@ -0,0 +1,39 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_PLATFORM_DISPLAY_INIT_PARAMS_H_
+#define COMPONENTS_MUS_WS_PLATFORM_DISPLAY_INIT_PARAMS_H_
+
+#include <stdint.h>
+
+#include "base/memory/ref_counted.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace shell {
+class Connector;
+}
+
+namespace mus {
+
+class GpuState;
+class SurfacesState;
+
+namespace ws {
+
+struct PlatformDisplayInitParams {
+ PlatformDisplayInitParams();
+ PlatformDisplayInitParams(const PlatformDisplayInitParams& other);
+ ~PlatformDisplayInitParams();
+
+ scoped_refptr<GpuState> gpu_state;
+ scoped_refptr<SurfacesState> surfaces_state;
+
+ gfx::Rect display_bounds;
+ int64_t display_id;
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_PLATFORM_DISPLAY_INIT_PARAMS_H_
diff --git a/chromium/components/mus/ws/platform_screen.h b/chromium/components/mus/ws/platform_screen.h
new file mode 100644
index 00000000000..3215c28c257
--- /dev/null
+++ b/chromium/components/mus/ws/platform_screen.h
@@ -0,0 +1,43 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_PLATFORM_SCREEN_H_
+#define COMPONENTS_MUS_WS_PLATFORM_SCREEN_H_
+
+#include <stdint.h>
+
+#include "base/callback.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace mus {
+namespace ws {
+
+// PlatformScreen provides the necessary functionality to configure all
+// attached physical displays.
+class PlatformScreen {
+ public:
+ virtual ~PlatformScreen() {}
+
+ // Creates a PlatformScreen instance.
+ static std::unique_ptr<PlatformScreen> Create();
+
+ // Initializes platform specific screen resources.
+ virtual void Init() = 0;
+
+ using ConfiguredDisplayCallback =
+ base::Callback<void(int64_t, const gfx::Rect&)>;
+
+ // ConfigurePhysicalDisplay() configures a single physical display and returns
+ // its id and bounds for it via |callback|.
+ virtual void ConfigurePhysicalDisplay(
+ const ConfiguredDisplayCallback& callback) = 0;
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_PLATFORM_SCREEN_H_
diff --git a/chromium/components/mus/ws/platform_screen_impl.cc b/chromium/components/mus/ws/platform_screen_impl.cc
new file mode 100644
index 00000000000..e5ec99f660f
--- /dev/null
+++ b/chromium/components/mus/ws/platform_screen_impl.cc
@@ -0,0 +1,43 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/platform_screen_impl.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/memory/ptr_util.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace mus {
+
+namespace ws {
+namespace {
+
+void FixedSizeScreenConfiguration(
+ const PlatformScreen::ConfiguredDisplayCallback& callback) {
+ callback.Run(1, gfx::Rect(1024, 768));
+}
+
+} // namespace
+
+// static
+std::unique_ptr<PlatformScreen> PlatformScreen::Create() {
+ return base::WrapUnique(new PlatformScreenImpl);
+}
+
+PlatformScreenImpl::PlatformScreenImpl() {}
+
+PlatformScreenImpl::~PlatformScreenImpl() {}
+
+void PlatformScreenImpl::Init() {}
+
+void PlatformScreenImpl::ConfigurePhysicalDisplay(
+ const PlatformScreen::ConfiguredDisplayCallback& callback) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&FixedSizeScreenConfiguration, callback));
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/platform_screen_impl.h b/chromium/components/mus/ws/platform_screen_impl.h
new file mode 100644
index 00000000000..166bff771b4
--- /dev/null
+++ b/chromium/components/mus/ws/platform_screen_impl.h
@@ -0,0 +1,39 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_PLATFORM_SCREEN_IMPL_H_
+#define COMPONENTS_MUS_WS_PLATFORM_SCREEN_IMPL_H_
+
+#include <stdint.h>
+
+#include "base/callback.h"
+#include "components/mus/ws/platform_screen.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace mus {
+namespace ws {
+
+// PlatformScreenImpl provides the necessary functionality to configure all
+// attached physical displays on non-ozone platforms.
+class PlatformScreenImpl : public PlatformScreen {
+ public:
+ PlatformScreenImpl();
+ ~PlatformScreenImpl() override;
+
+ private:
+ // PlatformScreen.
+ void Init() override;
+ void ConfigurePhysicalDisplay(
+ const PlatformScreen::ConfiguredDisplayCallback& callback) override;
+
+ DISALLOW_COPY_AND_ASSIGN(PlatformScreenImpl);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_PLATFORM_SCREEN_IMPL_H_
diff --git a/chromium/components/mus/ws/platform_screen_impl_ozone.cc b/chromium/components/mus/ws/platform_screen_impl_ozone.cc
new file mode 100644
index 00000000000..91f2ba249da
--- /dev/null
+++ b/chromium/components/mus/ws/platform_screen_impl_ozone.cc
@@ -0,0 +1,126 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/platform_screen_impl_ozone.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/memory/ptr_util.h"
+#include "base/sys_info.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "ui/display/types/display_snapshot.h"
+#include "ui/display/types/native_display_delegate.h"
+#include "ui/ozone/public/ozone_platform.h"
+
+namespace mus {
+
+namespace ws {
+namespace {
+
+// TODO(rjkroege): Remove this code once ozone oxygen has the same
+// display creation semantics as ozone drm.
+// Some ozone platforms do not configure physical displays and so do not
+// callback into this class via the implementation of NativeDisplayObserver.
+// FixedSizeScreenConfiguration() short-circuits the implementation of display
+// configuration in this case by calling the |callback| provided to
+// ConfigurePhysicalDisplay() with a hard-coded |id| and |bounds|.
+void FixedSizeScreenConfiguration(
+ const PlatformScreen::ConfiguredDisplayCallback& callback) {
+ callback.Run(1, gfx::Rect(1024, 768));
+}
+
+void GetDisplaysFinished(const std::vector<ui::DisplaySnapshot*>& displays) {
+ // We don't really care about list of displays, we just want the snapshots
+ // held by DrmDisplayManager to be updated. This only only happens when we
+ // call NativeDisplayDelegate::GetDisplays(). Although, this would be a good
+ // place to have PlatformScreen cache the snapshots if need be.
+}
+
+} // namespace
+
+// static
+std::unique_ptr<PlatformScreen> PlatformScreen::Create() {
+ return base::WrapUnique(new PlatformScreenImplOzone);
+}
+
+PlatformScreenImplOzone::PlatformScreenImplOzone() : weak_ptr_factory_(this) {}
+
+PlatformScreenImplOzone::~PlatformScreenImplOzone() {}
+
+void PlatformScreenImplOzone::Init() {
+ native_display_delegate_ =
+ ui::OzonePlatform::GetInstance()->CreateNativeDisplayDelegate();
+ native_display_delegate_->AddObserver(this);
+ native_display_delegate_->Initialize();
+}
+
+void PlatformScreenImplOzone::ConfigurePhysicalDisplay(
+ const PlatformScreen::ConfiguredDisplayCallback& callback) {
+#if defined(OS_CHROMEOS)
+ if (base::SysInfo::IsRunningOnChromeOS()) {
+ // Kick off the configuration of the physical displays comprising the
+ // |PlatformScreenImplOzone|
+
+ DCHECK(native_display_delegate_) << "DefaultDisplayManager::"
+ "OnConfigurationChanged requires a "
+ "native_display_delegate_ to work.";
+
+ native_display_delegate_->GetDisplays(
+ base::Bind(&PlatformScreenImplOzone::OnDisplaysAquired,
+ weak_ptr_factory_.GetWeakPtr(), callback));
+
+ return;
+ }
+#endif // defined(OS_CHROMEOS)
+ // PostTask()ed to maximize control flow similarity with the ChromeOS case.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&FixedSizeScreenConfiguration, callback));
+}
+
+void PlatformScreenImplOzone::OnConfigurationChanged() {}
+
+// The display subsystem calls |OnDisplaysAquired| to deliver |displays|
+// describing the attached displays.
+void PlatformScreenImplOzone::OnDisplaysAquired(
+ const ConfiguredDisplayCallback& callback,
+ const std::vector<ui::DisplaySnapshot*>& displays) {
+ DCHECK(native_display_delegate_) << "DefaultDisplayManager::"
+ "OnConfigurationChanged requires a "
+ "native_display_delegate_ to work.";
+ CHECK(displays.size() == 1) << "Mus only supports one 1 display\n";
+ gfx::Point origin;
+ for (auto display : displays) {
+ if (!display->native_mode()) {
+ LOG(ERROR) << "Display " << display->display_id()
+ << " doesn't have a native mode";
+ continue;
+ }
+ // Setup each native display. This places a task on the DRM thread's
+ // runqueue that configures the window size correctly before the call to
+ // Configure.
+ native_display_delegate_->Configure(
+ *display, display->native_mode(), origin,
+ base::Bind(&PlatformScreenImplOzone::OnDisplayConfigured,
+ weak_ptr_factory_.GetWeakPtr(), callback,
+ display->display_id(),
+ gfx::Rect(origin, display->native_mode()->size())));
+ origin.Offset(display->native_mode()->size().width(), 0);
+ }
+}
+
+void PlatformScreenImplOzone::OnDisplayConfigured(
+ const ConfiguredDisplayCallback& callback,
+ int64_t id,
+ const gfx::Rect& bounds,
+ bool success) {
+ if (success) {
+ native_display_delegate_->GetDisplays(base::Bind(&GetDisplaysFinished));
+ callback.Run(id, bounds);
+ } else {
+ LOG(FATAL) << "Failed to configure display at " << bounds.ToString();
+ }
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/platform_screen_impl_ozone.h b/chromium/components/mus/ws/platform_screen_impl_ozone.h
new file mode 100644
index 00000000000..55949e4b45c
--- /dev/null
+++ b/chromium/components/mus/ws/platform_screen_impl_ozone.h
@@ -0,0 +1,72 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_PLATFORM_SCREEN_IMPL_OZONE_H_
+#define COMPONENTS_MUS_WS_PLATFORM_SCREEN_IMPL_OZONE_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "components/mus/ws/platform_screen.h"
+#include "ui/display/types/native_display_observer.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace ui {
+class NativeDisplayDelegate;
+class DisplaySnapshot;
+}
+
+namespace mus {
+namespace ws {
+
+// PlatformScreenImplOzone provides the necessary functionality to configure all
+// attached physical displays on the ozone platform.
+class PlatformScreenImplOzone : public PlatformScreen,
+ public ui::NativeDisplayObserver {
+ public:
+ PlatformScreenImplOzone();
+ ~PlatformScreenImplOzone() override;
+
+ private:
+ // PlatformScreen
+ void Init() override; // Must not be called until after the ozone platform is
+ // initialized.
+ void ConfigurePhysicalDisplay(
+ const ConfiguredDisplayCallback& callback) override;
+
+ // TODO(rjkroege): NativeDisplayObserver is misnamed as it tracks changes in
+ // the physical "Screen". Consider renaming it to NativeScreenObserver.
+ // ui::NativeDisplayObserver:
+ void OnConfigurationChanged() override;
+
+ // Display management callback.
+ void OnDisplaysAquired(const ConfiguredDisplayCallback& callback,
+ const std::vector<ui::DisplaySnapshot*>& displays);
+
+ // The display subsystem calls |OnDisplayConfigured| for each display that has
+ // been successfully configured. This in turn calls |callback_| with the
+ // identity and bounds of each physical display.
+ void OnDisplayConfigured(const ConfiguredDisplayCallback& callback,
+ int64_t id,
+ const gfx::Rect& bounds,
+ bool success);
+
+ std::unique_ptr<ui::NativeDisplayDelegate> native_display_delegate_;
+
+ base::WeakPtrFactory<PlatformScreenImplOzone> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PlatformScreenImplOzone);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_PLATFORM_SCREEN_IMPL_OZONE_H_
diff --git a/chromium/components/mus/ws/scheduled_animation_group.cc b/chromium/components/mus/ws/scheduled_animation_group.cc
new file mode 100644
index 00000000000..d76b7895597
--- /dev/null
+++ b/chromium/components/mus/ws/scheduled_animation_group.cc
@@ -0,0 +1,356 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/scheduled_animation_group.h"
+
+#include <set>
+
+#include "components/mus/ws/server_window.h"
+
+using mus::mojom::AnimationProperty;
+
+namespace mus {
+namespace ws {
+namespace {
+
+using Sequences = std::vector<ScheduledAnimationSequence>;
+
+// Gets the value of |property| from |window| into |value|.
+void GetValueFromWindow(const ServerWindow* window,
+ AnimationProperty property,
+ ScheduledAnimationValue* value) {
+ switch (property) {
+ case AnimationProperty::NONE:
+ NOTREACHED();
+ break;
+ case AnimationProperty::OPACITY:
+ value->float_value = window->opacity();
+ break;
+ case AnimationProperty::TRANSFORM:
+ value->transform = window->transform();
+ break;
+ }
+}
+
+// Sets the value of |property| from |value| into |window|.
+void SetWindowPropertyFromValue(ServerWindow* window,
+ AnimationProperty property,
+ const ScheduledAnimationValue& value) {
+ switch (property) {
+ case AnimationProperty::NONE:
+ break;
+ case AnimationProperty::OPACITY:
+ window->SetOpacity(value.float_value);
+ break;
+ case AnimationProperty::TRANSFORM:
+ window->SetTransform(value.transform);
+ break;
+ }
+}
+
+// Sets the value of |property| into |window| between two points.
+void SetWindowPropertyFromValueBetween(ServerWindow* window,
+ AnimationProperty property,
+ double value,
+ gfx::Tween::Type tween_type,
+ const ScheduledAnimationValue& start,
+ const ScheduledAnimationValue& target) {
+ const double tween_value = gfx::Tween::CalculateValue(tween_type, value);
+ switch (property) {
+ case AnimationProperty::NONE:
+ break;
+ case AnimationProperty::OPACITY:
+ window->SetOpacity(gfx::Tween::FloatValueBetween(
+ tween_value, start.float_value, target.float_value));
+ break;
+ case AnimationProperty::TRANSFORM:
+ window->SetTransform(gfx::Tween::TransformValueBetween(
+ tween_value, start.transform, target.transform));
+ break;
+ }
+}
+
+// TODO(mfomitchev): Use struct traits for this?
+gfx::Tween::Type AnimationTypeToTweenType(mojom::AnimationTweenType type) {
+ switch (type) {
+ case mojom::AnimationTweenType::LINEAR:
+ return gfx::Tween::LINEAR;
+ case mojom::AnimationTweenType::EASE_IN:
+ return gfx::Tween::EASE_IN;
+ case mojom::AnimationTweenType::EASE_OUT:
+ return gfx::Tween::EASE_OUT;
+ case mojom::AnimationTweenType::EASE_IN_OUT:
+ return gfx::Tween::EASE_IN_OUT;
+ }
+ return gfx::Tween::LINEAR;
+}
+
+void ConvertToScheduledValue(const mojom::AnimationValue& transport_value,
+ ScheduledAnimationValue* value) {
+ value->float_value = transport_value.float_value;
+ value->transform = transport_value.transform;
+}
+
+void ConvertToScheduledElement(const mojom::AnimationElement& transport_element,
+ ScheduledAnimationElement* element) {
+ element->property = transport_element.property;
+ element->duration =
+ base::TimeDelta::FromMicroseconds(transport_element.duration);
+ element->tween_type = AnimationTypeToTweenType(transport_element.tween_type);
+ if (transport_element.property != AnimationProperty::NONE) {
+ if (transport_element.start_value.get()) {
+ element->is_start_valid = true;
+ ConvertToScheduledValue(*transport_element.start_value,
+ &(element->start_value));
+ } else {
+ element->is_start_valid = false;
+ }
+ ConvertToScheduledValue(*transport_element.target_value,
+ &(element->target_value));
+ }
+}
+
+bool IsAnimationValueValid(AnimationProperty property,
+ const mojom::AnimationValue& value) {
+ bool result;
+ switch (property) {
+ case AnimationProperty::NONE:
+ NOTREACHED();
+ return false;
+ case AnimationProperty::OPACITY:
+ result = value.float_value >= 0.f && value.float_value <= 1.f;
+ DVLOG_IF(1, !result) << "Invalid AnimationValue for opacity: "
+ << value.float_value;
+ return result;
+ case AnimationProperty::TRANSFORM:
+ return true;
+ }
+ return false;
+}
+
+bool IsAnimationElementValid(const mojom::AnimationElement& element) {
+ if (element.property == AnimationProperty::NONE)
+ return true; // None is a pause and doesn't need any values.
+ if (element.start_value.get() &&
+ !IsAnimationValueValid(element.property, *element.start_value))
+ return false;
+ // For all other properties we require a target.
+ return element.target_value.get() &&
+ IsAnimationValueValid(element.property, *element.target_value);
+}
+
+bool IsAnimationSequenceValid(const mojom::AnimationSequence& sequence) {
+ if (sequence.elements.size() == 0) {
+ DVLOG(1) << "Empty or null AnimationSequence - invalid.";
+ return false;
+ }
+
+ for (size_t i = 0; i < sequence.elements.size(); ++i) {
+ if (!IsAnimationElementValid(*sequence.elements[i]))
+ return false;
+ }
+ return true;
+}
+
+bool IsAnimationGroupValid(const mojom::AnimationGroup& transport_group) {
+ if (transport_group.sequences.size() == 0) {
+ DVLOG(1) << "Empty or null AnimationGroup - invalid; window_id="
+ << transport_group.window_id;
+ return false;
+ }
+ for (size_t i = 0; i < transport_group.sequences.size(); ++i) {
+ if (!IsAnimationSequenceValid(*transport_group.sequences[i]))
+ return false;
+ }
+ return true;
+}
+
+// If the start value for |element| isn't valid, the value for the property
+// is obtained from |window| and placed into |element|.
+void GetStartValueFromWindowIfNecessary(const ServerWindow* window,
+ ScheduledAnimationElement* element) {
+ if (element->property != AnimationProperty::NONE &&
+ !element->is_start_valid) {
+ GetValueFromWindow(window, element->property, &(element->start_value));
+ }
+}
+
+void GetScheduledAnimationProperties(const Sequences& sequences,
+ std::set<AnimationProperty>* properties) {
+ for (const ScheduledAnimationSequence& sequence : sequences) {
+ for (const ScheduledAnimationElement& element : sequence.elements)
+ properties->insert(element.property);
+ }
+}
+
+// Set |window|'s specified |property| to the value resulting from running all
+// the |sequences|.
+void SetPropertyToTargetProperty(ServerWindow* window,
+ mojom::AnimationProperty property,
+ const Sequences& sequences) {
+ // NOTE: this doesn't deal with |cycle_count| quite right, but I'm honestly
+ // not sure we really want to support the same property in multiple sequences
+ // animating at once so I'm not dealing.
+ base::TimeDelta max_end_duration;
+ std::unique_ptr<ScheduledAnimationValue> value;
+ for (const ScheduledAnimationSequence& sequence : sequences) {
+ base::TimeDelta duration;
+ for (const ScheduledAnimationElement& element : sequence.elements) {
+ if (element.property != property)
+ continue;
+
+ duration += element.duration;
+ if (duration > max_end_duration) {
+ max_end_duration = duration;
+ value.reset(new ScheduledAnimationValue(element.target_value));
+ }
+ }
+ }
+ if (value.get())
+ SetWindowPropertyFromValue(window, property, *value);
+}
+
+void ConvertSequenceToScheduled(
+ const mojom::AnimationSequence& transport_sequence,
+ base::TimeTicks now,
+ ScheduledAnimationSequence* sequence) {
+ sequence->run_until_stopped = transport_sequence.cycle_count == 0u;
+ sequence->cycle_count = transport_sequence.cycle_count;
+ DCHECK_NE(0u, transport_sequence.elements.size());
+ sequence->elements.resize(transport_sequence.elements.size());
+
+ base::TimeTicks element_start_time = now;
+ for (size_t i = 0; i < transport_sequence.elements.size(); ++i) {
+ ConvertToScheduledElement(*(transport_sequence.elements[i].get()),
+ &(sequence->elements[i]));
+ sequence->elements[i].start_time = element_start_time;
+ sequence->duration += sequence->elements[i].duration;
+ element_start_time += sequence->elements[i].duration;
+ }
+}
+
+bool AdvanceSequence(ServerWindow* window,
+ ScheduledAnimationSequence* sequence,
+ base::TimeTicks now) {
+ ScheduledAnimationElement* element =
+ &(sequence->elements[sequence->current_index]);
+ while (element->start_time + element->duration < now) {
+ SetWindowPropertyFromValue(window, element->property,
+ element->target_value);
+ if (++sequence->current_index == sequence->elements.size()) {
+ if (!sequence->run_until_stopped && --sequence->cycle_count == 0) {
+ return false;
+ }
+
+ sequence->current_index = 0;
+ }
+ sequence->elements[sequence->current_index].start_time =
+ element->start_time + element->duration;
+ element = &(sequence->elements[sequence->current_index]);
+ GetStartValueFromWindowIfNecessary(window, element);
+
+ // It's possible for the delta between now and |last_tick_time_| to be very
+ // big (could happen if machine sleeps and is woken up much later). Normally
+ // the repeat count is smallish, so we don't bother optimizing it. OTOH if
+ // a sequence repeats forever we optimize it lest we get stuck in this loop
+ // for a very long time.
+ if (sequence->run_until_stopped && sequence->current_index == 0) {
+ element->start_time =
+ now - base::TimeDelta::FromMicroseconds(
+ (now - element->start_time).InMicroseconds() %
+ sequence->duration.InMicroseconds());
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+ScheduledAnimationValue::ScheduledAnimationValue() : float_value(0) {}
+ScheduledAnimationValue::~ScheduledAnimationValue() {}
+
+ScheduledAnimationElement::ScheduledAnimationElement()
+ : property(AnimationProperty::OPACITY),
+ tween_type(gfx::Tween::EASE_IN),
+ is_start_valid(false) {}
+ScheduledAnimationElement::ScheduledAnimationElement(
+ const ScheduledAnimationElement& other) = default;
+ScheduledAnimationElement::~ScheduledAnimationElement() {}
+
+ScheduledAnimationSequence::ScheduledAnimationSequence()
+ : run_until_stopped(false), cycle_count(0), current_index(0u) {}
+ScheduledAnimationSequence::ScheduledAnimationSequence(
+ const ScheduledAnimationSequence& other) = default;
+ScheduledAnimationSequence::~ScheduledAnimationSequence() {}
+
+ScheduledAnimationGroup::~ScheduledAnimationGroup() {}
+
+// static
+std::unique_ptr<ScheduledAnimationGroup> ScheduledAnimationGroup::Create(
+ ServerWindow* window,
+ base::TimeTicks now,
+ uint32_t id,
+ const mojom::AnimationGroup& transport_group) {
+ if (!IsAnimationGroupValid(transport_group))
+ return nullptr;
+
+ std::unique_ptr<ScheduledAnimationGroup> group(
+ new ScheduledAnimationGroup(window, id, now));
+ group->sequences_.resize(transport_group.sequences.size());
+ for (size_t i = 0; i < transport_group.sequences.size(); ++i) {
+ const mojom::AnimationSequence& transport_sequence(
+ *(transport_group.sequences[i]));
+ DCHECK_NE(0u, transport_sequence.elements.size());
+ ConvertSequenceToScheduled(transport_sequence, now, &group->sequences_[i]);
+ }
+ return group;
+}
+
+void ScheduledAnimationGroup::ObtainStartValues() {
+ for (ScheduledAnimationSequence& sequence : sequences_)
+ GetStartValueFromWindowIfNecessary(window_, &(sequence.elements[0]));
+}
+
+void ScheduledAnimationGroup::SetValuesToTargetValuesForPropertiesNotIn(
+ const ScheduledAnimationGroup& other) {
+ std::set<AnimationProperty> our_properties;
+ GetScheduledAnimationProperties(sequences_, &our_properties);
+
+ std::set<AnimationProperty> other_properties;
+ GetScheduledAnimationProperties(other.sequences_, &other_properties);
+
+ for (AnimationProperty property : our_properties) {
+ if (other_properties.count(property) == 0 &&
+ property != AnimationProperty::NONE) {
+ SetPropertyToTargetProperty(window_, property, sequences_);
+ }
+ }
+}
+
+bool ScheduledAnimationGroup::Tick(base::TimeTicks time) {
+ for (Sequences::iterator i = sequences_.begin(); i != sequences_.end();) {
+ if (!AdvanceSequence(window_, &(*i), time)) {
+ i = sequences_.erase(i);
+ continue;
+ }
+ const ScheduledAnimationElement& active_element(
+ i->elements[i->current_index]);
+ const double percent =
+ (time - active_element.start_time).InMillisecondsF() /
+ active_element.duration.InMillisecondsF();
+ SetWindowPropertyFromValueBetween(
+ window_, active_element.property, percent, active_element.tween_type,
+ active_element.start_value, active_element.target_value);
+ ++i;
+ }
+ return sequences_.empty();
+}
+
+ScheduledAnimationGroup::ScheduledAnimationGroup(ServerWindow* window,
+ uint32_t id,
+ base::TimeTicks time_scheduled)
+ : window_(window), id_(id), time_scheduled_(time_scheduled) {}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/scheduled_animation_group.h b/chromium/components/mus/ws/scheduled_animation_group.h
new file mode 100644
index 00000000000..2c19bcd2e09
--- /dev/null
+++ b/chromium/components/mus/ws/scheduled_animation_group.h
@@ -0,0 +1,116 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_SCHEDULED_ANIMATION_GROUP_H_
+#define COMPONENTS_MUS_WS_SCHEDULED_ANIMATION_GROUP_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/time/time.h"
+#include "components/mus/public/interfaces/animations.mojom.h"
+#include "ui/gfx/animation/tween.h"
+#include "ui/gfx/transform.h"
+
+namespace mus {
+namespace ws {
+
+class ServerWindow;
+
+struct ScheduledAnimationValue {
+ ScheduledAnimationValue();
+ ~ScheduledAnimationValue();
+
+ float float_value;
+ gfx::Transform transform;
+};
+
+struct ScheduledAnimationElement {
+ ScheduledAnimationElement();
+ // Needed because we call resize() vector of elements.
+ ScheduledAnimationElement(const ScheduledAnimationElement& other);
+ ~ScheduledAnimationElement();
+
+ mojom::AnimationProperty property;
+ base::TimeDelta duration;
+ gfx::Tween::Type tween_type;
+ bool is_start_valid;
+ ScheduledAnimationValue start_value;
+ ScheduledAnimationValue target_value;
+ // Start time is based on scheduled time and relative to any other elements
+ // in the sequence.
+ base::TimeTicks start_time;
+};
+
+struct ScheduledAnimationSequence {
+ ScheduledAnimationSequence();
+ // Needed because we call resize() and erase() on vector of sequences.
+ ScheduledAnimationSequence(const ScheduledAnimationSequence& other);
+ ~ScheduledAnimationSequence();
+
+ bool run_until_stopped;
+ std::vector<ScheduledAnimationElement> elements;
+
+ // Sum of the duration of all elements. This does not take into account
+ // |cycle_count|.
+ base::TimeDelta duration;
+
+ // The following values are updated as the animation progresses.
+
+ // Number of cycles remaining. This is only used if |run_until_stopped| is
+ // false.
+ uint32_t cycle_count;
+
+ // Index into |elements| of the element currently animating.
+ size_t current_index;
+};
+
+// Corresponds to a mojom::AnimationGroup and is responsible for running the
+// actual animation.
+class ScheduledAnimationGroup {
+ public:
+ ~ScheduledAnimationGroup();
+
+ // Returns a new ScheduledAnimationGroup from the supplied parameters, or
+ // null if |transport_group| isn't valid.
+ static std::unique_ptr<ScheduledAnimationGroup> Create(
+ ServerWindow* window,
+ base::TimeTicks now,
+ uint32_t id,
+ const mojom::AnimationGroup& transport_group);
+
+ uint32_t id() const { return id_; }
+
+ // Gets the start value for any elements that don't have an explicit start.
+ // value.
+ void ObtainStartValues();
+
+ // Sets the values of any properties that are not in |other| to their final
+ // value.
+ void SetValuesToTargetValuesForPropertiesNotIn(
+ const ScheduledAnimationGroup& other);
+
+ // Advances the group. |time| is the current time. Returns true if the group
+ // is done (nothing left to animate).
+ bool Tick(base::TimeTicks time);
+
+ ServerWindow* window() { return window_; }
+
+ private:
+ ScheduledAnimationGroup(ServerWindow* window,
+ uint32_t id,
+ base::TimeTicks time_scheduled);
+
+ ServerWindow* window_;
+ const uint32_t id_;
+ base::TimeTicks time_scheduled_;
+ std::vector<ScheduledAnimationSequence> sequences_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScheduledAnimationGroup);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_SCHEDULED_ANIMATION_GROUP_H_
diff --git a/chromium/components/mus/ws/scheduled_animation_group_unittest.cc b/chromium/components/mus/ws/scheduled_animation_group_unittest.cc
new file mode 100644
index 00000000000..27c2f76c9b7
--- /dev/null
+++ b/chromium/components/mus/ws/scheduled_animation_group_unittest.cc
@@ -0,0 +1,92 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/scheduled_animation_group.h"
+
+#include "components/mus/public/interfaces/animations.mojom.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/test_server_window_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using mus::mojom::AnimationProperty;
+using mus::mojom::AnimationTweenType;
+using mus::mojom::AnimationGroup;
+using mus::mojom::AnimationSequence;
+using mus::mojom::AnimationElement;
+using mus::mojom::AnimationValue;
+
+namespace mus {
+namespace ws {
+namespace {
+
+bool IsAnimationGroupValid(const AnimationGroup& transport_group) {
+ TestServerWindowDelegate window_delegate;
+ ServerWindow window(&window_delegate, WindowId());
+ std::unique_ptr<ScheduledAnimationGroup> group(
+ ScheduledAnimationGroup::Create(&window, base::TimeTicks::Now(), 1,
+ transport_group));
+ return group.get() != nullptr;
+}
+
+} // namespace
+
+TEST(ScheduledAnimationGroupTest, IsAnimationGroupValid) {
+ AnimationGroup group;
+
+ // AnimationGroup with no sequences is not valid.
+ EXPECT_FALSE(IsAnimationGroupValid(group));
+
+ group.sequences.push_back(AnimationSequence::New());
+
+ // Sequence with no elements is not valid.
+ EXPECT_FALSE(IsAnimationGroupValid(group));
+
+ AnimationSequence& sequence = *(group.sequences[0]);
+ sequence.elements.push_back(AnimationElement::New());
+ AnimationElement& element = *(sequence.elements[0]);
+ element.property = AnimationProperty::OPACITY;
+ element.tween_type = AnimationTweenType::LINEAR;
+
+ // Element with no target_value is not valid.
+ EXPECT_FALSE(IsAnimationGroupValid(group));
+
+ // Opacity must be between 0 and 1.
+ element.target_value = AnimationValue::New();
+ element.target_value->float_value = 2.5f;
+ EXPECT_FALSE(IsAnimationGroupValid(group));
+
+ element.target_value->float_value = .5f;
+ EXPECT_TRUE(IsAnimationGroupValid(group));
+
+ // Bogus start value.
+ element.start_value = AnimationValue::New();
+ element.start_value->float_value = 2.5f;
+ EXPECT_FALSE(IsAnimationGroupValid(group));
+
+ element.start_value->float_value = .5f;
+ EXPECT_TRUE(IsAnimationGroupValid(group));
+
+ // All transforms are valid.
+ element.property = AnimationProperty::TRANSFORM;
+ EXPECT_TRUE(IsAnimationGroupValid(group));
+
+ // Add another empty sequence, should be invalid again.
+ group.sequences.push_back(AnimationSequence::New());
+ EXPECT_FALSE(IsAnimationGroupValid(group));
+
+ AnimationSequence& sequence2 = *(group.sequences[1]);
+ sequence2.elements.push_back(AnimationElement::New());
+ AnimationElement& element2 = *(sequence2.elements[0]);
+ element2.property = AnimationProperty::OPACITY;
+ element2.tween_type = AnimationTweenType::LINEAR;
+
+ // Element with no target_value is not valid.
+ EXPECT_FALSE(IsAnimationGroupValid(group));
+
+ element2.property = AnimationProperty::NONE;
+ EXPECT_TRUE(IsAnimationGroupValid(group));
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/server_window.cc b/chromium/components/mus/ws/server_window.cc
new file mode 100644
index 00000000000..f6a9304a03b
--- /dev/null
+++ b/chromium/components/mus/ws/server_window.cc
@@ -0,0 +1,470 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/server_window.h"
+
+#include <inttypes.h>
+#include <stddef.h>
+
+#include "base/strings/stringprintf.h"
+#include "components/mus/common/transient_window_utils.h"
+#include "components/mus/public/interfaces/window_manager.mojom.h"
+#include "components/mus/ws/server_window_delegate.h"
+#include "components/mus/ws/server_window_observer.h"
+#include "components/mus/ws/server_window_surface_manager.h"
+
+namespace mus {
+
+namespace ws {
+
+ServerWindow::ServerWindow(ServerWindowDelegate* delegate, const WindowId& id)
+ : ServerWindow(delegate, id, Properties()) {}
+
+ServerWindow::ServerWindow(ServerWindowDelegate* delegate,
+ const WindowId& id,
+ const Properties& properties)
+ : delegate_(delegate),
+ id_(id),
+ parent_(nullptr),
+ stacking_target_(nullptr),
+ transient_parent_(nullptr),
+ is_modal_(false),
+ visible_(false),
+ cursor_id_(mojom::Cursor::CURSOR_NULL),
+ non_client_cursor_id_(mojom::Cursor::CURSOR_NULL),
+ opacity_(1),
+ can_focus_(true),
+ properties_(properties),
+ // Don't notify newly added observers during notification. This causes
+ // problems for code that adds an observer as part of an observer
+ // notification (such as ServerWindowDrawTracker).
+ observers_(
+ base::ObserverList<ServerWindowObserver>::NOTIFY_EXISTING_ONLY) {
+ DCHECK(delegate); // Must provide a delegate.
+}
+
+ServerWindow::~ServerWindow() {
+ FOR_EACH_OBSERVER(ServerWindowObserver, observers_, OnWindowDestroying(this));
+
+ if (transient_parent_)
+ transient_parent_->RemoveTransientWindow(this);
+
+ // Destroy transient children, only after we've removed ourselves from our
+ // parent, as destroying an active transient child may otherwise attempt to
+ // refocus us.
+ Windows transient_children(transient_children_);
+ STLDeleteElements(&transient_children);
+ DCHECK(transient_children_.empty());
+
+ while (!children_.empty())
+ children_.front()->parent()->Remove(children_.front());
+
+ if (parent_)
+ parent_->Remove(this);
+
+ FOR_EACH_OBSERVER(ServerWindowObserver, observers_, OnWindowDestroyed(this));
+}
+
+void ServerWindow::AddObserver(ServerWindowObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void ServerWindow::RemoveObserver(ServerWindowObserver* observer) {
+ DCHECK(observers_.HasObserver(observer));
+ observers_.RemoveObserver(observer);
+}
+
+void ServerWindow::CreateSurface(mojom::SurfaceType surface_type,
+ mojo::InterfaceRequest<mojom::Surface> request,
+ mojom::SurfaceClientPtr client) {
+ GetOrCreateSurfaceManager()->CreateSurface(surface_type, std::move(request),
+ std::move(client));
+}
+
+void ServerWindow::Add(ServerWindow* child) {
+ // We assume validation checks happened already.
+ DCHECK(child);
+ DCHECK(child != this);
+ DCHECK(!child->Contains(this));
+ if (child->parent() == this) {
+ if (children_.size() == 1)
+ return; // Already in the right position.
+ child->Reorder(children_.back(), mojom::OrderDirection::ABOVE);
+ return;
+ }
+
+ ServerWindow* old_parent = child->parent();
+ FOR_EACH_OBSERVER(ServerWindowObserver, child->observers_,
+ OnWillChangeWindowHierarchy(child, this, old_parent));
+
+ if (child->parent())
+ child->parent()->RemoveImpl(child);
+
+ child->parent_ = this;
+ children_.push_back(child);
+
+ // Stack the child properly if it is a transient child of a sibling.
+ if (child->transient_parent_ && child->transient_parent_->parent() == this)
+ RestackTransientDescendants(child->transient_parent_, &GetStackingTarget,
+ &ReorderImpl);
+
+ FOR_EACH_OBSERVER(ServerWindowObserver, child->observers_,
+ OnWindowHierarchyChanged(child, this, old_parent));
+}
+
+void ServerWindow::Remove(ServerWindow* child) {
+ // We assume validation checks happened else where.
+ DCHECK(child);
+ DCHECK(child != this);
+ DCHECK(child->parent() == this);
+
+ FOR_EACH_OBSERVER(ServerWindowObserver, child->observers_,
+ OnWillChangeWindowHierarchy(child, nullptr, this));
+ RemoveImpl(child);
+
+ // Stack the child properly if it is a transient child of a sibling.
+ if (child->transient_parent_ && child->transient_parent_->parent() == this)
+ RestackTransientDescendants(child->transient_parent_, &GetStackingTarget,
+ &ReorderImpl);
+
+ FOR_EACH_OBSERVER(ServerWindowObserver, child->observers_,
+ OnWindowHierarchyChanged(child, nullptr, this));
+}
+
+void ServerWindow::Reorder(ServerWindow* relative,
+ mojom::OrderDirection direction) {
+ ReorderImpl(this, relative, direction);
+}
+
+void ServerWindow::StackChildAtBottom(ServerWindow* child) {
+ // There's nothing to do if the child is already at the bottom.
+ if (children_.size() <= 1 || child == children_.front())
+ return;
+ child->Reorder(children_.front(), mojom::OrderDirection::BELOW);
+}
+
+void ServerWindow::StackChildAtTop(ServerWindow* child) {
+ // There's nothing to do if the child is already at the top.
+ if (children_.size() <= 1 || child == children_.back())
+ return;
+ child->Reorder(children_.back(), mojom::OrderDirection::ABOVE);
+}
+
+void ServerWindow::SetBounds(const gfx::Rect& bounds) {
+ if (bounds_ == bounds)
+ return;
+
+ // TODO(fsamuel): figure out how will this work with CompositorFrames.
+
+ const gfx::Rect old_bounds = bounds_;
+ bounds_ = bounds;
+ FOR_EACH_OBSERVER(ServerWindowObserver, observers_,
+ OnWindowBoundsChanged(this, old_bounds, bounds));
+}
+
+void ServerWindow::SetClientArea(
+ const gfx::Insets& insets,
+ const std::vector<gfx::Rect>& additional_client_areas) {
+ if (client_area_ == insets &&
+ additional_client_areas == additional_client_areas_) {
+ return;
+ }
+
+ additional_client_areas_ = additional_client_areas;
+ client_area_ = insets;
+ FOR_EACH_OBSERVER(
+ ServerWindowObserver, observers_,
+ OnWindowClientAreaChanged(this, insets, additional_client_areas));
+}
+
+void ServerWindow::SetHitTestMask(const gfx::Rect& mask) {
+ hit_test_mask_.reset(new gfx::Rect(mask));
+}
+
+void ServerWindow::ClearHitTestMask() {
+ hit_test_mask_.reset();
+}
+
+const ServerWindow* ServerWindow::GetRoot() const {
+ return delegate_->GetRootWindow(this);
+}
+
+std::vector<const ServerWindow*> ServerWindow::GetChildren() const {
+ std::vector<const ServerWindow*> children;
+ children.reserve(children_.size());
+ for (size_t i = 0; i < children_.size(); ++i)
+ children.push_back(children_[i]);
+ return children;
+}
+
+std::vector<ServerWindow*> ServerWindow::GetChildren() {
+ // TODO(sky): rename to children() and fix return type.
+ return children_;
+}
+
+ServerWindow* ServerWindow::GetChildWindow(const WindowId& window_id) {
+ if (id_ == window_id)
+ return this;
+
+ for (ServerWindow* child : children_) {
+ ServerWindow* window = child->GetChildWindow(window_id);
+ if (window)
+ return window;
+ }
+
+ return nullptr;
+}
+
+bool ServerWindow::AddTransientWindow(ServerWindow* child) {
+ // A system modal window cannot become a transient child.
+ if (child->is_modal() && !child->transient_parent())
+ return false;
+
+ if (child->transient_parent())
+ child->transient_parent()->RemoveTransientWindow(child);
+
+ DCHECK(std::find(transient_children_.begin(), transient_children_.end(),
+ child) == transient_children_.end());
+ transient_children_.push_back(child);
+ child->transient_parent_ = this;
+
+ // Restack |child| properly above its transient parent, if they share the same
+ // parent.
+ if (child->parent() == parent())
+ RestackTransientDescendants(this, &GetStackingTarget, &ReorderImpl);
+
+ FOR_EACH_OBSERVER(ServerWindowObserver, observers_,
+ OnTransientWindowAdded(this, child));
+ return true;
+}
+
+void ServerWindow::RemoveTransientWindow(ServerWindow* child) {
+ Windows::iterator i =
+ std::find(transient_children_.begin(), transient_children_.end(), child);
+ DCHECK(i != transient_children_.end());
+ transient_children_.erase(i);
+ DCHECK_EQ(this, child->transient_parent());
+ child->transient_parent_ = nullptr;
+
+ // If |child| and its former transient parent share the same parent, |child|
+ // should be restacked properly so it is not among transient children of its
+ // former parent, anymore.
+ if (parent() == child->parent())
+ RestackTransientDescendants(this, &GetStackingTarget, &ReorderImpl);
+
+ FOR_EACH_OBSERVER(ServerWindowObserver, observers_,
+ OnTransientWindowRemoved(this, child));
+}
+
+void ServerWindow::SetModal() {
+ is_modal_ = true;
+}
+
+bool ServerWindow::Contains(const ServerWindow* window) const {
+ for (const ServerWindow* parent = window; parent; parent = parent->parent_) {
+ if (parent == this)
+ return true;
+ }
+ return false;
+}
+
+void ServerWindow::SetVisible(bool value) {
+ if (visible_ == value)
+ return;
+
+ FOR_EACH_OBSERVER(ServerWindowObserver, observers_,
+ OnWillChangeWindowVisibility(this));
+ visible_ = value;
+ FOR_EACH_OBSERVER(ServerWindowObserver, observers_,
+ OnWindowVisibilityChanged(this));
+}
+
+void ServerWindow::SetOpacity(float value) {
+ if (value == opacity_)
+ return;
+ float old_opacity = opacity_;
+ opacity_ = value;
+ delegate_->OnScheduleWindowPaint(this);
+ FOR_EACH_OBSERVER(ServerWindowObserver, observers_,
+ OnWindowOpacityChanged(this, old_opacity, opacity_));
+}
+
+void ServerWindow::SetPredefinedCursor(mus::mojom::Cursor value) {
+ if (value == cursor_id_)
+ return;
+ cursor_id_ = value;
+ FOR_EACH_OBSERVER(
+ ServerWindowObserver, observers_,
+ OnWindowPredefinedCursorChanged(this, static_cast<int32_t>(value)));
+}
+
+void ServerWindow::SetNonClientCursor(mus::mojom::Cursor value) {
+ if (value == non_client_cursor_id_)
+ return;
+ non_client_cursor_id_ = value;
+ FOR_EACH_OBSERVER(
+ ServerWindowObserver, observers_,
+ OnWindowNonClientCursorChanged(this, static_cast<int32_t>(value)));
+}
+
+void ServerWindow::SetTransform(const gfx::Transform& transform) {
+ if (transform_ == transform)
+ return;
+
+ transform_ = transform;
+ delegate_->OnScheduleWindowPaint(this);
+}
+
+void ServerWindow::SetProperty(const std::string& name,
+ const std::vector<uint8_t>* value) {
+ auto it = properties_.find(name);
+ if (it != properties_.end()) {
+ if (value && it->second == *value)
+ return;
+ } else if (!value) {
+ // This property isn't set in |properties_| and |value| is nullptr, so
+ // there's
+ // no change.
+ return;
+ }
+
+ if (value) {
+ properties_[name] = *value;
+ } else if (it != properties_.end()) {
+ properties_.erase(it);
+ }
+
+ FOR_EACH_OBSERVER(ServerWindowObserver, observers_,
+ OnWindowSharedPropertyChanged(this, name, value));
+}
+
+std::string ServerWindow::GetName() const {
+ auto it = properties_.find(mojom::WindowManager::kName_Property);
+ if (it == properties_.end())
+ return std::string();
+ return std::string(it->second.begin(), it->second.end());
+}
+
+void ServerWindow::SetTextInputState(const ui::TextInputState& state) {
+ const bool changed = !(text_input_state_ == state);
+ if (changed) {
+ text_input_state_ = state;
+ // keyboard even if the state is not changed. So we have to notify
+ // |observers_|.
+ FOR_EACH_OBSERVER(ServerWindowObserver, observers_,
+ OnWindowTextInputStateChanged(this, state));
+ }
+}
+
+bool ServerWindow::IsDrawn() const {
+ const ServerWindow* root = delegate_->GetRootWindow(this);
+ if (!root || !root->visible())
+ return false;
+ const ServerWindow* window = this;
+ while (window && window != root && window->visible())
+ window = window->parent();
+ return root == window;
+}
+
+void ServerWindow::DestroySurfacesScheduledForDestruction() {
+ if (!surface_manager_)
+ return;
+ ServerWindowSurface* surface = surface_manager_->GetDefaultSurface();
+ if (surface)
+ surface->DestroySurfacesScheduledForDestruction();
+
+ surface = surface_manager_->GetUnderlaySurface();
+ if (surface)
+ surface->DestroySurfacesScheduledForDestruction();
+}
+
+ServerWindowSurfaceManager* ServerWindow::GetOrCreateSurfaceManager() {
+ if (!surface_manager_.get())
+ surface_manager_.reset(new ServerWindowSurfaceManager(this));
+ return surface_manager_.get();
+}
+
+void ServerWindow::SetUnderlayOffset(const gfx::Vector2d& offset) {
+ if (offset == underlay_offset_)
+ return;
+
+ underlay_offset_ = offset;
+ delegate_->OnScheduleWindowPaint(this);
+}
+
+#if !defined(NDEBUG)
+std::string ServerWindow::GetDebugWindowHierarchy() const {
+ std::string result;
+ BuildDebugInfo(std::string(), &result);
+ return result;
+}
+
+void ServerWindow::BuildDebugInfo(const std::string& depth,
+ std::string* result) const {
+ std::string name = GetName();
+ *result += base::StringPrintf(
+ "%sid=%d,%d visible=%s bounds=%d,%d %dx%d %s\n", depth.c_str(),
+ static_cast<int>(id_.client_id), static_cast<int>(id_.window_id),
+ visible_ ? "true" : "false", bounds_.x(), bounds_.y(), bounds_.width(),
+ bounds_.height(), !name.empty() ? name.c_str() : "(no name)");
+ for (const ServerWindow* child : children_)
+ child->BuildDebugInfo(depth + " ", result);
+}
+#endif
+
+void ServerWindow::RemoveImpl(ServerWindow* window) {
+ window->parent_ = nullptr;
+ children_.erase(std::find(children_.begin(), children_.end(), window));
+}
+
+void ServerWindow::OnStackingChanged() {
+ if (stacking_target_) {
+ Windows::const_iterator window_i = std::find(
+ parent()->children().begin(), parent()->children().end(), this);
+ DCHECK(window_i != parent()->children().end());
+ if (window_i != parent()->children().begin() &&
+ (*(window_i - 1) == stacking_target_)) {
+ return;
+ }
+ }
+ RestackTransientDescendants(this, &GetStackingTarget, &ReorderImpl);
+}
+
+// static
+void ServerWindow::ReorderImpl(ServerWindow* window,
+ ServerWindow* relative,
+ mojom::OrderDirection direction) {
+ DCHECK(relative);
+ DCHECK_NE(window, relative);
+ DCHECK_EQ(window->parent(), relative->parent());
+
+ if (!AdjustStackingForTransientWindows(&window, &relative, &direction,
+ window->stacking_target_))
+ return;
+
+ window->parent_->children_.erase(std::find(window->parent_->children_.begin(),
+ window->parent_->children_.end(),
+ window));
+ Windows::iterator i = std::find(window->parent_->children_.begin(),
+ window->parent_->children_.end(), relative);
+ if (direction == mojom::OrderDirection::ABOVE) {
+ DCHECK(i != window->parent_->children_.end());
+ window->parent_->children_.insert(++i, window);
+ } else if (direction == mojom::OrderDirection::BELOW) {
+ DCHECK(i != window->parent_->children_.end());
+ window->parent_->children_.insert(i, window);
+ }
+ FOR_EACH_OBSERVER(ServerWindowObserver, window->observers_,
+ OnWindowReordered(window, relative, direction));
+ window->OnStackingChanged();
+}
+
+// static
+ServerWindow** ServerWindow::GetStackingTarget(ServerWindow* window) {
+ return &window->stacking_target_;
+}
+
+} // namespace ws
+
+} // namespace mus
diff --git a/chromium/components/mus/ws/server_window.h b/chromium/components/mus/ws/server_window.h
new file mode 100644
index 00000000000..75f4f081406
--- /dev/null
+++ b/chromium/components/mus/ws/server_window.h
@@ -0,0 +1,249 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_SERVER_WINDOW_H_
+#define COMPONENTS_MUS_WS_SERVER_WINDOW_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "components/mus/public/interfaces/surface.mojom.h"
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "components/mus/ws/ids.h"
+#include "components/mus/ws/server_window_surface.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/vector2d.h"
+#include "ui/gfx/transform.h"
+#include "ui/platform_window/text_input_state.h"
+
+namespace mus {
+namespace ws {
+
+class ServerWindowDelegate;
+class ServerWindowObserver;
+class ServerWindowSurfaceManager;
+
+// Server side representation of a window. Delegate is informed of interesting
+// events.
+//
+// It is assumed that all functions that mutate the tree have validated the
+// mutation is possible before hand. For example, Reorder() assumes the supplied
+// window is a child and not already in position.
+//
+// ServerWindows do not own their children. If you delete a window that has
+// children the children are implicitly removed. Similarly if a window has a
+// parent and the window is deleted the deleted window is implicitly removed
+// from the parent.
+class ServerWindow {
+ public:
+ using Properties = std::map<std::string, std::vector<uint8_t>>;
+ using Windows = std::vector<ServerWindow*>;
+
+ ServerWindow(ServerWindowDelegate* delegate, const WindowId& id);
+ ServerWindow(ServerWindowDelegate* delegate,
+ const WindowId& id,
+ const Properties& properties);
+ ~ServerWindow();
+
+ void AddObserver(ServerWindowObserver* observer);
+ void RemoveObserver(ServerWindowObserver* observer);
+
+ // Creates a new surface of the specified type, replacing the existing.
+ void CreateSurface(mojom::SurfaceType surface_type,
+ mojo::InterfaceRequest<mojom::Surface> request,
+ mojom::SurfaceClientPtr client);
+
+ const WindowId& id() const { return id_; }
+
+ void Add(ServerWindow* child);
+ void Remove(ServerWindow* child);
+ void Reorder(ServerWindow* relative, mojom::OrderDirection diretion);
+ void StackChildAtBottom(ServerWindow* child);
+ void StackChildAtTop(ServerWindow* child);
+
+ const gfx::Rect& bounds() const { return bounds_; }
+ // Sets the bounds. If the size changes this implicitly resets the client
+ // area to fill the whole bounds.
+ void SetBounds(const gfx::Rect& bounds);
+
+ const std::vector<gfx::Rect>& additional_client_areas() const {
+ return additional_client_areas_;
+ }
+ const gfx::Insets& client_area() const { return client_area_; }
+ void SetClientArea(const gfx::Insets& insets,
+ const std::vector<gfx::Rect>& additional_client_areas);
+
+ const gfx::Rect* hit_test_mask() const { return hit_test_mask_.get(); }
+ void SetHitTestMask(const gfx::Rect& mask);
+ void ClearHitTestMask();
+
+ int32_t cursor() const { return static_cast<int32_t>(cursor_id_); }
+ int32_t non_client_cursor() const {
+ return static_cast<int32_t>(non_client_cursor_id_);
+ }
+
+ const ServerWindow* parent() const { return parent_; }
+ ServerWindow* parent() { return parent_; }
+
+ const ServerWindow* GetRoot() const;
+ ServerWindow* GetRoot() {
+ return const_cast<ServerWindow*>(
+ const_cast<const ServerWindow*>(this)->GetRoot());
+ }
+
+ std::vector<const ServerWindow*> GetChildren() const;
+ std::vector<ServerWindow*> GetChildren();
+ const Windows& children() const { return children_; }
+
+ // Returns the ServerWindow object with the provided |id| if it lies in a
+ // subtree of |this|.
+ ServerWindow* GetChildWindow(const WindowId& id);
+
+ // Transient window management.
+ // Adding transient child fails if the child window is modal to system.
+ bool AddTransientWindow(ServerWindow* child);
+ void RemoveTransientWindow(ServerWindow* child);
+
+ ServerWindow* transient_parent() { return transient_parent_; }
+ const ServerWindow* transient_parent() const { return transient_parent_; }
+
+ const Windows& transient_children() const { return transient_children_; }
+
+ bool is_modal() const { return is_modal_; }
+ void SetModal();
+
+ // Returns true if this contains |window| or is |window|.
+ bool Contains(const ServerWindow* window) const;
+
+ // Returns the visibility requested by this window. IsDrawn() returns whether
+ // the window is actually visible on screen.
+ bool visible() const { return visible_; }
+ void SetVisible(bool value);
+
+ float opacity() const { return opacity_; }
+ void SetOpacity(float value);
+
+ void SetPredefinedCursor(mus::mojom::Cursor cursor_id);
+ void SetNonClientCursor(mus::mojom::Cursor cursor_id);
+
+ const gfx::Transform& transform() const { return transform_; }
+ void SetTransform(const gfx::Transform& transform);
+
+ const std::map<std::string, std::vector<uint8_t>>& properties() const {
+ return properties_;
+ }
+ void SetProperty(const std::string& name, const std::vector<uint8_t>* value);
+
+ std::string GetName() const;
+
+ void SetTextInputState(const ui::TextInputState& state);
+ const ui::TextInputState& text_input_state() const {
+ return text_input_state_;
+ }
+
+ void set_can_focus(bool can_focus) { can_focus_ = can_focus; }
+ bool can_focus() const { return can_focus_; }
+
+ // Returns true if this window is attached to a root and all ancestors are
+ // visible.
+ bool IsDrawn() const;
+
+ // Called when its appropriate to destroy surfaces scheduled for destruction.
+ void DestroySurfacesScheduledForDestruction();
+
+ const gfx::Insets& extended_hit_test_region() const {
+ return extended_hit_test_region_;
+ }
+ void set_extended_hit_test_region(const gfx::Insets& insets) {
+ extended_hit_test_region_ = insets;
+ }
+
+ ServerWindowSurfaceManager* GetOrCreateSurfaceManager();
+ ServerWindowSurfaceManager* surface_manager() {
+ return surface_manager_.get();
+ }
+ const ServerWindowSurfaceManager* surface_manager() const {
+ return surface_manager_.get();
+ }
+
+ // Offset of the underlay from the the window bounds (used for shadows).
+ const gfx::Vector2d& underlay_offset() const { return underlay_offset_; }
+ void SetUnderlayOffset(const gfx::Vector2d& offset);
+
+ ServerWindowDelegate* delegate() { return delegate_; }
+
+#if !defined(NDEBUG)
+ std::string GetDebugWindowHierarchy() const;
+ void BuildDebugInfo(const std::string& depth, std::string* result) const;
+#endif
+
+ private:
+ // Implementation of removing a window. Doesn't send any notification.
+ void RemoveImpl(ServerWindow* window);
+
+ // Called when this window's stacking order among its siblings is changed.
+ void OnStackingChanged();
+
+ static void ReorderImpl(ServerWindow* window,
+ ServerWindow* relative,
+ mojom::OrderDirection diretion);
+
+ // Returns a pointer to the stacking target that can be used by
+ // RestackTransientDescendants.
+ static ServerWindow** GetStackingTarget(ServerWindow* window);
+
+ ServerWindowDelegate* delegate_;
+ const WindowId id_;
+ ServerWindow* parent_;
+ Windows children_;
+
+ // Transient window management.
+ // If non-null we're actively restacking transient as the result of a
+ // transient ancestor changing.
+ ServerWindow* stacking_target_;
+ ServerWindow* transient_parent_;
+ Windows transient_children_;
+
+ bool is_modal_;
+ bool visible_;
+ gfx::Rect bounds_;
+ gfx::Insets client_area_;
+ std::vector<gfx::Rect> additional_client_areas_;
+ std::unique_ptr<ServerWindowSurfaceManager> surface_manager_;
+ mojom::Cursor cursor_id_;
+ mojom::Cursor non_client_cursor_id_;
+ float opacity_;
+ bool can_focus_;
+ gfx::Transform transform_;
+ ui::TextInputState text_input_state_;
+
+ Properties properties_;
+
+ gfx::Vector2d underlay_offset_;
+
+ // The hit test for windows extends outside the bounds of the window by this
+ // amount.
+ gfx::Insets extended_hit_test_region_;
+
+ // Mouse events outside the hit test mask don't hit the window. An empty mask
+ // means all events miss the window. If null there is no mask.
+ std::unique_ptr<gfx::Rect> hit_test_mask_;
+
+ base::ObserverList<ServerWindowObserver> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServerWindow);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_SERVER_WINDOW_H_
diff --git a/chromium/components/mus/ws/server_window_delegate.h b/chromium/components/mus/ws/server_window_delegate.h
new file mode 100644
index 00000000000..24d6bbd0daa
--- /dev/null
+++ b/chromium/components/mus/ws/server_window_delegate.h
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_SERVER_WINDOW_DELEGATE_H_
+#define COMPONENTS_MUS_WS_SERVER_WINDOW_DELEGATE_H_
+
+#include <memory>
+
+#include "components/mus/public/interfaces/mus_constants.mojom.h"
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+
+namespace mus {
+
+class SurfacesState;
+
+namespace ws {
+
+struct ClientWindowId;
+class ServerWindow;
+struct WindowId;
+
+class ServerWindowDelegate {
+ public:
+ virtual SurfacesState* GetSurfacesState() = 0;
+
+ virtual void OnScheduleWindowPaint(ServerWindow* window) = 0;
+
+ // Returns the root of the window tree to which this |window| is attached.
+ // Returns null if this window is not attached up through to a root window.
+ virtual const ServerWindow* GetRootWindow(
+ const ServerWindow* window) const = 0;
+
+ // Schedules a callback to DestroySurfacesScheduledForDestruction() at the
+ // appropriate time, which may be synchronously.
+ virtual void ScheduleSurfaceDestruction(ServerWindow* window) = 0;
+
+ protected:
+ virtual ~ServerWindowDelegate() {}
+};
+
+} // namespace ws
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_SERVER_WINDOW_DELEGATE_H_
diff --git a/chromium/components/mus/ws/server_window_drawn_tracker.cc b/chromium/components/mus/ws/server_window_drawn_tracker.cc
new file mode 100644
index 00000000000..cdbca8ccddf
--- /dev/null
+++ b/chromium/components/mus/ws/server_window_drawn_tracker.cc
@@ -0,0 +1,136 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/server_window_drawn_tracker.h"
+
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/server_window_drawn_tracker_observer.h"
+
+namespace mus {
+
+namespace ws {
+
+ServerWindowDrawnTracker::ServerWindowDrawnTracker(
+ ServerWindow* window,
+ ServerWindowDrawnTrackerObserver* observer)
+ : window_(window), observer_(observer), drawn_(window->IsDrawn()) {
+ AddObservers();
+}
+
+ServerWindowDrawnTracker::~ServerWindowDrawnTracker() {
+ RemoveObservers();
+}
+
+void ServerWindowDrawnTracker::SetDrawn(ServerWindow* ancestor, bool drawn) {
+ // If |windows_| is empty when this code runs, that means |window_| has been
+ // destroyed. So set |window_| to nullptr, but make sure the right value is
+ // sent to OnDrawnStateChanged().
+ ServerWindow* window = window_;
+ if (windows_.empty())
+ window_ = nullptr;
+
+ if (drawn == drawn_)
+ return;
+
+ drawn_ = drawn;
+ observer_->OnDrawnStateChanged(ancestor, window, drawn);
+}
+
+void ServerWindowDrawnTracker::AddObservers() {
+ if (!window_)
+ return;
+
+ for (ServerWindow* v = window_; v; v = v->parent()) {
+ v->AddObserver(this);
+ windows_.insert(v);
+ }
+}
+
+void ServerWindowDrawnTracker::RemoveObservers() {
+ for (ServerWindow* window : windows_)
+ window->RemoveObserver(this);
+
+ windows_.clear();
+}
+
+void ServerWindowDrawnTracker::OnWindowDestroying(ServerWindow* window) {
+ if (!drawn_)
+ return;
+ observer_->OnDrawnStateWillChange(window->parent(), window_, false);
+}
+
+void ServerWindowDrawnTracker::OnWindowDestroyed(ServerWindow* window) {
+ // As windows are removed before being destroyed, resulting in
+ // OnWindowHierarchyChanged() and us removing ourself as an observer, the only
+ // window we should ever get notified of destruction on is |window_|.
+ DCHECK_EQ(window, window_);
+ RemoveObservers();
+ SetDrawn(nullptr, false);
+}
+
+void ServerWindowDrawnTracker::OnWillChangeWindowHierarchy(
+ ServerWindow* window,
+ ServerWindow* new_parent,
+ ServerWindow* old_parent) {
+ bool new_is_drawn = new_parent && new_parent->IsDrawn();
+ if (new_is_drawn) {
+ for (ServerWindow* w = window_; new_is_drawn && w != old_parent;
+ w = w->parent()) {
+ new_is_drawn = w->visible();
+ }
+ }
+ if (drawn_ != new_is_drawn) {
+ observer_->OnDrawnStateWillChange(new_is_drawn ? nullptr : old_parent,
+ window_, new_is_drawn);
+ }
+}
+
+void ServerWindowDrawnTracker::OnWindowHierarchyChanged(
+ ServerWindow* window,
+ ServerWindow* new_parent,
+ ServerWindow* old_parent) {
+ RemoveObservers();
+ AddObservers();
+ const bool is_drawn = window_->IsDrawn();
+ SetDrawn(is_drawn ? nullptr : old_parent, is_drawn);
+}
+
+void ServerWindowDrawnTracker::OnWillChangeWindowVisibility(
+ ServerWindow* window) {
+ bool will_change = false;
+ if (drawn_) {
+ // If |window_| is currently drawn, then any change of visibility of the
+ // windows will toggle the drawn status.
+ will_change = true;
+ } else {
+ // If |window| is currently visible, then it's becoming invisible, and so
+ // |window_| will remain not drawn.
+ if (window->visible()) {
+ will_change = false;
+ } else {
+ bool is_drawn = (window->GetRoot() == window) ||
+ (window->parent() && window->parent()->IsDrawn());
+ if (is_drawn) {
+ for (ServerWindow* w = window_; is_drawn && w != window;
+ w = w->parent())
+ is_drawn = w->visible();
+ }
+ will_change = drawn_ != is_drawn;
+ }
+ }
+ if (will_change) {
+ bool new_is_drawn = !drawn_;
+ observer_->OnDrawnStateWillChange(new_is_drawn ? nullptr : window->parent(),
+ window_, new_is_drawn);
+ }
+}
+
+void ServerWindowDrawnTracker::OnWindowVisibilityChanged(ServerWindow* window) {
+ const bool is_drawn = window_->IsDrawn();
+ SetDrawn(is_drawn ? nullptr : window->parent(), is_drawn);
+}
+
+} // namespace ws
+
+} // namespace mus
diff --git a/chromium/components/mus/ws/server_window_drawn_tracker.h b/chromium/components/mus/ws/server_window_drawn_tracker.h
new file mode 100644
index 00000000000..36f2de27590
--- /dev/null
+++ b/chromium/components/mus/ws/server_window_drawn_tracker.h
@@ -0,0 +1,65 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_SERVER_WINDOW_DRAWN_TRACKER_H_
+#define COMPONENTS_MUS_WS_SERVER_WINDOW_DRAWN_TRACKER_H_
+
+#include <set>
+
+#include "base/macros.h"
+#include "components/mus/ws/server_window_observer.h"
+
+namespace mus {
+
+namespace ws {
+
+class ServerWindowDrawnTrackerObserver;
+
+// ServerWindowDrawnTracker notifies its observer any time the drawn state of
+// the supplied window changes.
+//
+// NOTE: you must ensure this class is destroyed before the root.
+class ServerWindowDrawnTracker : public ServerWindowObserver {
+ public:
+ ServerWindowDrawnTracker(ServerWindow* window,
+ ServerWindowDrawnTrackerObserver* observer);
+ ~ServerWindowDrawnTracker() override;
+
+ ServerWindow* window() { return window_; }
+
+ private:
+ void SetDrawn(ServerWindow* ancestor, bool drawn);
+
+ // Adds |this| as an observer to |window_| and its ancestors.
+ void AddObservers();
+
+ // Stops observerving any windows we added as an observer in AddObservers().
+ void RemoveObservers();
+
+ // ServerWindowObserver:
+ void OnWindowDestroying(ServerWindow* window) override;
+ void OnWindowDestroyed(ServerWindow* window) override;
+ void OnWillChangeWindowHierarchy(ServerWindow* window,
+ ServerWindow* new_parent,
+ ServerWindow* old_parent) override;
+ void OnWindowHierarchyChanged(ServerWindow* window,
+ ServerWindow* new_parent,
+ ServerWindow* old_parent) override;
+ void OnWillChangeWindowVisibility(ServerWindow* window) override;
+ void OnWindowVisibilityChanged(ServerWindow* window) override;
+
+ ServerWindow* window_;
+ ServerWindowDrawnTrackerObserver* observer_;
+ bool drawn_;
+ // Set of windows we're observing. This is |window_| and all its ancestors.
+ std::set<ServerWindow*> windows_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServerWindowDrawnTracker);
+};
+
+} // namespace ws
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_SERVER_WINDOW_DRAWN_TRACKER_H_
diff --git a/chromium/components/mus/ws/server_window_drawn_tracker_observer.h b/chromium/components/mus/ws/server_window_drawn_tracker_observer.h
new file mode 100644
index 00000000000..55f44cbbbc5
--- /dev/null
+++ b/chromium/components/mus/ws/server_window_drawn_tracker_observer.h
@@ -0,0 +1,41 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_SERVER_WINDOW_DRAWN_TRACKER_OBSERVER_H_
+#define COMPONENTS_MUS_WS_SERVER_WINDOW_DRAWN_TRACKER_OBSERVER_H_
+
+namespace mus {
+
+namespace ws {
+
+class ServerWindow;
+
+class ServerWindowDrawnTrackerObserver {
+ public:
+ // Invoked right before the drawn state changes. If |is_drawn| is false,
+ // |ancestor| identifies where the change will occur. In the case of a remove,
+ // |ancestor| is the parent of the window that will be removed (causing the
+ // drawn state to change). In the case of visibility change, |ancestor| is the
+ // parent of the window whose visibility will change.
+ virtual void OnDrawnStateWillChange(ServerWindow* ancestor,
+ ServerWindow* window,
+ bool is_drawn) {}
+
+ // Invoked when the drawn state changes. If |is_drawn| is false |ancestor|
+ // identifies where the change occurred. In the case of a remove |ancestor| is
+ // the parent of the window that was removed. In the case of a visibility
+ // change |ancestor| is the parent of the window whose visibility changed.
+ virtual void OnDrawnStateChanged(ServerWindow* ancestor,
+ ServerWindow* window,
+ bool is_drawn) {}
+
+ protected:
+ virtual ~ServerWindowDrawnTrackerObserver() {}
+};
+
+} // namespace ws
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_SERVER_WINDOW_DRAWN_TRACKER_OBSERVER_H_
diff --git a/chromium/components/mus/ws/server_window_drawn_tracker_unittest.cc b/chromium/components/mus/ws/server_window_drawn_tracker_unittest.cc
new file mode 100644
index 00000000000..53ffa39f7b1
--- /dev/null
+++ b/chromium/components/mus/ws/server_window_drawn_tracker_unittest.cc
@@ -0,0 +1,235 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/server_window_drawn_tracker.h"
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/server_window_drawn_tracker_observer.h"
+#include "components/mus/ws/test_server_window_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mus {
+
+namespace ws {
+namespace {
+
+class TestServerWindowDrawnTrackerObserver
+ : public ServerWindowDrawnTrackerObserver {
+ public:
+ TestServerWindowDrawnTrackerObserver()
+ : change_count_(0u),
+ ancestor_(nullptr),
+ window_(nullptr),
+ is_drawn_(false) {}
+
+ void clear_change_count() { change_count_ = 0u; }
+ size_t change_count() const { return change_count_; }
+ const ServerWindow* ancestor() const { return ancestor_; }
+ const ServerWindow* window() const { return window_; }
+ bool is_drawn() const { return is_drawn_; }
+
+ private:
+ // ServerWindowDrawnTrackerObserver:
+ void OnDrawnStateWillChange(ServerWindow* ancestor,
+ ServerWindow* window,
+ bool is_drawn) override {
+ change_count_++;
+ ancestor_ = ancestor;
+ window_ = window;
+ is_drawn_ = is_drawn;
+ }
+
+ void OnDrawnStateChanged(ServerWindow* ancestor,
+ ServerWindow* window,
+ bool is_drawn) override {
+ EXPECT_EQ(ancestor_, ancestor);
+ EXPECT_EQ(window_, window);
+ EXPECT_EQ(is_drawn_, is_drawn);
+ }
+
+ size_t change_count_;
+ const ServerWindow* ancestor_;
+ const ServerWindow* window_;
+ bool is_drawn_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestServerWindowDrawnTrackerObserver);
+};
+
+} // namespace
+
+TEST(ServerWindowDrawnTrackerTest, ChangeBecauseOfDeletionAndVisibility) {
+ TestServerWindowDelegate server_window_delegate;
+ std::unique_ptr<ServerWindow> window(
+ new ServerWindow(&server_window_delegate, WindowId()));
+ server_window_delegate.set_root_window(window.get());
+ TestServerWindowDrawnTrackerObserver drawn_observer;
+ ServerWindowDrawnTracker tracker(window.get(), &drawn_observer);
+ window->SetVisible(true);
+ EXPECT_EQ(1u, drawn_observer.change_count());
+ EXPECT_EQ(window.get(), drawn_observer.window());
+ EXPECT_EQ(nullptr, drawn_observer.ancestor());
+ EXPECT_TRUE(drawn_observer.is_drawn());
+ drawn_observer.clear_change_count();
+
+ window->SetVisible(false);
+ EXPECT_EQ(1u, drawn_observer.change_count());
+ EXPECT_EQ(window.get(), drawn_observer.window());
+ EXPECT_EQ(nullptr, drawn_observer.ancestor());
+ EXPECT_FALSE(drawn_observer.is_drawn());
+ drawn_observer.clear_change_count();
+
+ window->SetVisible(true);
+ EXPECT_EQ(1u, drawn_observer.change_count());
+ EXPECT_EQ(window.get(), drawn_observer.window());
+ EXPECT_EQ(nullptr, drawn_observer.ancestor());
+ EXPECT_TRUE(drawn_observer.is_drawn());
+ drawn_observer.clear_change_count();
+
+ ServerWindow* old_window = window.get();
+ window.reset();
+ EXPECT_EQ(1u, drawn_observer.change_count());
+ EXPECT_EQ(old_window, drawn_observer.window());
+ EXPECT_EQ(nullptr, drawn_observer.ancestor());
+ EXPECT_FALSE(drawn_observer.is_drawn());
+}
+
+TEST(ServerWindowDrawnTrackerTest, ChangeBecauseOfRemovingFromRoot) {
+ TestServerWindowDelegate server_window_delegate;
+ ServerWindow root(&server_window_delegate, WindowId());
+ server_window_delegate.set_root_window(&root);
+ root.SetVisible(true);
+ ServerWindow child(&server_window_delegate, WindowId());
+ child.SetVisible(true);
+ root.Add(&child);
+
+ TestServerWindowDrawnTrackerObserver drawn_observer;
+ ServerWindowDrawnTracker tracker(&child, &drawn_observer);
+ root.Remove(&child);
+ EXPECT_EQ(1u, drawn_observer.change_count());
+ EXPECT_EQ(&child, drawn_observer.window());
+ EXPECT_EQ(&root, drawn_observer.ancestor());
+ EXPECT_FALSE(drawn_observer.is_drawn());
+ drawn_observer.clear_change_count();
+
+ root.Add(&child);
+ EXPECT_EQ(1u, drawn_observer.change_count());
+ EXPECT_EQ(&child, drawn_observer.window());
+ EXPECT_EQ(nullptr, drawn_observer.ancestor());
+ EXPECT_TRUE(drawn_observer.is_drawn());
+}
+
+TEST(ServerWindowDrawnTrackerTest, ChangeBecauseOfRemovingAncestorFromRoot) {
+ TestServerWindowDelegate server_window_delegate;
+ ServerWindow root(&server_window_delegate, WindowId());
+ server_window_delegate.set_root_window(&root);
+ root.SetVisible(true);
+ ServerWindow child(&server_window_delegate, WindowId());
+ child.SetVisible(true);
+ root.Add(&child);
+
+ ServerWindow child_child(&server_window_delegate, WindowId());
+ child_child.SetVisible(true);
+ child.Add(&child_child);
+
+ TestServerWindowDrawnTrackerObserver drawn_observer;
+ ServerWindowDrawnTracker tracker(&child_child, &drawn_observer);
+ root.Remove(&child);
+ EXPECT_EQ(1u, drawn_observer.change_count());
+ EXPECT_EQ(&child_child, drawn_observer.window());
+ EXPECT_EQ(&root, drawn_observer.ancestor());
+ EXPECT_FALSE(drawn_observer.is_drawn());
+ drawn_observer.clear_change_count();
+
+ root.Add(&child_child);
+ EXPECT_EQ(1u, drawn_observer.change_count());
+ EXPECT_EQ(&child_child, drawn_observer.window());
+ EXPECT_EQ(nullptr, drawn_observer.ancestor());
+ EXPECT_TRUE(drawn_observer.is_drawn());
+}
+
+TEST(ServerWindowDrawnTrackerTest, VisibilityChangeFromNonParentAncestor) {
+ TestServerWindowDelegate server_window_delegate;
+ ServerWindow root(&server_window_delegate, WindowId());
+ ServerWindow child1(&server_window_delegate, WindowId());
+ ServerWindow child2(&server_window_delegate, WindowId());
+ ServerWindow child3(&server_window_delegate, WindowId());
+ server_window_delegate.set_root_window(&root);
+
+ root.Add(&child1);
+ child1.Add(&child2);
+ child2.Add(&child3);
+
+ root.SetVisible(true);
+ child1.SetVisible(false);
+ child2.SetVisible(false);
+ child3.SetVisible(true);
+
+ TestServerWindowDrawnTrackerObserver drawn_observer;
+ ServerWindowDrawnTracker tracker(&child3, &drawn_observer);
+
+ EXPECT_FALSE(child3.IsDrawn());
+
+ // Make |child1| visible. |child3| should still be not drawn, since |child2|
+ // is still invisible.
+ child1.SetVisible(true);
+ EXPECT_EQ(0u, drawn_observer.change_count());
+ EXPECT_EQ(nullptr, drawn_observer.window());
+ EXPECT_EQ(nullptr, drawn_observer.ancestor());
+ EXPECT_FALSE(drawn_observer.is_drawn());
+ EXPECT_FALSE(child3.IsDrawn());
+
+ child2.SetVisible(true);
+ EXPECT_EQ(1u, drawn_observer.change_count());
+ EXPECT_EQ(&child3, drawn_observer.window());
+ EXPECT_EQ(nullptr, drawn_observer.ancestor());
+ EXPECT_TRUE(drawn_observer.is_drawn());
+ EXPECT_TRUE(child3.IsDrawn());
+}
+
+TEST(ServerWindowDrawnTrackerTest, TreeHierarchyChangeFromNonParentAncestor) {
+ TestServerWindowDelegate server_window_delegate;
+ ServerWindow root(&server_window_delegate, WindowId());
+ ServerWindow child1(&server_window_delegate, WindowId());
+ ServerWindow child2(&server_window_delegate, WindowId());
+ ServerWindow child11(&server_window_delegate, WindowId());
+ ServerWindow child111(&server_window_delegate, WindowId());
+ server_window_delegate.set_root_window(&root);
+
+ root.Add(&child1);
+ root.Add(&child2);
+ child1.Add(&child11);
+ child11.Add(&child111);
+
+ root.SetVisible(true);
+ child1.SetVisible(false);
+ child2.SetVisible(true);
+ child11.SetVisible(false);
+ child111.SetVisible(true);
+
+ TestServerWindowDrawnTrackerObserver drawn_observer;
+ ServerWindowDrawnTracker tracker(&child111, &drawn_observer);
+ EXPECT_FALSE(child111.IsDrawn());
+
+ // Move |child11| as a child of |child2|. |child111| should remain not drawn.
+ child2.Add(&child11);
+ EXPECT_EQ(0u, drawn_observer.change_count());
+ EXPECT_EQ(nullptr, drawn_observer.window());
+ EXPECT_EQ(nullptr, drawn_observer.ancestor());
+ EXPECT_FALSE(drawn_observer.is_drawn());
+ EXPECT_FALSE(child111.IsDrawn());
+
+ child11.SetVisible(true);
+ EXPECT_EQ(1u, drawn_observer.change_count());
+ EXPECT_EQ(&child111, drawn_observer.window());
+ EXPECT_EQ(nullptr, drawn_observer.ancestor());
+ EXPECT_TRUE(drawn_observer.is_drawn());
+ EXPECT_TRUE(child111.IsDrawn());
+}
+
+} // namespace ws
+
+} // namespace mus
diff --git a/chromium/components/mus/ws/server_window_observer.h b/chromium/components/mus/ws/server_window_observer.h
new file mode 100644
index 00000000000..2ea2f4c310a
--- /dev/null
+++ b/chromium/components/mus/ws/server_window_observer.h
@@ -0,0 +1,97 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_SERVER_WINDOW_OBSERVER_H_
+#define COMPONENTS_MUS_WS_SERVER_WINDOW_OBSERVER_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "components/mus/public/interfaces/mus_constants.mojom.h"
+
+namespace gfx {
+class Insets;
+class Rect;
+}
+
+namespace ui {
+struct TextInputState;
+}
+
+namespace mus {
+
+namespace ws {
+
+class ServerWindow;
+
+// TODO(sky): rename to OnDid and OnWill everywhere.
+class ServerWindowObserver {
+ public:
+ // Invoked when a window is about to be destroyed; before any of the children
+ // have been removed and before the window has been removed from its parent.
+ virtual void OnWindowDestroying(ServerWindow* window) {}
+
+ // Invoked at the end of the window's destructor (after it has been removed
+ // from the hierarchy.
+ virtual void OnWindowDestroyed(ServerWindow* window) {}
+
+ virtual void OnWillChangeWindowHierarchy(ServerWindow* window,
+ ServerWindow* new_parent,
+ ServerWindow* old_parent) {}
+
+ virtual void OnWindowHierarchyChanged(ServerWindow* window,
+ ServerWindow* new_parent,
+ ServerWindow* old_parent) {}
+
+ virtual void OnWindowBoundsChanged(ServerWindow* window,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {}
+
+ virtual void OnWindowClientAreaChanged(
+ ServerWindow* window,
+ const gfx::Insets& new_client_area,
+ const std::vector<gfx::Rect>& new_additional_client_areas) {}
+
+ virtual void OnWindowReordered(ServerWindow* window,
+ ServerWindow* relative,
+ mojom::OrderDirection direction) {}
+
+ virtual void OnWillChangeWindowVisibility(ServerWindow* window) {}
+ virtual void OnWindowVisibilityChanged(ServerWindow* window) {}
+ virtual void OnWindowOpacityChanged(ServerWindow* window,
+ float old_opacity,
+ float new_opacity) {}
+
+ virtual void OnWindowPredefinedCursorChanged(ServerWindow* window,
+ int32_t cursor_id) {}
+ virtual void OnWindowNonClientCursorChanged(ServerWindow* window,
+ int32_t cursor_id) {}
+
+ virtual void OnWindowTextInputStateChanged(ServerWindow* window,
+ const ui::TextInputState& state) {}
+
+ virtual void OnWindowSharedPropertyChanged(
+ ServerWindow* window,
+ const std::string& name,
+ const std::vector<uint8_t>* new_data) {}
+
+ // Called when a transient child is added to |window|.
+ virtual void OnTransientWindowAdded(ServerWindow* window,
+ ServerWindow* transient_child) {}
+
+ // Called when a transient child is removed from |window|.
+ virtual void OnTransientWindowRemoved(ServerWindow* window,
+ ServerWindow* transient_child) {}
+
+ protected:
+ virtual ~ServerWindowObserver() {}
+};
+
+} // namespace ws
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_SERVER_WINDOW_OBSERVER_H_
diff --git a/chromium/components/mus/ws/server_window_surface.cc b/chromium/components/mus/ws/server_window_surface.cc
new file mode 100644
index 00000000000..d03bc5bd789
--- /dev/null
+++ b/chromium/components/mus/ws/server_window_surface.cc
@@ -0,0 +1,110 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/server_window_surface.h"
+
+#include "base/callback.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/quads/shared_quad_state.h"
+#include "cc/quads/surface_draw_quad.h"
+#include "components/mus/surfaces/surfaces_state.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/server_window_delegate.h"
+#include "components/mus/ws/server_window_surface_manager.h"
+
+namespace mus {
+namespace ws {
+namespace {
+
+void CallCallback(const base::Closure& callback, cc::SurfaceDrawStatus status) {
+ callback.Run();
+}
+
+} // namespace
+
+ServerWindowSurface::ServerWindowSurface(
+ ServerWindowSurfaceManager* manager,
+ mojo::InterfaceRequest<Surface> request,
+ mojom::SurfaceClientPtr client)
+ : manager_(manager),
+ surface_id_(manager->GenerateId()),
+ surface_factory_(manager_->GetSurfaceManager(), this),
+ client_(std::move(client)),
+ binding_(this, std::move(request)),
+ registered_surface_factory_client_(false) {
+ surface_factory_.Create(surface_id_);
+}
+
+ServerWindowSurface::~ServerWindowSurface() {
+ // SurfaceFactory's destructor will attempt to return resources which will
+ // call back into here and access |client_| so we should destroy
+ // |surface_factory_|'s resources early on.
+ surface_factory_.DestroyAll();
+
+ if (registered_surface_factory_client_) {
+ cc::SurfaceManager* surface_manager = manager_->GetSurfaceManager();
+ surface_manager->UnregisterSurfaceFactoryClient(manager_->id_namespace());
+ }
+}
+
+void ServerWindowSurface::SubmitCompositorFrame(
+ cc::CompositorFrame frame,
+ const SubmitCompositorFrameCallback& callback) {
+ gfx::Size frame_size =
+ frame.delegated_frame_data->render_pass_list[0]->output_rect.size();
+ if (!surface_id_.is_null()) {
+ // If the size of the CompostiorFrame has changed then destroy the existing
+ // Surface and create a new one of the appropriate size.
+ if (frame_size != last_submitted_frame_size_) {
+ // Rendering of the topmost frame happens in two phases. First the frame
+ // is generated and submitted, and a later date it is actually drawn.
+ // During the time the frame is generated and drawn we can't destroy the
+ // surface, otherwise when drawn you get an empty surface. To deal with
+ // this we schedule destruction via the delegate. The delegate will call
+ // us back when we're not waiting on a frame to be drawn (which may be
+ // synchronously).
+ surfaces_scheduled_for_destruction_.insert(surface_id_);
+ window()->delegate()->ScheduleSurfaceDestruction(window());
+ surface_id_ = manager_->GenerateId();
+ surface_factory_.Create(surface_id_);
+ }
+ }
+ surface_factory_.SubmitCompositorFrame(surface_id_, std::move(frame),
+ base::Bind(&CallCallback, callback));
+ last_submitted_frame_size_ = frame_size;
+ window()->delegate()->OnScheduleWindowPaint(window());
+}
+
+void ServerWindowSurface::DestroySurfacesScheduledForDestruction() {
+ std::set<cc::SurfaceId> surfaces;
+ surfaces.swap(surfaces_scheduled_for_destruction_);
+ for (auto& id : surfaces)
+ surface_factory_.Destroy(id);
+}
+
+void ServerWindowSurface::RegisterForBeginFrames() {
+ DCHECK(!registered_surface_factory_client_);
+ registered_surface_factory_client_ = true;
+ cc::SurfaceManager* surface_manager = manager_->GetSurfaceManager();
+ surface_manager->RegisterSurfaceFactoryClient(manager_->id_namespace(), this);
+}
+
+ServerWindow* ServerWindowSurface::window() {
+ return manager_->window();
+}
+
+void ServerWindowSurface::ReturnResources(
+ const cc::ReturnedResourceArray& resources) {
+ if (!client_ || !base::MessageLoop::current())
+ return;
+ client_->ReturnResources(mojo::Array<cc::ReturnedResource>::From(resources));
+}
+
+void ServerWindowSurface::SetBeginFrameSource(
+ cc::BeginFrameSource* begin_frame_source) {
+ // TODO(tansell): Implement this.
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/server_window_surface.h b/chromium/components/mus/ws/server_window_surface.h
new file mode 100644
index 00000000000..126a99f73fc
--- /dev/null
+++ b/chromium/components/mus/ws/server_window_surface.h
@@ -0,0 +1,86 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_SERVER_WINDOW_SURFACE_H_
+#define COMPONENTS_MUS_WS_SERVER_WINDOW_SURFACE_H_
+
+#include <set>
+
+#include "base/macros.h"
+#include "cc/ipc/compositor_frame.mojom.h"
+#include "cc/surfaces/surface_factory.h"
+#include "cc/surfaces/surface_factory_client.h"
+#include "cc/surfaces/surface_id.h"
+#include "cc/surfaces/surface_id_allocator.h"
+#include "components/mus/public/interfaces/surface.mojom.h"
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "components/mus/ws/ids.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace mus {
+
+class SurfacesState;
+
+namespace ws {
+
+class ServerWindow;
+class ServerWindowSurfaceManager;
+
+// Server side representation of a WindowSurface.
+class ServerWindowSurface : public mojom::Surface,
+ public cc::SurfaceFactoryClient {
+ public:
+ ServerWindowSurface(ServerWindowSurfaceManager* manager,
+ mojo::InterfaceRequest<mojom::Surface> request,
+ mojom::SurfaceClientPtr client);
+
+ ~ServerWindowSurface() override;
+
+ const gfx::Size& last_submitted_frame_size() const {
+ return last_submitted_frame_size_;
+ }
+
+ // mojom::Surface:
+ void SubmitCompositorFrame(
+ cc::CompositorFrame frame,
+ const SubmitCompositorFrameCallback& callback) override;
+
+ const cc::SurfaceId& id() const { return surface_id_; }
+
+ // Destroys old surfaces that have been outdated by a new surface.
+ void DestroySurfacesScheduledForDestruction();
+
+ // Registers this with the SurfaceManager
+ void RegisterForBeginFrames();
+
+ private:
+ ServerWindow* window();
+
+ // SurfaceFactoryClient implementation.
+ void ReturnResources(const cc::ReturnedResourceArray& resources) override;
+ void SetBeginFrameSource(cc::BeginFrameSource* begin_frame_source) override;
+
+ ServerWindowSurfaceManager* manager_; // Owns this.
+
+ gfx::Size last_submitted_frame_size_;
+
+ cc::SurfaceId surface_id_;
+ cc::SurfaceFactory surface_factory_;
+
+ mojom::SurfaceClientPtr client_;
+ mojo::Binding<Surface> binding_;
+
+ // Set of surface ids that need to be destroyed.
+ std::set<cc::SurfaceId> surfaces_scheduled_for_destruction_;
+
+ bool registered_surface_factory_client_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServerWindowSurface);
+};
+
+} // namespace ws
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_SERVER_WINDOW_SURFACE_H_
diff --git a/chromium/components/mus/ws/server_window_surface_manager.cc b/chromium/components/mus/ws/server_window_surface_manager.cc
new file mode 100644
index 00000000000..1109d6ee8ac
--- /dev/null
+++ b/chromium/components/mus/ws/server_window_surface_manager.cc
@@ -0,0 +1,102 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/server_window_surface_manager.h"
+
+#include "components/mus/surfaces/surfaces_state.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/server_window_delegate.h"
+#include "components/mus/ws/server_window_surface.h"
+
+namespace mus {
+namespace ws {
+
+ServerWindowSurfaceManager::ServerWindowSurfaceManager(ServerWindow* window)
+ : window_(window),
+ surface_id_allocator_(
+ window->delegate()->GetSurfacesState()->next_id_namespace()),
+ waiting_for_initial_frames_(
+ window_->properties().count(mus::mojom::kWaitForUnderlay_Property) >
+ 0) {
+ surface_id_allocator_.RegisterSurfaceIdNamespace(GetSurfaceManager());
+}
+
+ServerWindowSurfaceManager::~ServerWindowSurfaceManager() {
+ // Explicitly clear the type to surface manager so that this manager
+ // is still valid prior during ~ServerWindowSurface.
+ type_to_surface_map_.clear();
+}
+
+bool ServerWindowSurfaceManager::ShouldDraw() {
+ if (!waiting_for_initial_frames_)
+ return true;
+
+ waiting_for_initial_frames_ =
+ !IsSurfaceReadyAndNonEmpty(mojom::SurfaceType::DEFAULT) ||
+ !IsSurfaceReadyAndNonEmpty(mojom::SurfaceType::UNDERLAY);
+ return !waiting_for_initial_frames_;
+}
+
+void ServerWindowSurfaceManager::CreateSurface(
+ mojom::SurfaceType surface_type,
+ mojo::InterfaceRequest<mojom::Surface> request,
+ mojom::SurfaceClientPtr client) {
+ std::unique_ptr<ServerWindowSurface> surface(
+ new ServerWindowSurface(this, std::move(request), std::move(client)));
+ if (!HasAnySurface()) {
+ // Only one SurfaceFactoryClient can be registered per surface id namespace,
+ // so register the first one. Since all surfaces created by this manager
+ // represent the same window, the begin frame source can be shared by
+ // all surfaces created here.
+ surface->RegisterForBeginFrames();
+ }
+ type_to_surface_map_[surface_type] = std::move(surface);
+}
+
+ServerWindowSurface* ServerWindowSurfaceManager::GetDefaultSurface() const {
+ return GetSurfaceByType(mojom::SurfaceType::DEFAULT);
+}
+
+ServerWindowSurface* ServerWindowSurfaceManager::GetUnderlaySurface() const {
+ return GetSurfaceByType(mojom::SurfaceType::UNDERLAY);
+}
+
+ServerWindowSurface* ServerWindowSurfaceManager::GetSurfaceByType(
+ mojom::SurfaceType type) const {
+ auto iter = type_to_surface_map_.find(type);
+ return iter == type_to_surface_map_.end() ? nullptr : iter->second.get();
+}
+
+bool ServerWindowSurfaceManager::HasSurfaceOfType(
+ mojom::SurfaceType type) const {
+ return type_to_surface_map_.count(type) > 0;
+}
+
+bool ServerWindowSurfaceManager::HasAnySurface() const {
+ return GetDefaultSurface() || GetUnderlaySurface();
+}
+
+cc::SurfaceManager* ServerWindowSurfaceManager::GetSurfaceManager() {
+ return window()->delegate()->GetSurfacesState()->manager();
+}
+
+bool ServerWindowSurfaceManager::IsSurfaceReadyAndNonEmpty(
+ mojom::SurfaceType type) const {
+ auto iter = type_to_surface_map_.find(type);
+ if (iter == type_to_surface_map_.end())
+ return false;
+ if (iter->second->last_submitted_frame_size().IsEmpty())
+ return false;
+ const gfx::Size& last_submitted_frame_size =
+ iter->second->last_submitted_frame_size();
+ return last_submitted_frame_size.width() >= window_->bounds().width() &&
+ last_submitted_frame_size.height() >= window_->bounds().height();
+}
+
+cc::SurfaceId ServerWindowSurfaceManager::GenerateId() {
+ return surface_id_allocator_.GenerateId();
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/server_window_surface_manager.h b/chromium/components/mus/ws/server_window_surface_manager.h
new file mode 100644
index 00000000000..1bae2008ca8
--- /dev/null
+++ b/chromium/components/mus/ws/server_window_surface_manager.h
@@ -0,0 +1,84 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_SERVER_WINDOW_SURFACE_MANAGER_H_
+#define COMPONENTS_MUS_WS_SERVER_WINDOW_SURFACE_MANAGER_H_
+
+#include <map>
+
+#include "base/macros.h"
+#include "cc/ipc/compositor_frame.mojom.h"
+#include "cc/surfaces/surface_factory.h"
+#include "cc/surfaces/surface_id.h"
+#include "cc/surfaces/surface_id_allocator.h"
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace mus {
+namespace ws {
+
+class ServerWindow;
+class ServerWindowSurface;
+class ServerWindowSurfaceManagerTestApi;
+
+// ServerWindowSurfaceManager tracks the surfaces associated with a
+// ServerWindow.
+class ServerWindowSurfaceManager {
+ public:
+ explicit ServerWindowSurfaceManager(ServerWindow* window);
+ ~ServerWindowSurfaceManager();
+
+ // Returns true if the surfaces from this manager should be drawn.
+ bool ShouldDraw();
+
+ // Creates a new surface of the specified type, replacing the existing one of
+ // the specified type.
+ void CreateSurface(mojom::SurfaceType surface_type,
+ mojo::InterfaceRequest<mojom::Surface> request,
+ mojom::SurfaceClientPtr client);
+
+ ServerWindow* window() { return window_; }
+
+ ServerWindowSurface* GetDefaultSurface() const;
+ ServerWindowSurface* GetUnderlaySurface() const;
+ ServerWindowSurface* GetSurfaceByType(mojom::SurfaceType type) const;
+ bool HasSurfaceOfType(mojom::SurfaceType type) const;
+ bool HasAnySurface() const;
+
+ uint32_t id_namespace() const { return surface_id_allocator_.id_namespace(); }
+ cc::SurfaceManager* GetSurfaceManager();
+
+ private:
+ friend class ServerWindowSurfaceManagerTestApi;
+ friend class ServerWindowSurface;
+
+ // Returns true if a surface of |type| has been set and its size is greater
+ // than the size of the window.
+ bool IsSurfaceReadyAndNonEmpty(mojom::SurfaceType type) const;
+
+ cc::SurfaceId GenerateId();
+
+ ServerWindow* window_;
+
+ cc::SurfaceIdAllocator surface_id_allocator_;
+
+ using TypeToSurfaceMap =
+ std::map<mojom::SurfaceType, std::unique_ptr<ServerWindowSurface>>;
+
+ TypeToSurfaceMap type_to_surface_map_;
+
+ // While true the window is not drawn. This is initially true if the window
+ // has the property |kWaitForUnderlay_Property|. This is set to false once
+ // the underlay and default surface have been set *and* their size is at
+ // least that of the window. Ideally we would wait for sizes to match, but
+ // the underlay is not necessarily as big as the window.
+ bool waiting_for_initial_frames_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServerWindowSurfaceManager);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_SERVER_WINDOW_SURFACE_MANAGER_H_
diff --git a/chromium/components/mus/ws/server_window_surface_manager_test_api.cc b/chromium/components/mus/ws/server_window_surface_manager_test_api.cc
new file mode 100644
index 00000000000..d65ae0f6376
--- /dev/null
+++ b/chromium/components/mus/ws/server_window_surface_manager_test_api.cc
@@ -0,0 +1,39 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/server_window_surface_manager_test_api.h"
+
+#include "components/mus/ws/server_window.h"
+
+namespace mus {
+namespace ws {
+
+ServerWindowSurfaceManagerTestApi::ServerWindowSurfaceManagerTestApi(
+ ServerWindowSurfaceManager* manager)
+ : manager_(manager) {}
+
+ServerWindowSurfaceManagerTestApi::~ServerWindowSurfaceManagerTestApi() {}
+
+void ServerWindowSurfaceManagerTestApi::CreateEmptyDefaultSurface() {
+ manager_->type_to_surface_map_[mojom::SurfaceType::DEFAULT] = nullptr;
+}
+
+void ServerWindowSurfaceManagerTestApi::DestroyDefaultSurface() {
+ manager_->type_to_surface_map_.erase(mojom::SurfaceType::DEFAULT);
+}
+
+void EnableHitTest(ServerWindow* window) {
+ ServerWindowSurfaceManagerTestApi test_api(
+ window->GetOrCreateSurfaceManager());
+ test_api.CreateEmptyDefaultSurface();
+}
+
+void DisableHitTest(ServerWindow* window) {
+ ServerWindowSurfaceManagerTestApi test_api(
+ window->GetOrCreateSurfaceManager());
+ test_api.DestroyDefaultSurface();
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/server_window_surface_manager_test_api.h b/chromium/components/mus/ws/server_window_surface_manager_test_api.h
new file mode 100644
index 00000000000..bc34027110a
--- /dev/null
+++ b/chromium/components/mus/ws/server_window_surface_manager_test_api.h
@@ -0,0 +1,39 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_SERVER_WINDOW_SURFACE_MANAGER_TEST_API_H_
+#define COMPONENTS_MUS_WS_SERVER_WINDOW_SURFACE_MANAGER_TEST_API_H_
+
+#include "base/macros.h"
+#include "components/mus/ws/server_window_surface_manager.h"
+
+namespace mus {
+namespace ws {
+
+class ServerWindow;
+
+// Use to poke at the internals of ServerWindowSurfaceManager.
+class ServerWindowSurfaceManagerTestApi {
+ public:
+ explicit ServerWindowSurfaceManagerTestApi(
+ ServerWindowSurfaceManager* manager);
+ ~ServerWindowSurfaceManagerTestApi();
+
+ void CreateEmptyDefaultSurface();
+ void DestroyDefaultSurface();
+
+ private:
+ ServerWindowSurfaceManager* manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServerWindowSurfaceManagerTestApi);
+};
+
+// Use to make |window| a target for events.
+void EnableHitTest(ServerWindow* window);
+void DisableHitTest(ServerWindow* window);
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_SERVER_WINDOW_SURFACE_MANAGER_TEST_API_H_
diff --git a/chromium/components/mus/ws/server_window_tracker.h b/chromium/components/mus/ws/server_window_tracker.h
new file mode 100644
index 00000000000..d3b73f06be1
--- /dev/null
+++ b/chromium/components/mus/ws/server_window_tracker.h
@@ -0,0 +1,25 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_SERVER_WINDOW_TRACKER_H_
+#define COMPONENTS_MUS_WS_SERVER_WINDOW_TRACKER_H_
+
+#include <stdint.h>
+#include <set>
+
+#include "base/macros.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/server_window_observer.h"
+#include "ui/base/window_tracker_template.h"
+
+namespace mus {
+namespace ws {
+
+using ServerWindowTracker =
+ ui::WindowTrackerTemplate<ServerWindow, ServerWindowObserver>;
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_SERVER_WINDOW_TRACKER_H_
diff --git a/chromium/components/mus/ws/test_change_tracker.cc b/chromium/components/mus/ws/test_change_tracker.cc
new file mode 100644
index 00000000000..26cd89ed64a
--- /dev/null
+++ b/chromium/components/mus/ws/test_change_tracker.cc
@@ -0,0 +1,448 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/test_change_tracker.h"
+
+#include <stddef.h>
+
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "components/mus/common/util.h"
+#include "mojo/common/common_type_converters.h"
+
+using mojo::Array;
+using mojo::String;
+
+namespace mus {
+
+namespace ws {
+
+std::string WindowIdToString(Id id) {
+ return (id == 0) ? "null"
+ : base::StringPrintf("%d,%d", HiWord(id), LoWord(id));
+}
+
+namespace {
+
+std::string DirectionToString(mojom::OrderDirection direction) {
+ return direction == mojom::OrderDirection::ABOVE ? "above" : "below";
+}
+
+enum class ChangeDescriptionType {
+ ONE,
+ TWO,
+};
+
+std::string ChangeToDescription(const Change& change,
+ ChangeDescriptionType type) {
+ switch (change.type) {
+ case CHANGE_TYPE_EMBED:
+ if (type == ChangeDescriptionType::ONE)
+ return "OnEmbed";
+ return base::StringPrintf("OnEmbed drawn=%s",
+ change.bool_value ? "true" : "false");
+
+ case CHANGE_TYPE_EMBEDDED_APP_DISCONNECTED:
+ return base::StringPrintf("OnEmbeddedAppDisconnected window=%s",
+ WindowIdToString(change.window_id).c_str());
+
+ case CHANGE_TYPE_UNEMBED:
+ return base::StringPrintf("OnUnembed window=%s",
+ WindowIdToString(change.window_id).c_str());
+
+ case CHANGE_TYPE_LOST_CAPTURE:
+ return base::StringPrintf("OnLostCapture window=%s",
+ WindowIdToString(change.window_id).c_str());
+
+ case CHANGE_TYPE_NODE_ADD_TRANSIENT_WINDOW:
+ return base::StringPrintf("AddTransientWindow parent = %s child = %s",
+ WindowIdToString(change.window_id).c_str(),
+ WindowIdToString(change.window_id2).c_str());
+
+ case CHANGE_TYPE_NODE_BOUNDS_CHANGED:
+ return base::StringPrintf(
+ "BoundsChanged window=%s old_bounds=%s new_bounds=%s",
+ WindowIdToString(change.window_id).c_str(),
+ change.bounds.ToString().c_str(), change.bounds2.ToString().c_str());
+
+ case CHANGE_TYPE_NODE_HIERARCHY_CHANGED:
+ return base::StringPrintf(
+ "HierarchyChanged window=%s old_parent=%s new_parent=%s",
+ WindowIdToString(change.window_id).c_str(),
+ WindowIdToString(change.window_id2).c_str(),
+ WindowIdToString(change.window_id3).c_str());
+
+ case CHANGE_TYPE_NODE_REMOVE_TRANSIENT_WINDOW_FROM_PARENT:
+ return base::StringPrintf(
+ "RemoveTransientWindowFromParent parent = %s child = %s",
+ WindowIdToString(change.window_id).c_str(),
+ WindowIdToString(change.window_id2).c_str());
+
+ case CHANGE_TYPE_NODE_REORDERED:
+ return base::StringPrintf("Reordered window=%s relative=%s direction=%s",
+ WindowIdToString(change.window_id).c_str(),
+ WindowIdToString(change.window_id2).c_str(),
+ DirectionToString(change.direction).c_str());
+
+ case CHANGE_TYPE_NODE_DELETED:
+ return base::StringPrintf("WindowDeleted window=%s",
+ WindowIdToString(change.window_id).c_str());
+
+ case CHANGE_TYPE_NODE_VISIBILITY_CHANGED:
+ return base::StringPrintf("VisibilityChanged window=%s visible=%s",
+ WindowIdToString(change.window_id).c_str(),
+ change.bool_value ? "true" : "false");
+
+ case CHANGE_TYPE_NODE_DRAWN_STATE_CHANGED:
+ return base::StringPrintf("DrawnStateChanged window=%s drawn=%s",
+ WindowIdToString(change.window_id).c_str(),
+ change.bool_value ? "true" : "false");
+
+ case CHANGE_TYPE_INPUT_EVENT: {
+ std::string result = base::StringPrintf(
+ "InputEvent window=%s event_action=%d",
+ WindowIdToString(change.window_id).c_str(), change.event_action);
+ if (change.event_observer_id != 0)
+ base::StringAppendF(&result, " event_observer_id=%u",
+ change.event_observer_id);
+ return result;
+ }
+
+ case CHANGE_TYPE_EVENT_OBSERVED:
+ return base::StringPrintf(
+ "EventObserved event_action=%d event_observer_id=%u",
+ change.event_action, change.event_observer_id);
+
+ case CHANGE_TYPE_PROPERTY_CHANGED:
+ return base::StringPrintf("PropertyChanged window=%s key=%s value=%s",
+ WindowIdToString(change.window_id).c_str(),
+ change.property_key.c_str(),
+ change.property_value.c_str());
+
+ case CHANGE_TYPE_FOCUSED:
+ return base::StringPrintf("Focused id=%s",
+ WindowIdToString(change.window_id).c_str());
+
+ case CHANGE_TYPE_CURSOR_CHANGED:
+ return base::StringPrintf("CursorChanged id=%s cursor_id=%d",
+ WindowIdToString(change.window_id).c_str(),
+ change.cursor_id);
+ case CHANGE_TYPE_ON_CHANGE_COMPLETED:
+ return base::StringPrintf("ChangeCompleted id=%d sucess=%s",
+ change.change_id,
+ change.bool_value ? "true" : "false");
+
+ case CHANGE_TYPE_ON_TOP_LEVEL_CREATED:
+ return base::StringPrintf("TopLevelCreated id=%d window_id=%s drawn=%s",
+ change.change_id,
+ WindowIdToString(change.window_id).c_str(),
+ change.bool_value ? "true" : "false");
+ case CHANGE_TYPE_OPACITY:
+ return base::StringPrintf("OpacityChanged window_id=%s opacity=%.2f",
+ WindowIdToString(change.window_id).c_str(),
+ change.float_value);
+ }
+ return std::string();
+}
+
+std::string SingleChangeToDescriptionImpl(const std::vector<Change>& changes,
+ ChangeDescriptionType change_type) {
+ std::string result;
+ for (auto& change : changes) {
+ if (!result.empty())
+ result += "\n";
+ result += ChangeToDescription(change, change_type);
+ }
+ return result;
+}
+
+} // namespace
+
+std::vector<std::string> ChangesToDescription1(
+ const std::vector<Change>& changes) {
+ std::vector<std::string> strings(changes.size());
+ for (size_t i = 0; i < changes.size(); ++i)
+ strings[i] = ChangeToDescription(changes[i], ChangeDescriptionType::ONE);
+ return strings;
+}
+
+std::string SingleChangeToDescription(const std::vector<Change>& changes) {
+ return SingleChangeToDescriptionImpl(changes, ChangeDescriptionType::ONE);
+}
+
+std::string SingleChangeToDescription2(const std::vector<Change>& changes) {
+ return SingleChangeToDescriptionImpl(changes, ChangeDescriptionType::TWO);
+}
+
+std::string SingleWindowDescription(const std::vector<TestWindow>& windows) {
+ if (windows.empty())
+ return "no windows";
+ std::string result;
+ for (const TestWindow& window : windows)
+ result += window.ToString();
+ return result;
+}
+
+std::string ChangeWindowDescription(const std::vector<Change>& changes) {
+ if (changes.size() != 1)
+ return std::string();
+ std::vector<std::string> window_strings(changes[0].windows.size());
+ for (size_t i = 0; i < changes[0].windows.size(); ++i)
+ window_strings[i] = "[" + changes[0].windows[i].ToString() + "]";
+ return base::JoinString(window_strings, ",");
+}
+
+TestWindow WindowDataToTestWindow(const mojom::WindowDataPtr& data) {
+ TestWindow window;
+ window.parent_id = data->parent_id;
+ window.window_id = data->window_id;
+ window.visible = data->visible;
+ window.properties =
+ data->properties.To<std::map<std::string, std::vector<uint8_t>>>();
+ return window;
+}
+
+void WindowDatasToTestWindows(const Array<mojom::WindowDataPtr>& data,
+ std::vector<TestWindow>* test_windows) {
+ for (size_t i = 0; i < data.size(); ++i)
+ test_windows->push_back(WindowDataToTestWindow(data[i]));
+}
+
+Change::Change()
+ : type(CHANGE_TYPE_EMBED),
+ client_id(0),
+ window_id(0),
+ window_id2(0),
+ window_id3(0),
+ event_action(0),
+ event_observer_id(0u),
+ direction(mojom::OrderDirection::ABOVE),
+ bool_value(false),
+ float_value(0.f),
+ cursor_id(0),
+ change_id(0u) {}
+
+Change::Change(const Change& other) = default;
+
+Change::~Change() {}
+
+TestChangeTracker::TestChangeTracker() : delegate_(NULL) {}
+
+TestChangeTracker::~TestChangeTracker() {}
+
+void TestChangeTracker::OnEmbed(ClientSpecificId client_id,
+ mojom::WindowDataPtr root,
+ bool drawn) {
+ Change change;
+ change.type = CHANGE_TYPE_EMBED;
+ change.client_id = client_id;
+ change.bool_value = drawn;
+ change.windows.push_back(WindowDataToTestWindow(root));
+ AddChange(change);
+}
+
+void TestChangeTracker::OnEmbeddedAppDisconnected(Id window_id) {
+ Change change;
+ change.type = CHANGE_TYPE_EMBEDDED_APP_DISCONNECTED;
+ change.window_id = window_id;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnWindowBoundsChanged(Id window_id,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {
+ Change change;
+ change.type = CHANGE_TYPE_NODE_BOUNDS_CHANGED;
+ change.window_id = window_id;
+ change.bounds = old_bounds;
+ change.bounds2 = new_bounds;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnUnembed(Id window_id) {
+ Change change;
+ change.type = CHANGE_TYPE_UNEMBED;
+ change.window_id = window_id;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnTransientWindowAdded(Id window_id,
+ Id transient_window_id) {
+ Change change;
+ change.type = CHANGE_TYPE_NODE_ADD_TRANSIENT_WINDOW;
+ change.window_id = window_id;
+ change.window_id2 = transient_window_id;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnTransientWindowRemoved(Id window_id,
+ Id transient_window_id) {
+ Change change;
+ change.type = CHANGE_TYPE_NODE_REMOVE_TRANSIENT_WINDOW_FROM_PARENT;
+ change.window_id = window_id;
+ change.window_id2 = transient_window_id;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnLostCapture(Id window_id) {
+ Change change;
+ change.type = CHANGE_TYPE_LOST_CAPTURE;
+ change.window_id = window_id;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnWindowHierarchyChanged(
+ Id window_id,
+ Id old_parent_id,
+ Id new_parent_id,
+ Array<mojom::WindowDataPtr> windows) {
+ Change change;
+ change.type = CHANGE_TYPE_NODE_HIERARCHY_CHANGED;
+ change.window_id = window_id;
+ change.window_id2 = old_parent_id;
+ change.window_id3 = new_parent_id;
+ WindowDatasToTestWindows(windows, &change.windows);
+ AddChange(change);
+}
+
+void TestChangeTracker::OnWindowReordered(Id window_id,
+ Id relative_window_id,
+ mojom::OrderDirection direction) {
+ Change change;
+ change.type = CHANGE_TYPE_NODE_REORDERED;
+ change.window_id = window_id;
+ change.window_id2 = relative_window_id;
+ change.direction = direction;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnWindowDeleted(Id window_id) {
+ Change change;
+ change.type = CHANGE_TYPE_NODE_DELETED;
+ change.window_id = window_id;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnWindowVisibilityChanged(Id window_id, bool visible) {
+ Change change;
+ change.type = CHANGE_TYPE_NODE_VISIBILITY_CHANGED;
+ change.window_id = window_id;
+ change.bool_value = visible;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnWindowOpacityChanged(Id window_id, float opacity) {
+ Change change;
+ change.type = CHANGE_TYPE_OPACITY;
+ change.window_id = window_id;
+ change.float_value = opacity;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnWindowParentDrawnStateChanged(Id window_id,
+ bool drawn) {
+ Change change;
+ change.type = CHANGE_TYPE_NODE_DRAWN_STATE_CHANGED;
+ change.window_id = window_id;
+ change.bool_value = drawn;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnWindowInputEvent(Id window_id,
+ const ui::Event& event,
+ uint32_t event_observer_id) {
+ Change change;
+ change.type = CHANGE_TYPE_INPUT_EVENT;
+ change.window_id = window_id;
+ change.event_action = static_cast<int32_t>(event.type());
+ change.event_observer_id = event_observer_id;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnEventObserved(const ui::Event& event,
+ uint32_t event_observer_id) {
+ Change change;
+ change.type = CHANGE_TYPE_EVENT_OBSERVED;
+ change.event_action = static_cast<int32_t>(event.type());
+ change.event_observer_id = event_observer_id;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnWindowSharedPropertyChanged(Id window_id,
+ String name,
+ Array<uint8_t> data) {
+ Change change;
+ change.type = CHANGE_TYPE_PROPERTY_CHANGED;
+ change.window_id = window_id;
+ change.property_key = name;
+ if (data.is_null())
+ change.property_value = "NULL";
+ else
+ change.property_value = data.To<std::string>();
+ AddChange(change);
+}
+
+void TestChangeTracker::OnWindowFocused(Id window_id) {
+ Change change;
+ change.type = CHANGE_TYPE_FOCUSED;
+ change.window_id = window_id;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnWindowPredefinedCursorChanged(
+ Id window_id,
+ mojom::Cursor cursor_id) {
+ Change change;
+ change.type = CHANGE_TYPE_CURSOR_CHANGED;
+ change.window_id = window_id;
+ change.cursor_id = static_cast<int32_t>(cursor_id);
+ AddChange(change);
+}
+
+void TestChangeTracker::OnChangeCompleted(uint32_t change_id, bool success) {
+ Change change;
+ change.type = CHANGE_TYPE_ON_CHANGE_COMPLETED;
+ change.change_id = change_id;
+ change.bool_value = success;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnTopLevelCreated(uint32_t change_id,
+ mojom::WindowDataPtr window_data,
+ bool drawn) {
+ Change change;
+ change.type = CHANGE_TYPE_ON_TOP_LEVEL_CREATED;
+ change.change_id = change_id;
+ change.window_id = window_data->window_id;
+ change.bool_value = drawn;
+ AddChange(change);
+}
+
+void TestChangeTracker::AddChange(const Change& change) {
+ changes_.push_back(change);
+ if (delegate_)
+ delegate_->OnChangeAdded();
+}
+
+TestWindow::TestWindow() {}
+
+TestWindow::TestWindow(const TestWindow& other) = default;
+
+TestWindow::~TestWindow() {}
+
+std::string TestWindow::ToString() const {
+ return base::StringPrintf("window=%s parent=%s",
+ WindowIdToString(window_id).c_str(),
+ WindowIdToString(parent_id).c_str());
+}
+
+std::string TestWindow::ToString2() const {
+ return base::StringPrintf(
+ "window=%s parent=%s visible=%s", WindowIdToString(window_id).c_str(),
+ WindowIdToString(parent_id).c_str(), visible ? "true" : "false");
+}
+
+} // namespace ws
+
+} // namespace mus
diff --git a/chromium/components/mus/ws/test_change_tracker.h b/chromium/components/mus/ws/test_change_tracker.h
new file mode 100644
index 00000000000..7d82b2f339b
--- /dev/null
+++ b/chromium/components/mus/ws/test_change_tracker.h
@@ -0,0 +1,185 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_TEST_CHANGE_TRACKER_H_
+#define COMPONENTS_MUS_WS_TEST_CHANGE_TRACKER_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/mus/common/types.h"
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "ui/gfx/geometry/mojo/geometry.mojom.h"
+
+namespace mus {
+
+namespace ws {
+
+enum ChangeType {
+ CHANGE_TYPE_EMBED,
+ CHANGE_TYPE_EMBEDDED_APP_DISCONNECTED,
+ CHANGE_TYPE_UNEMBED,
+ CHANGE_TYPE_LOST_CAPTURE,
+ // TODO(sky): nuke NODE.
+ CHANGE_TYPE_NODE_ADD_TRANSIENT_WINDOW,
+ CHANGE_TYPE_NODE_BOUNDS_CHANGED,
+ CHANGE_TYPE_NODE_HIERARCHY_CHANGED,
+ CHANGE_TYPE_NODE_REMOVE_TRANSIENT_WINDOW_FROM_PARENT,
+ CHANGE_TYPE_NODE_REORDERED,
+ CHANGE_TYPE_NODE_VISIBILITY_CHANGED,
+ CHANGE_TYPE_NODE_DRAWN_STATE_CHANGED,
+ CHANGE_TYPE_NODE_DELETED,
+ CHANGE_TYPE_INPUT_EVENT,
+ CHANGE_TYPE_EVENT_OBSERVED,
+ CHANGE_TYPE_PROPERTY_CHANGED,
+ CHANGE_TYPE_FOCUSED,
+ CHANGE_TYPE_CURSOR_CHANGED,
+ CHANGE_TYPE_ON_CHANGE_COMPLETED,
+ CHANGE_TYPE_ON_TOP_LEVEL_CREATED,
+ CHANGE_TYPE_OPACITY,
+};
+
+// TODO(sky): consider nuking and converting directly to WindowData.
+struct TestWindow {
+ TestWindow();
+ TestWindow(const TestWindow& other);
+ ~TestWindow();
+
+ // Returns a string description of this.
+ std::string ToString() const;
+
+ // Returns a string description that includes visible and drawn.
+ std::string ToString2() const;
+
+ Id parent_id;
+ Id window_id;
+ bool visible;
+ std::map<std::string, std::vector<uint8_t>> properties;
+};
+
+// Tracks a call to WindowTreeClient. See the individual functions for the
+// fields that are used.
+struct Change {
+ Change();
+ Change(const Change& other);
+ ~Change();
+
+ ChangeType type;
+ ClientSpecificId client_id;
+ std::vector<TestWindow> windows;
+ Id window_id;
+ Id window_id2;
+ Id window_id3;
+ gfx::Rect bounds;
+ gfx::Rect bounds2;
+ int32_t event_action;
+ uint32_t event_observer_id;
+ mojo::String embed_url;
+ mojom::OrderDirection direction;
+ bool bool_value;
+ float float_value;
+ std::string property_key;
+ std::string property_value;
+ int32_t cursor_id;
+ uint32_t change_id;
+};
+
+// Converts Changes to string descriptions.
+std::vector<std::string> ChangesToDescription1(
+ const std::vector<Change>& changes);
+
+// Convenience for returning the description of the first item in |changes|.
+// Returns an empty string if |changes| has something other than one entry.
+std::string SingleChangeToDescription(const std::vector<Change>& changes);
+std::string SingleChangeToDescription2(const std::vector<Change>& changes);
+
+// Convenience for returning the description of the first item in |windows|.
+// Returns an empty string if |windows| has something other than one entry.
+std::string SingleWindowDescription(const std::vector<TestWindow>& windows);
+
+// Returns a string description of |changes[0].windows|. Returns an empty string
+// if change.size() != 1.
+std::string ChangeWindowDescription(const std::vector<Change>& changes);
+
+// Converts WindowDatas to TestWindows.
+void WindowDatasToTestWindows(const mojo::Array<mojom::WindowDataPtr>& data,
+ std::vector<TestWindow>* test_windows);
+
+// TestChangeTracker is used to record WindowTreeClient functions. It notifies
+// a delegate any time a change is added.
+class TestChangeTracker {
+ public:
+ // Used to notify the delegate when a change is added. A change corresponds to
+ // a single WindowTreeClient function.
+ class Delegate {
+ public:
+ virtual void OnChangeAdded() = 0;
+
+ protected:
+ virtual ~Delegate() {}
+ };
+
+ TestChangeTracker();
+ ~TestChangeTracker();
+
+ void set_delegate(Delegate* delegate) { delegate_ = delegate; }
+
+ std::vector<Change>* changes() { return &changes_; }
+
+ // Each of these functions generate a Change. There is one per
+ // WindowTreeClient function.
+ void OnEmbed(ClientSpecificId client_id,
+ mojom::WindowDataPtr root,
+ bool drawn);
+ void OnEmbeddedAppDisconnected(Id window_id);
+ void OnUnembed(Id window_id);
+ void OnLostCapture(Id window_id);
+ void OnTransientWindowAdded(Id window_id, Id transient_window_id);
+ void OnTransientWindowRemoved(Id window_id, Id transient_window_id);
+ void OnWindowBoundsChanged(Id window_id,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds);
+ void OnWindowHierarchyChanged(Id window_id,
+ Id old_parent_id,
+ Id new_parent_id,
+ mojo::Array<mojom::WindowDataPtr> windows);
+ void OnWindowReordered(Id window_id,
+ Id relative_window_id,
+ mojom::OrderDirection direction);
+ void OnWindowDeleted(Id window_id);
+ void OnWindowVisibilityChanged(Id window_id, bool visible);
+ void OnWindowOpacityChanged(Id window_id, float opacity);
+ void OnWindowParentDrawnStateChanged(Id window_id, bool drawn);
+ void OnWindowInputEvent(Id window_id,
+ const ui::Event& event,
+ uint32_t event_observer_id);
+ void OnEventObserved(const ui::Event& event, uint32_t event_observer_id);
+ void OnWindowSharedPropertyChanged(Id window_id,
+ mojo::String name,
+ mojo::Array<uint8_t> data);
+ void OnWindowFocused(Id window_id);
+ void OnWindowPredefinedCursorChanged(Id window_id, mojom::Cursor cursor_id);
+ void OnChangeCompleted(uint32_t change_id, bool success);
+ void OnTopLevelCreated(uint32_t change_id,
+ mojom::WindowDataPtr window_data,
+ bool drawn);
+
+ private:
+ void AddChange(const Change& change);
+
+ Delegate* delegate_;
+ std::vector<Change> changes_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestChangeTracker);
+};
+
+} // namespace ws
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_TEST_CHANGE_TRACKER_H_
diff --git a/chromium/components/mus/ws/test_server_window_delegate.cc b/chromium/components/mus/ws/test_server_window_delegate.cc
new file mode 100644
index 00000000000..57f1aaec6ed
--- /dev/null
+++ b/chromium/components/mus/ws/test_server_window_delegate.cc
@@ -0,0 +1,34 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/surfaces/surfaces_state.h"
+#include "components/mus/ws/test_server_window_delegate.h"
+#include "components/mus/ws/server_window.h"
+
+namespace mus {
+
+namespace ws {
+
+TestServerWindowDelegate::TestServerWindowDelegate()
+ : root_window_(nullptr), surfaces_state_(new SurfacesState()) {}
+
+TestServerWindowDelegate::~TestServerWindowDelegate() {}
+
+mus::SurfacesState* TestServerWindowDelegate::GetSurfacesState() {
+ return surfaces_state_.get();
+}
+
+void TestServerWindowDelegate::OnScheduleWindowPaint(ServerWindow* window) {}
+
+const ServerWindow* TestServerWindowDelegate::GetRootWindow(
+ const ServerWindow* window) const {
+ return root_window_;
+}
+
+void TestServerWindowDelegate::ScheduleSurfaceDestruction(
+ ServerWindow* window) {}
+
+} // namespace ws
+
+} // namespace mus
diff --git a/chromium/components/mus/ws/test_server_window_delegate.h b/chromium/components/mus/ws/test_server_window_delegate.h
new file mode 100644
index 00000000000..77808771d9d
--- /dev/null
+++ b/chromium/components/mus/ws/test_server_window_delegate.h
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_TEST_SERVER_WINDOW_DELEGATE_H_
+#define COMPONENTS_MUS_WS_TEST_SERVER_WINDOW_DELEGATE_H_
+
+#include "base/macros.h"
+#include "components/mus/ws/server_window_delegate.h"
+
+namespace mus {
+
+namespace ws {
+
+struct WindowId;
+
+class TestServerWindowDelegate : public ServerWindowDelegate {
+ public:
+ TestServerWindowDelegate();
+ ~TestServerWindowDelegate() override;
+
+ void set_root_window(const ServerWindow* window) { root_window_ = window; }
+
+ private:
+ // ServerWindowDelegate:
+ mus::SurfacesState* GetSurfacesState() override;
+ void OnScheduleWindowPaint(ServerWindow* window) override;
+ const ServerWindow* GetRootWindow(const ServerWindow* window) const override;
+ void ScheduleSurfaceDestruction(ServerWindow* window) override;
+
+ const ServerWindow* root_window_;
+ scoped_refptr<mus::SurfacesState> surfaces_state_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestServerWindowDelegate);
+};
+
+} // namespace ws
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_TEST_SERVER_WINDOW_DELEGATE_H_
diff --git a/chromium/components/mus/ws/test_utils.cc b/chromium/components/mus/ws/test_utils.cc
new file mode 100644
index 00000000000..78f7d4714d3
--- /dev/null
+++ b/chromium/components/mus/ws/test_utils.cc
@@ -0,0 +1,496 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/test_utils.h"
+
+#include "base/memory/ptr_util.h"
+#include "cc/output/copy_output_request.h"
+#include "components/mus/surfaces/surfaces_state.h"
+#include "components/mus/ws/display_binding.h"
+#include "components/mus/ws/display_manager.h"
+#include "components/mus/ws/server_window_surface_manager_test_api.h"
+#include "components/mus/ws/window_manager_access_policy.h"
+#include "components/mus/ws/window_manager_window_tree_factory.h"
+#include "services/shell/public/interfaces/connector.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mus {
+namespace ws {
+namespace test {
+namespace {
+
+// -----------------------------------------------------------------------------
+// Empty implementation of PlatformDisplay.
+class TestPlatformDisplay : public PlatformDisplay {
+ public:
+ explicit TestPlatformDisplay(int32_t* cursor_id_storage)
+ : cursor_id_storage_(cursor_id_storage) {
+ display_metrics_.size_in_pixels = gfx::Size(400, 300);
+ display_metrics_.device_scale_factor = 1.f;
+ }
+ ~TestPlatformDisplay() override {}
+
+ // PlatformDisplay:
+ void Init(PlatformDisplayDelegate* delegate) override {
+ // It is necessary to tell the delegate about the ViewportMetrics to make
+ // sure that the DisplayBinding is correctly initialized (and a root-window
+ // is created).
+ delegate->OnViewportMetricsChanged(ViewportMetrics(), display_metrics_);
+ }
+ void SchedulePaint(const ServerWindow* window,
+ const gfx::Rect& bounds) override {}
+ void SetViewportSize(const gfx::Size& size) override {}
+ void SetTitle(const base::string16& title) override {}
+ void SetCapture() override {}
+ void ReleaseCapture() override {}
+ void SetCursorById(int32_t cursor) override { *cursor_id_storage_ = cursor; }
+ mojom::Rotation GetRotation() override { return mojom::Rotation::VALUE_0; }
+ float GetDeviceScaleFactor() override {
+ return display_metrics_.device_scale_factor;
+ }
+ void UpdateTextInputState(const ui::TextInputState& state) override {}
+ void SetImeVisibility(bool visible) override {}
+ bool IsFramePending() const override { return false; }
+ void RequestCopyOfOutput(
+ std::unique_ptr<cc::CopyOutputRequest> output_request) override {}
+ int64_t GetDisplayId() const override { return 1; }
+
+ private:
+ ViewportMetrics display_metrics_;
+
+ int32_t* cursor_id_storage_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestPlatformDisplay);
+};
+
+ClientWindowId NextUnusedClientWindowId(WindowTree* tree) {
+ ClientWindowId client_id;
+ for (ClientSpecificId id = 1;; ++id) {
+ // Used the id of the client in the upper bits to simplify things.
+ const ClientWindowId client_id =
+ ClientWindowId(WindowIdToTransportId(WindowId(tree->id(), id)));
+ if (!tree->GetWindowByClientId(client_id))
+ return client_id;
+ }
+}
+
+} // namespace
+
+// WindowManagerWindowTreeFactorySetTestApi ------------------------------------
+
+WindowManagerWindowTreeFactorySetTestApi::
+ WindowManagerWindowTreeFactorySetTestApi(
+ WindowManagerWindowTreeFactorySet*
+ window_manager_window_tree_factory_set)
+ : window_manager_window_tree_factory_set_(
+ window_manager_window_tree_factory_set) {}
+
+WindowManagerWindowTreeFactorySetTestApi::
+ ~WindowManagerWindowTreeFactorySetTestApi() {}
+
+void WindowManagerWindowTreeFactorySetTestApi::Add(const UserId& user_id) {
+ WindowManagerWindowTreeFactory* factory =
+ window_manager_window_tree_factory_set_->Add(user_id, nullptr);
+ factory->CreateWindowTree(nullptr, nullptr);
+}
+
+// TestPlatformDisplayFactory -------------------------------------------------
+
+TestPlatformDisplayFactory::TestPlatformDisplayFactory(
+ int32_t* cursor_id_storage)
+ : cursor_id_storage_(cursor_id_storage) {}
+
+TestPlatformDisplayFactory::~TestPlatformDisplayFactory() {}
+
+PlatformDisplay* TestPlatformDisplayFactory::CreatePlatformDisplay() {
+ return new TestPlatformDisplay(cursor_id_storage_);
+}
+
+// WindowTreeTestApi ---------------------------------------------------------
+
+WindowTreeTestApi::WindowTreeTestApi(WindowTree* tree) : tree_(tree) {}
+WindowTreeTestApi::~WindowTreeTestApi() {}
+
+void WindowTreeTestApi::SetEventObserver(mojom::EventMatcherPtr matcher,
+ uint32_t event_observer_id) {
+ tree_->SetEventObserver(std::move(matcher), event_observer_id);
+}
+
+// DisplayTestApi ------------------------------------------------------------
+
+DisplayTestApi::DisplayTestApi(Display* display) : display_(display) {}
+DisplayTestApi::~DisplayTestApi() {}
+
+// EventDispatcherTestApi ----------------------------------------------------
+
+bool EventDispatcherTestApi::IsWindowPointerTarget(
+ const ServerWindow* window) const {
+ for (const auto& pair : ed_->pointer_targets_) {
+ if (pair.second.window == window)
+ return true;
+ }
+ return false;
+}
+
+int EventDispatcherTestApi::NumberPointerTargetsForWindow(
+ ServerWindow* window) {
+ int count = 0;
+ for (const auto& pair : ed_->pointer_targets_)
+ if (pair.second.window == window)
+ count++;
+ return count;
+}
+
+// TestDisplayBinding ---------------------------------------------------------
+
+WindowTree* TestDisplayBinding::CreateWindowTree(ServerWindow* root) {
+ const uint32_t embed_flags = 0;
+ WindowTree* tree = window_server_->EmbedAtWindow(
+ root, shell::mojom::kRootUserID, mus::mojom::WindowTreeClientPtr(),
+ embed_flags, base::WrapUnique(new WindowManagerAccessPolicy));
+ tree->ConfigureWindowManager();
+ return tree;
+}
+
+// TestWindowManager ----------------------------------------------------------
+
+void TestWindowManager::WmCreateTopLevelWindow(
+ uint32_t change_id,
+ ClientSpecificId requesting_client_id,
+ mojo::Map<mojo::String, mojo::Array<uint8_t>> properties) {
+ got_create_top_level_window_ = true;
+ change_id_ = change_id;
+}
+
+void TestWindowManager::WmClientJankinessChanged(ClientSpecificId client_id,
+ bool janky) {}
+
+void TestWindowManager::OnAccelerator(uint32_t id,
+ std::unique_ptr<ui::Event> event) {
+ on_accelerator_called_ = true;
+ on_accelerator_id_ = id;
+}
+
+// TestWindowTreeClient -------------------------------------------------------
+
+TestWindowTreeClient::TestWindowTreeClient()
+ : binding_(this), record_on_change_completed_(false) {}
+TestWindowTreeClient::~TestWindowTreeClient() {}
+
+void TestWindowTreeClient::Bind(
+ mojo::InterfaceRequest<mojom::WindowTreeClient> request) {
+ binding_.Bind(std::move(request));
+}
+
+void TestWindowTreeClient::OnEmbed(uint16_t client_id,
+ mojom::WindowDataPtr root,
+ mus::mojom::WindowTreePtr tree,
+ int64_t display_id,
+ Id focused_window_id,
+ bool drawn) {
+ // TODO(sky): add test coverage of |focused_window_id|.
+ tracker_.OnEmbed(client_id, std::move(root), drawn);
+}
+
+void TestWindowTreeClient::OnEmbeddedAppDisconnected(uint32_t window) {
+ tracker_.OnEmbeddedAppDisconnected(window);
+}
+
+void TestWindowTreeClient::OnUnembed(Id window_id) {
+ tracker_.OnUnembed(window_id);
+}
+
+void TestWindowTreeClient::OnLostCapture(Id window_id) {}
+
+void TestWindowTreeClient::OnTopLevelCreated(uint32_t change_id,
+ mojom::WindowDataPtr data,
+ int64_t display_id,
+ bool drawn) {
+ tracker_.OnTopLevelCreated(change_id, std::move(data), drawn);
+}
+
+void TestWindowTreeClient::OnWindowBoundsChanged(uint32_t window,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {
+ tracker_.OnWindowBoundsChanged(window, std::move(old_bounds),
+ std::move(new_bounds));
+}
+
+void TestWindowTreeClient::OnClientAreaChanged(
+ uint32_t window_id,
+ const gfx::Insets& new_client_area,
+ mojo::Array<gfx::Rect> new_additional_client_areas) {}
+
+void TestWindowTreeClient::OnTransientWindowAdded(
+ uint32_t window_id,
+ uint32_t transient_window_id) {}
+
+void TestWindowTreeClient::OnTransientWindowRemoved(
+ uint32_t window_id,
+ uint32_t transient_window_id) {}
+
+void TestWindowTreeClient::OnWindowHierarchyChanged(
+ uint32_t window,
+ uint32_t old_parent,
+ uint32_t new_parent,
+ mojo::Array<mojom::WindowDataPtr> windows) {
+ tracker_.OnWindowHierarchyChanged(window, old_parent, new_parent,
+ std::move(windows));
+}
+
+void TestWindowTreeClient::OnWindowReordered(uint32_t window_id,
+ uint32_t relative_window_id,
+ mojom::OrderDirection direction) {
+ tracker_.OnWindowReordered(window_id, relative_window_id, direction);
+}
+
+void TestWindowTreeClient::OnWindowDeleted(uint32_t window) {
+ tracker_.OnWindowDeleted(window);
+}
+
+void TestWindowTreeClient::OnWindowVisibilityChanged(uint32_t window,
+ bool visible) {
+ tracker_.OnWindowVisibilityChanged(window, visible);
+}
+
+void TestWindowTreeClient::OnWindowOpacityChanged(uint32_t window,
+ float old_opacity,
+ float new_opacity) {
+ tracker_.OnWindowOpacityChanged(window, new_opacity);
+}
+
+void TestWindowTreeClient::OnWindowParentDrawnStateChanged(uint32_t window,
+ bool drawn) {
+ tracker_.OnWindowParentDrawnStateChanged(window, drawn);
+}
+
+void TestWindowTreeClient::OnWindowSharedPropertyChanged(
+ uint32_t window,
+ const mojo::String& name,
+ mojo::Array<uint8_t> new_data) {
+ tracker_.OnWindowSharedPropertyChanged(window, name, std::move(new_data));
+}
+
+void TestWindowTreeClient::OnWindowInputEvent(uint32_t event_id,
+ uint32_t window,
+ std::unique_ptr<ui::Event> event,
+ uint32_t event_observer_id) {
+ tracker_.OnWindowInputEvent(window, *event.get(), event_observer_id);
+}
+
+void TestWindowTreeClient::OnEventObserved(std::unique_ptr<ui::Event> event,
+ uint32_t event_observer_id) {
+ tracker_.OnEventObserved(*event.get(), event_observer_id);
+}
+
+void TestWindowTreeClient::OnWindowFocused(uint32_t focused_window_id) {
+ tracker_.OnWindowFocused(focused_window_id);
+}
+
+void TestWindowTreeClient::OnWindowPredefinedCursorChanged(
+ uint32_t window_id,
+ mojom::Cursor cursor_id) {
+ tracker_.OnWindowPredefinedCursorChanged(window_id, cursor_id);
+}
+
+void TestWindowTreeClient::OnChangeCompleted(uint32_t change_id, bool success) {
+ if (record_on_change_completed_)
+ tracker_.OnChangeCompleted(change_id, success);
+}
+
+void TestWindowTreeClient::RequestClose(uint32_t window_id) {}
+
+void TestWindowTreeClient::GetWindowManager(
+ mojo::AssociatedInterfaceRequest<mojom::WindowManager> internal) {}
+
+// TestWindowTreeBinding ------------------------------------------------------
+
+TestWindowTreeBinding::TestWindowTreeBinding(WindowTree* tree)
+ : WindowTreeBinding(&client_), tree_(tree) {}
+TestWindowTreeBinding::~TestWindowTreeBinding() {}
+
+mojom::WindowManager* TestWindowTreeBinding::GetWindowManager() {
+ if (!window_manager_.get())
+ window_manager_.reset(new TestWindowManager);
+ return window_manager_.get();
+}
+void TestWindowTreeBinding::SetIncomingMethodCallProcessingPaused(bool paused) {
+ is_paused_ = paused;
+}
+
+// TestWindowServerDelegate ----------------------------------------------
+
+TestWindowServerDelegate::TestWindowServerDelegate() {}
+TestWindowServerDelegate::~TestWindowServerDelegate() {}
+
+Display* TestWindowServerDelegate::AddDisplay() {
+ // Display manages its own lifetime.
+ Display* display = new Display(window_server_, PlatformDisplayInitParams());
+ display->Init(nullptr);
+ return display;
+}
+
+void TestWindowServerDelegate::OnNoMoreDisplays() {
+ got_on_no_more_displays_ = true;
+}
+
+std::unique_ptr<WindowTreeBinding>
+TestWindowServerDelegate::CreateWindowTreeBinding(
+ BindingType type,
+ ws::WindowServer* window_server,
+ ws::WindowTree* tree,
+ mojom::WindowTreeRequest* tree_request,
+ mojom::WindowTreeClientPtr* client) {
+ std::unique_ptr<TestWindowTreeBinding> binding(
+ new TestWindowTreeBinding(tree));
+ bindings_.push_back(binding.get());
+ return std::move(binding);
+}
+
+void TestWindowServerDelegate::CreateDefaultDisplays() {
+ DCHECK(num_displays_to_create_);
+ DCHECK(window_server_);
+
+ for (int i = 0; i < num_displays_to_create_; ++i)
+ AddDisplay();
+}
+
+bool TestWindowServerDelegate::IsTestConfig() const {
+ return true;
+}
+
+// WindowEventTargetingHelper ------------------------------------------------
+
+WindowEventTargetingHelper::WindowEventTargetingHelper()
+ : wm_client_(nullptr),
+ cursor_id_(0),
+ platform_display_factory_(&cursor_id_),
+ display_binding_(nullptr),
+ display_(nullptr),
+ surfaces_state_(new SurfacesState()),
+ window_server_(nullptr) {
+ PlatformDisplay::set_factory_for_testing(&platform_display_factory_);
+ window_server_.reset(
+ new WindowServer(&window_server_delegate_, surfaces_state_));
+ PlatformDisplayInitParams display_init_params;
+ display_init_params.surfaces_state = surfaces_state_;
+ display_ = new Display(window_server_.get(), display_init_params);
+ display_binding_ = new TestDisplayBinding(window_server_.get());
+ display_->Init(base::WrapUnique(display_binding_));
+ wm_client_ = window_server_delegate_.last_client();
+ wm_client_->tracker()->changes()->clear();
+}
+
+WindowEventTargetingHelper::~WindowEventTargetingHelper() {}
+
+ServerWindow* WindowEventTargetingHelper::CreatePrimaryTree(
+ const gfx::Rect& root_window_bounds,
+ const gfx::Rect& window_bounds) {
+ WindowTree* wm_tree = window_server_->GetTreeWithId(1);
+ const ClientWindowId embed_window_id(
+ WindowIdToTransportId(WindowId(wm_tree->id(), 1)));
+ EXPECT_TRUE(wm_tree->NewWindow(embed_window_id, ServerWindow::Properties()));
+ EXPECT_TRUE(wm_tree->SetWindowVisibility(embed_window_id, true));
+ EXPECT_TRUE(wm_tree->AddWindow(FirstRootId(wm_tree), embed_window_id));
+ display_->root_window()->SetBounds(root_window_bounds);
+ mojom::WindowTreeClientPtr client;
+ mojom::WindowTreeClientRequest client_request = GetProxy(&client);
+ wm_client_->Bind(std::move(client_request));
+ const uint32_t embed_flags = 0;
+ wm_tree->Embed(embed_window_id, std::move(client), embed_flags);
+ ServerWindow* embed_window = wm_tree->GetWindowByClientId(embed_window_id);
+ WindowTree* tree1 = window_server_->GetTreeWithRoot(embed_window);
+ EXPECT_NE(nullptr, tree1);
+ EXPECT_NE(tree1, wm_tree);
+ WindowTreeTestApi(tree1).set_user_id(wm_tree->user_id());
+
+ embed_window->SetBounds(window_bounds);
+
+ return embed_window;
+}
+
+void WindowEventTargetingHelper::CreateSecondaryTree(
+ ServerWindow* embed_window,
+ const gfx::Rect& window_bounds,
+ TestWindowTreeClient** out_client,
+ WindowTree** window_tree,
+ ServerWindow** window) {
+ WindowTree* tree1 = window_server_->GetTreeWithRoot(embed_window);
+ ASSERT_TRUE(tree1 != nullptr);
+ const ClientWindowId child1_id(
+ WindowIdToTransportId(WindowId(tree1->id(), 1)));
+ EXPECT_TRUE(tree1->NewWindow(child1_id, ServerWindow::Properties()));
+ ServerWindow* child1 = tree1->GetWindowByClientId(child1_id);
+ ASSERT_TRUE(child1);
+ EXPECT_TRUE(tree1->AddWindow(ClientWindowIdForWindow(tree1, embed_window),
+ child1_id));
+ tree1->GetDisplay(embed_window)->AddActivationParent(embed_window);
+
+ child1->SetVisible(true);
+ child1->SetBounds(window_bounds);
+ EnableHitTest(child1);
+
+ TestWindowTreeClient* embed_client =
+ window_server_delegate_.last_client();
+ embed_client->tracker()->changes()->clear();
+ wm_client_->tracker()->changes()->clear();
+
+ *out_client = embed_client;
+ *window_tree = tree1;
+ *window = child1;
+}
+
+void WindowEventTargetingHelper::SetTaskRunner(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ message_loop_.SetTaskRunner(task_runner);
+}
+
+// ----------------------------------------------------------------------------
+
+ServerWindow* FirstRoot(WindowTree* tree) {
+ return tree->roots().size() == 1u
+ ? tree->GetWindow((*tree->roots().begin())->id())
+ : nullptr;
+}
+
+ClientWindowId FirstRootId(WindowTree* tree) {
+ ServerWindow* first_root = FirstRoot(tree);
+ return first_root ? ClientWindowIdForWindow(tree, first_root)
+ : ClientWindowId();
+}
+
+ClientWindowId ClientWindowIdForWindow(WindowTree* tree,
+ const ServerWindow* window) {
+ ClientWindowId client_window_id;
+ // If window isn't known we'll return 0, which should then error out.
+ tree->IsWindowKnown(window, &client_window_id);
+ return client_window_id;
+}
+
+ServerWindow* NewWindowInTree(WindowTree* tree, ClientWindowId* client_id) {
+ return NewWindowInTreeWithParent(tree, FirstRoot(tree), client_id);
+}
+
+ServerWindow* NewWindowInTreeWithParent(WindowTree* tree,
+ ServerWindow* parent,
+ ClientWindowId* client_id) {
+ if (!parent)
+ return nullptr;
+ ClientWindowId parent_client_id;
+ if (!tree->IsWindowKnown(parent, &parent_client_id))
+ return nullptr;
+ ClientWindowId client_window_id = NextUnusedClientWindowId(tree);
+ if (!tree->NewWindow(client_window_id, ServerWindow::Properties()))
+ return nullptr;
+ if (!tree->SetWindowVisibility(client_window_id, true))
+ return nullptr;
+ if (!tree->AddWindow(parent_client_id, client_window_id))
+ return nullptr;
+ *client_id = client_window_id;
+ return tree->GetWindowByClientId(client_window_id);
+}
+
+} // namespace test
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/test_utils.h b/chromium/components/mus/ws/test_utils.h
new file mode 100644
index 00000000000..ede927722ec
--- /dev/null
+++ b/chromium/components/mus/ws/test_utils.h
@@ -0,0 +1,537 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_TEST_UTILS_H_
+#define COMPONENTS_MUS_WS_TEST_UTILS_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "components/mus/public/interfaces/display.mojom.h"
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "components/mus/ws/display.h"
+#include "components/mus/ws/display_binding.h"
+#include "components/mus/ws/event_dispatcher.h"
+#include "components/mus/ws/platform_display.h"
+#include "components/mus/ws/platform_display_factory.h"
+#include "components/mus/ws/test_change_tracker.h"
+#include "components/mus/ws/user_activity_monitor.h"
+#include "components/mus/ws/user_display_manager.h"
+#include "components/mus/ws/user_id.h"
+#include "components/mus/ws/window_manager_state.h"
+#include "components/mus/ws/window_manager_window_tree_factory_set.h"
+#include "components/mus/ws/window_server_delegate.h"
+#include "components/mus/ws/window_tree.h"
+#include "components/mus/ws/window_tree_binding.h"
+
+namespace mus {
+namespace ws {
+namespace test {
+
+// Collection of utilities useful in creating mus tests.
+
+class WindowManagerWindowTreeFactorySetTestApi {
+ public:
+ explicit WindowManagerWindowTreeFactorySetTestApi(
+ WindowManagerWindowTreeFactorySet*
+ window_manager_window_tree_factory_set);
+ ~WindowManagerWindowTreeFactorySetTestApi();
+
+ void Add(const UserId& user_id);
+
+ private:
+ WindowManagerWindowTreeFactorySet* window_manager_window_tree_factory_set_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowManagerWindowTreeFactorySetTestApi);
+};
+
+// -----------------------------------------------------------------------------
+
+class UserDisplayManagerTestApi {
+ public:
+ explicit UserDisplayManagerTestApi(UserDisplayManager* udm) : udm_(udm) {}
+ ~UserDisplayManagerTestApi() {}
+
+ void SetTestObserver(mojom::DisplayManagerObserver* observer) {
+ udm_->test_observer_ = observer;
+ if (observer)
+ udm_->OnObserverAdded(observer);
+ }
+
+ private:
+ UserDisplayManager* udm_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserDisplayManagerTestApi);
+};
+
+// -----------------------------------------------------------------------------
+
+class UserActivityMonitorTestApi {
+ public:
+ explicit UserActivityMonitorTestApi(UserActivityMonitor* monitor)
+ : monitor_(monitor) {}
+
+ void SetTimerTaskRunner(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ monitor_->idle_timer_.SetTaskRunner(task_runner);
+ }
+
+ private:
+ UserActivityMonitor* monitor_;
+ DISALLOW_COPY_AND_ASSIGN(UserActivityMonitorTestApi);
+};
+
+// -----------------------------------------------------------------------------
+
+class WindowTreeTestApi {
+ public:
+ explicit WindowTreeTestApi(WindowTree* tree);
+ ~WindowTreeTestApi();
+
+ void set_user_id(const UserId& user_id) { tree_->user_id_ = user_id; }
+ void set_window_manager_internal(mojom::WindowManager* wm_internal) {
+ tree_->window_manager_internal_ = wm_internal;
+ }
+ void AckOldestEvent() {
+ tree_->OnWindowInputEventAck(tree_->event_ack_id_,
+ mojom::EventResult::UNHANDLED);
+ }
+ void EnableCapture() { tree_->event_ack_id_ = 1u; }
+ void AckLastEvent(mojom::EventResult result) {
+ tree_->OnWindowInputEventAck(tree_->event_ack_id_, result);
+ }
+
+ void SetEventObserver(mojom::EventMatcherPtr matcher,
+ uint32_t event_observer_id);
+
+ private:
+ WindowTree* tree_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowTreeTestApi);
+};
+
+// -----------------------------------------------------------------------------
+
+class DisplayTestApi {
+ public:
+ explicit DisplayTestApi(Display* display);
+ ~DisplayTestApi();
+
+ void OnEvent(const ui::Event& event) { display_->OnEvent(event); }
+
+ private:
+ Display* display_;
+
+ DISALLOW_COPY_AND_ASSIGN(DisplayTestApi);
+};
+
+// -----------------------------------------------------------------------------
+
+class EventDispatcherTestApi {
+ public:
+ explicit EventDispatcherTestApi(EventDispatcher* ed) : ed_(ed) {}
+ ~EventDispatcherTestApi() {}
+
+ bool AreAnyPointersDown() const { return ed_->AreAnyPointersDown(); }
+ bool is_mouse_button_down() const { return ed_->mouse_button_down_; }
+ bool IsWindowPointerTarget(const ServerWindow* window) const;
+ int NumberPointerTargetsForWindow(ServerWindow* window);
+ ModalWindowController* modal_window_controller() const {
+ return &ed_->modal_window_controller_;
+ }
+ ServerWindow* capture_window() { return ed_->capture_window_; }
+
+ private:
+ EventDispatcher* ed_;
+
+ DISALLOW_COPY_AND_ASSIGN(EventDispatcherTestApi);
+};
+
+// -----------------------------------------------------------------------------
+
+class ModalWindowControllerTestApi {
+ public:
+ explicit ModalWindowControllerTestApi(ModalWindowController* mwc)
+ : mwc_(mwc) {}
+ ~ModalWindowControllerTestApi() {}
+
+ ServerWindow* GetActiveSystemModalWindow() const {
+ return mwc_->GetActiveSystemModalWindow();
+ }
+
+ private:
+ ModalWindowController* mwc_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModalWindowControllerTestApi);
+};
+
+// -----------------------------------------------------------------------------
+
+class WindowManagerStateTestApi {
+ public:
+ explicit WindowManagerStateTestApi(WindowManagerState* wms) : wms_(wms) {}
+ ~WindowManagerStateTestApi() {}
+
+ void DispatchInputEventToWindow(ServerWindow* target,
+ ClientSpecificId client_id,
+ const ui::Event& event,
+ Accelerator* accelerator) {
+ wms_->DispatchInputEventToWindow(target, client_id, event, accelerator);
+ }
+
+ ClientSpecificId GetEventTargetClientId(ServerWindow* window,
+ bool in_nonclient_area) {
+ return wms_->GetEventTargetClientId(window, in_nonclient_area);
+ }
+
+ void ProcessEvent(const ui::Event& event) { wms_->ProcessEvent(event); }
+
+ void OnEventAckTimeout(ClientSpecificId client_id) {
+ wms_->OnEventAckTimeout(client_id);
+ }
+
+ ClientSpecificId GetEventTargetClientId(const ServerWindow* window,
+ bool in_nonclient_area) {
+ return wms_->GetEventTargetClientId(window, in_nonclient_area);
+ }
+
+ mojom::WindowTree* tree_awaiting_input_ack() {
+ return wms_->tree_awaiting_input_ack_;
+ }
+
+ private:
+ WindowManagerState* wms_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowManagerStateTestApi);
+};
+
+// -----------------------------------------------------------------------------
+
+// Factory that always embeds the new WindowTree as the root user id.
+class TestDisplayBinding : public DisplayBinding {
+ public:
+ explicit TestDisplayBinding(WindowServer* window_server)
+ : window_server_(window_server) {}
+ ~TestDisplayBinding() override {}
+
+ private:
+ // DisplayBinding:
+ WindowTree* CreateWindowTree(ServerWindow* root) override;
+
+ WindowServer* window_server_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestDisplayBinding);
+};
+
+// -----------------------------------------------------------------------------
+
+// Factory that dispenses TestPlatformDisplays.
+class TestPlatformDisplayFactory : public PlatformDisplayFactory {
+ public:
+ explicit TestPlatformDisplayFactory(int32_t* cursor_id_storage);
+ ~TestPlatformDisplayFactory();
+
+ // PlatformDisplayFactory:
+ PlatformDisplay* CreatePlatformDisplay() override;
+
+ private:
+ int32_t* cursor_id_storage_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestPlatformDisplayFactory);
+};
+
+// -----------------------------------------------------------------------------
+
+class TestWindowManager : public mojom::WindowManager {
+ public:
+ TestWindowManager()
+ : got_create_top_level_window_(false),
+ change_id_(0u),
+ on_accelerator_called_(false),
+ on_accelerator_id_(0u) {}
+ ~TestWindowManager() override {}
+
+ bool did_call_create_top_level_window(uint32_t* change_id) {
+ if (!got_create_top_level_window_)
+ return false;
+
+ got_create_top_level_window_ = false;
+ *change_id = change_id_;
+ return true;
+ }
+
+ bool on_accelerator_called() { return on_accelerator_called_; }
+ uint32_t on_accelerator_id() { return on_accelerator_id_; }
+
+ private:
+ // WindowManager:
+ void OnConnect(uint16_t client_id) override {}
+ void WmNewDisplayAdded(mus::mojom::DisplayPtr display,
+ mus::mojom::WindowDataPtr root,
+ bool drawn) override {}
+ void WmSetBounds(uint32_t change_id,
+ uint32_t window_id,
+ const gfx::Rect& bounds) override {}
+ void WmSetProperty(uint32_t change_id,
+ uint32_t window_id,
+ const mojo::String& name,
+ mojo::Array<uint8_t> value) override {}
+ void WmCreateTopLevelWindow(
+ uint32_t change_id,
+ ClientSpecificId requesting_client_id,
+ mojo::Map<mojo::String, mojo::Array<uint8_t>> properties) override;
+ void WmClientJankinessChanged(ClientSpecificId client_id,
+ bool janky) override;
+ void OnAccelerator(uint32_t id, std::unique_ptr<ui::Event> event) override;
+
+ bool got_create_top_level_window_;
+ uint32_t change_id_;
+
+ bool on_accelerator_called_;
+ uint32_t on_accelerator_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestWindowManager);
+};
+
+// -----------------------------------------------------------------------------
+
+// WindowTreeClient implementation that logs all calls to a TestChangeTracker.
+class TestWindowTreeClient : public mus::mojom::WindowTreeClient {
+ public:
+ TestWindowTreeClient();
+ ~TestWindowTreeClient() override;
+
+ TestChangeTracker* tracker() { return &tracker_; }
+
+ void Bind(mojo::InterfaceRequest<mojom::WindowTreeClient> request);
+
+ void set_record_on_change_completed(bool value) {
+ record_on_change_completed_ = value;
+ }
+
+ private:
+ // WindowTreeClient:
+ void OnEmbed(uint16_t client_id,
+ mojom::WindowDataPtr root,
+ mus::mojom::WindowTreePtr tree,
+ int64_t display_id,
+ Id focused_window_id,
+ bool drawn) override;
+ void OnEmbeddedAppDisconnected(uint32_t window) override;
+ void OnUnembed(Id window_id) override;
+ void OnLostCapture(Id window_id) override;
+ void OnTopLevelCreated(uint32_t change_id,
+ mojom::WindowDataPtr data,
+ int64_t display_id,
+ bool drawn) override;
+ void OnWindowBoundsChanged(uint32_t window,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) override;
+ void OnClientAreaChanged(
+ uint32_t window_id,
+ const gfx::Insets& new_client_area,
+ mojo::Array<gfx::Rect> new_additional_client_areas) override;
+ void OnTransientWindowAdded(uint32_t window_id,
+ uint32_t transient_window_id) override;
+ void OnTransientWindowRemoved(uint32_t window_id,
+ uint32_t transient_window_id) override;
+ void OnWindowHierarchyChanged(
+ uint32_t window,
+ uint32_t old_parent,
+ uint32_t new_parent,
+ mojo::Array<mojom::WindowDataPtr> windows) override;
+ void OnWindowReordered(uint32_t window_id,
+ uint32_t relative_window_id,
+ mojom::OrderDirection direction) override;
+ void OnWindowDeleted(uint32_t window) override;
+ void OnWindowVisibilityChanged(uint32_t window, bool visible) override;
+ void OnWindowOpacityChanged(uint32_t window,
+ float old_opacity,
+ float new_opacity) override;
+ void OnWindowParentDrawnStateChanged(uint32_t window, bool drawn) override;
+ void OnWindowSharedPropertyChanged(uint32_t window,
+ const mojo::String& name,
+ mojo::Array<uint8_t> new_data) override;
+ void OnWindowInputEvent(uint32_t event_id,
+ uint32_t window,
+ std::unique_ptr<ui::Event> event,
+ uint32_t event_observer_id) override;
+ void OnEventObserved(std::unique_ptr<ui::Event> event,
+ uint32_t event_observer_id) override;
+ void OnWindowFocused(uint32_t focused_window_id) override;
+ void OnWindowPredefinedCursorChanged(uint32_t window_id,
+ mojom::Cursor cursor_id) override;
+ void OnChangeCompleted(uint32_t change_id, bool success) override;
+ void RequestClose(uint32_t window_id) override;
+ void GetWindowManager(
+ mojo::AssociatedInterfaceRequest<mojom::WindowManager> internal) override;
+
+ TestChangeTracker tracker_;
+ mojo::Binding<mojom::WindowTreeClient> binding_;
+ bool record_on_change_completed_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(TestWindowTreeClient);
+};
+
+// -----------------------------------------------------------------------------
+
+// WindowTreeBinding implementation that vends TestWindowTreeBinding.
+class TestWindowTreeBinding : public WindowTreeBinding {
+ public:
+ explicit TestWindowTreeBinding(WindowTree* tree);
+ ~TestWindowTreeBinding() override;
+
+ WindowTree* tree() { return tree_; }
+ TestWindowTreeClient* client() { return &client_; }
+
+ bool is_paused() const { return is_paused_; }
+
+ // WindowTreeBinding:
+ mojom::WindowManager* GetWindowManager() override;
+ void SetIncomingMethodCallProcessingPaused(bool paused) override;
+
+ private:
+ WindowTree* tree_;
+ TestWindowTreeClient client_;
+ bool is_paused_ = false;
+ std::unique_ptr<TestWindowManager> window_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestWindowTreeBinding);
+};
+
+// -----------------------------------------------------------------------------
+
+// WindowServerDelegate that creates TestWindowTreeClients.
+class TestWindowServerDelegate : public WindowServerDelegate {
+ public:
+ TestWindowServerDelegate();
+ ~TestWindowServerDelegate() override;
+
+ void set_window_server(WindowServer* window_server) {
+ window_server_ = window_server;
+ }
+
+ void set_num_displays_to_create(int count) {
+ num_displays_to_create_ = count;
+ }
+
+ TestWindowTreeClient* last_client() {
+ return last_binding() ? last_binding()->client() : nullptr;
+ }
+ TestWindowTreeBinding* last_binding() {
+ return bindings_.empty() ? nullptr : bindings_.back();
+ }
+
+ std::vector<TestWindowTreeBinding*>* bindings() { return &bindings_; }
+
+ bool got_on_no_more_displays() const { return got_on_no_more_displays_; }
+
+ Display* AddDisplay();
+
+ // WindowServerDelegate:
+ void OnNoMoreDisplays() override;
+ std::unique_ptr<WindowTreeBinding> CreateWindowTreeBinding(
+ BindingType type,
+ ws::WindowServer* window_server,
+ ws::WindowTree* tree,
+ mojom::WindowTreeRequest* tree_request,
+ mojom::WindowTreeClientPtr* client) override;
+ void CreateDefaultDisplays() override;
+ bool IsTestConfig() const override;
+
+ private:
+ // If CreateDefaultDisplays() this is the number of Displays that are
+ // created. The default is 0, which results in a DCHECK.
+ int num_displays_to_create_ = 0;
+ WindowServer* window_server_ = nullptr;
+ bool got_on_no_more_displays_ = false;
+ // All TestWindowTreeBinding objects created via CreateWindowTreeBinding.
+ // These are owned by the corresponding WindowTree.
+ std::vector<TestWindowTreeBinding*> bindings_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestWindowServerDelegate);
+};
+
+// -----------------------------------------------------------------------------
+
+// Helper class which owns all of the necessary objects to test event targeting
+// of ServerWindow objects.
+class WindowEventTargetingHelper {
+ public:
+ WindowEventTargetingHelper();
+ ~WindowEventTargetingHelper();
+
+ // Creates |window| as an embeded window of the primary tree. This window is a
+ // root window of its own tree, with bounds |window_bounds|. The bounds of the
+ // root window of |display_| are defined by |root_window_bounds|.
+ ServerWindow* CreatePrimaryTree(const gfx::Rect& root_window_bounds,
+ const gfx::Rect& window_bounds);
+ // Creates a secondary tree, embedded as a child of |embed_window|. The
+ // resulting |window| is setup for event targeting, with bounds
+ // |window_bounds|.
+ void CreateSecondaryTree(ServerWindow* embed_window,
+ const gfx::Rect& window_bounds,
+ TestWindowTreeClient** out_client,
+ WindowTree** window_tree,
+ ServerWindow** window);
+ // Sets the task runner for |message_loop_|
+ void SetTaskRunner(scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
+ int32_t cursor_id() { return cursor_id_; }
+ Display* display() { return display_; }
+ TestWindowTreeBinding* last_binding() {
+ return window_server_delegate_.last_binding();
+ }
+ TestWindowTreeClient* last_window_tree_client() {
+ return window_server_delegate_.last_client();
+ }
+ TestWindowTreeClient* wm_client() { return wm_client_; }
+ WindowServer* window_server() { return window_server_.get(); }
+
+ private:
+ // TestWindowTreeClient that is used for the WM client. Owned by
+ // |window_server_delegate_|
+ TestWindowTreeClient* wm_client_;
+ int32_t cursor_id_;
+ TestPlatformDisplayFactory platform_display_factory_;
+ TestWindowServerDelegate window_server_delegate_;
+ // Owned by WindowServer
+ TestDisplayBinding* display_binding_;
+ // Owned by WindowServer's DisplayManager.
+ Display* display_;
+ scoped_refptr<SurfacesState> surfaces_state_;
+ std::unique_ptr<WindowServer> window_server_;
+ // Needed to Bind to |wm_client_|
+ base::MessageLoop message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowEventTargetingHelper);
+};
+
+// -----------------------------------------------------------------------------
+
+// Returns the first and only root of |tree|. If |tree| has zero or more than
+// one root returns null.
+ServerWindow* FirstRoot(WindowTree* tree);
+
+// Returns the ClientWindowId of the first root of |tree|, or an empty
+// ClientWindowId if |tree| has zero or more than one root.
+ClientWindowId FirstRootId(WindowTree* tree);
+
+// Returns |tree|s ClientWindowId for |window|.
+ClientWindowId ClientWindowIdForWindow(WindowTree* tree,
+ const ServerWindow* window);
+
+// Creates a new visible window as a child of the single root of |tree|.
+// |client_id| set to the ClientWindowId of the new window.
+ServerWindow* NewWindowInTree(WindowTree* tree, ClientWindowId* client_id);
+ServerWindow* NewWindowInTreeWithParent(WindowTree* tree,
+ ServerWindow* parent,
+ ClientWindowId* client_id);
+
+} // namespace test
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_TEST_UTILS_H_
diff --git a/chromium/components/mus/ws/touch_controller.cc b/chromium/components/mus/ws/touch_controller.cc
new file mode 100644
index 00000000000..e78b3489dc9
--- /dev/null
+++ b/chromium/components/mus/ws/touch_controller.cc
@@ -0,0 +1,105 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/touch_controller.h"
+
+#include <set>
+#include <vector>
+
+#include "base/logging.h"
+#include "components/mus/ws/display.h"
+#include "components/mus/ws/display_manager.h"
+#include "ui/events/devices/device_data_manager.h"
+#include "ui/events/devices/touchscreen_device.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/transform.h"
+
+namespace mus {
+namespace ws {
+
+namespace {
+
+// Computes the scale ratio for the TouchEvent's radius.
+double ComputeTouchResolutionScale(const Display* touch_display,
+ const ui::TouchscreenDevice& touch_device) {
+ gfx::Size touch_display_size = touch_display->GetSize();
+
+ if (touch_device.size.IsEmpty() || touch_display_size.IsEmpty())
+ return 1.0;
+
+ double display_area = touch_display_size.GetArea();
+ double touch_area = touch_device.size.GetArea();
+ double ratio = std::sqrt(display_area / touch_area);
+
+ return ratio;
+}
+
+// Computes a touch transform that maps from window bounds to touchscreen size.
+// Assumes scale factor of 1.0.
+gfx::Transform ComputeTouchTransform(
+ const Display* touch_display,
+ const ui::TouchscreenDevice& touch_device) {
+ gfx::Size touch_display_size = touch_display->GetSize();
+
+ gfx::SizeF current_size(touch_display_size);
+ gfx::SizeF touch_area(touch_device.size);
+ gfx::Transform transform;
+
+ if (current_size.IsEmpty() || touch_area.IsEmpty())
+ return transform;
+
+ // Take care of scaling between touchscreen area and display resolution.
+ transform.Scale(current_size.width() / touch_area.width(),
+ current_size.height() / touch_area.height());
+ return transform;
+}
+
+} // namespace
+
+TouchController::TouchController(DisplayManager* display_manager)
+ : display_manager_(display_manager) {
+ DCHECK(display_manager_);
+ ui::DeviceDataManager::GetInstance()->AddObserver(this);
+}
+
+TouchController::~TouchController() {
+ ui::DeviceDataManager::GetInstance()->RemoveObserver(this);
+}
+
+void TouchController::UpdateTouchTransforms() const {
+ ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance();
+ device_manager->ClearTouchDeviceAssociations();
+
+ const std::set<Display*>& touch_displays = display_manager_->displays();
+ const std::vector<ui::TouchscreenDevice>& touch_devices =
+ device_manager->GetTouchscreenDevices();
+
+ // Mash can only handle a single display so this doesn't implement support for
+ // matching touchscreens with multiple displays.
+ // TODO(kylechar): Implement support for multiple displays when needed.
+ if (touch_displays.size() == 1 && touch_devices.size() == 1) {
+ const Display* touch_display = *touch_displays.begin();
+ const ui::TouchscreenDevice& touch_device = touch_devices[0];
+
+ int64_t touch_display_id = touch_display->GetPlatformDisplayId();
+ int touch_device_id = touch_device.id;
+
+ if (touch_device_id != ui::InputDevice::kInvalidId) {
+ device_manager->UpdateTouchRadiusScale(
+ touch_device_id,
+ ComputeTouchResolutionScale(touch_display, touch_device));
+
+ device_manager->UpdateTouchInfoForDisplay(
+ touch_display_id, touch_device_id,
+ ComputeTouchTransform(touch_display, touch_device));
+ }
+ }
+}
+
+void TouchController::OnTouchscreenDeviceConfigurationChanged() {
+ UpdateTouchTransforms();
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/touch_controller.h b/chromium/components/mus/ws/touch_controller.h
new file mode 100644
index 00000000000..4505ce47164
--- /dev/null
+++ b/chromium/components/mus/ws/touch_controller.h
@@ -0,0 +1,37 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_TOUCH_CONTROLLER_H_
+#define COMPONENTS_MUS_WS_TOUCH_CONTROLLER_H_
+
+#include "base/macros.h"
+#include "ui/events/devices/input_device_event_observer.h"
+
+namespace mus {
+namespace ws {
+
+class DisplayManager;
+
+// Tracks changes to displays and touchscreen devices. Updates the mapping
+// between display devices and touchscreen devices when changes occur.
+class TouchController : public ui::InputDeviceEventObserver {
+ public:
+ explicit TouchController(DisplayManager* display_manager);
+ ~TouchController() override;
+
+ void UpdateTouchTransforms() const;
+
+ // ui::InputDeviceEventObserver:
+ void OnTouchscreenDeviceConfigurationChanged() override;
+
+ private:
+ DisplayManager* display_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(TouchController);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_TOUCH_CONTROLLER_H_
diff --git a/chromium/components/mus/ws/transient_windows_unittest.cc b/chromium/components/mus/ws/transient_windows_unittest.cc
new file mode 100644
index 00000000000..b20e40e467f
--- /dev/null
+++ b/chromium/components/mus/ws/transient_windows_unittest.cc
@@ -0,0 +1,342 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/server_window_observer.h"
+#include "components/mus/ws/test_server_window_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mus {
+namespace ws {
+
+namespace {
+
+class TestTransientWindowObserver : public ServerWindowObserver {
+ public:
+ TestTransientWindowObserver() : add_count_(0), remove_count_(0) {}
+
+ ~TestTransientWindowObserver() override {}
+
+ int add_count() const { return add_count_; }
+ int remove_count() const { return remove_count_; }
+
+ // TransientWindowObserver overrides:
+ void OnTransientWindowAdded(ServerWindow* window,
+ ServerWindow* transient) override {
+ add_count_++;
+ }
+ void OnTransientWindowRemoved(ServerWindow* window,
+ ServerWindow* transient) override {
+ remove_count_++;
+ }
+
+ private:
+ int add_count_;
+ int remove_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestTransientWindowObserver);
+};
+
+ServerWindow* CreateTestWindow(TestServerWindowDelegate* delegate,
+ const WindowId& window_id,
+ ServerWindow* parent) {
+ ServerWindow* window = new ServerWindow(delegate, window_id);
+ window->SetVisible(true);
+ if (parent)
+ parent->Add(window);
+ else
+ delegate->set_root_window(window);
+ return window;
+}
+
+std::string ChildWindowIDsAsString(ServerWindow* parent) {
+ std::string result;
+ for (auto i = parent->children().begin(); i != parent->children().end();
+ ++i) {
+ if (!result.empty())
+ result += " ";
+ result += base::IntToString(WindowIdToTransportId((*i)->id()));
+ }
+ return result;
+}
+
+} // namespace
+
+class TransientWindowsTest : public testing::Test {
+ public:
+ TransientWindowsTest() {}
+ ~TransientWindowsTest() override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TransientWindowsTest);
+};
+
+TEST_F(TransientWindowsTest, TransientChildren) {
+ TestServerWindowDelegate server_window_delegate;
+
+ std::unique_ptr<ServerWindow> parent(
+ CreateTestWindow(&server_window_delegate, WindowId(), nullptr));
+ std::unique_ptr<ServerWindow> w1(
+ CreateTestWindow(&server_window_delegate, WindowId(1, 1), parent.get()));
+ std::unique_ptr<ServerWindow> w3(
+ CreateTestWindow(&server_window_delegate, WindowId(1, 2), parent.get()));
+
+ ServerWindow* w2 =
+ CreateTestWindow(&server_window_delegate, WindowId(1, 3), parent.get());
+
+ // w2 is now owned by w1.
+ w1->AddTransientWindow(w2);
+ // Stack w1 at the top (end), this should force w2 to be last (on top of w1).
+ parent->StackChildAtTop(w1.get());
+ ASSERT_EQ(3u, parent->children().size());
+ EXPECT_EQ(w2, parent->children().back());
+
+ // Destroy w1, which should also destroy w3 (since it's a transient child).
+ w1.reset();
+ w2 = nullptr;
+ ASSERT_EQ(1u, parent->children().size());
+ EXPECT_EQ(w3.get(), parent->children()[0]);
+}
+
+// Tests that transient children are stacked as a unit when using stack above.
+TEST_F(TransientWindowsTest, TransientChildrenGroupAbove) {
+ TestServerWindowDelegate server_window_delegate;
+
+ std::unique_ptr<ServerWindow> parent(
+ CreateTestWindow(&server_window_delegate, WindowId(), nullptr));
+ std::unique_ptr<ServerWindow> w1(
+ CreateTestWindow(&server_window_delegate, WindowId(0, 1), parent.get()));
+
+ ServerWindow* w11 =
+ CreateTestWindow(&server_window_delegate, WindowId(0, 11), parent.get());
+ std::unique_ptr<ServerWindow> w2(
+ CreateTestWindow(&server_window_delegate, WindowId(0, 2), parent.get()));
+
+ ServerWindow* w21 =
+ CreateTestWindow(&server_window_delegate, WindowId(0, 21), parent.get());
+ ServerWindow* w211 =
+ CreateTestWindow(&server_window_delegate, WindowId(0, 211), parent.get());
+ ServerWindow* w212 =
+ CreateTestWindow(&server_window_delegate, WindowId(0, 212), parent.get());
+ ServerWindow* w213 =
+ CreateTestWindow(&server_window_delegate, WindowId(0, 213), parent.get());
+ ServerWindow* w22 =
+ CreateTestWindow(&server_window_delegate, WindowId(0, 22), parent.get());
+ ASSERT_EQ(8u, parent->children().size());
+
+ // w11 is now owned by w1.
+ w1->AddTransientWindow(w11);
+ // w21 is now owned by w2.
+ w2->AddTransientWindow(w21);
+ // w22 is now owned by w2.
+ w2->AddTransientWindow(w22);
+ // w211 is now owned by w21.
+ w21->AddTransientWindow(w211);
+ // w212 is now owned by w21.
+ w21->AddTransientWindow(w212);
+ // w213 is now owned by w21.
+ w21->AddTransientWindow(w213);
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ // Stack w1 at the top (end), this should force w11 to be last (on top of w1).
+ parent->StackChildAtTop(w1.get());
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
+
+ // This tests that the order in children_ array rather than in
+ // transient_children_ array is used when reinserting transient children.
+ // If transient_children_ array was used '22' would be following '21'.
+ parent->StackChildAtTop(w2.get());
+ EXPECT_EQ(w22, parent->children().back());
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ w11->Reorder(w2.get(), mojom::OrderDirection::ABOVE);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
+
+ w21->Reorder(w1.get(), mojom::OrderDirection::ABOVE);
+ EXPECT_EQ(w22, parent->children().back());
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ w21->Reorder(w22, mojom::OrderDirection::ABOVE);
+ EXPECT_EQ(w213, parent->children().back());
+ EXPECT_EQ("1 11 2 22 21 211 212 213", ChildWindowIDsAsString(parent.get()));
+
+ w11->Reorder(w21, mojom::OrderDirection::ABOVE);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 211 212 213 1 11", ChildWindowIDsAsString(parent.get()));
+
+ w213->Reorder(w21, mojom::OrderDirection::ABOVE);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+
+ // No change when stacking a transient parent above its transient child.
+ w21->Reorder(w211, mojom::OrderDirection::ABOVE);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+
+ // This tests that the order in children_ array rather than in
+ // transient_children_ array is used when reinserting transient children.
+ // If transient_children_ array was used '22' would be following '21'.
+ w2->Reorder(w1.get(), mojom::OrderDirection::ABOVE);
+ EXPECT_EQ(w212, parent->children().back());
+ EXPECT_EQ("1 11 2 22 21 213 211 212", ChildWindowIDsAsString(parent.get()));
+
+ w11->Reorder(w213, mojom::OrderDirection::ABOVE);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+}
+
+TEST_F(TransientWindowsTest, TransienChildGroupBelow) {
+ TestServerWindowDelegate server_window_delegate;
+
+ std::unique_ptr<ServerWindow> parent(
+ CreateTestWindow(&server_window_delegate, WindowId(), nullptr));
+ std::unique_ptr<ServerWindow> w1(
+ CreateTestWindow(&server_window_delegate, WindowId(0, 1), parent.get()));
+
+ ServerWindow* w11 =
+ CreateTestWindow(&server_window_delegate, WindowId(0, 11), parent.get());
+ std::unique_ptr<ServerWindow> w2(
+ CreateTestWindow(&server_window_delegate, WindowId(0, 2), parent.get()));
+
+ ServerWindow* w21 =
+ CreateTestWindow(&server_window_delegate, WindowId(0, 21), parent.get());
+ ServerWindow* w211 =
+ CreateTestWindow(&server_window_delegate, WindowId(0, 211), parent.get());
+ ServerWindow* w212 =
+ CreateTestWindow(&server_window_delegate, WindowId(0, 212), parent.get());
+ ServerWindow* w213 =
+ CreateTestWindow(&server_window_delegate, WindowId(0, 213), parent.get());
+ ServerWindow* w22 =
+ CreateTestWindow(&server_window_delegate, WindowId(0, 22), parent.get());
+ ASSERT_EQ(8u, parent->children().size());
+
+ // w11 is now owned by w1.
+ w1->AddTransientWindow(w11);
+ // w21 is now owned by w2.
+ w2->AddTransientWindow(w21);
+ // w22 is now owned by w2.
+ w2->AddTransientWindow(w22);
+ // w211 is now owned by w21.
+ w21->AddTransientWindow(w211);
+ // w212 is now owned by w21.
+ w21->AddTransientWindow(w212);
+ // w213 is now owned by w21.
+ w21->AddTransientWindow(w213);
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ // Stack w2 at the bottom, this should force w11 to be last (on top of w1).
+ // This also tests that the order in children_ array rather than in
+ // transient_children_ array is used when reinserting transient children.
+ // If transient_children_ array was used '22' would be following '21'.
+ parent->StackChildAtBottom(w2.get());
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAtBottom(w1.get());
+ EXPECT_EQ(w22, parent->children().back());
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ w21->Reorder(w1.get(), mojom::OrderDirection::BELOW);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
+
+ w11->Reorder(w2.get(), mojom::OrderDirection::BELOW);
+ EXPECT_EQ(w22, parent->children().back());
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ w22->Reorder(w21, mojom::OrderDirection::BELOW);
+ EXPECT_EQ(w213, parent->children().back());
+ EXPECT_EQ("1 11 2 22 21 211 212 213", ChildWindowIDsAsString(parent.get()));
+
+ w21->Reorder(w11, mojom::OrderDirection::BELOW);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 211 212 213 1 11", ChildWindowIDsAsString(parent.get()));
+
+ w213->Reorder(w211, mojom::OrderDirection::BELOW);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+
+ // No change when stacking a transient parent below its transient child.
+ w21->Reorder(w211, mojom::OrderDirection::BELOW);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+
+ w1->Reorder(w2.get(), mojom::OrderDirection::BELOW);
+ EXPECT_EQ(w212, parent->children().back());
+ EXPECT_EQ("1 11 2 22 21 213 211 212", ChildWindowIDsAsString(parent.get()));
+
+ w213->Reorder(w11, mojom::OrderDirection::BELOW);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+}
+
+// Tests that transient windows are stacked properly when created.
+TEST_F(TransientWindowsTest, StackUponCreation) {
+ TestServerWindowDelegate delegate;
+ std::unique_ptr<ServerWindow> parent(
+ CreateTestWindow(&delegate, WindowId(), nullptr));
+ std::unique_ptr<ServerWindow> window0(
+ CreateTestWindow(&delegate, WindowId(0, 1), parent.get()));
+ std::unique_ptr<ServerWindow> window1(
+ CreateTestWindow(&delegate, WindowId(0, 2), parent.get()));
+
+ ServerWindow* window2 =
+ CreateTestWindow(&delegate, WindowId(0, 3), parent.get());
+ window0->AddTransientWindow(window2);
+ EXPECT_EQ("1 3 2", ChildWindowIDsAsString(parent.get()));
+}
+
+// Tests that windows are restacked properly after a call to
+// AddTransientWindow() or RemoveTransientWindow().
+TEST_F(TransientWindowsTest, RestackUponAddOrRemoveTransientWindow) {
+ TestServerWindowDelegate delegate;
+ std::unique_ptr<ServerWindow> parent(
+ CreateTestWindow(&delegate, WindowId(), nullptr));
+ std::unique_ptr<ServerWindow> windows[4];
+ for (int i = 0; i < 4; i++)
+ windows[i].reset(CreateTestWindow(&delegate, WindowId(0, i), parent.get()));
+
+ EXPECT_EQ("0 1 2 3", ChildWindowIDsAsString(parent.get()));
+
+ windows[0]->AddTransientWindow(windows[2].get());
+ EXPECT_EQ("0 2 1 3", ChildWindowIDsAsString(parent.get()));
+
+ windows[0]->AddTransientWindow(windows[3].get());
+ EXPECT_EQ("0 2 3 1", ChildWindowIDsAsString(parent.get()));
+
+ windows[0]->RemoveTransientWindow(windows[2].get());
+ EXPECT_EQ("0 3 2 1", ChildWindowIDsAsString(parent.get()));
+
+ windows[0]->RemoveTransientWindow(windows[3].get());
+ EXPECT_EQ("0 3 2 1", ChildWindowIDsAsString(parent.get()));
+}
+
+// Verifies TransientWindowObserver is notified appropriately.
+TEST_F(TransientWindowsTest, TransientWindowObserverNotified) {
+ TestServerWindowDelegate delegate;
+ std::unique_ptr<ServerWindow> parent(
+ CreateTestWindow(&delegate, WindowId(), nullptr));
+ std::unique_ptr<ServerWindow> w1(
+ CreateTestWindow(&delegate, WindowId(0, 1), parent.get()));
+
+ TestTransientWindowObserver test_observer;
+ parent->AddObserver(&test_observer);
+
+ parent->AddTransientWindow(w1.get());
+ EXPECT_EQ(1, test_observer.add_count());
+ EXPECT_EQ(0, test_observer.remove_count());
+
+ parent->RemoveTransientWindow(w1.get());
+ EXPECT_EQ(1, test_observer.add_count());
+ EXPECT_EQ(1, test_observer.remove_count());
+
+ parent->RemoveObserver(&test_observer);
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/user_activity_monitor.cc b/chromium/components/mus/ws/user_activity_monitor.cc
new file mode 100644
index 00000000000..3cd66419a27
--- /dev/null
+++ b/chromium/components/mus/ws/user_activity_monitor.cc
@@ -0,0 +1,134 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/user_activity_monitor.h"
+
+#include "base/bind.h"
+#include "base/time/default_tick_clock.h"
+
+namespace mus {
+namespace ws {
+
+UserActivityMonitor::UserActivityMonitor(std::unique_ptr<base::TickClock> clock)
+ : now_clock_(std::move(clock)) {
+ if (!now_clock_)
+ now_clock_ = base::WrapUnique(new base::DefaultTickClock);
+ last_activity_ = now_clock_->NowTicks();
+}
+
+UserActivityMonitor::~UserActivityMonitor() {}
+
+void UserActivityMonitor::OnUserActivity() {
+ base::TimeTicks now = now_clock_->NowTicks();
+ for (auto& pair : activity_observers_) {
+ ActivityObserverInfo* info = &(pair.first);
+ if (info->last_activity_notification.is_null() ||
+ (now - info->last_activity_notification) > info->delay) {
+ pair.second->OnUserActivity();
+ info->last_activity_notification = now;
+ }
+ }
+
+ // Wake up all the 'idle' observers.
+ for (auto& pair : idle_observers_) {
+ IdleObserverInfo* info = &(pair.first);
+ if (info->idle_state == mojom::UserIdleObserver::IdleState::ACTIVE)
+ continue;
+ info->last_idle_state_notification = now;
+ info->idle_state = mojom::UserIdleObserver::IdleState::ACTIVE;
+ pair.second->OnUserIdleStateChanged(info->idle_state);
+ }
+
+ last_activity_ = now;
+
+ // Start the timer if there are some idle observers.
+ if (idle_timer_.IsRunning())
+ idle_timer_.Reset();
+}
+
+void UserActivityMonitor::Add(mojom::UserActivityMonitorRequest request) {
+ bindings_.AddBinding(this, std::move(request));
+}
+
+void UserActivityMonitor::AddUserActivityObserver(
+ uint32_t delay_between_notify_secs,
+ mojom::UserActivityObserverPtr observer) {
+ ActivityObserverInfo info;
+ info.delay = base::TimeDelta::FromSeconds(delay_between_notify_secs);
+ observer.set_connection_error_handler(
+ base::Bind(&UserActivityMonitor::OnActivityObserverDisconnected,
+ base::Unretained(this), observer.get()));
+ activity_observers_.push_back(std::make_pair(info, std::move(observer)));
+}
+
+void UserActivityMonitor::AddUserIdleObserver(
+ uint32_t idleness_in_minutes,
+ mojom::UserIdleObserverPtr observer) {
+ IdleObserverInfo info;
+ info.idle_duration = base::TimeDelta::FromMinutes(idleness_in_minutes);
+ base::TimeTicks now = now_clock_->NowTicks();
+ DCHECK(!last_activity_.is_null());
+ bool user_is_active = (now - last_activity_ < info.idle_duration);
+ info.idle_state = user_is_active ? mojom::UserIdleObserver::IdleState::ACTIVE
+ : mojom::UserIdleObserver::IdleState::IDLE;
+ info.last_idle_state_notification = now;
+ observer->OnUserIdleStateChanged(info.idle_state);
+ observer.set_connection_error_handler(
+ base::Bind(&UserActivityMonitor::OnIdleObserverDisconnected,
+ base::Unretained(this), observer.get()));
+ idle_observers_.push_back(std::make_pair(info, std::move(observer)));
+ if (user_is_active)
+ ActivateIdleTimer();
+}
+
+void UserActivityMonitor::ActivateIdleTimer() {
+ if (idle_timer_.IsRunning())
+ return;
+ idle_timer_.Start(FROM_HERE, base::TimeDelta::FromMinutes(1), this,
+ &UserActivityMonitor::OnMinuteTimer);
+}
+
+void UserActivityMonitor::OnMinuteTimer() {
+ base::TimeTicks now = now_clock_->NowTicks();
+ bool active_observer = false;
+ for (auto& pair : idle_observers_) {
+ IdleObserverInfo* info = &(pair.first);
+ if (info->idle_state == mojom::UserIdleObserver::IdleState::IDLE)
+ continue;
+ if (now - info->last_idle_state_notification < info->idle_duration) {
+ active_observer = true;
+ continue;
+ }
+ info->last_idle_state_notification = now;
+ info->idle_state = mojom::UserIdleObserver::IdleState::IDLE;
+ pair.second->OnUserIdleStateChanged(info->idle_state);
+ }
+ // All observers are already notified of IDLE. No point running the timer
+ // anymore.
+ if (!active_observer)
+ idle_timer_.Stop();
+}
+
+void UserActivityMonitor::OnActivityObserverDisconnected(
+ mojom::UserActivityObserver* observer) {
+ activity_observers_.erase(std::remove_if(
+ activity_observers_.begin(), activity_observers_.end(),
+ [observer](const std::pair<ActivityObserverInfo,
+ mojom::UserActivityObserverPtr>& pair) {
+ return pair.second.get() == observer;
+ }));
+}
+
+void UserActivityMonitor::OnIdleObserverDisconnected(
+ mojom::UserIdleObserver* observer) {
+ idle_observers_.erase(std::remove_if(
+ idle_observers_.begin(), idle_observers_.end(),
+ [observer](
+ const std::pair<IdleObserverInfo, mojom::UserIdleObserverPtr>& pair) {
+ return pair.second.get() == observer;
+ }));
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/user_activity_monitor.h b/chromium/components/mus/ws/user_activity_monitor.h
new file mode 100644
index 00000000000..f490a6dfdfb
--- /dev/null
+++ b/chromium/components/mus/ws/user_activity_monitor.h
@@ -0,0 +1,82 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_USER_ACTIVITY_MONITOR_H_
+#define COMPONENTS_MUS_WS_USER_ACTIVITY_MONITOR_H_
+
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "components/mus/public/interfaces/user_activity_monitor.mojom.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_ptr_set.h"
+
+namespace mus {
+namespace ws {
+
+namespace test {
+class UserActivityMonitorTestApi;
+}
+
+class UserActivityMonitor : public mojom::UserActivityMonitor {
+ public:
+ // |now_clock| is used to get the timestamp. If |now_clock| is nullptr, then
+ // DefaultTickClock is used.
+ explicit UserActivityMonitor(std::unique_ptr<base::TickClock> now_clock);
+ ~UserActivityMonitor() override;
+
+ // Should be called whenever some input event is received from the user.
+ void OnUserActivity();
+
+ // Provides an implementation for the remote request.
+ void Add(mojom::UserActivityMonitorRequest request);
+
+ // mojom::UserActivityMonitor:
+ void AddUserActivityObserver(
+ uint32_t delay_between_notify_secs,
+ mojom::UserActivityObserverPtr observer) override;
+ void AddUserIdleObserver(uint32_t idleness_in_minutes,
+ mojom::UserIdleObserverPtr observer) override;
+
+ private:
+ friend class test::UserActivityMonitorTestApi;
+
+ // Makes sure the idle timer is running.
+ void ActivateIdleTimer();
+
+ // Called every minute when |idle_timer_| is active.
+ void OnMinuteTimer();
+
+ void OnActivityObserverDisconnected(mojom::UserActivityObserver* observer);
+ void OnIdleObserverDisconnected(mojom::UserIdleObserver* observer);
+
+ mojo::BindingSet<mojom::UserActivityMonitor> bindings_;
+ std::unique_ptr<base::TickClock> now_clock_;
+
+ struct ActivityObserverInfo {
+ base::TimeTicks last_activity_notification;
+ base::TimeDelta delay;
+ };
+ std::vector<std::pair<ActivityObserverInfo, mojom::UserActivityObserverPtr>>
+ activity_observers_;
+
+ struct IdleObserverInfo {
+ base::TimeTicks last_idle_state_notification;
+ base::TimeDelta idle_duration;
+ mojom::UserIdleObserver::IdleState idle_state;
+ };
+ std::vector<std::pair<IdleObserverInfo, mojom::UserIdleObserverPtr>>
+ idle_observers_;
+ // Timer used to determine user's idleness. The timer is run only when at
+ // least one of the idle-observers are notified ACTIVE.
+ base::RepeatingTimer idle_timer_;
+ base::TimeTicks last_activity_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserActivityMonitor);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_USER_ACTIVITY_MONITOR_H_
diff --git a/chromium/components/mus/ws/user_activity_monitor_unittest.cc b/chromium/components/mus/ws/user_activity_monitor_unittest.cc
new file mode 100644
index 00000000000..6c26ead8608
--- /dev/null
+++ b/chromium/components/mus/ws/user_activity_monitor_unittest.cc
@@ -0,0 +1,218 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/user_activity_monitor.h"
+
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "components/mus/ws/test_utils.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using mus::mojom::UserIdleObserver;
+
+namespace mus {
+namespace ws {
+namespace test {
+
+class TestUserActivityObserver : public mojom::UserActivityObserver {
+ public:
+ TestUserActivityObserver() : binding_(this) {}
+ ~TestUserActivityObserver() override {}
+
+ mojom::UserActivityObserverPtr GetPtr() {
+ return binding_.CreateInterfacePtrAndBind();
+ }
+
+ bool GetAndResetReceivedUserActivity() {
+ bool val = received_user_activity_;
+ received_user_activity_ = false;
+ return val;
+ }
+
+ private:
+ // mojom::UserActivityObserver:
+ void OnUserActivity() override { received_user_activity_ = true; }
+
+ mojo::Binding<mojom::UserActivityObserver> binding_;
+ bool received_user_activity_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(TestUserActivityObserver);
+};
+
+class TestUserIdleObserver : public mojom::UserIdleObserver {
+ public:
+ TestUserIdleObserver() : binding_(this) {}
+ ~TestUserIdleObserver() override {}
+
+ mojom::UserIdleObserverPtr GetPtr() {
+ return binding_.CreateInterfacePtrAndBind();
+ }
+
+ bool GetAndResetIdleState(UserIdleObserver::IdleState* state) {
+ if (!received_idle_state_)
+ return false;
+ received_idle_state_ = false;
+ *state = idle_state_;
+ return true;
+ }
+
+ private:
+ // mojom::UserIdleObserver:
+ void OnUserIdleStateChanged(UserIdleObserver::IdleState new_state) override {
+ received_idle_state_ = true;
+ idle_state_ = new_state;
+ }
+
+ mojo::Binding<mojom::UserIdleObserver> binding_;
+ bool received_idle_state_ = false;
+ UserIdleObserver::IdleState idle_state_ = UserIdleObserver::IdleState::ACTIVE;
+
+ DISALLOW_COPY_AND_ASSIGN(TestUserIdleObserver);
+};
+
+class UserActivityMonitorTest : public testing::Test {
+ public:
+ UserActivityMonitorTest() {}
+ ~UserActivityMonitorTest() override {}
+
+ UserActivityMonitor* monitor() { return monitor_.get(); }
+
+ void FastForwardBy(base::TimeDelta delta) {
+ task_runner_->FastForwardBy(delta);
+ }
+ void RunUntilIdle() { task_runner_->RunUntilIdle(); }
+
+ private:
+ // testing::Test:
+ void SetUp() override {
+ task_runner_ = make_scoped_refptr(new base::TestMockTimeTaskRunner(
+ base::Time::Now(), base::TimeTicks::Now()));
+ message_loop_.SetTaskRunner(task_runner_);
+ monitor_.reset(new UserActivityMonitor(task_runner_->GetMockTickClock()));
+ }
+
+ base::MessageLoop message_loop_;
+ scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
+ std::unique_ptr<UserActivityMonitor> monitor_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserActivityMonitorTest);
+};
+
+TEST_F(UserActivityMonitorTest, UserActivityObserver) {
+ TestUserActivityObserver first_observer, second_observer;
+ monitor()->AddUserActivityObserver(3, first_observer.GetPtr());
+ monitor()->AddUserActivityObserver(4, second_observer.GetPtr());
+
+ // The first activity should notify both observers.
+ monitor()->OnUserActivity();
+ RunUntilIdle();
+ EXPECT_TRUE(first_observer.GetAndResetReceivedUserActivity());
+ EXPECT_TRUE(second_observer.GetAndResetReceivedUserActivity());
+
+ // The next activity after just one second should not notify either observer.
+ FastForwardBy(base::TimeDelta::FromSeconds(1));
+ monitor()->OnUserActivity();
+ RunUntilIdle();
+ EXPECT_FALSE(first_observer.GetAndResetReceivedUserActivity());
+ EXPECT_FALSE(second_observer.GetAndResetReceivedUserActivity());
+
+ FastForwardBy(base::TimeDelta::FromMilliseconds(2001));
+ monitor()->OnUserActivity();
+ RunUntilIdle();
+ EXPECT_TRUE(first_observer.GetAndResetReceivedUserActivity());
+ EXPECT_FALSE(second_observer.GetAndResetReceivedUserActivity());
+
+ FastForwardBy(base::TimeDelta::FromSeconds(1));
+ monitor()->OnUserActivity();
+ RunUntilIdle();
+ EXPECT_FALSE(first_observer.GetAndResetReceivedUserActivity());
+ EXPECT_TRUE(second_observer.GetAndResetReceivedUserActivity());
+}
+
+// Tests that idleness observers receive the correct notification upon
+// connection.
+TEST_F(UserActivityMonitorTest, UserIdleObserverConnectNotification) {
+ UserIdleObserver::IdleState idle_state;
+
+ // If an observer is added without any user activity, then it still receives
+ // an ACTIVE notification immediately.
+ TestUserIdleObserver first_observer;
+ monitor()->AddUserIdleObserver(1, first_observer.GetPtr());
+ RunUntilIdle();
+ EXPECT_TRUE(first_observer.GetAndResetIdleState(&idle_state));
+ EXPECT_EQ(UserIdleObserver::IdleState::ACTIVE, idle_state);
+
+ // If an observer is added without any user activity and the system has been
+ // idle, then the observer receives an IDLE notification immediately.
+ FastForwardBy(base::TimeDelta::FromMinutes(5));
+ TestUserIdleObserver second_observer;
+ monitor()->AddUserIdleObserver(4, second_observer.GetPtr());
+ RunUntilIdle();
+ EXPECT_TRUE(second_observer.GetAndResetIdleState(&idle_state));
+ EXPECT_EQ(UserIdleObserver::IdleState::IDLE, idle_state);
+ EXPECT_TRUE(first_observer.GetAndResetIdleState(&idle_state));
+ EXPECT_EQ(UserIdleObserver::IdleState::IDLE, idle_state);
+
+ // If an observer is added after some user activity, then it receives an
+ // immediate ACTIVE notification.
+ monitor()->OnUserActivity();
+ TestUserIdleObserver third_observer;
+ monitor()->AddUserIdleObserver(1, third_observer.GetPtr());
+ RunUntilIdle();
+ EXPECT_TRUE(third_observer.GetAndResetIdleState(&idle_state));
+ EXPECT_EQ(UserIdleObserver::IdleState::ACTIVE, idle_state);
+ EXPECT_TRUE(second_observer.GetAndResetIdleState(&idle_state));
+ EXPECT_EQ(UserIdleObserver::IdleState::ACTIVE, idle_state);
+ EXPECT_TRUE(first_observer.GetAndResetIdleState(&idle_state));
+ EXPECT_EQ(UserIdleObserver::IdleState::ACTIVE, idle_state);
+
+ FastForwardBy(base::TimeDelta::FromMinutes(10));
+ TestUserIdleObserver fourth_observer;
+ monitor()->AddUserIdleObserver(1, fourth_observer.GetPtr());
+ RunUntilIdle();
+ EXPECT_TRUE(fourth_observer.GetAndResetIdleState(&idle_state));
+ EXPECT_EQ(UserIdleObserver::IdleState::IDLE, idle_state);
+ EXPECT_TRUE(third_observer.GetAndResetIdleState(&idle_state));
+ EXPECT_EQ(UserIdleObserver::IdleState::IDLE, idle_state);
+ EXPECT_TRUE(second_observer.GetAndResetIdleState(&idle_state));
+ EXPECT_EQ(UserIdleObserver::IdleState::IDLE, idle_state);
+ EXPECT_TRUE(first_observer.GetAndResetIdleState(&idle_state));
+ EXPECT_EQ(UserIdleObserver::IdleState::IDLE, idle_state);
+
+ // All observers are idle. These should not receive any IDLE notifications as
+ // more time passes by.
+ FastForwardBy(base::TimeDelta::FromMinutes(100));
+ RunUntilIdle();
+ EXPECT_FALSE(first_observer.GetAndResetIdleState(&idle_state));
+ EXPECT_FALSE(second_observer.GetAndResetIdleState(&idle_state));
+ EXPECT_FALSE(third_observer.GetAndResetIdleState(&idle_state));
+ EXPECT_FALSE(fourth_observer.GetAndResetIdleState(&idle_state));
+
+ // Some activity would notify ACTIVE to all observers.
+ monitor()->OnUserActivity();
+ RunUntilIdle();
+ EXPECT_TRUE(fourth_observer.GetAndResetIdleState(&idle_state));
+ EXPECT_EQ(UserIdleObserver::IdleState::ACTIVE, idle_state);
+ EXPECT_TRUE(third_observer.GetAndResetIdleState(&idle_state));
+ EXPECT_EQ(UserIdleObserver::IdleState::ACTIVE, idle_state);
+ EXPECT_TRUE(second_observer.GetAndResetIdleState(&idle_state));
+ EXPECT_EQ(UserIdleObserver::IdleState::ACTIVE, idle_state);
+ EXPECT_TRUE(first_observer.GetAndResetIdleState(&idle_state));
+ EXPECT_EQ(UserIdleObserver::IdleState::ACTIVE, idle_state);
+
+ // Yet more activity should not send any notifications.
+ monitor()->OnUserActivity();
+ RunUntilIdle();
+ EXPECT_FALSE(first_observer.GetAndResetIdleState(&idle_state));
+ EXPECT_FALSE(second_observer.GetAndResetIdleState(&idle_state));
+ EXPECT_FALSE(third_observer.GetAndResetIdleState(&idle_state));
+ EXPECT_FALSE(fourth_observer.GetAndResetIdleState(&idle_state));
+}
+
+} // namespace test
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/user_display_manager.cc b/chromium/components/mus/ws/user_display_manager.cc
new file mode 100644
index 00000000000..92aeac675fc
--- /dev/null
+++ b/chromium/components/mus/ws/user_display_manager.cc
@@ -0,0 +1,151 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/user_display_manager.h"
+
+#include "components/mus/ws/display.h"
+#include "components/mus/ws/display_manager.h"
+#include "components/mus/ws/display_manager_delegate.h"
+
+namespace mus {
+namespace ws {
+
+UserDisplayManager::UserDisplayManager(ws::DisplayManager* display_manager,
+ DisplayManagerDelegate* delegate,
+ const UserId& user_id)
+ : display_manager_(display_manager),
+ delegate_(delegate),
+ user_id_(user_id),
+ got_valid_frame_decorations_(
+ delegate->GetFrameDecorationsForUser(user_id, nullptr)),
+ current_cursor_location_(0) {}
+
+UserDisplayManager::~UserDisplayManager() {}
+
+void UserDisplayManager::OnFrameDecorationValuesChanged() {
+ if (!got_valid_frame_decorations_) {
+ got_valid_frame_decorations_ = true;
+ display_manager_observers_.ForAllPtrs([this](
+ mojom::DisplayManagerObserver* observer) { CallOnDisplays(observer); });
+ if (test_observer_)
+ CallOnDisplays(test_observer_);
+ return;
+ }
+
+ mojo::Array<mojom::DisplayPtr> displays = GetAllDisplays();
+ display_manager_observers_.ForAllPtrs(
+ [this, &displays](mojom::DisplayManagerObserver* observer) {
+ observer->OnDisplaysChanged(displays.Clone());
+ });
+ if (test_observer_)
+ test_observer_->OnDisplaysChanged(displays.Clone());
+}
+
+void UserDisplayManager::AddDisplayManagerBinding(
+ mojo::InterfaceRequest<mojom::DisplayManager> request) {
+ display_manager_bindings_.AddBinding(this, std::move(request));
+}
+
+void UserDisplayManager::OnWillDestroyDisplay(Display* display) {
+ if (!got_valid_frame_decorations_)
+ return;
+
+ display_manager_observers_.ForAllPtrs(
+ [this, &display](mojom::DisplayManagerObserver* observer) {
+ observer->OnDisplayRemoved(display->id());
+ });
+ if (test_observer_)
+ test_observer_->OnDisplayRemoved(display->id());
+}
+
+void UserDisplayManager::OnMouseCursorLocationChanged(const gfx::Point& point) {
+ current_cursor_location_ =
+ static_cast<base::subtle::Atomic32>(
+ (point.x() & 0xFFFF) << 16 | (point.y() & 0xFFFF));
+ if (cursor_location_memory()) {
+ base::subtle::NoBarrier_Store(cursor_location_memory(),
+ current_cursor_location_);
+ }
+}
+
+void UserDisplayManager::OnDisplayUpdate(Display* display) {
+ if (!got_valid_frame_decorations_)
+ return;
+
+ mojo::Array<mojom::DisplayPtr> displays(1);
+ displays[0] = display->ToMojomDisplay();
+ delegate_->GetFrameDecorationsForUser(
+ user_id_, &(displays[0]->frame_decoration_values));
+ display_manager_observers_.ForAllPtrs(
+ [this, &displays](mojom::DisplayManagerObserver* observer) {
+ observer->OnDisplaysChanged(displays.Clone());
+ });
+ if (test_observer_)
+ test_observer_->OnDisplaysChanged(displays.Clone());
+}
+
+mojo::ScopedSharedBufferHandle UserDisplayManager::GetCursorLocationMemory() {
+ if (!cursor_location_handle_.is_valid()) {
+ // Create our shared memory segment to share the cursor state with our
+ // window clients.
+ cursor_location_handle_ =
+ mojo::SharedBufferHandle::Create(sizeof(base::subtle::Atomic32));
+
+ if (!cursor_location_handle_.is_valid())
+ return mojo::ScopedSharedBufferHandle();
+
+ cursor_location_mapping_ =
+ cursor_location_handle_->Map(sizeof(base::subtle::Atomic32));
+ if (!cursor_location_mapping_)
+ return mojo::ScopedSharedBufferHandle();
+ base::subtle::NoBarrier_Store(cursor_location_memory(),
+ current_cursor_location_);
+ }
+
+ return cursor_location_handle_->Clone(
+ mojo::SharedBufferHandle::AccessMode::READ_ONLY);
+}
+
+
+void UserDisplayManager::OnObserverAdded(
+ mojom::DisplayManagerObserver* observer) {
+ // Many clients key off the frame decorations to size widgets. Wait for frame
+ // decorations before notifying so that we don't have to worry about clients
+ // resizing appropriately.
+ if (!got_valid_frame_decorations_)
+ return;
+
+ CallOnDisplays(observer);
+}
+
+mojo::Array<mojom::DisplayPtr> UserDisplayManager::GetAllDisplays() {
+ const std::set<Display*>& displays = display_manager_->displays();
+ mojo::Array<mojom::DisplayPtr> display_ptrs(displays.size());
+ {
+ size_t i = 0;
+ // TODO(sky): need ordering!
+ for (Display* display : displays) {
+ display_ptrs[i] = display->ToMojomDisplay();
+ delegate_->GetFrameDecorationsForUser(
+ user_id_, &(display_ptrs[i]->frame_decoration_values));
+ ++i;
+ }
+ }
+ return display_ptrs;
+}
+
+void UserDisplayManager::CallOnDisplays(
+ mojom::DisplayManagerObserver* observer) {
+ observer->OnDisplays(GetAllDisplays());
+}
+
+void UserDisplayManager::AddObserver(
+ mojom::DisplayManagerObserverPtr observer) {
+ mojom::DisplayManagerObserver* observer_impl = observer.get();
+ display_manager_observers_.AddPtr(std::move(observer));
+ OnObserverAdded(observer_impl);
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/user_display_manager.h b/chromium/components/mus/ws/user_display_manager.h
new file mode 100644
index 00000000000..1bfcdf81d8c
--- /dev/null
+++ b/chromium/components/mus/ws/user_display_manager.h
@@ -0,0 +1,118 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_USER_DISPLAY_MANAGER_H_
+#define COMPONENTS_MUS_WS_USER_DISPLAY_MANAGER_H_
+
+#include <set>
+
+#include "base/atomicops.h"
+#include "base/macros.h"
+#include "components/mus/public/interfaces/display.mojom.h"
+#include "components/mus/ws/user_id.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_ptr_set.h"
+
+namespace gfx {
+class Point;
+}
+
+namespace mus {
+namespace ws {
+
+class Display;
+class DisplayManager;
+class DisplayManagerDelegate;
+
+namespace test {
+class UserDisplayManagerTestApi;
+}
+
+// Provides per user display state.
+class UserDisplayManager : public mojom::DisplayManager {
+ public:
+ UserDisplayManager(ws::DisplayManager* display_manager,
+ DisplayManagerDelegate* delegate,
+ const UserId& user_id);
+ ~UserDisplayManager() override;
+
+ // Called when the frame decorations for this user change.
+ void OnFrameDecorationValuesChanged();
+
+ void AddDisplayManagerBinding(
+ mojo::InterfaceRequest<mojom::DisplayManager> request);
+
+ // Called by Display prior to |display| being removed and destroyed.
+ void OnWillDestroyDisplay(Display* display);
+
+ // Called from WindowManagerState when its EventDispatcher receives a mouse
+ // event.
+ void OnMouseCursorLocationChanged(const gfx::Point& point);
+
+ // Called when something about the display (e.g. pixel-ratio, size) changes.
+ void OnDisplayUpdate(Display* display);
+
+ // Returns a read-only handle to the shared memory which contains the global
+ // mouse cursor position. Each call returns a new handle.
+ mojo::ScopedSharedBufferHandle GetCursorLocationMemory();
+
+ private:
+ friend class test::UserDisplayManagerTestApi;
+
+ // Called when a new observer is added. If frame decorations are available
+ // notifies the observer immediately.
+ void OnObserverAdded(mojom::DisplayManagerObserver* observer);
+
+ mojo::Array<mojom::DisplayPtr> GetAllDisplays();
+
+ // Calls OnDisplays() on |observer|.
+ void CallOnDisplays(mojom::DisplayManagerObserver* observer);
+
+ // Overriden from mojom::DisplayManager:
+ void AddObserver(mojom::DisplayManagerObserverPtr observer) override;
+
+ base::subtle::Atomic32* cursor_location_memory() {
+ return reinterpret_cast<base::subtle::Atomic32*>(
+ cursor_location_mapping_.get());
+ }
+
+ ws::DisplayManager* display_manager_;
+
+ DisplayManagerDelegate* delegate_;
+
+ const UserId user_id_;
+
+ // Set to true the first time at least one Display has valid frame values.
+ bool got_valid_frame_decorations_;
+
+ mojo::BindingSet<mojom::DisplayManager> display_manager_bindings_;
+
+ // WARNING: only use these once |got_valid_frame_decorations_| is true.
+ mojo::InterfacePtrSet<mojom::DisplayManagerObserver>
+ display_manager_observers_;
+
+ // Observer used for tests.
+ mojom::DisplayManagerObserver* test_observer_ = nullptr;
+
+ // The current location of the cursor. This is always kept up to date so we
+ // can atomically write this to |cursor_location_memory()| once it is created.
+ base::subtle::Atomic32 current_cursor_location_;
+
+ // A handle to a shared memory buffer that is one 64 bit integer long. We
+ // share this with any client as the same user. This buffer is lazily
+ // created on the first access.
+ mojo::ScopedSharedBufferHandle cursor_location_handle_;
+
+ // The one int32 in |cursor_location_handle_|. When we write to this
+ // location, we must always write to it atomically. (On the other side of the
+ // mojo connection, this data must be read atomically.)
+ mojo::ScopedSharedBufferMapping cursor_location_mapping_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserDisplayManager);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_USER_DISPLAY_MANAGER_H_
diff --git a/chromium/components/mus/ws/user_display_manager_unittest.cc b/chromium/components/mus/ws/user_display_manager_unittest.cc
new file mode 100644
index 00000000000..2c2506cce26
--- /dev/null
+++ b/chromium/components/mus/ws/user_display_manager_unittest.cc
@@ -0,0 +1,244 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/atomicops.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/mus/common/types.h"
+#include "components/mus/common/util.h"
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "components/mus/surfaces/surfaces_state.h"
+#include "components/mus/ws/display_binding.h"
+#include "components/mus/ws/display_manager.h"
+#include "components/mus/ws/ids.h"
+#include "components/mus/ws/platform_display.h"
+#include "components/mus/ws/platform_display_factory.h"
+#include "components/mus/ws/platform_display_init_params.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/test_utils.h"
+#include "components/mus/ws/window_manager_state.h"
+#include "components/mus/ws/window_manager_window_tree_factory_set.h"
+#include "components/mus/ws/window_server.h"
+#include "components/mus/ws/window_server_delegate.h"
+#include "components/mus/ws/window_tree.h"
+#include "components/mus/ws/window_tree_binding.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace mus {
+namespace ws {
+namespace test {
+namespace {
+
+class TestDisplayManagerObserver : public mojom::DisplayManagerObserver {
+ public:
+ TestDisplayManagerObserver() {}
+ ~TestDisplayManagerObserver() override {}
+
+ std::string GetAndClearObserverCalls() {
+ std::string result;
+ std::swap(observer_calls_, result);
+ return result;
+ }
+
+ private:
+ void AddCall(const std::string& call) {
+ if (!observer_calls_.empty())
+ observer_calls_ += "\n";
+ observer_calls_ += call;
+ }
+
+ std::string DisplayIdsToString(
+ const mojo::Array<mojom::DisplayPtr>& displays) {
+ std::string display_ids;
+ for (const auto& display : displays) {
+ if (!display_ids.empty())
+ display_ids += " ";
+ display_ids += base::Int64ToString(display->id);
+ }
+ return display_ids;
+ }
+
+ // mojom::DisplayManagerObserver:
+ void OnDisplays(mojo::Array<mojom::DisplayPtr> displays) override {
+ AddCall("OnDisplays " + DisplayIdsToString(displays));
+ }
+ void OnDisplaysChanged(mojo::Array<mojom::DisplayPtr> displays) override {
+ AddCall("OnDisplaysChanged " + DisplayIdsToString(displays));
+ }
+ void OnDisplayRemoved(int64_t id) override {
+ AddCall("OnDisplayRemoved " + base::Int64ToString(id));
+ }
+
+ std::string observer_calls_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestDisplayManagerObserver);
+};
+
+mojom::FrameDecorationValuesPtr CreateDefaultFrameDecorationValues() {
+ return mojom::FrameDecorationValues::New();
+}
+
+} // namespace
+
+// -----------------------------------------------------------------------------
+
+class UserDisplayManagerTest : public testing::Test {
+ public:
+ UserDisplayManagerTest()
+ : cursor_id_(0), platform_display_factory_(&cursor_id_) {}
+ ~UserDisplayManagerTest() override {}
+
+ protected:
+ // testing::Test:
+ void SetUp() override {
+ PlatformDisplay::set_factory_for_testing(&platform_display_factory_);
+ window_server_.reset(new WindowServer(&window_server_delegate_,
+ scoped_refptr<SurfacesState>()));
+ window_server_delegate_.set_window_server(window_server_.get());
+ }
+
+ protected:
+ int32_t cursor_id_;
+ TestPlatformDisplayFactory platform_display_factory_;
+ TestWindowServerDelegate window_server_delegate_;
+ std::unique_ptr<WindowServer> window_server_;
+ base::MessageLoop message_loop_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(UserDisplayManagerTest);
+};
+
+TEST_F(UserDisplayManagerTest, OnlyNotifyWhenFrameDecorationsSet) {
+ window_server_delegate_.set_num_displays_to_create(1);
+
+ const UserId kUserId1 = "2";
+ TestDisplayManagerObserver display_manager_observer1;
+ DisplayManager* display_manager = window_server_->display_manager();
+ WindowManagerWindowTreeFactorySetTestApi(
+ window_server_->window_manager_window_tree_factory_set())
+ .Add(kUserId1);
+ UserDisplayManager* user_display_manager1 =
+ display_manager->GetUserDisplayManager(kUserId1);
+ ASSERT_TRUE(user_display_manager1);
+ UserDisplayManagerTestApi(user_display_manager1)
+ .SetTestObserver(&display_manager_observer1);
+ // Observer should not have been notified yet.
+ EXPECT_EQ(std::string(),
+ display_manager_observer1.GetAndClearObserverCalls());
+
+ // Set the frame decoration values, which should trigger sending immediately.
+ ASSERT_EQ(1u, display_manager->displays().size());
+ window_server_->window_manager_window_tree_factory_set()
+ ->GetWindowManagerStateForUser(kUserId1)
+ ->SetFrameDecorationValues(CreateDefaultFrameDecorationValues());
+ EXPECT_EQ("OnDisplays 1",
+ display_manager_observer1.GetAndClearObserverCalls());
+
+ UserDisplayManagerTestApi(user_display_manager1).SetTestObserver(nullptr);
+}
+
+TEST_F(UserDisplayManagerTest, AddObserverAfterFrameDecorationsSet) {
+ window_server_delegate_.set_num_displays_to_create(1);
+
+ const UserId kUserId1 = "2";
+ TestDisplayManagerObserver display_manager_observer1;
+ DisplayManager* display_manager = window_server_->display_manager();
+ WindowManagerWindowTreeFactorySetTestApi(
+ window_server_->window_manager_window_tree_factory_set())
+ .Add(kUserId1);
+ UserDisplayManager* user_display_manager1 =
+ display_manager->GetUserDisplayManager(kUserId1);
+ ASSERT_TRUE(user_display_manager1);
+ ASSERT_EQ(1u, display_manager->displays().size());
+ window_server_->window_manager_window_tree_factory_set()
+ ->GetWindowManagerStateForUser(kUserId1)
+ ->SetFrameDecorationValues(CreateDefaultFrameDecorationValues());
+
+ UserDisplayManagerTestApi(user_display_manager1)
+ .SetTestObserver(&display_manager_observer1);
+ EXPECT_EQ("OnDisplays 1",
+ display_manager_observer1.GetAndClearObserverCalls());
+
+ UserDisplayManagerTestApi(user_display_manager1).SetTestObserver(nullptr);
+}
+
+TEST_F(UserDisplayManagerTest, AddRemoveDisplay) {
+ window_server_delegate_.set_num_displays_to_create(1);
+
+ const UserId kUserId1 = "2";
+ TestDisplayManagerObserver display_manager_observer1;
+ DisplayManager* display_manager = window_server_->display_manager();
+ WindowManagerWindowTreeFactorySetTestApi(
+ window_server_->window_manager_window_tree_factory_set())
+ .Add(kUserId1);
+ UserDisplayManager* user_display_manager1 =
+ display_manager->GetUserDisplayManager(kUserId1);
+ ASSERT_TRUE(user_display_manager1);
+ ASSERT_EQ(1u, display_manager->displays().size());
+ window_server_->window_manager_window_tree_factory_set()
+ ->GetWindowManagerStateForUser(kUserId1)
+ ->SetFrameDecorationValues(CreateDefaultFrameDecorationValues());
+ UserDisplayManagerTestApi(user_display_manager1)
+ .SetTestObserver(&display_manager_observer1);
+ EXPECT_EQ("OnDisplays 1",
+ display_manager_observer1.GetAndClearObserverCalls());
+
+ // Add another display.
+ Display* display2 =
+ new Display(window_server_.get(), PlatformDisplayInitParams());
+ display2->Init(nullptr);
+
+ // Observer should be notified immediately as frame decorations were set.
+ EXPECT_EQ("OnDisplaysChanged 2",
+ display_manager_observer1.GetAndClearObserverCalls());
+
+ // Remove the display and verify observer is notified.
+ display_manager->DestroyDisplay(display2);
+ display2 = nullptr;
+ EXPECT_EQ("OnDisplayRemoved 2",
+ display_manager_observer1.GetAndClearObserverCalls());
+
+ UserDisplayManagerTestApi(user_display_manager1).SetTestObserver(nullptr);
+}
+
+TEST_F(UserDisplayManagerTest, NegativeCoordinates) {
+ window_server_delegate_.set_num_displays_to_create(1);
+
+ const UserId kUserId1 = "2";
+ TestDisplayManagerObserver display_manager_observer1;
+ DisplayManager* display_manager = window_server_->display_manager();
+ WindowManagerWindowTreeFactorySetTestApi(
+ window_server_->window_manager_window_tree_factory_set())
+ .Add(kUserId1);
+ UserDisplayManager* user_display_manager1 =
+ display_manager->GetUserDisplayManager(kUserId1);
+ ASSERT_TRUE(user_display_manager1);
+
+ user_display_manager1->OnMouseCursorLocationChanged(gfx::Point(-10, -11));
+
+ base::subtle::Atomic32* cursor_location_memory = nullptr;
+ mojo::ScopedSharedBufferHandle handle =
+ user_display_manager1->GetCursorLocationMemory();
+ mojo::ScopedSharedBufferMapping cursor_location_mapping =
+ handle->Map(sizeof(base::subtle::Atomic32));
+ ASSERT_TRUE(cursor_location_mapping);
+ cursor_location_memory =
+ reinterpret_cast<base::subtle::Atomic32*>(cursor_location_mapping.get());
+
+ base::subtle::Atomic32 location =
+ base::subtle::NoBarrier_Load(cursor_location_memory);
+ EXPECT_EQ(gfx::Point(static_cast<int16_t>(location >> 16),
+ static_cast<int16_t>(location & 0xFFFF)),
+ gfx::Point(-10, -11));
+}
+
+} // namespace test
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/user_id.h b/chromium/components/mus/ws/user_id.h
new file mode 100644
index 00000000000..dbb59965487
--- /dev/null
+++ b/chromium/components/mus/ws/user_id.h
@@ -0,0 +1,22 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_USER_ID_H_
+#define COMPONENTS_MUS_WS_USER_ID_H_
+
+#include <string>
+
+namespace mus {
+namespace ws {
+
+using UserId = std::string;
+
+inline UserId InvalidUserId() {
+ return std::string();
+}
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_USER_ID_H_
diff --git a/chromium/components/mus/ws/user_id_tracker.cc b/chromium/components/mus/ws/user_id_tracker.cc
new file mode 100644
index 00000000000..40a7dde987b
--- /dev/null
+++ b/chromium/components/mus/ws/user_id_tracker.cc
@@ -0,0 +1,68 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/user_id_tracker.h"
+
+#include "components/mus/ws/user_id_tracker_observer.h"
+#include "services/shell/public/interfaces/connector.mojom.h"
+
+namespace mus {
+namespace ws {
+
+UserIdTracker::UserIdTracker() : active_id_(shell::mojom::kRootUserID) {
+ ids_.insert(active_id_);
+}
+
+UserIdTracker::~UserIdTracker() {
+}
+
+bool UserIdTracker::IsValidUserId(const UserId& id) const {
+ return ids_.count(id) > 0;
+}
+
+void UserIdTracker::SetActiveUserId(const UserId& id) {
+ DCHECK(IsValidUserId(id));
+ if (id == active_id_)
+ return;
+
+ const UserId previously_active_id = active_id_;
+ active_id_ = id;
+ FOR_EACH_OBSERVER(UserIdTrackerObserver, observers_,
+ OnActiveUserIdChanged(previously_active_id, id));
+}
+
+void UserIdTracker::AddUserId(const UserId& id) {
+ if (IsValidUserId(id))
+ return;
+
+ ids_.insert(id);
+ FOR_EACH_OBSERVER(UserIdTrackerObserver, observers_, OnUserIdAdded(id));
+}
+
+void UserIdTracker::RemoveUserId(const UserId& id) {
+ DCHECK(IsValidUserId(id));
+ ids_.erase(id);
+ FOR_EACH_OBSERVER(UserIdTrackerObserver, observers_, OnUserIdRemoved(id));
+}
+
+void UserIdTracker::AddObserver(UserIdTrackerObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void UserIdTracker::RemoveObserver(UserIdTrackerObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void UserIdTracker::Bind(mojom::UserAccessManagerRequest request) {
+ bindings_.AddBinding(this, std::move(request));
+}
+
+void UserIdTracker::SetActiveUser(const mojo::String& user_id) {
+ if (!IsValidUserId(user_id))
+ AddUserId(user_id);
+ SetActiveUserId(user_id);
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/user_id_tracker.h b/chromium/components/mus/ws/user_id_tracker.h
new file mode 100644
index 00000000000..c0b2ca54407
--- /dev/null
+++ b/chromium/components/mus/ws/user_id_tracker.h
@@ -0,0 +1,62 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_USER_ID_TRACKER_H_
+#define COMPONENTS_MUS_WS_USER_ID_TRACKER_H_
+
+#include <stdint.h>
+
+#include <set>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "components/mus/public/interfaces/user_access_manager.mojom.h"
+#include "components/mus/ws/user_id.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+
+namespace mus {
+namespace ws {
+
+class UserIdTrackerObserver;
+
+// Tracks the set of known/valid user ids.
+class UserIdTracker : public mojom::UserAccessManager {
+ public:
+ UserIdTracker();
+ ~UserIdTracker() override;
+
+ bool IsValidUserId(const UserId& id) const;
+
+ const UserId& active_id() const { return active_id_; }
+
+ // Adds a new known user. Does nothing if |id| is not known.
+ void SetActiveUserId(const UserId& id);
+ void AddUserId(const UserId& id);
+ void RemoveUserId(const UserId& id);
+
+ void AddObserver(UserIdTrackerObserver* observer);
+ void RemoveObserver(UserIdTrackerObserver* observer);
+
+ void Bind(mojom::UserAccessManagerRequest request);
+
+ private:
+ using UserIdSet = std::set<UserId>;
+
+ // mojom::UserAccessManager:
+ void SetActiveUser(const mojo::String& user_id) override;
+
+ base::ObserverList<UserIdTrackerObserver> observers_;
+
+ UserIdSet ids_;
+ UserId active_id_;
+
+ mojo::BindingSet<mojom::UserAccessManager> bindings_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserIdTracker);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_USER_ID_TRACKER_H_
diff --git a/chromium/components/mus/ws/user_id_tracker_observer.h b/chromium/components/mus/ws/user_id_tracker_observer.h
new file mode 100644
index 00000000000..e770ac3de23
--- /dev/null
+++ b/chromium/components/mus/ws/user_id_tracker_observer.h
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_USER_ID_TRACKER_OBSERVER_H_
+#define COMPONENTS_MUS_WS_USER_ID_TRACKER_OBSERVER_H_
+
+#include <stdint.h>
+
+#include "components/mus/ws/user_id.h"
+
+namespace mus {
+namespace ws {
+
+class UserIdTrackerObserver {
+ public:
+ virtual void OnActiveUserIdChanged(const UserId& previously_active_id,
+ const UserId& active_id) {}
+ virtual void OnUserIdAdded(const UserId& id) {}
+ virtual void OnUserIdRemoved(const UserId& id) {}
+
+ protected:
+ virtual ~UserIdTrackerObserver() {}
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_USER_ID_TRACKER_OBSERVER_H_
diff --git a/chromium/components/mus/ws/window_coordinate_conversions.cc b/chromium/components/mus/ws/window_coordinate_conversions.cc
new file mode 100644
index 00000000000..81530fa820e
--- /dev/null
+++ b/chromium/components/mus/ws/window_coordinate_conversions.cc
@@ -0,0 +1,74 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/window_coordinate_conversions.h"
+
+#include "components/mus/ws/server_window.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/vector2d.h"
+#include "ui/gfx/geometry/vector2d_f.h"
+
+namespace mus {
+
+namespace ws {
+
+namespace {
+
+gfx::Vector2dF CalculateOffsetToAncestor(const ServerWindow* window,
+ const ServerWindow* ancestor) {
+ DCHECK(ancestor->Contains(window));
+ gfx::Vector2d result;
+ for (const ServerWindow* v = window; v != ancestor; v = v->parent())
+ result += v->bounds().OffsetFromOrigin();
+ return gfx::Vector2dF(result.x(), result.y());
+}
+
+} // namespace
+
+gfx::Point ConvertPointBetweenWindows(const ServerWindow* from,
+ const ServerWindow* to,
+ const gfx::Point& point) {
+ return gfx::ToFlooredPoint(
+ ConvertPointFBetweenWindows(from, to, gfx::PointF(point.x(), point.y())));
+}
+
+gfx::PointF ConvertPointFBetweenWindows(const ServerWindow* from,
+ const ServerWindow* to,
+ const gfx::PointF& point) {
+ DCHECK(from);
+ DCHECK(to);
+ if (from == to)
+ return point;
+
+ if (from->Contains(to)) {
+ const gfx::Vector2dF offset(CalculateOffsetToAncestor(to, from));
+ return point - offset;
+ }
+ DCHECK(to->Contains(from));
+ const gfx::Vector2dF offset(CalculateOffsetToAncestor(from, to));
+ return point + offset;
+}
+
+gfx::Rect ConvertRectBetweenWindows(const ServerWindow* from,
+ const ServerWindow* to,
+ const gfx::Rect& rect) {
+ DCHECK(from);
+ DCHECK(to);
+ if (from == to)
+ return rect;
+
+ const gfx::Point top_left(
+ ConvertPointBetweenWindows(from, to, rect.origin()));
+ const gfx::Point bottom_right(gfx::ToCeiledPoint(ConvertPointFBetweenWindows(
+ from, to, gfx::PointF(rect.right(), rect.bottom()))));
+ return gfx::Rect(top_left.x(), top_left.y(), bottom_right.x() - top_left.x(),
+ bottom_right.y() - top_left.y());
+}
+
+} // namespace ws
+
+} // namespace mus
diff --git a/chromium/components/mus/ws/window_coordinate_conversions.h b/chromium/components/mus/ws/window_coordinate_conversions.h
new file mode 100644
index 00000000000..39601590a80
--- /dev/null
+++ b/chromium/components/mus/ws/window_coordinate_conversions.h
@@ -0,0 +1,39 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_WINDOW_COORDINATE_CONVERSIONS_H_
+#define COMPONENTS_MUS_WS_WINDOW_COORDINATE_CONVERSIONS_H_
+
+namespace gfx {
+class Point;
+class PointF;
+class Rect;
+}
+
+namespace mus {
+
+namespace ws {
+
+class ServerWindow;
+
+// Converts |point| from the coordinates of |from| to the coordinates of |to|.
+// |from| and |to| must be an ancestors or descendants of each other.
+gfx::Point ConvertPointBetweenWindows(const ServerWindow* from,
+ const ServerWindow* to,
+ const gfx::Point& point);
+gfx::PointF ConvertPointFBetweenWindows(const ServerWindow* from,
+ const ServerWindow* to,
+ const gfx::PointF& point);
+
+// Converts |rect| from the coordinates of |from| to the coordinates of |to|.
+// |from| and |to| must be an ancestors or descendants of each other.
+gfx::Rect ConvertRectBetweenWindows(const ServerWindow* from,
+ const ServerWindow* to,
+ const gfx::Rect& rect);
+
+} // namespace ws
+
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_WINDOW_COORDINATE_CONVERSIONS_H_
diff --git a/chromium/components/mus/ws/window_coordinate_conversions_unittest.cc b/chromium/components/mus/ws/window_coordinate_conversions_unittest.cc
new file mode 100644
index 00000000000..8c42c09f6c7
--- /dev/null
+++ b/chromium/components/mus/ws/window_coordinate_conversions_unittest.cc
@@ -0,0 +1,62 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/window_coordinate_conversions.h"
+
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/server_window_delegate.h"
+#include "components/mus/ws/test_server_window_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace mus {
+
+namespace ws {
+
+using WindowCoordinateConversionsTest = testing::Test;
+
+TEST_F(WindowCoordinateConversionsTest, ConvertRectBetweenWindows) {
+ TestServerWindowDelegate d1, d2, d3;
+ ServerWindow v1(&d1, WindowId()), v2(&d2, WindowId()), v3(&d3, WindowId());
+ v1.SetBounds(gfx::Rect(1, 2, 100, 100));
+ v2.SetBounds(gfx::Rect(3, 4, 100, 100));
+ v3.SetBounds(gfx::Rect(5, 6, 100, 100));
+ v1.Add(&v2);
+ v2.Add(&v3);
+
+ EXPECT_EQ(gfx::Rect(2, 1, 8, 9),
+ ConvertRectBetweenWindows(&v1, &v3, gfx::Rect(10, 11, 8, 9)));
+
+ EXPECT_EQ(gfx::Rect(18, 21, 8, 9),
+ ConvertRectBetweenWindows(&v3, &v1, gfx::Rect(10, 11, 8, 9)));
+}
+
+TEST_F(WindowCoordinateConversionsTest, ConvertPointFBetweenWindows) {
+ TestServerWindowDelegate d1, d2, d3;
+ ServerWindow v1(&d1, WindowId()), v2(&d2, WindowId()), v3(&d3, WindowId());
+ v1.SetBounds(gfx::Rect(1, 2, 100, 100));
+ v2.SetBounds(gfx::Rect(3, 4, 100, 100));
+ v3.SetBounds(gfx::Rect(5, 6, 100, 100));
+ v1.Add(&v2);
+ v2.Add(&v3);
+
+ {
+ const gfx::PointF result(
+ ConvertPointFBetweenWindows(&v1, &v3, gfx::PointF(10.5f, 11.9f)));
+ EXPECT_FLOAT_EQ(2.5f, result.x());
+ EXPECT_FLOAT_EQ(1.9f, result.y());
+ }
+
+ {
+ const gfx::PointF result(
+ ConvertPointFBetweenWindows(&v3, &v1, gfx::PointF(10.2f, 11.4f)));
+ EXPECT_FLOAT_EQ(18.2f, result.x());
+ EXPECT_FLOAT_EQ(21.4f, result.y());
+ }
+}
+
+} // namespace ws
+
+} // namespace mus
diff --git a/chromium/components/mus/ws/window_finder.cc b/chromium/components/mus/ws/window_finder.cc
new file mode 100644
index 00000000000..466bd5a6a8e
--- /dev/null
+++ b/chromium/components/mus/ws/window_finder.cc
@@ -0,0 +1,68 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/window_finder.h"
+
+#include "components/mus/surfaces/surfaces_state.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/server_window_delegate.h"
+#include "components/mus/ws/server_window_surface.h"
+#include "components/mus/ws/server_window_surface_manager.h"
+#include "components/mus/ws/window_coordinate_conversions.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/transform.h"
+
+namespace mus {
+namespace ws {
+
+bool IsValidWindowForEvents(ServerWindow* window) {
+ ServerWindowSurfaceManager* surface_manager = window->surface_manager();
+ return surface_manager &&
+ surface_manager->HasSurfaceOfType(mojom::SurfaceType::DEFAULT);
+}
+
+ServerWindow* FindDeepestVisibleWindowForEvents(ServerWindow* window,
+ gfx::Point* location) {
+ const ServerWindow::Windows children(window->GetChildren());
+ for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
+ ServerWindow* child = *iter;
+ if (!child->visible())
+ continue;
+
+ // TODO(sky): support transform.
+ gfx::Point child_location(location->x() - child->bounds().x(),
+ location->y() - child->bounds().y());
+ gfx::Rect child_bounds(child->bounds().size());
+ child_bounds.Inset(-child->extended_hit_test_region().left(),
+ -child->extended_hit_test_region().top(),
+ -child->extended_hit_test_region().right(),
+ -child->extended_hit_test_region().bottom());
+ if (!child_bounds.Contains(child_location))
+ continue;
+
+ if (child->hit_test_mask() &&
+ !child->hit_test_mask()->Contains(child_location))
+ continue;
+
+ *location = child_location;
+ ServerWindow* result = FindDeepestVisibleWindowForEvents(child, location);
+ if (IsValidWindowForEvents(result))
+ return result;
+ }
+ return window;
+}
+
+gfx::Transform GetTransformToWindow(ServerWindow* window) {
+ gfx::Transform transform;
+ ServerWindow* current = window;
+ while (current->parent()) {
+ transform.Translate(-current->bounds().x(), -current->bounds().y());
+ current = current->parent();
+ }
+ return transform;
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/window_finder.h b/chromium/components/mus/ws/window_finder.h
new file mode 100644
index 00000000000..2b535fcb35b
--- /dev/null
+++ b/chromium/components/mus/ws/window_finder.h
@@ -0,0 +1,33 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_WINDOW_FINDER_H_
+#define COMPONENTS_MUS_WS_WINDOW_FINDER_H_
+
+namespace gfx {
+class Point;
+class Transform;
+}
+
+namespace mus {
+namespace ws {
+
+class ServerWindow;
+
+// Find the deepest visible child of |root| that should receive an event at
+// |location|. |location| is initially in the coordinate space of
+// |root_window|, on return it is converted to the coordinates of the return
+// value.
+ServerWindow* FindDeepestVisibleWindowForEvents(
+ ServerWindow* root_window,
+ gfx::Point* location);
+
+// Retrieve the transform to the provided |window|'s coordinate space from the
+// root.
+gfx::Transform GetTransformToWindow(ServerWindow* window);
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_WINDOW_FINDER_H_
diff --git a/chromium/components/mus/ws/window_finder_unittest.cc b/chromium/components/mus/ws/window_finder_unittest.cc
new file mode 100644
index 00000000000..f910624ce43
--- /dev/null
+++ b/chromium/components/mus/ws/window_finder_unittest.cc
@@ -0,0 +1,79 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/window_finder.h"
+
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/server_window_surface_manager.h"
+#include "components/mus/ws/server_window_surface_manager_test_api.h"
+#include "components/mus/ws/test_server_window_delegate.h"
+#include "components/mus/ws/window_finder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mus {
+namespace ws {
+
+TEST(WindowFinderTest, FindDeepestVisibleWindow) {
+ TestServerWindowDelegate window_delegate;
+ ServerWindow root(&window_delegate, WindowId(1, 2));
+ window_delegate.set_root_window(&root);
+ root.SetVisible(true);
+ root.SetBounds(gfx::Rect(0, 0, 100, 100));
+
+ ServerWindow child1(&window_delegate, WindowId(1, 3));
+ root.Add(&child1);
+ EnableHitTest(&child1);
+ child1.SetVisible(true);
+ child1.SetBounds(gfx::Rect(10, 10, 20, 20));
+
+ ServerWindow child2(&window_delegate, WindowId(1, 4));
+ root.Add(&child2);
+ EnableHitTest(&child2);
+ child2.SetVisible(true);
+ child2.SetBounds(gfx::Rect(15, 15, 20, 20));
+
+ gfx::Point local_point(16, 16);
+ EXPECT_EQ(&child2, FindDeepestVisibleWindowForEvents(&root, &local_point));
+ EXPECT_EQ(gfx::Point(1, 1), local_point);
+
+ local_point.SetPoint(13, 14);
+ EXPECT_EQ(&child1, FindDeepestVisibleWindowForEvents(&root, &local_point));
+ EXPECT_EQ(gfx::Point(3, 4), local_point);
+
+ child2.set_extended_hit_test_region(gfx::Insets(10, 10, 10, 10));
+ local_point.SetPoint(13, 14);
+ EXPECT_EQ(&child2, FindDeepestVisibleWindowForEvents(&root, &local_point));
+ EXPECT_EQ(gfx::Point(-2, -1), local_point);
+}
+
+TEST(WindowFinderTest, FindDeepestVisibleWindowHitTestMask) {
+ TestServerWindowDelegate window_delegate;
+ ServerWindow root(&window_delegate, WindowId(1, 2));
+ window_delegate.set_root_window(&root);
+ EnableHitTest(&root);
+ root.SetVisible(true);
+ root.SetBounds(gfx::Rect(0, 0, 100, 100));
+
+ ServerWindow child_with_mask(&window_delegate, WindowId(1, 4));
+ root.Add(&child_with_mask);
+ EnableHitTest(&child_with_mask);
+ child_with_mask.SetVisible(true);
+ child_with_mask.SetBounds(gfx::Rect(10, 10, 20, 20));
+ child_with_mask.SetHitTestMask(gfx::Rect(2, 2, 16, 16));
+
+ // Test a point inside the window but outside the mask.
+ gfx::Point point_outside_mask(11, 11);
+ EXPECT_EQ(&root,
+ FindDeepestVisibleWindowForEvents(&root, &point_outside_mask));
+ EXPECT_EQ(gfx::Point(11, 11), point_outside_mask);
+
+ // Test a point inside the window and inside the mask.
+ gfx::Point point_inside_mask(15, 15);
+ EXPECT_EQ(&child_with_mask,
+ FindDeepestVisibleWindowForEvents(&root, &point_inside_mask));
+ EXPECT_EQ(gfx::Point(5, 5), point_inside_mask);
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/window_manager_access_policy.cc b/chromium/components/mus/ws/window_manager_access_policy.cc
new file mode 100644
index 00000000000..7682c264383
--- /dev/null
+++ b/chromium/components/mus/ws/window_manager_access_policy.cc
@@ -0,0 +1,184 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/window_manager_access_policy.h"
+
+#include "components/mus/ws/access_policy_delegate.h"
+#include "components/mus/ws/server_window.h"
+
+namespace mus {
+namespace ws {
+
+WindowManagerAccessPolicy::WindowManagerAccessPolicy() {}
+
+WindowManagerAccessPolicy::~WindowManagerAccessPolicy() {}
+
+void WindowManagerAccessPolicy::Init(ClientSpecificId client_id,
+ AccessPolicyDelegate* delegate) {
+ client_id_ = client_id;
+ delegate_ = delegate;
+}
+
+bool WindowManagerAccessPolicy::CanRemoveWindowFromParent(
+ const ServerWindow* window) const {
+ return true;
+}
+
+bool WindowManagerAccessPolicy::CanAddWindow(const ServerWindow* parent,
+ const ServerWindow* child) const {
+ return true;
+}
+
+bool WindowManagerAccessPolicy::CanAddTransientWindow(
+ const ServerWindow* parent,
+ const ServerWindow* child) const {
+ return true;
+}
+
+bool WindowManagerAccessPolicy::CanRemoveTransientWindowFromParent(
+ const ServerWindow* window) const {
+ return true;
+}
+
+bool WindowManagerAccessPolicy::CanSetModal(
+ const ServerWindow* window) const {
+ return true;
+}
+
+bool WindowManagerAccessPolicy::CanReorderWindow(
+ const ServerWindow* window,
+ const ServerWindow* relative_window,
+ mojom::OrderDirection direction) const {
+ return true;
+}
+
+bool WindowManagerAccessPolicy::CanDeleteWindow(
+ const ServerWindow* window) const {
+ return WasCreatedByThisClient(window);
+}
+
+bool WindowManagerAccessPolicy::CanGetWindowTree(
+ const ServerWindow* window) const {
+ return true;
+}
+
+bool WindowManagerAccessPolicy::CanDescendIntoWindowForWindowTree(
+ const ServerWindow* window) const {
+ return true;
+}
+
+bool WindowManagerAccessPolicy::CanEmbed(const ServerWindow* window) const {
+ return !delegate_->HasRootForAccessPolicy(window);
+}
+
+bool WindowManagerAccessPolicy::CanChangeWindowVisibility(
+ const ServerWindow* window) const {
+ if (WasCreatedByThisClient(window))
+ return true;
+ // The WindowManager can change the visibility of the WindowManager root.
+ const ServerWindow* root = window->GetRoot();
+ return root && window->parent() == root;
+}
+
+bool WindowManagerAccessPolicy::CanChangeWindowOpacity(
+ const ServerWindow* window) const {
+ return WasCreatedByThisClient(window);
+}
+
+bool WindowManagerAccessPolicy::CanSetWindowSurface(
+ const ServerWindow* window,
+ mus::mojom::SurfaceType surface_type) const {
+ if (surface_type == mojom::SurfaceType::UNDERLAY)
+ return WasCreatedByThisClient(window);
+
+ if (delegate_->IsWindowRootOfAnotherTreeForAccessPolicy(window))
+ return false;
+ return WasCreatedByThisClient(window) ||
+ (delegate_->HasRootForAccessPolicy(window));
+}
+
+bool WindowManagerAccessPolicy::CanSetWindowBounds(
+ const ServerWindow* window) const {
+ return WasCreatedByThisClient(window);
+}
+
+bool WindowManagerAccessPolicy::CanSetWindowProperties(
+ const ServerWindow* window) const {
+ return WasCreatedByThisClient(window);
+}
+
+bool WindowManagerAccessPolicy::CanSetWindowTextInputState(
+ const ServerWindow* window) const {
+ return WasCreatedByThisClient(window);
+}
+
+bool WindowManagerAccessPolicy::CanSetCapture(
+ const ServerWindow* window) const {
+ return WasCreatedByThisClient(window);
+}
+
+bool WindowManagerAccessPolicy::CanSetFocus(const ServerWindow* window) const {
+ return true;
+}
+
+bool WindowManagerAccessPolicy::CanSetClientArea(
+ const ServerWindow* window) const {
+ return WasCreatedByThisClient(window) ||
+ delegate_->HasRootForAccessPolicy(window);
+}
+
+bool WindowManagerAccessPolicy::CanSetHitTestMask(
+ const ServerWindow* window) const {
+ return WasCreatedByThisClient(window) ||
+ delegate_->HasRootForAccessPolicy(window);
+}
+
+bool WindowManagerAccessPolicy::CanSetCursorProperties(
+ const ServerWindow* window) const {
+ return WasCreatedByThisClient(window) ||
+ delegate_->HasRootForAccessPolicy(window);
+}
+
+bool WindowManagerAccessPolicy::ShouldNotifyOnHierarchyChange(
+ const ServerWindow* window,
+ const ServerWindow** new_parent,
+ const ServerWindow** old_parent) const {
+ // Notify if we've already told the window manager about the window, or if
+ // we've
+ // already told the window manager about the parent. The later handles the
+ // case of a window that wasn't parented to the root getting added to the
+ // root.
+ return IsWindowKnown(window) || (*new_parent && IsWindowKnown(*new_parent));
+}
+
+bool WindowManagerAccessPolicy::CanSetWindowManager() const {
+ return true;
+}
+
+const ServerWindow* WindowManagerAccessPolicy::GetWindowForFocusChange(
+ const ServerWindow* focused) {
+ return focused;
+}
+
+bool WindowManagerAccessPolicy::IsWindowKnown(
+ const ServerWindow* window) const {
+ return delegate_->IsWindowKnownForAccessPolicy(window);
+}
+
+bool WindowManagerAccessPolicy::IsValidIdForNewWindow(
+ const ClientWindowId& id) const {
+ // The WindowManager see windows created from other clients. If the WM doesn't
+ // use the client id when creating windows the WM could end up with two
+ // windows with the same id. Because of this the wm must use the same
+ // client id for all windows it creates.
+ return WindowIdFromTransportId(id.id).client_id == client_id_;
+}
+
+bool WindowManagerAccessPolicy::WasCreatedByThisClient(
+ const ServerWindow* window) const {
+ return window->id().client_id == client_id_;
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/window_manager_access_policy.h b/chromium/components/mus/ws/window_manager_access_policy.h
new file mode 100644
index 00000000000..8b9c6edcec9
--- /dev/null
+++ b/chromium/components/mus/ws/window_manager_access_policy.h
@@ -0,0 +1,76 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_WINDOW_MANAGER_ACCESS_POLICY_H_
+#define COMPONENTS_MUS_WS_WINDOW_MANAGER_ACCESS_POLICY_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "components/mus/ws/access_policy.h"
+
+namespace mus {
+namespace ws {
+
+class AccessPolicyDelegate;
+
+class WindowManagerAccessPolicy : public AccessPolicy {
+ public:
+ WindowManagerAccessPolicy();
+ ~WindowManagerAccessPolicy() override;
+
+ // AccessPolicy:
+ void Init(ClientSpecificId client_id,
+ AccessPolicyDelegate* delegate) override;
+ bool CanRemoveWindowFromParent(const ServerWindow* window) const override;
+ bool CanAddWindow(const ServerWindow* parent,
+ const ServerWindow* child) const override;
+ bool CanAddTransientWindow(const ServerWindow* parent,
+ const ServerWindow* child) const override;
+ bool CanRemoveTransientWindowFromParent(
+ const ServerWindow* window) const override;
+ bool CanSetModal(const ServerWindow* window) const override;
+ bool CanReorderWindow(const ServerWindow* window,
+ const ServerWindow* relative_window,
+ mojom::OrderDirection direction) const override;
+ bool CanDeleteWindow(const ServerWindow* window) const override;
+ bool CanGetWindowTree(const ServerWindow* window) const override;
+ bool CanDescendIntoWindowForWindowTree(
+ const ServerWindow* window) const override;
+ bool CanEmbed(const ServerWindow* window) const override;
+ bool CanChangeWindowVisibility(const ServerWindow* window) const override;
+ bool CanChangeWindowOpacity(const ServerWindow* window) const override;
+ bool CanSetWindowSurface(const ServerWindow* window,
+ mus::mojom::SurfaceType surface_type) const override;
+ bool CanSetWindowBounds(const ServerWindow* window) const override;
+ bool CanSetWindowProperties(const ServerWindow* window) const override;
+ bool CanSetWindowTextInputState(const ServerWindow* window) const override;
+ bool CanSetCapture(const ServerWindow* window) const override;
+ bool CanSetFocus(const ServerWindow* window) const override;
+ bool CanSetClientArea(const ServerWindow* window) const override;
+ bool CanSetHitTestMask(const ServerWindow* window) const override;
+ bool CanSetCursorProperties(const ServerWindow* window) const override;
+ bool ShouldNotifyOnHierarchyChange(
+ const ServerWindow* window,
+ const ServerWindow** new_parent,
+ const ServerWindow** old_parent) const override;
+ const ServerWindow* GetWindowForFocusChange(
+ const ServerWindow* focused) override;
+ bool CanSetWindowManager() const override;
+ bool IsValidIdForNewWindow(const ClientWindowId& id) const override;
+
+ private:
+ bool IsWindowKnown(const ServerWindow* window) const;
+ bool WasCreatedByThisClient(const ServerWindow* window) const;
+
+ ClientSpecificId client_id_ = 0u;
+ AccessPolicyDelegate* delegate_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowManagerAccessPolicy);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_WINDOW_MANAGER_ACCESS_POLICY_H_
diff --git a/chromium/components/mus/ws/window_manager_client_unittest.cc b/chromium/components/mus/ws/window_manager_client_unittest.cc
new file mode 100644
index 00000000000..a8049c6b69b
--- /dev/null
+++ b/chromium/components/mus/ws/window_manager_client_unittest.cc
@@ -0,0 +1,1171 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "components/mus/common/util.h"
+#include "components/mus/public/cpp/lib/window_private.h"
+#include "components/mus/public/cpp/tests/window_server_test_base.h"
+#include "components/mus/public/cpp/window_observer.h"
+#include "components/mus/public/cpp/window_tree_client.h"
+#include "components/mus/public/cpp/window_tree_client_delegate.h"
+#include "components/mus/public/cpp/window_tree_client_observer.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace mus {
+namespace ws {
+
+namespace {
+
+Id server_id(mus::Window* window) {
+ return WindowPrivate(window).server_id();
+}
+
+mus::Window* GetChildWindowByServerId(WindowTreeClient* client,
+ uint32_t id) {
+ return client->GetWindowByServerId(id);
+}
+
+int ValidIndexOf(const Window::Children& windows, Window* window) {
+ Window::Children::const_iterator it =
+ std::find(windows.begin(), windows.end(), window);
+ return (it != windows.end()) ? (it - windows.begin()) : -1;
+}
+
+class TestWindowManagerDelegate : public WindowManagerDelegate {
+ public:
+ TestWindowManagerDelegate() {}
+ ~TestWindowManagerDelegate() override {}
+
+ // WindowManagerDelegate:
+ void SetWindowManagerClient(WindowManagerClient* client) override {}
+ bool OnWmSetBounds(Window* window, gfx::Rect* bounds) override {
+ return false;
+ }
+ bool OnWmSetProperty(
+ Window* window,
+ const std::string& name,
+ std::unique_ptr<std::vector<uint8_t>>* new_data) override {
+ return true;
+ }
+ Window* OnWmCreateTopLevelWindow(
+ std::map<std::string, std::vector<uint8_t>>* properties) override {
+ return nullptr;
+ }
+ void OnWmClientJankinessChanged(const std::set<Window*>& client_windows,
+ bool janky) override {}
+ void OnWmNewDisplay(Window* window,
+ const display::Display& display) override {}
+ void OnAccelerator(uint32_t id, const ui::Event& event) override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestWindowManagerDelegate);
+};
+
+class BoundsChangeObserver : public WindowObserver {
+ public:
+ explicit BoundsChangeObserver(Window* window) : window_(window) {
+ window_->AddObserver(this);
+ }
+ ~BoundsChangeObserver() override { window_->RemoveObserver(this); }
+
+ private:
+ // Overridden from WindowObserver:
+ void OnWindowBoundsChanged(Window* window,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) override {
+ DCHECK_EQ(window, window_);
+ EXPECT_TRUE(WindowServerTestBase::QuitRunLoop());
+ }
+
+ Window* window_;
+
+ DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver);
+};
+
+// Wait until the bounds of the supplied window change; returns false on
+// timeout.
+bool WaitForBoundsToChange(Window* window) {
+ BoundsChangeObserver observer(window);
+ return WindowServerTestBase::DoRunLoopWithTimeout();
+}
+
+class ClientAreaChangeObserver : public WindowObserver {
+ public:
+ explicit ClientAreaChangeObserver(Window* window) : window_(window) {
+ window_->AddObserver(this);
+ }
+ ~ClientAreaChangeObserver() override { window_->RemoveObserver(this); }
+
+ private:
+ // Overridden from WindowObserver:
+ void OnWindowClientAreaChanged(
+ Window* window,
+ const gfx::Insets& old_client_area,
+ const std::vector<gfx::Rect>& old_additional_client_areas) override {
+ DCHECK_EQ(window, window_);
+ EXPECT_TRUE(WindowServerTestBase::QuitRunLoop());
+ }
+
+ Window* window_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClientAreaChangeObserver);
+};
+
+// Wait until the bounds of the supplied window change; returns false on
+// timeout.
+bool WaitForClientAreaToChange(Window* window) {
+ ClientAreaChangeObserver observer(window);
+ return WindowServerTestBase::DoRunLoopWithTimeout();
+}
+
+// Spins a run loop until the tree beginning at |root| has |tree_size| windows
+// (including |root|).
+class TreeSizeMatchesObserver : public WindowObserver {
+ public:
+ TreeSizeMatchesObserver(Window* tree, size_t tree_size)
+ : tree_(tree), tree_size_(tree_size) {
+ tree_->AddObserver(this);
+ }
+ ~TreeSizeMatchesObserver() override { tree_->RemoveObserver(this); }
+
+ bool IsTreeCorrectSize() { return CountWindows(tree_) == tree_size_; }
+
+ private:
+ // Overridden from WindowObserver:
+ void OnTreeChanged(const TreeChangeParams& params) override {
+ if (IsTreeCorrectSize())
+ EXPECT_TRUE(WindowServerTestBase::QuitRunLoop());
+ }
+
+ size_t CountWindows(const Window* window) const {
+ size_t count = 1;
+ Window::Children::const_iterator it = window->children().begin();
+ for (; it != window->children().end(); ++it)
+ count += CountWindows(*it);
+ return count;
+ }
+
+ Window* tree_;
+ size_t tree_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver);
+};
+
+// Wait until |window| has |tree_size| descendants; returns false on timeout.
+// The count includes |window|. For example, if you want to wait for |window| to
+// have a single child, use a |tree_size| of 2.
+bool WaitForTreeSizeToMatch(Window* window, size_t tree_size) {
+ TreeSizeMatchesObserver observer(window, tree_size);
+ return observer.IsTreeCorrectSize() ||
+ WindowServerTestBase::DoRunLoopWithTimeout();
+}
+
+class OrderChangeObserver : public WindowObserver {
+ public:
+ OrderChangeObserver(Window* window) : window_(window) {
+ window_->AddObserver(this);
+ }
+ ~OrderChangeObserver() override { window_->RemoveObserver(this); }
+
+ private:
+ // Overridden from WindowObserver:
+ void OnWindowReordered(Window* window,
+ Window* relative_window,
+ mojom::OrderDirection direction) override {
+ DCHECK_EQ(window, window_);
+ EXPECT_TRUE(WindowServerTestBase::QuitRunLoop());
+ }
+
+ Window* window_;
+
+ DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver);
+};
+
+// Wait until |window|'s tree size matches |tree_size|; returns false on
+// timeout.
+bool WaitForOrderChange(WindowTreeClient* client, Window* window) {
+ OrderChangeObserver observer(window);
+ return WindowServerTestBase::DoRunLoopWithTimeout();
+}
+
+// Tracks a window's destruction. Query is_valid() for current state.
+class WindowTracker : public WindowObserver {
+ public:
+ explicit WindowTracker(Window* window) : window_(window) {
+ window_->AddObserver(this);
+ }
+ ~WindowTracker() override {
+ if (window_)
+ window_->RemoveObserver(this);
+ }
+
+ bool is_valid() const { return !!window_; }
+
+ private:
+ // Overridden from WindowObserver:
+ void OnWindowDestroyed(Window* window) override {
+ DCHECK_EQ(window, window_);
+ window_ = nullptr;
+ }
+
+ Window* window_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowTracker);
+};
+
+} // namespace
+
+// WindowServer
+// -----------------------------------------------------------------
+
+struct EmbedResult {
+ EmbedResult(WindowTreeClient* client, ClientSpecificId id)
+ : client(client), client_id(id) {}
+ EmbedResult() : client(nullptr), client_id(0) {}
+
+ WindowTreeClient* client;
+
+ // The id supplied to the callback from OnEmbed(). Depending upon the
+ // access policy this may or may not match the client id of
+ // |client|.
+ ClientSpecificId client_id;
+};
+
+Window* GetFirstRoot(WindowTreeClient* client) {
+ return client->GetRoots().empty() ? nullptr : *client->GetRoots().begin();
+}
+
+// These tests model synchronization of two peer clients of the window server,
+// that are given access to some root window.
+
+class WindowServerTest : public WindowServerTestBase {
+ public:
+ WindowServerTest() {}
+
+ Window* GetFirstWMRoot() { return GetFirstRoot(window_manager()); }
+
+ Window* NewVisibleWindow(Window* parent, WindowTreeClient* client) {
+ Window* window = client->NewWindow();
+ window->SetVisible(true);
+ parent->AddChild(window);
+ return window;
+ }
+
+ // Embeds another version of the test app @ window. This runs a run loop until
+ // a response is received, or a timeout. On success the new WindowServer is
+ // returned.
+ EmbedResult Embed(Window* window) {
+ DCHECK(!embed_details_);
+ embed_details_.reset(new EmbedDetails);
+ window->Embed(ConnectAndGetWindowServerClient(),
+ base::Bind(&WindowServerTest::EmbedCallbackImpl,
+ base::Unretained(this)));
+ embed_details_->waiting = true;
+ if (!WindowServerTestBase::DoRunLoopWithTimeout())
+ return EmbedResult();
+ const EmbedResult result(embed_details_->client,
+ embed_details_->client_id);
+ embed_details_.reset();
+ return result;
+ }
+
+ // Establishes a connection to this application and asks for a
+ // WindowTreeClient.
+ mus::mojom::WindowTreeClientPtr ConnectAndGetWindowServerClient() {
+ mus::mojom::WindowTreeClientPtr client;
+ connector()->ConnectToInterface(test_name(), &client);
+ return client;
+ }
+
+ // WindowServerTestBase:
+ void OnEmbed(Window* root) override {
+ if (!embed_details_) {
+ WindowServerTestBase::OnEmbed(root);
+ return;
+ }
+
+ embed_details_->client = root->window_tree();
+ if (embed_details_->callback_run)
+ EXPECT_TRUE(WindowServerTestBase::QuitRunLoop());
+ }
+
+ private:
+ // Used to track the state of a call to window->Embed().
+ struct EmbedDetails {
+ EmbedDetails()
+ : callback_run(false),
+ result(false),
+ waiting(false),
+ client(nullptr) {}
+
+ // The callback supplied to Embed() was received.
+ bool callback_run;
+
+ // The boolean supplied to the Embed() callback.
+ bool result;
+
+ // Whether a MessageLoop is running.
+ bool waiting;
+
+ // Client id supplied to the Embed() callback.
+ ClientSpecificId client_id;
+
+ // The WindowTreeClient that resulted from the Embed(). null if |result| is
+ // false.
+ WindowTreeClient* client;
+ };
+
+ void EmbedCallbackImpl(bool result) {
+ embed_details_->callback_run = true;
+ embed_details_->result = result;
+ if (embed_details_->waiting && (!result || embed_details_->client))
+ EXPECT_TRUE(WindowServerTestBase::QuitRunLoop());
+ }
+
+ std::unique_ptr<EmbedDetails> embed_details_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowServerTest);
+};
+
+TEST_F(WindowServerTest, RootWindow) {
+ ASSERT_NE(nullptr, window_manager());
+ EXPECT_EQ(1u, window_manager()->GetRoots().size());
+}
+
+TEST_F(WindowServerTest, Embed) {
+ Window* window = window_manager()->NewWindow();
+ ASSERT_NE(nullptr, window);
+ window->SetVisible(true);
+ GetFirstWMRoot()->AddChild(window);
+ WindowTreeClient* embedded = Embed(window).client;
+ ASSERT_NE(nullptr, embedded);
+
+ Window* window_in_embedded = GetFirstRoot(embedded);
+ ASSERT_NE(nullptr, window_in_embedded);
+ EXPECT_EQ(server_id(window), server_id(window_in_embedded));
+ EXPECT_EQ(nullptr, window_in_embedded->parent());
+ EXPECT_TRUE(window_in_embedded->children().empty());
+}
+
+// Window manager has two windows, N1 and N11. Embeds A at N1. A should not see
+// N11.
+TEST_F(WindowServerTest, EmbeddedDoesntSeeChild) {
+ Window* window = window_manager()->NewWindow();
+ ASSERT_NE(nullptr, window);
+ window->SetVisible(true);
+ GetFirstWMRoot()->AddChild(window);
+ Window* nested = window_manager()->NewWindow();
+ ASSERT_NE(nullptr, nested);
+ nested->SetVisible(true);
+ window->AddChild(nested);
+
+ WindowTreeClient* embedded = Embed(window).client;
+ ASSERT_NE(nullptr, embedded);
+ Window* window_in_embedded = GetFirstRoot(embedded);
+ EXPECT_EQ(server_id(window), server_id(window_in_embedded));
+ EXPECT_EQ(nullptr, window_in_embedded->parent());
+ EXPECT_TRUE(window_in_embedded->children().empty());
+}
+
+// TODO(beng): write a replacement test for the one that once existed here:
+// This test validates the following scenario:
+// - a window originating from one client
+// - a window originating from a second client
+// + the client originating the window is destroyed
+// -> the window should still exist (since the second client is live) but
+// should be disconnected from any windows.
+// http://crbug.com/396300
+//
+// TODO(beng): The new test should validate the scenario as described above
+// except that the second client still has a valid tree.
+
+// Verifies that bounds changes applied to a window hierarchy in one client
+// are reflected to another.
+TEST_F(WindowServerTest, SetBounds) {
+ Window* window = window_manager()->NewWindow();
+ window->SetVisible(true);
+ GetFirstWMRoot()->AddChild(window);
+ WindowTreeClient* embedded = Embed(window).client;
+ ASSERT_NE(nullptr, embedded);
+
+ Window* window_in_embedded =
+ GetChildWindowByServerId(embedded, server_id(window));
+ EXPECT_EQ(window->bounds(), window_in_embedded->bounds());
+
+ window->SetBounds(gfx::Rect(0, 0, 100, 100));
+ ASSERT_TRUE(WaitForBoundsToChange(window_in_embedded));
+ EXPECT_TRUE(window->bounds() == window_in_embedded->bounds());
+}
+
+// Verifies that bounds changes applied to a window owned by a different
+// client can be refused.
+TEST_F(WindowServerTest, SetBoundsSecurity) {
+ TestWindowManagerDelegate wm_delegate;
+ set_window_manager_delegate(&wm_delegate);
+
+ Window* window = window_manager()->NewWindow();
+ window->SetVisible(true);
+ GetFirstWMRoot()->AddChild(window);
+ WindowTreeClient* embedded = Embed(window).client;
+ ASSERT_NE(nullptr, embedded);
+
+ Window* window_in_embedded =
+ GetChildWindowByServerId(embedded, server_id(window));
+ window->SetBounds(gfx::Rect(0, 0, 800, 600));
+ ASSERT_TRUE(WaitForBoundsToChange(window_in_embedded));
+
+ window_in_embedded->SetBounds(gfx::Rect(0, 0, 1024, 768));
+ // Bounds change is initially accepted, but the server declines the request.
+ EXPECT_FALSE(window->bounds() == window_in_embedded->bounds());
+
+ // The client is notified when the requested is declined, and updates the
+ // local bounds accordingly.
+ ASSERT_TRUE(WaitForBoundsToChange(window_in_embedded));
+ EXPECT_TRUE(window->bounds() == window_in_embedded->bounds());
+ set_window_manager_delegate(nullptr);
+}
+
+// Verifies that a root window can always be destroyed.
+TEST_F(WindowServerTest, DestroySecurity) {
+ Window* window = window_manager()->NewWindow();
+ window->SetVisible(true);
+ GetFirstWMRoot()->AddChild(window);
+
+ WindowTreeClient* embedded = Embed(window).client;
+ ASSERT_NE(nullptr, embedded);
+
+ // The root can be destroyed, even though it was not created by the client.
+ Window* embed_root = GetChildWindowByServerId(embedded, server_id(window));
+ WindowTracker tracker1(window);
+ WindowTracker tracker2(embed_root);
+ embed_root->Destroy();
+ EXPECT_FALSE(tracker2.is_valid());
+ EXPECT_TRUE(tracker1.is_valid());
+
+ window->Destroy();
+ EXPECT_FALSE(tracker1.is_valid());
+}
+
+TEST_F(WindowServerTest, MultiRoots) {
+ Window* window1 = window_manager()->NewWindow();
+ window1->SetVisible(true);
+ GetFirstWMRoot()->AddChild(window1);
+ Window* window2 = window_manager()->NewWindow();
+ window2->SetVisible(true);
+ GetFirstWMRoot()->AddChild(window2);
+ WindowTreeClient* embedded1 = Embed(window1).client;
+ ASSERT_NE(nullptr, embedded1);
+ WindowTreeClient* embedded2 = Embed(window2).client;
+ ASSERT_NE(nullptr, embedded2);
+ EXPECT_NE(embedded1, embedded2);
+}
+
+TEST_F(WindowServerTest, Reorder) {
+ Window* window1 = window_manager()->NewWindow();
+ window1->SetVisible(true);
+ GetFirstWMRoot()->AddChild(window1);
+
+ WindowTreeClient* embedded = Embed(window1).client;
+ ASSERT_NE(nullptr, embedded);
+
+ Window* window11 = embedded->NewWindow();
+ window11->SetVisible(true);
+ GetFirstRoot(embedded)->AddChild(window11);
+ Window* window12 = embedded->NewWindow();
+ window12->SetVisible(true);
+ GetFirstRoot(embedded)->AddChild(window12);
+ ASSERT_TRUE(WaitForTreeSizeToMatch(window1, 3u));
+
+ Window* root_in_embedded = GetFirstRoot(embedded);
+
+ {
+ window11->MoveToFront();
+ // The |embedded| tree should be updated immediately.
+ EXPECT_EQ(root_in_embedded->children().front(),
+ GetChildWindowByServerId(embedded, server_id(window12)));
+ EXPECT_EQ(root_in_embedded->children().back(),
+ GetChildWindowByServerId(embedded, server_id(window11)));
+
+ // The |window_manager()| tree is still not updated.
+ EXPECT_EQ(window1->children().back(),
+ GetChildWindowByServerId(window_manager(), server_id(window12)));
+
+ // Wait until |window_manager()| tree is updated.
+ ASSERT_TRUE(WaitForOrderChange(
+ window_manager(),
+ GetChildWindowByServerId(window_manager(), server_id(window11))));
+ EXPECT_EQ(window1->children().front(),
+ GetChildWindowByServerId(window_manager(), server_id(window12)));
+ EXPECT_EQ(window1->children().back(),
+ GetChildWindowByServerId(window_manager(), server_id(window11)));
+ }
+
+ {
+ window11->MoveToBack();
+ // |embedded| should be updated immediately.
+ EXPECT_EQ(root_in_embedded->children().front(),
+ GetChildWindowByServerId(embedded, server_id(window11)));
+ EXPECT_EQ(root_in_embedded->children().back(),
+ GetChildWindowByServerId(embedded, server_id(window12)));
+
+ // |window_manager()| is also eventually updated.
+ EXPECT_EQ(window1->children().back(),
+ GetChildWindowByServerId(window_manager(), server_id(window11)));
+ ASSERT_TRUE(WaitForOrderChange(
+ window_manager(),
+ GetChildWindowByServerId(window_manager(), server_id(window11))));
+ EXPECT_EQ(window1->children().front(),
+ GetChildWindowByServerId(window_manager(), server_id(window11)));
+ EXPECT_EQ(window1->children().back(),
+ GetChildWindowByServerId(window_manager(), server_id(window12)));
+ }
+}
+
+namespace {
+
+class VisibilityChangeObserver : public WindowObserver {
+ public:
+ explicit VisibilityChangeObserver(Window* window) : window_(window) {
+ window_->AddObserver(this);
+ }
+ ~VisibilityChangeObserver() override { window_->RemoveObserver(this); }
+
+ private:
+ // Overridden from WindowObserver:
+ void OnWindowVisibilityChanged(Window* window) override {
+ EXPECT_EQ(window, window_);
+ EXPECT_TRUE(WindowServerTestBase::QuitRunLoop());
+ }
+
+ Window* window_;
+
+ DISALLOW_COPY_AND_ASSIGN(VisibilityChangeObserver);
+};
+
+} // namespace
+
+TEST_F(WindowServerTest, Visible) {
+ Window* window1 = window_manager()->NewWindow();
+ window1->SetVisible(true);
+ GetFirstWMRoot()->AddChild(window1);
+
+ // Embed another app and verify initial state.
+ WindowTreeClient* embedded = Embed(window1).client;
+ ASSERT_NE(nullptr, embedded);
+ ASSERT_NE(nullptr, GetFirstRoot(embedded));
+ Window* embedded_root = GetFirstRoot(embedded);
+ EXPECT_TRUE(embedded_root->visible());
+ EXPECT_TRUE(embedded_root->IsDrawn());
+
+ // Change the visible state from the first client and verify its mirrored
+ // correctly to the embedded app.
+ {
+ VisibilityChangeObserver observer(embedded_root);
+ window1->SetVisible(false);
+ ASSERT_TRUE(WindowServerTestBase::DoRunLoopWithTimeout());
+ }
+
+ EXPECT_FALSE(window1->visible());
+ EXPECT_FALSE(window1->IsDrawn());
+
+ EXPECT_FALSE(embedded_root->visible());
+ EXPECT_FALSE(embedded_root->IsDrawn());
+
+ // Make the node visible again.
+ {
+ VisibilityChangeObserver observer(embedded_root);
+ window1->SetVisible(true);
+ ASSERT_TRUE(WindowServerTestBase::DoRunLoopWithTimeout());
+ }
+
+ EXPECT_TRUE(window1->visible());
+ EXPECT_TRUE(window1->IsDrawn());
+
+ EXPECT_TRUE(embedded_root->visible());
+ EXPECT_TRUE(embedded_root->IsDrawn());
+}
+
+namespace {
+
+class DrawnChangeObserver : public WindowObserver {
+ public:
+ explicit DrawnChangeObserver(Window* window) : window_(window) {
+ window_->AddObserver(this);
+ }
+ ~DrawnChangeObserver() override { window_->RemoveObserver(this); }
+
+ private:
+ // Overridden from WindowObserver:
+ void OnWindowDrawnChanged(Window* window) override {
+ EXPECT_EQ(window, window_);
+ EXPECT_TRUE(WindowServerTestBase::QuitRunLoop());
+ }
+
+ Window* window_;
+
+ DISALLOW_COPY_AND_ASSIGN(DrawnChangeObserver);
+};
+
+} // namespace
+
+TEST_F(WindowServerTest, Drawn) {
+ Window* window1 = window_manager()->NewWindow();
+ window1->SetVisible(true);
+ GetFirstWMRoot()->AddChild(window1);
+
+ // Embed another app and verify initial state.
+ WindowTreeClient* embedded = Embed(window1).client;
+ ASSERT_NE(nullptr, embedded);
+ ASSERT_NE(nullptr, GetFirstRoot(embedded));
+ Window* embedded_root = GetFirstRoot(embedded);
+ EXPECT_TRUE(embedded_root->visible());
+ EXPECT_TRUE(embedded_root->IsDrawn());
+
+ // Change the visibility of the root, this should propagate a drawn state
+ // change to |embedded|.
+ {
+ DrawnChangeObserver observer(embedded_root);
+ GetFirstWMRoot()->SetVisible(false);
+ ASSERT_TRUE(DoRunLoopWithTimeout());
+ }
+
+ EXPECT_TRUE(window1->visible());
+ EXPECT_FALSE(window1->IsDrawn());
+
+ EXPECT_TRUE(embedded_root->visible());
+ EXPECT_FALSE(embedded_root->IsDrawn());
+}
+
+// TODO(beng): tests for window event dispatcher.
+// - verify that we see events for all windows.
+
+namespace {
+
+class FocusChangeObserver : public WindowObserver {
+ public:
+ explicit FocusChangeObserver(Window* window)
+ : window_(window),
+ last_gained_focus_(nullptr),
+ last_lost_focus_(nullptr),
+ quit_on_change_(true) {
+ window_->AddObserver(this);
+ }
+ ~FocusChangeObserver() override { window_->RemoveObserver(this); }
+
+ void set_quit_on_change(bool value) { quit_on_change_ = value; }
+
+ Window* last_gained_focus() { return last_gained_focus_; }
+
+ Window* last_lost_focus() { return last_lost_focus_; }
+
+ private:
+ // Overridden from WindowObserver.
+ void OnWindowFocusChanged(Window* gained_focus, Window* lost_focus) override {
+ EXPECT_TRUE(!gained_focus || gained_focus->HasFocus());
+ EXPECT_FALSE(lost_focus && lost_focus->HasFocus());
+ last_gained_focus_ = gained_focus;
+ last_lost_focus_ = lost_focus;
+ if (quit_on_change_)
+ EXPECT_TRUE(WindowServerTestBase::QuitRunLoop());
+ }
+
+ Window* window_;
+ Window* last_gained_focus_;
+ Window* last_lost_focus_;
+ bool quit_on_change_;
+
+ DISALLOW_COPY_AND_ASSIGN(FocusChangeObserver);
+};
+
+class NullFocusChangeObserver : public WindowTreeClientObserver {
+ public:
+ explicit NullFocusChangeObserver(WindowTreeClient* client)
+ : client_(client) {
+ client_->AddObserver(this);
+ }
+ ~NullFocusChangeObserver() override { client_->RemoveObserver(this); }
+
+ private:
+ // Overridden from WindowTreeClientObserver.
+ void OnWindowTreeFocusChanged(Window* gained_focus,
+ Window* lost_focus) override {
+ if (!gained_focus)
+ EXPECT_TRUE(WindowServerTestBase::QuitRunLoop());
+ }
+
+ WindowTreeClient* client_;
+
+ DISALLOW_COPY_AND_ASSIGN(NullFocusChangeObserver);
+};
+
+bool WaitForWindowToHaveFocus(Window* window) {
+ if (window->HasFocus())
+ return true;
+ FocusChangeObserver observer(window);
+ return WindowServerTestBase::DoRunLoopWithTimeout();
+}
+
+bool WaitForNoWindowToHaveFocus(WindowTreeClient* client) {
+ if (!client->GetFocusedWindow())
+ return true;
+ NullFocusChangeObserver observer(client);
+ return WindowServerTestBase::DoRunLoopWithTimeout();
+}
+
+} // namespace
+
+TEST_F(WindowServerTest, Focus) {
+ Window* window1 = window_manager()->NewWindow();
+ window1->SetVisible(true);
+ GetFirstWMRoot()->AddChild(window1);
+
+ WindowTreeClient* embedded = Embed(window1).client;
+ ASSERT_NE(nullptr, embedded);
+ Window* window11 = embedded->NewWindow();
+ window11->SetVisible(true);
+ GetFirstRoot(embedded)->AddChild(window11);
+
+ {
+ // Focus the embed root in |embedded|.
+ Window* embedded_root = GetFirstRoot(embedded);
+ FocusChangeObserver observer(embedded_root);
+ observer.set_quit_on_change(false);
+ embedded_root->SetFocus();
+ ASSERT_TRUE(embedded_root->HasFocus());
+ ASSERT_NE(nullptr, observer.last_gained_focus());
+ EXPECT_EQ(server_id(embedded_root),
+ server_id(observer.last_gained_focus()));
+
+ // |embedded_root| is the same as |window1|, make sure |window1| got
+ // focus too.
+ ASSERT_TRUE(WaitForWindowToHaveFocus(window1));
+ }
+
+ // Focus a child of GetFirstRoot(embedded).
+ {
+ FocusChangeObserver observer(window11);
+ observer.set_quit_on_change(false);
+ window11->SetFocus();
+ ASSERT_TRUE(window11->HasFocus());
+ ASSERT_NE(nullptr, observer.last_gained_focus());
+ ASSERT_NE(nullptr, observer.last_lost_focus());
+ EXPECT_EQ(server_id(window11), server_id(observer.last_gained_focus()));
+ EXPECT_EQ(server_id(GetFirstRoot(embedded)),
+ server_id(observer.last_lost_focus()));
+ }
+
+ {
+ // Add an observer on the Window that loses focus, and make sure the
+ // observer sees the right values.
+ FocusChangeObserver observer(window11);
+ observer.set_quit_on_change(false);
+ GetFirstRoot(embedded)->SetFocus();
+ ASSERT_NE(nullptr, observer.last_gained_focus());
+ ASSERT_NE(nullptr, observer.last_lost_focus());
+ EXPECT_EQ(server_id(window11), server_id(observer.last_lost_focus()));
+ EXPECT_EQ(server_id(GetFirstRoot(embedded)),
+ server_id(observer.last_gained_focus()));
+ }
+}
+
+TEST_F(WindowServerTest, ClearFocus) {
+ Window* window1 = window_manager()->NewWindow();
+ window1->SetVisible(true);
+ GetFirstWMRoot()->AddChild(window1);
+
+ WindowTreeClient* embedded = Embed(window1).client;
+ ASSERT_NE(nullptr, embedded);
+ Window* window11 = embedded->NewWindow();
+ window11->SetVisible(true);
+ GetFirstRoot(embedded)->AddChild(window11);
+
+ // Focus the embed root in |embedded|.
+ Window* embedded_root = GetFirstRoot(embedded);
+ {
+ FocusChangeObserver observer(embedded_root);
+ observer.set_quit_on_change(false);
+ embedded_root->SetFocus();
+ ASSERT_TRUE(embedded_root->HasFocus());
+ ASSERT_NE(nullptr, observer.last_gained_focus());
+ EXPECT_EQ(server_id(embedded_root),
+ server_id(observer.last_gained_focus()));
+
+ // |embedded_root| is the same as |window1|, make sure |window1| got
+ // focus too.
+ ASSERT_TRUE(WaitForWindowToHaveFocus(window1));
+ }
+
+ {
+ FocusChangeObserver observer(window1);
+ embedded->ClearFocus();
+ ASSERT_FALSE(embedded_root->HasFocus());
+ EXPECT_FALSE(embedded->GetFocusedWindow());
+
+ ASSERT_TRUE(WindowServerTestBase::DoRunLoopWithTimeout());
+ EXPECT_FALSE(window1->HasFocus());
+ EXPECT_FALSE(window_manager()->GetFocusedWindow());
+ }
+}
+
+TEST_F(WindowServerTest, FocusNonFocusableWindow) {
+ Window* window = window_manager()->NewWindow();
+ window->SetVisible(true);
+ GetFirstWMRoot()->AddChild(window);
+
+ WindowTreeClient* client = Embed(window).client;
+ ASSERT_NE(nullptr, client);
+ ASSERT_FALSE(client->GetRoots().empty());
+ Window* client_window = *client->GetRoots().begin();
+ client_window->SetCanFocus(false);
+
+ client_window->SetFocus();
+ ASSERT_TRUE(client_window->HasFocus());
+
+ WaitForNoWindowToHaveFocus(client);
+ ASSERT_FALSE(client_window->HasFocus());
+}
+
+TEST_F(WindowServerTest, Activation) {
+ Window* parent = NewVisibleWindow(GetFirstWMRoot(), window_manager());
+
+ // Allow the child windows to be activated. Do this before we wait, that way
+ // we're guaranteed that when we request focus from a separate client the
+ // requests are processed in order.
+ window_manager_client()->AddActivationParent(parent);
+
+ Window* child1 = NewVisibleWindow(parent, window_manager());
+ Window* child2 = NewVisibleWindow(parent, window_manager());
+ Window* child3 = NewVisibleWindow(parent, window_manager());
+
+ child1->AddTransientWindow(child3);
+
+ WindowTreeClient* embedded1 = Embed(child1).client;
+ ASSERT_NE(nullptr, embedded1);
+ WindowTreeClient* embedded2 = Embed(child2).client;
+ ASSERT_NE(nullptr, embedded2);
+
+ Window* child11 = NewVisibleWindow(GetFirstRoot(embedded1), embedded1);
+ Window* child21 = NewVisibleWindow(GetFirstRoot(embedded2), embedded2);
+
+ WaitForTreeSizeToMatch(parent, 6);
+
+ // |child2| and |child3| are stacked about |child1|.
+ EXPECT_GT(ValidIndexOf(parent->children(), child2),
+ ValidIndexOf(parent->children(), child1));
+ EXPECT_GT(ValidIndexOf(parent->children(), child3),
+ ValidIndexOf(parent->children(), child1));
+
+ // Set focus on |child11|. This should activate |child1|, and raise it over
+ // |child2|. But |child3| should still be above |child1| because of
+ // transiency.
+ child11->SetFocus();
+ ASSERT_TRUE(WaitForWindowToHaveFocus(child11));
+ ASSERT_TRUE(WaitForWindowToHaveFocus(
+ GetChildWindowByServerId(window_manager(), server_id(child11))));
+ EXPECT_EQ(server_id(child11),
+ server_id(window_manager()->GetFocusedWindow()));
+ EXPECT_EQ(server_id(child11), server_id(embedded1->GetFocusedWindow()));
+ EXPECT_EQ(nullptr, embedded2->GetFocusedWindow());
+ EXPECT_GT(ValidIndexOf(parent->children(), child1),
+ ValidIndexOf(parent->children(), child2));
+ EXPECT_GT(ValidIndexOf(parent->children(), child3),
+ ValidIndexOf(parent->children(), child1));
+
+ // Set focus on |child21|. This should activate |child2|, and raise it over
+ // |child1|.
+ child21->SetFocus();
+ ASSERT_TRUE(WaitForWindowToHaveFocus(child21));
+ ASSERT_TRUE(WaitForWindowToHaveFocus(
+ GetChildWindowByServerId(window_manager(), server_id(child21))));
+ EXPECT_EQ(server_id(child21),
+ server_id(window_manager()->GetFocusedWindow()));
+ EXPECT_EQ(server_id(child21), server_id(embedded2->GetFocusedWindow()));
+ EXPECT_TRUE(WaitForNoWindowToHaveFocus(embedded1));
+ EXPECT_EQ(nullptr, embedded1->GetFocusedWindow());
+ EXPECT_GT(ValidIndexOf(parent->children(), child2),
+ ValidIndexOf(parent->children(), child1));
+ EXPECT_GT(ValidIndexOf(parent->children(), child3),
+ ValidIndexOf(parent->children(), child1));
+}
+
+TEST_F(WindowServerTest, ActivationNext) {
+ Window* parent = GetFirstWMRoot();
+ Window* child1 = NewVisibleWindow(parent, window_manager());
+ Window* child2 = NewVisibleWindow(parent, window_manager());
+ Window* child3 = NewVisibleWindow(parent, window_manager());
+
+ WindowTreeClient* embedded1 = Embed(child1).client;
+ ASSERT_NE(nullptr, embedded1);
+ WindowTreeClient* embedded2 = Embed(child2).client;
+ ASSERT_NE(nullptr, embedded2);
+ WindowTreeClient* embedded3 = Embed(child3).client;
+ ASSERT_NE(nullptr, embedded3);
+
+ Window* child11 = NewVisibleWindow(GetFirstRoot(embedded1), embedded1);
+ Window* child21 = NewVisibleWindow(GetFirstRoot(embedded2), embedded2);
+ Window* child31 = NewVisibleWindow(GetFirstRoot(embedded3), embedded3);
+ WaitForTreeSizeToMatch(parent, 7);
+
+ Window* expecteds[] = { child3, child2, child1, child3, nullptr };
+ Window* focused[] = { child31, child21, child11, child31, nullptr };
+ for (size_t index = 0; expecteds[index]; ++index) {
+ window_manager_client()->ActivateNextWindow();
+ WaitForWindowToHaveFocus(focused[index]);
+ EXPECT_TRUE(focused[index]->HasFocus());
+ EXPECT_EQ(parent->children().back(), expecteds[index]);
+ }
+}
+
+namespace {
+
+class DestroyedChangedObserver : public WindowObserver {
+ public:
+ DestroyedChangedObserver(WindowServerTestBase* test,
+ Window* window,
+ bool* got_destroy)
+ : test_(test), window_(window), got_destroy_(got_destroy) {
+ window_->AddObserver(this);
+ }
+ ~DestroyedChangedObserver() override {
+ if (window_)
+ window_->RemoveObserver(this);
+ }
+
+ private:
+ // Overridden from WindowObserver:
+ void OnWindowDestroyed(Window* window) override {
+ EXPECT_EQ(window, window_);
+ window_->RemoveObserver(this);
+ *got_destroy_ = true;
+ window_ = nullptr;
+
+ // We should always get OnWindowDestroyed() before
+ // OnWindowTreeClientDestroyed().
+ EXPECT_FALSE(test_->window_tree_client_destroyed());
+ }
+
+ WindowServerTestBase* test_;
+ Window* window_;
+ bool* got_destroy_;
+
+ DISALLOW_COPY_AND_ASSIGN(DestroyedChangedObserver);
+};
+
+} // namespace
+
+// Verifies deleting a WindowServer sends the right notifications.
+TEST_F(WindowServerTest, DeleteWindowServer) {
+ Window* window = window_manager()->NewWindow();
+ ASSERT_NE(nullptr, window);
+ window->SetVisible(true);
+ GetFirstWMRoot()->AddChild(window);
+ WindowTreeClient* client = Embed(window).client;
+ ASSERT_TRUE(client);
+ bool got_destroy = false;
+ DestroyedChangedObserver observer(this, GetFirstRoot(client),
+ &got_destroy);
+ delete client;
+ EXPECT_TRUE(window_tree_client_destroyed());
+ EXPECT_TRUE(got_destroy);
+}
+
+// Verifies two Embed()s in the same window trigger deletion of the first
+// WindowServer.
+TEST_F(WindowServerTest, DisconnectTriggersDelete) {
+ Window* window = window_manager()->NewWindow();
+ ASSERT_NE(nullptr, window);
+ window->SetVisible(true);
+ GetFirstWMRoot()->AddChild(window);
+ WindowTreeClient* client = Embed(window).client;
+ EXPECT_NE(client, window_manager());
+ Window* embedded_window = client->NewWindow();
+ // Embed again, this should trigger disconnect and deletion of client.
+ bool got_destroy;
+ DestroyedChangedObserver observer(this, embedded_window, &got_destroy);
+ EXPECT_FALSE(window_tree_client_destroyed());
+ Embed(window);
+ EXPECT_TRUE(window_tree_client_destroyed());
+}
+
+class WindowRemovedFromParentObserver : public WindowObserver {
+ public:
+ explicit WindowRemovedFromParentObserver(Window* window)
+ : window_(window), was_removed_(false) {
+ window_->AddObserver(this);
+ }
+ ~WindowRemovedFromParentObserver() override { window_->RemoveObserver(this); }
+
+ bool was_removed() const { return was_removed_; }
+
+ private:
+ // Overridden from WindowObserver:
+ void OnTreeChanged(const TreeChangeParams& params) override {
+ if (params.target == window_ && !params.new_parent)
+ was_removed_ = true;
+ }
+
+ Window* window_;
+ bool was_removed_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowRemovedFromParentObserver);
+};
+
+TEST_F(WindowServerTest, EmbedRemovesChildren) {
+ Window* window1 = window_manager()->NewWindow();
+ Window* window2 = window_manager()->NewWindow();
+ GetFirstWMRoot()->AddChild(window1);
+ window1->AddChild(window2);
+
+ WindowRemovedFromParentObserver observer(window2);
+ window1->Embed(ConnectAndGetWindowServerClient());
+ EXPECT_TRUE(observer.was_removed());
+ EXPECT_EQ(nullptr, window2->parent());
+ EXPECT_TRUE(window1->children().empty());
+
+ // Run the message loop so the Embed() call above completes. Without this
+ // we may end up reconnecting to the test and rerunning the test, which is
+ // problematic since the other services don't shut down.
+ ASSERT_TRUE(DoRunLoopWithTimeout());
+}
+
+namespace {
+
+class DestroyObserver : public WindowObserver {
+ public:
+ DestroyObserver(WindowServerTestBase* test,
+ WindowTreeClient* client,
+ bool* got_destroy)
+ : test_(test), got_destroy_(got_destroy) {
+ GetFirstRoot(client)->AddObserver(this);
+ }
+ ~DestroyObserver() override {}
+
+ private:
+ // Overridden from WindowObserver:
+ void OnWindowDestroyed(Window* window) override {
+ *got_destroy_ = true;
+ window->RemoveObserver(this);
+
+ // We should always get OnWindowDestroyed() before
+ // OnWindowManagerDestroyed().
+ EXPECT_FALSE(test_->window_tree_client_destroyed());
+
+ EXPECT_TRUE(WindowServerTestBase::QuitRunLoop());
+ }
+
+ WindowServerTestBase* test_;
+ bool* got_destroy_;
+
+ DISALLOW_COPY_AND_ASSIGN(DestroyObserver);
+};
+
+} // namespace
+
+// Verifies deleting a Window that is the root of another client notifies
+// observers in the right order (OnWindowDestroyed() before
+// OnWindowManagerDestroyed()).
+TEST_F(WindowServerTest, WindowServerDestroyedAfterRootObserver) {
+ Window* embed_window = window_manager()->NewWindow();
+ GetFirstWMRoot()->AddChild(embed_window);
+
+ WindowTreeClient* embedded_client = Embed(embed_window).client;
+
+ bool got_destroy = false;
+ DestroyObserver observer(this, embedded_client, &got_destroy);
+ // Delete the window |embedded_client| is embedded in. This is async,
+ // but will eventually trigger deleting |embedded_client|.
+ embed_window->Destroy();
+ EXPECT_TRUE(DoRunLoopWithTimeout());
+ EXPECT_TRUE(got_destroy);
+}
+
+TEST_F(WindowServerTest, ClientAreaChanged) {
+ Window* embed_window = window_manager()->NewWindow();
+ GetFirstWMRoot()->AddChild(embed_window);
+
+ WindowTreeClient* embedded_client = Embed(embed_window).client;
+
+ // Verify change from embedded makes it to parent.
+ GetFirstRoot(embedded_client)->SetClientArea(gfx::Insets(1, 2, 3, 4));
+ ASSERT_TRUE(WaitForClientAreaToChange(embed_window));
+ EXPECT_TRUE(gfx::Insets(1, 2, 3, 4) == embed_window->client_area());
+
+ // Changing bounds shouldn't effect client area.
+ embed_window->SetBounds(gfx::Rect(21, 22, 23, 24));
+ WaitForBoundsToChange(GetFirstRoot(embedded_client));
+ EXPECT_TRUE(gfx::Rect(21, 22, 23, 24) ==
+ GetFirstRoot(embedded_client)->bounds());
+ EXPECT_TRUE(gfx::Insets(1, 2, 3, 4) ==
+ GetFirstRoot(embedded_client)->client_area());
+}
+
+class EstablishConnectionViaFactoryDelegate : public TestWindowManagerDelegate {
+ public:
+ explicit EstablishConnectionViaFactoryDelegate(
+ WindowTreeClient* client)
+ : client_(client), run_loop_(nullptr), created_window_(nullptr) {}
+ ~EstablishConnectionViaFactoryDelegate() override {}
+
+ bool QuitOnCreate() {
+ if (run_loop_)
+ return false;
+
+ created_window_ = nullptr;
+ run_loop_.reset(new base::RunLoop);
+ run_loop_->Run();
+ run_loop_.reset();
+ return created_window_ != nullptr;
+ }
+
+ Window* created_window() { return created_window_; }
+
+ // WindowManagerDelegate:
+ Window* OnWmCreateTopLevelWindow(
+ std::map<std::string, std::vector<uint8_t>>* properties) override {
+ created_window_ = client_->NewWindow(properties);
+ (*client_->GetRoots().begin())->AddChild(created_window_);
+ if (run_loop_)
+ run_loop_->Quit();
+ return created_window_;
+ }
+
+ private:
+ WindowTreeClient* client_;
+ std::unique_ptr<base::RunLoop> run_loop_;
+ Window* created_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(EstablishConnectionViaFactoryDelegate);
+};
+
+TEST_F(WindowServerTest, EstablishConnectionViaFactory) {
+ EstablishConnectionViaFactoryDelegate delegate(window_manager());
+ set_window_manager_delegate(&delegate);
+ std::unique_ptr<WindowTreeClient> second_client(
+ new WindowTreeClient(this, nullptr, nullptr));
+ second_client->ConnectViaWindowTreeFactory(connector());
+ Window* window_in_second_client =
+ second_client->NewTopLevelWindow(nullptr);
+ ASSERT_TRUE(window_in_second_client);
+ ASSERT_TRUE(second_client->GetRoots().count(window_in_second_client) >
+ 0);
+ // Wait for the window to appear in the wm.
+ ASSERT_TRUE(delegate.QuitOnCreate());
+
+ Window* window_in_wm = delegate.created_window();
+ ASSERT_TRUE(window_in_wm);
+
+ // Change the bounds in the wm, and make sure the child sees it.
+ window_in_wm->SetBounds(gfx::Rect(1, 11, 12, 101));
+ ASSERT_TRUE(WaitForBoundsToChange(window_in_second_client));
+ EXPECT_EQ(gfx::Rect(1, 11, 12, 101), window_in_second_client->bounds());
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/window_manager_display_root.cc b/chromium/components/mus/ws/window_manager_display_root.cc
new file mode 100644
index 00000000000..351208d4af6
--- /dev/null
+++ b/chromium/components/mus/ws/window_manager_display_root.cc
@@ -0,0 +1,34 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/window_manager_display_root.h"
+
+#include "components/mus/ws/display_manager.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/window_server.h"
+
+namespace mus {
+namespace ws {
+
+WindowManagerDisplayRoot::WindowManagerDisplayRoot(Display* display)
+ : display_(display) {
+ root_.reset(window_server()->CreateServerWindow(
+ window_server()->display_manager()->GetAndAdvanceNextRootId(),
+ ServerWindow::Properties()));
+ // Our root is always a child of the Display's root. Do this
+ // before the WindowTree has been created so that the client doesn't get
+ // notified of the add, bounds change and visibility change.
+ root_->SetBounds(gfx::Rect(display->root_window()->bounds().size()));
+ root_->SetVisible(true);
+ display->root_window()->Add(root_.get());
+}
+
+WindowManagerDisplayRoot::~WindowManagerDisplayRoot() {}
+
+WindowServer* WindowManagerDisplayRoot::window_server() {
+ return display_->window_server();
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/window_manager_display_root.h b/chromium/components/mus/ws/window_manager_display_root.h
new file mode 100644
index 00000000000..94eda737201
--- /dev/null
+++ b/chromium/components/mus/ws/window_manager_display_root.h
@@ -0,0 +1,62 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_WINDOW_MANAGER_DISPLAY_ROOT_H_
+#define COMPONENTS_MUS_WS_WINDOW_MANAGER_DISPLAY_ROOT_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+
+namespace mus {
+namespace ws {
+
+class Display;
+class ServerWindow;
+class WindowManagerState;
+class WindowServer;
+
+namespace test {
+class WindowManagerDisplayRootTestApi;
+}
+
+// Owns the root window of a window manager for one display. Each window manager
+// has one WindowManagerDisplayRoot for each Display. The root window is
+// parented to the root of a Display.
+class WindowManagerDisplayRoot {
+ public:
+ explicit WindowManagerDisplayRoot(Display* display);
+ ~WindowManagerDisplayRoot();
+
+ ServerWindow* root() { return root_.get(); }
+ const ServerWindow* root() const { return root_.get(); }
+
+ Display* display() { return display_; }
+ const Display* display() const { return display_; }
+
+ WindowManagerState* window_manager_state() { return window_manager_state_; }
+ const WindowManagerState* window_manager_state() const {
+ return window_manager_state_;
+ }
+
+ private:
+ friend class Display;
+
+ WindowServer* window_server();
+
+ Display* display_;
+ // Root ServerWindow of this WindowManagerDisplayRoot. |root_| has a parent,
+ // the root ServerWindow of the Display.
+ std::unique_ptr<ServerWindow> root_;
+ WindowManagerState* window_manager_state_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowManagerDisplayRoot);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_WINDOW_MANAGER_DISPLAY_ROOT_H_
diff --git a/chromium/components/mus/ws/window_manager_state.cc b/chromium/components/mus/ws/window_manager_state.cc
new file mode 100644
index 00000000000..b394514bef2
--- /dev/null
+++ b/chromium/components/mus/ws/window_manager_state.cc
@@ -0,0 +1,485 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/window_manager_state.h"
+
+#include "base/memory/weak_ptr.h"
+#include "components/mus/common/event_matcher_util.h"
+#include "components/mus/ws/accelerator.h"
+#include "components/mus/ws/display_manager.h"
+#include "components/mus/ws/platform_display.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/user_display_manager.h"
+#include "components/mus/ws/user_id_tracker.h"
+#include "components/mus/ws/window_manager_display_root.h"
+#include "components/mus/ws/window_server.h"
+#include "components/mus/ws/window_tree.h"
+#include "services/shell/public/interfaces/connector.mojom.h"
+#include "ui/events/event.h"
+
+namespace mus {
+namespace ws {
+namespace {
+
+// Debug accelerator IDs start far above the highest valid Windows command ID
+// (0xDFFF) and Chrome's highest IDC command ID.
+const uint32_t kPrintWindowsDebugAcceleratorId = 1u << 31;
+
+base::TimeDelta GetDefaultAckTimerDelay() {
+#if defined(NDEBUG)
+ return base::TimeDelta::FromMilliseconds(100);
+#else
+ return base::TimeDelta::FromMilliseconds(1000);
+#endif
+}
+
+bool EventsCanBeCoalesced(const ui::Event& one, const ui::Event& two) {
+ if (one.type() != two.type() || one.flags() != two.flags())
+ return false;
+
+ // TODO(sad): wheel events can also be merged.
+ if (one.type() != ui::ET_POINTER_MOVED)
+ return false;
+
+ return one.AsPointerEvent()->pointer_id() ==
+ two.AsPointerEvent()->pointer_id();
+}
+
+std::unique_ptr<ui::Event> CoalesceEvents(std::unique_ptr<ui::Event> first,
+ std::unique_ptr<ui::Event> second) {
+ DCHECK(first->type() == ui::ET_POINTER_MOVED)
+ << " Non-move events cannot be merged yet.";
+ // For mouse moves, the new event just replaces the old event.
+ return second;
+}
+
+const ServerWindow* GetEmbedRoot(const ServerWindow* window) {
+ DCHECK(window);
+ const ServerWindow* embed_root = window->parent();
+ while (embed_root && embed_root->id().client_id == window->id().client_id)
+ embed_root = embed_root->parent();
+ return embed_root;
+}
+
+} // namespace
+
+class WindowManagerState::ProcessedEventTarget {
+ public:
+ ProcessedEventTarget(ServerWindow* window,
+ ClientSpecificId client_id,
+ Accelerator* accelerator)
+ : client_id_(client_id) {
+ tracker_.Add(window);
+ if (accelerator)
+ accelerator_ = accelerator->GetWeakPtr();
+ }
+
+ ~ProcessedEventTarget() {}
+
+ // Return true if the event is still valid. The event becomes invalid if
+ // the window is destroyed while waiting to dispatch.
+ bool IsValid() const { return !tracker_.windows().empty(); }
+
+ ServerWindow* window() {
+ DCHECK(IsValid());
+ return tracker_.windows().front();
+ }
+
+ ClientSpecificId client_id() const { return client_id_; }
+
+ base::WeakPtr<Accelerator> accelerator() { return accelerator_; }
+
+ private:
+ ServerWindowTracker tracker_;
+ const ClientSpecificId client_id_;
+ base::WeakPtr<Accelerator> accelerator_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessedEventTarget);
+};
+
+WindowManagerState::QueuedEvent::QueuedEvent() {}
+WindowManagerState::QueuedEvent::~QueuedEvent() {}
+
+WindowManagerState::WindowManagerState(WindowTree* window_tree)
+ : window_tree_(window_tree), event_dispatcher_(this), weak_factory_(this) {
+ frame_decoration_values_ = mojom::FrameDecorationValues::New();
+ frame_decoration_values_->max_title_bar_button_width = 0u;
+
+ AddDebugAccelerators();
+}
+
+WindowManagerState::~WindowManagerState() {}
+
+void WindowManagerState::SetFrameDecorationValues(
+ mojom::FrameDecorationValuesPtr values) {
+ got_frame_decoration_values_ = true;
+ frame_decoration_values_ = values.Clone();
+ display_manager()
+ ->GetUserDisplayManager(user_id())
+ ->OnFrameDecorationValuesChanged();
+}
+
+bool WindowManagerState::SetCapture(ServerWindow* window,
+ ClientSpecificId client_id) {
+ DCHECK(IsActive());
+ if (capture_window() == window &&
+ client_id == event_dispatcher_.capture_window_client_id()) {
+ return true;
+ }
+#if !defined(NDEBUG)
+ if (window) {
+ WindowManagerDisplayRoot* display_root =
+ display_manager()->GetWindowManagerDisplayRoot(window);
+ DCHECK(display_root && display_root->window_manager_state() == this);
+ }
+#endif
+ return event_dispatcher_.SetCaptureWindow(window, client_id);
+}
+
+void WindowManagerState::ReleaseCaptureBlockedByModalWindow(
+ const ServerWindow* modal_window) {
+ event_dispatcher_.ReleaseCaptureBlockedByModalWindow(modal_window);
+}
+
+void WindowManagerState::ReleaseCaptureBlockedByAnyModalWindow() {
+ event_dispatcher_.ReleaseCaptureBlockedByAnyModalWindow();
+}
+
+void WindowManagerState::AddSystemModalWindow(ServerWindow* window) {
+ DCHECK(!window->transient_parent());
+ event_dispatcher_.AddSystemModalWindow(window);
+}
+
+const UserId& WindowManagerState::user_id() const {
+ return window_tree_->user_id();
+}
+
+void WindowManagerState::OnWillDestroyTree(WindowTree* tree) {
+ if (tree_awaiting_input_ack_ != tree)
+ return;
+
+ // The WindowTree is dying. So it's not going to ack the event.
+ // If the dying tree matches the root |tree_| marked as handled so we don't
+ // notify it of accelerators.
+ OnEventAck(tree_awaiting_input_ack_, tree == window_tree_
+ ? mojom::EventResult::HANDLED
+ : mojom::EventResult::UNHANDLED);
+}
+
+bool WindowManagerState::IsActive() const {
+ return window_server()->user_id_tracker()->active_id() == user_id();
+}
+
+void WindowManagerState::Activate(const gfx::Point& mouse_location_on_screen) {
+ SetAllRootWindowsVisible(true);
+ event_dispatcher_.Reset();
+ event_dispatcher_.SetMousePointerScreenLocation(mouse_location_on_screen);
+}
+
+void WindowManagerState::Deactivate() {
+ SetAllRootWindowsVisible(false);
+ event_dispatcher_.Reset();
+ // The tree is no longer active, so no point in dispatching any further
+ // events.
+ std::queue<std::unique_ptr<QueuedEvent>> event_queue;
+ event_queue.swap(event_queue_);
+}
+
+void WindowManagerState::ProcessEvent(const ui::Event& event) {
+ // If this is still waiting for an ack from a previously sent event, then
+ // queue up the event to be dispatched once the ack is received.
+ if (event_ack_timer_.IsRunning()) {
+ if (!event_queue_.empty() && !event_queue_.back()->processed_target &&
+ EventsCanBeCoalesced(*event_queue_.back()->event, event)) {
+ event_queue_.back()->event = CoalesceEvents(
+ std::move(event_queue_.back()->event), ui::Event::Clone(event));
+ return;
+ }
+ QueueEvent(event, nullptr);
+ return;
+ }
+ event_dispatcher_.ProcessEvent(event);
+}
+
+void WindowManagerState::OnEventAck(mojom::WindowTree* tree,
+ mojom::EventResult result) {
+ if (tree_awaiting_input_ack_ != tree) {
+ // TODO(sad): The ack must have arrived after the timeout. We should do
+ // something here, and in OnEventAckTimeout().
+ return;
+ }
+ tree_awaiting_input_ack_ = nullptr;
+ event_ack_timer_.Stop();
+
+ if (result == mojom::EventResult::UNHANDLED && post_target_accelerator_)
+ OnAccelerator(post_target_accelerator_->id(), *event_awaiting_input_ack_);
+
+ ProcessNextEventFromQueue();
+}
+
+const WindowServer* WindowManagerState::window_server() const {
+ return window_tree_->window_server();
+}
+
+WindowServer* WindowManagerState::window_server() {
+ return window_tree_->window_server();
+}
+
+DisplayManager* WindowManagerState::display_manager() {
+ return window_tree_->display_manager();
+}
+
+const DisplayManager* WindowManagerState::display_manager() const {
+ return window_tree_->display_manager();
+}
+
+void WindowManagerState::SetAllRootWindowsVisible(bool value) {
+ for (Display* display : display_manager()->displays()) {
+ WindowManagerDisplayRoot* display_root =
+ display->GetWindowManagerDisplayRootForUser(user_id());
+ if (display_root)
+ display_root->root()->SetVisible(value);
+ }
+}
+
+ServerWindow* WindowManagerState::GetWindowManagerRoot(ServerWindow* window) {
+ for (Display* display : display_manager()->displays()) {
+ WindowManagerDisplayRoot* display_root =
+ display->GetWindowManagerDisplayRootForUser(user_id());
+ if (display_root && display_root->root()->parent() == window)
+ return display_root->root();
+ }
+ NOTREACHED();
+ return nullptr;
+}
+
+void WindowManagerState::OnEventAckTimeout(ClientSpecificId client_id) {
+ WindowTree* hung_tree = window_server()->GetTreeWithId(client_id);
+ if (hung_tree && !hung_tree->janky())
+ window_tree_->ClientJankinessChanged(hung_tree);
+ OnEventAck(tree_awaiting_input_ack_, mojom::EventResult::UNHANDLED);
+}
+
+void WindowManagerState::QueueEvent(
+ const ui::Event& event,
+ std::unique_ptr<ProcessedEventTarget> processed_event_target) {
+ std::unique_ptr<QueuedEvent> queued_event(new QueuedEvent);
+ queued_event->event = ui::Event::Clone(event);
+ queued_event->processed_target = std::move(processed_event_target);
+ event_queue_.push(std::move(queued_event));
+}
+
+void WindowManagerState::ProcessNextEventFromQueue() {
+ // Loop through |event_queue_| stopping after dispatching the first valid
+ // event.
+ while (!event_queue_.empty()) {
+ std::unique_ptr<QueuedEvent> queued_event = std::move(event_queue_.front());
+ event_queue_.pop();
+ if (!queued_event->processed_target) {
+ event_dispatcher_.ProcessEvent(*queued_event->event);
+ return;
+ }
+ if (queued_event->processed_target->IsValid()) {
+ DispatchInputEventToWindowImpl(
+ queued_event->processed_target->window(),
+ queued_event->processed_target->client_id(), *queued_event->event,
+ queued_event->processed_target->accelerator());
+ return;
+ }
+ }
+}
+
+void WindowManagerState::DispatchInputEventToWindowImpl(
+ ServerWindow* target,
+ ClientSpecificId client_id,
+ const ui::Event& event,
+ base::WeakPtr<Accelerator> accelerator) {
+ if (target && target->parent() == nullptr)
+ target = GetWindowManagerRoot(target);
+
+ if (event.IsMousePointerEvent()) {
+ DCHECK(event_dispatcher_.mouse_cursor_source_window());
+
+ int32_t cursor_id = 0;
+ if (event_dispatcher_.GetCurrentMouseCursor(&cursor_id)) {
+ WindowManagerDisplayRoot* display_root =
+ display_manager()->GetWindowManagerDisplayRoot(target);
+ display_root->display()->UpdateNativeCursor(cursor_id);
+ }
+ }
+
+ WindowTree* tree = window_server()->GetTreeWithId(client_id);
+
+ // TOOD(sad): Adjust this delay, possibly make this dynamic.
+ const base::TimeDelta max_delay = base::debug::BeingDebugged()
+ ? base::TimeDelta::FromDays(1)
+ : GetDefaultAckTimerDelay();
+ event_ack_timer_.Start(
+ FROM_HERE, max_delay,
+ base::Bind(&WindowManagerState::OnEventAckTimeout,
+ weak_factory_.GetWeakPtr(), tree->id()));
+
+ tree_awaiting_input_ack_ = tree;
+ if (accelerator) {
+ event_awaiting_input_ack_ = ui::Event::Clone(event);
+ post_target_accelerator_ = accelerator;
+ }
+
+ // Ignore |tree| because it will receive the event via normal dispatch.
+ window_server()->SendToEventObservers(event, user_id(), tree);
+
+ tree->DispatchInputEvent(target, event);
+}
+
+void WindowManagerState::AddDebugAccelerators() {
+ // Always register the accelerators, even if they only work in debug, so that
+ // keyboard behavior is the same in release and debug builds.
+ mojom::EventMatcherPtr matcher = CreateKeyMatcher(
+ ui::mojom::KeyboardCode::S, ui::mojom::kEventFlagControlDown |
+ ui::mojom::kEventFlagAltDown |
+ ui::mojom::kEventFlagShiftDown);
+ event_dispatcher_.AddAccelerator(kPrintWindowsDebugAcceleratorId,
+ std::move(matcher));
+}
+
+bool WindowManagerState::HandleDebugAccelerator(uint32_t accelerator_id) {
+#if !defined(NDEBUG)
+ if (accelerator_id == kPrintWindowsDebugAcceleratorId) {
+ // Error so it will be collected in system logs.
+ for (Display* display : display_manager()->displays()) {
+ WindowManagerDisplayRoot* display_root =
+ display->GetWindowManagerDisplayRootForUser(user_id());
+ if (display_root) {
+ LOG(ERROR) << "ServerWindow hierarchy:\n"
+ << display_root->root()->GetDebugWindowHierarchy();
+ }
+ }
+ return true;
+ }
+#endif
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// EventDispatcherDelegate:
+
+void WindowManagerState::OnAccelerator(uint32_t accelerator_id,
+ const ui::Event& event) {
+ DCHECK(IsActive());
+ if (HandleDebugAccelerator(accelerator_id))
+ return;
+ window_tree_->OnAccelerator(accelerator_id, event);
+}
+
+void WindowManagerState::SetFocusedWindowFromEventDispatcher(
+ ServerWindow* new_focused_window) {
+ DCHECK(IsActive());
+ window_server()->SetFocusedWindow(new_focused_window);
+}
+
+ServerWindow* WindowManagerState::GetFocusedWindowForEventDispatcher() {
+ return window_server()->GetFocusedWindow();
+}
+
+void WindowManagerState::SetNativeCapture(ServerWindow* window) {
+ DCHECK(IsActive());
+ WindowManagerDisplayRoot* display_root =
+ display_manager()->GetWindowManagerDisplayRoot(window);
+ DCHECK(display_root);
+ platform_display_with_capture_ = display_root->display()->platform_display();
+ platform_display_with_capture_->SetCapture();
+}
+
+void WindowManagerState::ReleaseNativeCapture() {
+ // Tests trigger calling this without a corresponding SetNativeCapture().
+ // TODO(sky): maybe abstract this away so that DCHECK can be added?
+ if (!platform_display_with_capture_)
+ return;
+
+ platform_display_with_capture_->ReleaseCapture();
+ platform_display_with_capture_ = nullptr;
+}
+
+void WindowManagerState::OnServerWindowCaptureLost(ServerWindow* window) {
+ DCHECK(window);
+ window_server()->ProcessLostCapture(window);
+}
+
+void WindowManagerState::OnMouseCursorLocationChanged(const gfx::Point& point) {
+ window_server()
+ ->display_manager()
+ ->GetUserDisplayManager(user_id())
+ ->OnMouseCursorLocationChanged(point);
+}
+
+void WindowManagerState::DispatchInputEventToWindow(ServerWindow* target,
+ ClientSpecificId client_id,
+ const ui::Event& event,
+ Accelerator* accelerator) {
+ DCHECK(IsActive());
+ // TODO(sky): this needs to see if another wms has capture and if so forward
+ // to it.
+ if (event_ack_timer_.IsRunning()) {
+ std::unique_ptr<ProcessedEventTarget> processed_event_target(
+ new ProcessedEventTarget(target, client_id, accelerator));
+ QueueEvent(event, std::move(processed_event_target));
+ return;
+ }
+
+ base::WeakPtr<Accelerator> weak_accelerator;
+ if (accelerator)
+ weak_accelerator = accelerator->GetWeakPtr();
+ DispatchInputEventToWindowImpl(target, client_id, event, weak_accelerator);
+}
+
+ClientSpecificId WindowManagerState::GetEventTargetClientId(
+ const ServerWindow* window,
+ bool in_nonclient_area) {
+ // If the event is in the non-client area the event goes to the owner of
+ // the window.
+ WindowTree* tree = nullptr;
+ if (in_nonclient_area) {
+ tree = window_server()->GetTreeWithId(window->id().client_id);
+ } else {
+ // If the window is an embed root, forward to the embedded window.
+ tree = window_server()->GetTreeWithRoot(window);
+ if (!tree)
+ tree = window_server()->GetTreeWithId(window->id().client_id);
+ }
+
+ const ServerWindow* embed_root =
+ tree->HasRoot(window) ? window : GetEmbedRoot(window);
+ while (tree && tree->embedder_intercepts_events()) {
+ DCHECK(tree->HasRoot(embed_root));
+ tree = window_server()->GetTreeWithId(embed_root->id().client_id);
+ embed_root = GetEmbedRoot(embed_root);
+ }
+
+ if (!tree) {
+ DCHECK(in_nonclient_area);
+ tree = window_tree_;
+ }
+ return tree->id();
+}
+
+ServerWindow* WindowManagerState::GetRootWindowContaining(
+ const gfx::Point& location) {
+ if (display_manager()->displays().empty())
+ return nullptr;
+
+ // TODO(sky): this isn't right. To correctly implement need bounds of
+ // Display, which we aren't tracking yet. For now, use the first display.
+ Display* display = *(display_manager()->displays().begin());
+ WindowManagerDisplayRoot* display_root =
+ display->GetWindowManagerDisplayRootForUser(user_id());
+ return display_root ? display_root->root() : nullptr;
+}
+
+void WindowManagerState::OnEventTargetNotFound(const ui::Event& event) {
+ window_server()->SendToEventObservers(event, user_id(),
+ nullptr /* ignore_tree */);
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/window_manager_state.h b/chromium/components/mus/ws/window_manager_state.h
new file mode 100644
index 00000000000..dca6670d400
--- /dev/null
+++ b/chromium/components/mus/ws/window_manager_state.h
@@ -0,0 +1,183 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_WINDOW_MANAGER_STATE_H_
+#define COMPONENTS_MUS_WS_WINDOW_MANAGER_STATE_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
+#include "components/mus/public/interfaces/display.mojom.h"
+#include "components/mus/ws/event_dispatcher.h"
+#include "components/mus/ws/event_dispatcher_delegate.h"
+#include "components/mus/ws/user_id.h"
+#include "components/mus/ws/window_server.h"
+
+namespace mus {
+namespace ws {
+
+class DisplayManager;
+class WindowTree;
+
+namespace test {
+class WindowManagerStateTestApi;
+}
+
+// Manages state specific to a WindowManager that is shared across displays.
+// WindowManagerState is owned by the WindowTree the window manager is
+// associated with.
+class WindowManagerState : public EventDispatcherDelegate {
+ public:
+ explicit WindowManagerState(WindowTree* window_tree);
+ ~WindowManagerState() override;
+
+ const UserId& user_id() const;
+
+ WindowTree* window_tree() { return window_tree_; }
+ const WindowTree* window_tree() const { return window_tree_; }
+
+ void OnWillDestroyTree(WindowTree* tree);
+
+ void SetFrameDecorationValues(mojom::FrameDecorationValuesPtr values);
+ const mojom::FrameDecorationValues& frame_decoration_values() const {
+ return *frame_decoration_values_;
+ }
+ bool got_frame_decoration_values() const {
+ return got_frame_decoration_values_;
+ }
+
+ bool SetCapture(ServerWindow* window, ClientSpecificId client_id);
+ ServerWindow* capture_window() { return event_dispatcher_.capture_window(); }
+ const ServerWindow* capture_window() const {
+ return event_dispatcher_.capture_window();
+ }
+
+ void ReleaseCaptureBlockedByModalWindow(const ServerWindow* modal_window);
+ void ReleaseCaptureBlockedByAnyModalWindow();
+
+ void AddSystemModalWindow(ServerWindow* window);
+
+ // TODO(sky): EventDispatcher is really an implementation detail and should
+ // not be exposed.
+ EventDispatcher* event_dispatcher() { return &event_dispatcher_; }
+
+ // Returns true if this is the WindowManager of the active user.
+ bool IsActive() const;
+
+ void Activate(const gfx::Point& mouse_location_on_screen);
+ void Deactivate();
+
+ // Processes an event from PlatformDisplay.
+ void ProcessEvent(const ui::Event& event);
+
+ // Called when the ack from an event dispatched to WindowTree |tree| is
+ // received.
+ // TODO(sky): make this private and use a callback.
+ void OnEventAck(mojom::WindowTree* tree, mojom::EventResult result);
+
+ private:
+ class ProcessedEventTarget;
+ friend class Display;
+ friend class test::WindowManagerStateTestApi;
+
+ // There are two types of events that may be queued, both occur only when
+ // waiting for an ack from a client.
+ // . We get an event from the PlatformDisplay. This results in |event| being
+ // set, but |processed_target| is null.
+ // . We get an event from the EventDispatcher. In this case both |event| and
+ // |processed_target| are valid.
+ // The second case happens if EventDispatcher generates more than one event
+ // at a time.
+ struct QueuedEvent {
+ QueuedEvent();
+ ~QueuedEvent();
+
+ std::unique_ptr<ui::Event> event;
+ std::unique_ptr<ProcessedEventTarget> processed_target;
+ };
+
+ const WindowServer* window_server() const;
+ WindowServer* window_server();
+
+ DisplayManager* display_manager();
+ const DisplayManager* display_manager() const;
+
+ // Sets the visibility of all window manager roots windows to |value|.
+ void SetAllRootWindowsVisible(bool value);
+
+ // Returns the ServerWindow that is the root of the WindowManager for
+ // |window|. |window| corresponds to the root of a Display.
+ ServerWindow* GetWindowManagerRoot(ServerWindow* window);
+
+ void OnEventAckTimeout(ClientSpecificId client_id);
+
+ // Schedules an event to be processed later.
+ void QueueEvent(const ui::Event& event,
+ std::unique_ptr<ProcessedEventTarget> processed_event_target);
+
+ // Processes the next valid event in |event_queue_|. If the event has already
+ // been processed it is dispatched, otherwise the event is passed to the
+ // EventDispatcher for processing.
+ void ProcessNextEventFromQueue();
+
+ // Dispatches the event to the appropriate client and starts the ack timer.
+ void DispatchInputEventToWindowImpl(ServerWindow* target,
+ ClientSpecificId client_id,
+ const ui::Event& event,
+ base::WeakPtr<Accelerator> accelerator);
+
+ // Registers accelerators used internally for debugging.
+ void AddDebugAccelerators();
+
+ // Returns true if the accelerator was handled.
+ bool HandleDebugAccelerator(uint32_t accelerator_id);
+
+ // EventDispatcherDelegate:
+ void OnAccelerator(uint32_t accelerator_id, const ui::Event& event) override;
+ void SetFocusedWindowFromEventDispatcher(ServerWindow* window) override;
+ ServerWindow* GetFocusedWindowForEventDispatcher() override;
+ void SetNativeCapture(ServerWindow* window) override;
+ void ReleaseNativeCapture() override;
+ void OnServerWindowCaptureLost(ServerWindow* window) override;
+ void OnMouseCursorLocationChanged(const gfx::Point& point) override;
+ void DispatchInputEventToWindow(ServerWindow* target,
+ ClientSpecificId client_id,
+ const ui::Event& event,
+ Accelerator* accelerator) override;
+ ClientSpecificId GetEventTargetClientId(const ServerWindow* window,
+ bool in_nonclient_area) override;
+ ServerWindow* GetRootWindowContaining(const gfx::Point& location) override;
+ void OnEventTargetNotFound(const ui::Event& event) override;
+
+ // The single WindowTree this WindowManagerState is associated with.
+ // |window_tree_| owns this.
+ WindowTree* window_tree_;
+
+ // Set to true the first time SetFrameDecorationValues() is called.
+ bool got_frame_decoration_values_ = false;
+ mojom::FrameDecorationValuesPtr frame_decoration_values_;
+
+ mojom::WindowTree* tree_awaiting_input_ack_ = nullptr;
+ std::unique_ptr<ui::Event> event_awaiting_input_ack_;
+ base::WeakPtr<Accelerator> post_target_accelerator_;
+ std::queue<std::unique_ptr<QueuedEvent>> event_queue_;
+ base::OneShotTimer event_ack_timer_;
+
+ EventDispatcher event_dispatcher_;
+
+ // PlatformDisplay that currently has capture.
+ PlatformDisplay* platform_display_with_capture_ = nullptr;
+
+ base::WeakPtrFactory<WindowManagerState> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowManagerState);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_WINDOW_MANAGER_STATE_H_
diff --git a/chromium/components/mus/ws/window_manager_state_unittest.cc b/chromium/components/mus/ws/window_manager_state_unittest.cc
new file mode 100644
index 00000000000..20980f75b4c
--- /dev/null
+++ b/chromium/components/mus/ws/window_manager_state_unittest.cc
@@ -0,0 +1,419 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/window_manager_state.h"
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/mus/common/event_matcher_util.h"
+#include "components/mus/surfaces/surfaces_state.h"
+#include "components/mus/ws/accelerator.h"
+#include "components/mus/ws/display.h"
+#include "components/mus/ws/display_binding.h"
+#include "components/mus/ws/platform_display.h"
+#include "components/mus/ws/platform_display_init_params.h"
+#include "components/mus/ws/server_window_surface_manager_test_api.h"
+#include "components/mus/ws/test_change_tracker.h"
+#include "components/mus/ws/test_server_window_delegate.h"
+#include "components/mus/ws/test_utils.h"
+#include "components/mus/ws/window_manager_access_policy.h"
+#include "components/mus/ws/window_manager_display_root.h"
+#include "components/mus/ws/window_manager_state.h"
+#include "components/mus/ws/window_server.h"
+#include "components/mus/ws/window_tree.h"
+#include "services/shell/public/interfaces/connector.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/event.h"
+
+namespace mus {
+namespace ws {
+namespace test {
+
+class WindowManagerStateTest : public testing::Test {
+ public:
+ WindowManagerStateTest();
+ ~WindowManagerStateTest() override {}
+
+ std::unique_ptr<Accelerator> CreateAccelerator();
+
+ // Creates a child |server_window| with associataed |window_tree| and
+ // |test_client|. The window is setup for processing input.
+ void CreateSecondaryTree(TestWindowTreeClient** test_client,
+ WindowTree** window_tree,
+ ServerWindow** server_window);
+
+ void DispatchInputEventToWindow(ServerWindow* target,
+ const ui::Event& event,
+ Accelerator* accelerator);
+ void OnEventAckTimeout(ClientSpecificId client_id);
+
+ WindowTree* tree() {
+ return window_event_targeting_helper_.window_server()->GetTreeWithId(1);
+ }
+ WindowTree* window_tree() { return window_tree_; }
+ TestWindowTreeClient* window_tree_client() { return window_tree_client_; }
+ ServerWindow* window() { return window_; }
+ TestWindowManager* window_manager() { return &window_manager_; }
+ TestWindowTreeClient* wm_client() {
+ return window_event_targeting_helper_.wm_client();
+ }
+ TestWindowTreeClient* last_tree_client() {
+ return window_event_targeting_helper_.last_window_tree_client();
+ }
+ WindowTree* last_tree() {
+ return window_event_targeting_helper_.last_binding()->tree();
+ }
+ WindowManagerState* window_manager_state() { return window_manager_state_; }
+
+ void EmbedAt(WindowTree* tree,
+ const ClientWindowId& embed_window_id,
+ uint32_t embed_flags,
+ WindowTree** embed_tree,
+ TestWindowTreeClient** embed_client_proxy) {
+ mojom::WindowTreeClientPtr embed_client;
+ mojom::WindowTreeClientRequest client_request = GetProxy(&embed_client);
+ ASSERT_TRUE(
+ tree->Embed(embed_window_id, std::move(embed_client), embed_flags));
+ TestWindowTreeClient* client =
+ window_event_targeting_helper_.last_window_tree_client();
+ ASSERT_EQ(1u, client->tracker()->changes()->size());
+ EXPECT_EQ(CHANGE_TYPE_EMBED, (*client->tracker()->changes())[0].type);
+ client->tracker()->changes()->clear();
+ *embed_client_proxy = client;
+ *embed_tree = window_event_targeting_helper_.last_binding()->tree();
+ }
+
+ // testing::Test:
+ void SetUp() override;
+
+ private:
+ WindowEventTargetingHelper window_event_targeting_helper_;
+
+ WindowManagerState* window_manager_state_;
+
+ // Handles WindowStateManager ack timeouts.
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ TestWindowManager window_manager_;
+ ServerWindow* window_ = nullptr;
+ WindowTree* window_tree_ = nullptr;
+ TestWindowTreeClient* window_tree_client_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowManagerStateTest);
+};
+
+WindowManagerStateTest::WindowManagerStateTest()
+ : task_runner_(new base::TestSimpleTaskRunner) {}
+
+std::unique_ptr<Accelerator> WindowManagerStateTest::CreateAccelerator() {
+ mojom::EventMatcherPtr matcher = mus::CreateKeyMatcher(
+ ui::mojom::KeyboardCode::W, ui::mojom::kEventFlagControlDown);
+ matcher->accelerator_phase = ui::mojom::AcceleratorPhase::POST_TARGET;
+ uint32_t accelerator_id = 1;
+ std::unique_ptr<Accelerator> accelerator(
+ new Accelerator(accelerator_id, *matcher));
+ return accelerator;
+}
+
+void WindowManagerStateTest::CreateSecondaryTree(
+ TestWindowTreeClient** test_client,
+ WindowTree** window_tree,
+ ServerWindow** server_window) {
+ window_event_targeting_helper_.CreateSecondaryTree(
+ window_, gfx::Rect(20, 20, 20, 20), test_client, window_tree,
+ server_window);
+}
+
+void WindowManagerStateTest::DispatchInputEventToWindow(
+ ServerWindow* target,
+ const ui::Event& event,
+ Accelerator* accelerator) {
+ WindowManagerStateTestApi test_api(window_manager_state_);
+ ClientSpecificId client_id = test_api.GetEventTargetClientId(target, false);
+ test_api.DispatchInputEventToWindow(target, client_id, event, accelerator);
+}
+
+void WindowManagerStateTest::OnEventAckTimeout(
+ ClientSpecificId client_id) {
+ WindowManagerStateTestApi test_api(window_manager_state_);
+ test_api.OnEventAckTimeout(client_id);
+}
+
+void WindowManagerStateTest::SetUp() {
+ window_event_targeting_helper_.SetTaskRunner(task_runner_);
+ window_manager_state_ = window_event_targeting_helper_.display()
+ ->GetActiveWindowManagerDisplayRoot()
+ ->window_manager_state();
+ window_ = window_event_targeting_helper_.CreatePrimaryTree(
+ gfx::Rect(0, 0, 100, 100), gfx::Rect(0, 0, 50, 50));
+ window_tree_ = window_event_targeting_helper_.last_binding()->tree();
+ window_tree_client_ =
+ window_event_targeting_helper_.last_window_tree_client();
+ DCHECK(window_tree_->HasRoot(window_));
+
+ WindowTreeTestApi(tree()).set_window_manager_internal(&window_manager_);
+ wm_client()->tracker()->changes()->clear();
+ window_tree_client_->tracker()->changes()->clear();
+}
+
+// Tests that when an event is dispatched with no accelerator, that post target
+// accelerator is not triggered.
+TEST_F(WindowManagerStateTest, NullAccelerator) {
+ WindowManagerState* state = window_manager_state();
+ EXPECT_TRUE(state);
+
+ ServerWindow* target = window();
+ ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN);
+ DispatchInputEventToWindow(target, key, nullptr);
+ WindowTree* target_tree = window_tree();
+ TestChangeTracker* tracker = window_tree_client()->tracker();
+ ASSERT_EQ(1u, tracker->changes()->size());
+ EXPECT_EQ("InputEvent window=1,1 event_action=7",
+ ChangesToDescription1(*tracker->changes())[0]);
+
+ WindowTreeTestApi(target_tree).AckOldestEvent();
+ EXPECT_FALSE(window_manager()->on_accelerator_called());
+}
+
+// Tests that when a post target accelerator is provided on an event, that it is
+// called on ack.
+TEST_F(WindowManagerStateTest, PostTargetAccelerator) {
+ ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN);
+ std::unique_ptr<Accelerator> accelerator = CreateAccelerator();
+
+ ServerWindow* target = window();
+ DispatchInputEventToWindow(target, key, accelerator.get());
+ TestChangeTracker* tracker = window_tree_client()->tracker();
+ ASSERT_EQ(1u, tracker->changes()->size());
+ EXPECT_EQ("InputEvent window=1,1 event_action=7",
+ ChangesToDescription1(*tracker->changes())[0]);
+
+ WindowTreeTestApi(window_tree()).AckOldestEvent();
+ EXPECT_TRUE(window_manager()->on_accelerator_called());
+ EXPECT_EQ(accelerator->id(), window_manager()->on_accelerator_id());
+}
+
+// Tests that when a client handles an event that post target accelerators are
+// not called.
+TEST_F(WindowManagerStateTest, ClientHandlesEvent) {
+ ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN);
+ std::unique_ptr<Accelerator> accelerator = CreateAccelerator();
+
+ ServerWindow* target = window();
+ DispatchInputEventToWindow(target, key, accelerator.get());
+ TestChangeTracker* tracker = window_tree_client()->tracker();
+ ASSERT_EQ(1u, tracker->changes()->size());
+ EXPECT_EQ("InputEvent window=1,1 event_action=7",
+ ChangesToDescription1(*tracker->changes())[0]);
+
+ window_manager_state()->OnEventAck(tree(), mojom::EventResult::HANDLED);
+ EXPECT_FALSE(window_manager()->on_accelerator_called());
+}
+
+// Tests that when an accelerator is deleted before an ack, that it is not
+// called.
+TEST_F(WindowManagerStateTest, AcceleratorDeleted) {
+ ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN);
+ std::unique_ptr<Accelerator> accelerator(CreateAccelerator());
+
+ ServerWindow* target = window();
+ DispatchInputEventToWindow(target, key, accelerator.get());
+ TestChangeTracker* tracker = window_tree_client()->tracker();
+ ASSERT_EQ(1u, tracker->changes()->size());
+ EXPECT_EQ("InputEvent window=1,1 event_action=7",
+ ChangesToDescription1(*tracker->changes())[0]);
+
+ accelerator.reset();
+ window_manager_state()->OnEventAck(tree(), mojom::EventResult::UNHANDLED);
+ EXPECT_FALSE(window_manager()->on_accelerator_called());
+}
+
+// Tests that a events arriving before an ack don't notify the tree until the
+// ack arrives, and that the correct accelerator is called.
+TEST_F(WindowManagerStateTest, EnqueuedAccelerators) {
+ ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN);
+ std::unique_ptr<Accelerator> accelerator(CreateAccelerator());
+
+ ServerWindow* target = window();
+ DispatchInputEventToWindow(target, key, accelerator.get());
+ TestChangeTracker* tracker = window_tree_client()->tracker();
+ ASSERT_EQ(1u, tracker->changes()->size());
+ EXPECT_EQ("InputEvent window=1,1 event_action=7",
+ ChangesToDescription1(*tracker->changes())[0]);
+
+ tracker->changes()->clear();
+ ui::KeyEvent key2(ui::ET_KEY_PRESSED, ui::VKEY_Y, ui::EF_CONTROL_DOWN);
+ mojom::EventMatcherPtr matcher = mus::CreateKeyMatcher(
+ ui::mojom::KeyboardCode::Y, ui::mojom::kEventFlagControlDown);
+ matcher->accelerator_phase = ui::mojom::AcceleratorPhase::POST_TARGET;
+ uint32_t accelerator_id = 2;
+ std::unique_ptr<Accelerator> accelerator2(
+ new Accelerator(accelerator_id, *matcher));
+ DispatchInputEventToWindow(target, key2, accelerator2.get());
+ EXPECT_TRUE(tracker->changes()->empty());
+
+ WindowTreeTestApi(window_tree()).AckOldestEvent();
+ ASSERT_EQ(1u, tracker->changes()->size());
+ EXPECT_EQ("InputEvent window=1,1 event_action=7",
+ ChangesToDescription1(*tracker->changes())[0]);
+ EXPECT_TRUE(window_manager()->on_accelerator_called());
+ EXPECT_EQ(accelerator->id(), window_manager()->on_accelerator_id());
+}
+
+// Tests that the accelerator is not sent when the tree is dying.
+TEST_F(WindowManagerStateTest, DeleteTree) {
+ ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN);
+ std::unique_ptr<Accelerator> accelerator = CreateAccelerator();
+
+ ServerWindow* target = window();
+ DispatchInputEventToWindow(target, key, accelerator.get());
+ TestChangeTracker* tracker = window_tree_client()->tracker();
+ ASSERT_EQ(1u, tracker->changes()->size());
+ EXPECT_EQ("InputEvent window=1,1 event_action=7",
+ ChangesToDescription1(*tracker->changes())[0]);
+
+ window_manager_state()->OnWillDestroyTree(tree());
+ EXPECT_FALSE(window_manager()->on_accelerator_called());
+}
+
+// Tests that if a tree is destroyed before acking, that the accelerator is
+// still sent if it is not the root tree.
+TEST_F(WindowManagerStateTest, DeleteNonRootTree) {
+ TestWindowTreeClient* embed_connection = nullptr;
+ WindowTree* target_tree = nullptr;
+ ServerWindow* target = nullptr;
+ CreateSecondaryTree(&embed_connection, &target_tree, &target);
+ TestWindowManager target_window_manager;
+ WindowTreeTestApi(target_tree)
+ .set_window_manager_internal(&target_window_manager);
+
+ ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN);
+ std::unique_ptr<Accelerator> accelerator = CreateAccelerator();
+ DispatchInputEventToWindow(target, key, accelerator.get());
+ TestChangeTracker* tracker = embed_connection->tracker();
+ ASSERT_EQ(1u, tracker->changes()->size());
+ EXPECT_EQ("InputEvent window=2,1 event_action=7",
+ ChangesToDescription1(*tracker->changes())[0]);
+ EXPECT_TRUE(wm_client()->tracker()->changes()->empty());
+
+ window_manager_state()->OnWillDestroyTree(target_tree);
+ EXPECT_FALSE(target_window_manager.on_accelerator_called());
+ EXPECT_TRUE(window_manager()->on_accelerator_called());
+}
+
+// Tests that when an ack times out that the accelerator is notified.
+TEST_F(WindowManagerStateTest, AckTimeout) {
+ ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN);
+ std::unique_ptr<Accelerator> accelerator = CreateAccelerator();
+ DispatchInputEventToWindow(window(), key, accelerator.get());
+ TestChangeTracker* tracker = window_tree_client()->tracker();
+ ASSERT_EQ(1u, tracker->changes()->size());
+ EXPECT_EQ("InputEvent window=1,1 event_action=7",
+ ChangesToDescription1(*tracker->changes())[0]);
+
+ OnEventAckTimeout(window()->id().client_id);
+ EXPECT_TRUE(window_manager()->on_accelerator_called());
+ EXPECT_EQ(accelerator->id(), window_manager()->on_accelerator_id());
+}
+
+TEST_F(WindowManagerStateTest, InterceptingEmbedderReceivesEvents) {
+ WindowTree* embedder_tree = tree();
+ ServerWindow* embedder_root = window();
+ const ClientWindowId embed_window_id(
+ WindowIdToTransportId(WindowId(embedder_tree->id(), 12)));
+ embedder_tree->NewWindow(embed_window_id, ServerWindow::Properties());
+ ServerWindow* embedder_window =
+ embedder_tree->GetWindowByClientId(embed_window_id);
+ ASSERT_TRUE(embedder_tree->AddWindow(
+ ClientWindowId(WindowIdToTransportId(embedder_root->id())),
+ embed_window_id));
+
+ TestWindowTreeClient* embedder_client = wm_client();
+
+ {
+ // Do a normal embed.
+ const uint32_t embed_flags = 0;
+ WindowTree* embed_tree = nullptr;
+ TestWindowTreeClient* embed_client_proxy = nullptr;
+ EmbedAt(embedder_tree, embed_window_id, embed_flags, &embed_tree,
+ &embed_client_proxy);
+ ASSERT_TRUE(embed_client_proxy);
+
+ // Send an event to the embed window. It should go to the embedded client.
+ ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(),
+ base::TimeTicks(), 0, 0);
+ DispatchInputEventToWindow(embedder_window, mouse, nullptr);
+ ASSERT_EQ(1u, embed_client_proxy->tracker()->changes()->size());
+ EXPECT_EQ(CHANGE_TYPE_INPUT_EVENT,
+ (*embed_client_proxy->tracker()->changes())[0].type);
+ WindowTreeTestApi(embed_tree).AckLastEvent(mojom::EventResult::UNHANDLED);
+ embed_client_proxy->tracker()->changes()->clear();
+ }
+
+ {
+ // Do an embed where the embedder wants to intercept events to the embedded
+ // tree.
+ const uint32_t embed_flags = mojom::kEmbedFlagEmbedderInterceptsEvents;
+ WindowTree* embed_tree = nullptr;
+ TestWindowTreeClient* embed_client_proxy = nullptr;
+ EmbedAt(embedder_tree, embed_window_id, embed_flags, &embed_tree,
+ &embed_client_proxy);
+ ASSERT_TRUE(embed_client_proxy);
+ embedder_client->tracker()->changes()->clear();
+
+ // Send an event to the embed window. But this time, it should reach the
+ // embedder.
+ ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(),
+ base::TimeTicks(), 0, 0);
+ DispatchInputEventToWindow(embedder_window, mouse, nullptr);
+ ASSERT_EQ(0u, embed_client_proxy->tracker()->changes()->size());
+ ASSERT_EQ(1u, embedder_client->tracker()->changes()->size());
+ EXPECT_EQ(CHANGE_TYPE_INPUT_EVENT,
+ (*embedder_client->tracker()->changes())[0].type);
+ WindowTreeTestApi(embedder_tree)
+ .AckLastEvent(mojom::EventResult::UNHANDLED);
+ embedder_client->tracker()->changes()->clear();
+
+ // Embed another tree in the embedded tree.
+ const ClientWindowId nested_embed_window_id(
+ WindowIdToTransportId(WindowId(embed_tree->id(), 23)));
+ embed_tree->NewWindow(nested_embed_window_id, ServerWindow::Properties());
+ const ClientWindowId embed_root_id(
+ WindowIdToTransportId((*embed_tree->roots().begin())->id()));
+ ASSERT_TRUE(embed_tree->AddWindow(embed_root_id, nested_embed_window_id));
+
+ WindowTree* nested_embed_tree = nullptr;
+ TestWindowTreeClient* nested_embed_client_proxy = nullptr;
+ EmbedAt(embed_tree, nested_embed_window_id, embed_flags, &nested_embed_tree,
+ &nested_embed_client_proxy);
+ ASSERT_TRUE(nested_embed_client_proxy);
+ embed_client_proxy->tracker()->changes()->clear();
+ embedder_client->tracker()->changes()->clear();
+
+ // Send an event to the nested embed window. The event should still reach
+ // the outermost embedder.
+ ServerWindow* nested_embed_window =
+ embed_tree->GetWindowByClientId(nested_embed_window_id);
+ DCHECK(nested_embed_window->parent());
+ mouse = ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(),
+ base::TimeTicks(), 0, 0);
+ DispatchInputEventToWindow(nested_embed_window, mouse, nullptr);
+ ASSERT_EQ(0u, nested_embed_client_proxy->tracker()->changes()->size());
+ ASSERT_EQ(0u, embed_client_proxy->tracker()->changes()->size());
+
+ ASSERT_EQ(1u, embedder_client->tracker()->changes()->size());
+ EXPECT_EQ(CHANGE_TYPE_INPUT_EVENT,
+ (*embedder_client->tracker()->changes())[0].type);
+ WindowTreeTestApi(embedder_tree)
+ .AckLastEvent(mojom::EventResult::UNHANDLED);
+ }
+}
+
+} // namespace test
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/window_manager_window_tree_factory.cc b/chromium/components/mus/ws/window_manager_window_tree_factory.cc
new file mode 100644
index 00000000000..a851092bd48
--- /dev/null
+++ b/chromium/components/mus/ws/window_manager_window_tree_factory.cc
@@ -0,0 +1,64 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/window_manager_window_tree_factory.h"
+
+#include "base/bind.h"
+#include "components/mus/ws/window_manager_window_tree_factory_set.h"
+#include "components/mus/ws/window_server.h"
+#include "components/mus/ws/window_tree.h"
+
+namespace mus {
+namespace ws {
+
+WindowManagerWindowTreeFactory::WindowManagerWindowTreeFactory(
+ WindowManagerWindowTreeFactorySet* window_manager_window_tree_factory_set,
+ const UserId& user_id,
+ mojo::InterfaceRequest<mojom::WindowManagerWindowTreeFactory> request)
+ : window_manager_window_tree_factory_set_(
+ window_manager_window_tree_factory_set),
+ user_id_(user_id),
+ binding_(this),
+ window_tree_(nullptr) {
+ if (request.is_pending())
+ binding_.Bind(std::move(request));
+}
+
+WindowManagerWindowTreeFactory::~WindowManagerWindowTreeFactory() {}
+
+void WindowManagerWindowTreeFactory::CreateWindowTree(
+ mojom::WindowTreeRequest window_tree_request,
+ mojom::WindowTreeClientPtr window_tree_client) {
+ // CreateWindowTree() can only be called once, so there is no reason to keep
+ // the binding around.
+ if (binding_.is_bound())
+ binding_.Close();
+
+ SetWindowTree(GetWindowServer()->CreateTreeForWindowManager(
+ user_id_, std::move(window_tree_request), std::move(window_tree_client)));
+}
+
+WindowManagerWindowTreeFactory::WindowManagerWindowTreeFactory(
+ WindowManagerWindowTreeFactorySet* window_manager_window_tree_factory_set,
+ const UserId& user_id)
+ : window_manager_window_tree_factory_set_(
+ window_manager_window_tree_factory_set),
+ user_id_(user_id),
+ binding_(this),
+ window_tree_(nullptr) {}
+
+WindowServer* WindowManagerWindowTreeFactory::GetWindowServer() {
+ return window_manager_window_tree_factory_set_->window_server();
+}
+
+void WindowManagerWindowTreeFactory::SetWindowTree(WindowTree* window_tree) {
+ DCHECK(!window_tree_);
+ window_tree_ = window_tree;
+
+ window_manager_window_tree_factory_set_
+ ->OnWindowManagerWindowTreeFactoryReady(this);
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/window_manager_window_tree_factory.h b/chromium/components/mus/ws/window_manager_window_tree_factory.h
new file mode 100644
index 00000000000..909e3391d7f
--- /dev/null
+++ b/chromium/components/mus/ws/window_manager_window_tree_factory.h
@@ -0,0 +1,68 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_WINDOW_MANAGER_WINDOW_TREE_FACTORY_H_
+#define COMPONENTS_MUS_WS_WINDOW_MANAGER_WINDOW_TREE_FACTORY_H_
+
+#include <stdint.h>
+
+#include "components/mus/public/interfaces/window_manager_window_tree_factory.mojom.h"
+#include "components/mus/ws/user_id.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace mus {
+namespace ws {
+
+class ServerWindow;
+class WindowManagerWindowTreeFactorySet;
+class WindowServer;
+class WindowTree;
+
+namespace test {
+class WindowManagerWindowTreeFactorySetTestApi;
+}
+
+// Implementation of mojom::WindowManagerWindowTreeFactory.
+class WindowManagerWindowTreeFactory
+ : public mojom::WindowManagerWindowTreeFactory {
+ public:
+ WindowManagerWindowTreeFactory(
+ WindowManagerWindowTreeFactorySet* window_manager_window_tree_factory_set,
+ const UserId& user_id,
+ mojo::InterfaceRequest<mojom::WindowManagerWindowTreeFactory> request);
+ ~WindowManagerWindowTreeFactory() override;
+
+ const UserId& user_id() const { return user_id_; }
+
+ WindowTree* window_tree() { return window_tree_; }
+
+ // mojom::WindowManagerWindowTreeFactory:
+ void CreateWindowTree(mojom::WindowTreeRequest window_tree_request,
+ mojom::WindowTreeClientPtr window_tree_client) override;
+
+ private:
+ friend class test::WindowManagerWindowTreeFactorySetTestApi;
+
+ // Used by tests.
+ WindowManagerWindowTreeFactory(WindowManagerWindowTreeFactorySet* registry,
+ const UserId& user_id);
+
+ WindowServer* GetWindowServer();
+
+ void SetWindowTree(WindowTree* window_tree);
+
+ WindowManagerWindowTreeFactorySet* window_manager_window_tree_factory_set_;
+ const UserId user_id_;
+ mojo::Binding<mojom::WindowManagerWindowTreeFactory> binding_;
+
+ // Owned by WindowServer.
+ WindowTree* window_tree_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowManagerWindowTreeFactory);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_WINDOW_MANAGER_WINDOW_TREE_FACTORY_H_
diff --git a/chromium/components/mus/ws/window_manager_window_tree_factory_set.cc b/chromium/components/mus/ws/window_manager_window_tree_factory_set.cc
new file mode 100644
index 00000000000..91d01a8ab47
--- /dev/null
+++ b/chromium/components/mus/ws/window_manager_window_tree_factory_set.cc
@@ -0,0 +1,99 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/window_manager_window_tree_factory_set.h"
+
+#include "components/mus/ws/user_id_tracker_observer.h"
+#include "components/mus/ws/window_manager_window_tree_factory.h"
+#include "components/mus/ws/window_manager_window_tree_factory_set_observer.h"
+#include "components/mus/ws/window_server.h"
+#include "components/mus/ws/window_tree.h"
+
+namespace mus {
+namespace ws {
+
+WindowManagerWindowTreeFactorySet::WindowManagerWindowTreeFactorySet(
+ WindowServer* window_server,
+ UserIdTracker* id_tracker)
+ : id_tracker_(id_tracker), window_server_(window_server) {
+ id_tracker_->AddObserver(this);
+}
+
+WindowManagerWindowTreeFactorySet::~WindowManagerWindowTreeFactorySet() {
+ id_tracker_->RemoveObserver(this);
+}
+
+WindowManagerWindowTreeFactory* WindowManagerWindowTreeFactorySet::Add(
+ const UserId& user_id,
+ mojo::InterfaceRequest<mojom::WindowManagerWindowTreeFactory> request) {
+ if (factories_.count(user_id)) {
+ DVLOG(1) << "can only have one factory per user";
+ return nullptr;
+ }
+
+ std::unique_ptr<WindowManagerWindowTreeFactory> factory_ptr(
+ new WindowManagerWindowTreeFactory(this, user_id, std::move(request)));
+ WindowManagerWindowTreeFactory* factory = factory_ptr.get();
+ factories_[user_id] = std::move(factory_ptr);
+ return factory;
+}
+
+WindowManagerState*
+WindowManagerWindowTreeFactorySet::GetWindowManagerStateForUser(
+ const UserId& user_id) {
+ auto it = factories_.find(user_id);
+ if (it == factories_.end())
+ return nullptr;
+ return it->second->window_tree()
+ ? it->second->window_tree()->window_manager_state()
+ : nullptr;
+}
+
+void WindowManagerWindowTreeFactorySet::DeleteFactoryAssociatedWithTree(
+ WindowTree* window_tree) {
+ for (auto it = factories_.begin(); it != factories_.end(); ++it) {
+ if (it->second->window_tree() == window_tree) {
+ factories_.erase(it);
+ return;
+ }
+ }
+}
+
+std::vector<WindowManagerWindowTreeFactory*>
+WindowManagerWindowTreeFactorySet::GetFactories() {
+ std::vector<WindowManagerWindowTreeFactory*> result;
+ for (auto& pair : factories_)
+ result.push_back(pair.second.get());
+ return result;
+}
+
+void WindowManagerWindowTreeFactorySet::AddObserver(
+ WindowManagerWindowTreeFactorySetObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void WindowManagerWindowTreeFactorySet::RemoveObserver(
+ WindowManagerWindowTreeFactorySetObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void WindowManagerWindowTreeFactorySet::OnWindowManagerWindowTreeFactoryReady(
+ WindowManagerWindowTreeFactory* factory) {
+ const bool is_first_valid_factory = !got_valid_factory_;
+ got_valid_factory_ = true;
+ FOR_EACH_OBSERVER(WindowManagerWindowTreeFactorySetObserver, observers_,
+ OnWindowManagerWindowTreeFactoryReady(factory));
+
+ // Notify after other observers as WindowServer triggers other
+ // observers being added, which will have already processed the add.
+ if (is_first_valid_factory)
+ window_server_->OnFirstWindowManagerWindowTreeFactoryReady();
+}
+
+void WindowManagerWindowTreeFactorySet::OnUserIdRemoved(const UserId& id) {
+ factories_.erase(id);
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/window_manager_window_tree_factory_set.h b/chromium/components/mus/ws/window_manager_window_tree_factory_set.h
new file mode 100644
index 00000000000..a118e6978bc
--- /dev/null
+++ b/chromium/components/mus/ws/window_manager_window_tree_factory_set.h
@@ -0,0 +1,93 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_WINDOW_MANAGER_WINDOW_TREE_FACTORY_SET_H_
+#define COMPONENTS_MUS_WS_WINDOW_MANAGER_WINDOW_TREE_FACTORY_SET_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "components/mus/public/interfaces/window_manager_window_tree_factory.mojom.h"
+#include "components/mus/ws/user_id_tracker_observer.h"
+
+namespace mus {
+namespace ws {
+
+class UserIdTracker;
+class WindowManagerState;
+class WindowManagerWindowTreeFactory;
+class WindowManagerWindowTreeFactorySetObserver;
+class WindowServer;
+class WindowTree;
+
+namespace test {
+class WindowManagerWindowTreeFactorySetTestApi;
+}
+
+// WindowManagerWindowTreeFactorySet tracks the set of registered
+// WindowManagerWindowTreeHostFactories.
+class WindowManagerWindowTreeFactorySet : public UserIdTrackerObserver {
+ public:
+ WindowManagerWindowTreeFactorySet(WindowServer* window_server,
+ UserIdTracker* tracker);
+ ~WindowManagerWindowTreeFactorySet() override;
+
+ WindowServer* window_server() { return window_server_; }
+
+ // Creates a new WindowManagerWindowTreeFactory for the specified user,
+ // unless one has been set, in which case the call is ignored. The newly
+ // created WindowManagerWindowTreeFactory does not immediately have a
+ // WindowTree associated with it.
+ WindowManagerWindowTreeFactory* Add(
+ const UserId& user_id,
+ mojo::InterfaceRequest<mojom::WindowManagerWindowTreeFactory> request);
+
+ // Returns the WindowManagerState for the specified user, or null if
+ // not yet set.
+ WindowManagerState* GetWindowManagerStateForUser(const UserId& user_id);
+
+ // Deletes the WindowManagerWindowTreeFactory associated with |tree|. Does
+ // nothing if there is no WindowManagerWindowTreeFactory associated with
+ // |tree|.
+ void DeleteFactoryAssociatedWithTree(WindowTree* tree);
+
+ // Returns all the factories, even those that may not have a WindowTree
+ // associated with them.
+ std::vector<WindowManagerWindowTreeFactory*> GetFactories();
+
+ void AddObserver(WindowManagerWindowTreeFactorySetObserver* observer);
+ void RemoveObserver(WindowManagerWindowTreeFactorySetObserver* observer);
+
+ private:
+ friend class WindowManagerWindowTreeFactory;
+ friend class test::WindowManagerWindowTreeFactorySetTestApi;
+
+ // Called by WindowManagerWindowTreeFactory when CreateWindowTree() has
+ // been called.
+ void OnWindowManagerWindowTreeFactoryReady(
+ WindowManagerWindowTreeFactory* factory);
+
+ // UserIdTrackerObserver:
+ void OnUserIdRemoved(const UserId& id) override;
+
+ // Set to true the first time a valid factory has been found.
+ bool got_valid_factory_ = false;
+ UserIdTracker* id_tracker_;
+ WindowServer* window_server_;
+
+ std::map<UserId, std::unique_ptr<WindowManagerWindowTreeFactory>> factories_;
+
+ base::ObserverList<WindowManagerWindowTreeFactorySetObserver> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowManagerWindowTreeFactorySet);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_WINDOW_MANAGER_WINDOW_TREE_FACTORY_SET_H_
diff --git a/chromium/components/mus/ws/window_manager_window_tree_factory_set_observer.h b/chromium/components/mus/ws/window_manager_window_tree_factory_set_observer.h
new file mode 100644
index 00000000000..dd5f876d839
--- /dev/null
+++ b/chromium/components/mus/ws/window_manager_window_tree_factory_set_observer.h
@@ -0,0 +1,26 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_WINDOW_MANAGER_WINDOW_TREE_FACTORY_SET_OBSERVER_H_
+#define COMPONENTS_MUS_WS_WINDOW_MANAGER_WINDOW_TREE_FACTORY_SET_OBSERVER_H_
+
+namespace mus {
+namespace ws {
+
+class WindowManagerWindowTreeFactory;
+
+class WindowManagerWindowTreeFactorySetObserver {
+ public:
+ // Called when the WindowTree associated with |factory| has been set
+ virtual void OnWindowManagerWindowTreeFactoryReady(
+ WindowManagerWindowTreeFactory* factory) = 0;
+
+ protected:
+ virtual ~WindowManagerWindowTreeFactorySetObserver() {}
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_WINDOW_MANAGER_WINDOW_TREE_FACTORY_SET_OBSERVER_H_
diff --git a/chromium/components/mus/ws/window_server.cc b/chromium/components/mus/ws/window_server.cc
new file mode 100644
index 00000000000..77ef0e2595c
--- /dev/null
+++ b/chromium/components/mus/ws/window_server.cc
@@ -0,0 +1,707 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/window_server.h"
+
+#include <set>
+#include <string>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
+#include "components/mus/ws/display.h"
+#include "components/mus/ws/display_binding.h"
+#include "components/mus/ws/display_manager.h"
+#include "components/mus/ws/operation.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/user_activity_monitor.h"
+#include "components/mus/ws/window_coordinate_conversions.h"
+#include "components/mus/ws/window_manager_access_policy.h"
+#include "components/mus/ws/window_manager_display_root.h"
+#include "components/mus/ws/window_manager_state.h"
+#include "components/mus/ws/window_manager_window_tree_factory.h"
+#include "components/mus/ws/window_server_delegate.h"
+#include "components/mus/ws/window_tree.h"
+#include "components/mus/ws/window_tree_binding.h"
+#include "services/shell/public/cpp/connection.h"
+#include "ui/gfx/geometry/size_conversions.h"
+
+namespace mus {
+namespace ws {
+
+WindowServer::WindowServer(
+ WindowServerDelegate* delegate,
+ const scoped_refptr<mus::SurfacesState>& surfaces_state)
+ : delegate_(delegate),
+ surfaces_state_(surfaces_state),
+ next_client_id_(1),
+ display_manager_(new DisplayManager(this, &user_id_tracker_)),
+ current_operation_(nullptr),
+ in_destructor_(false),
+ next_wm_change_id_(0),
+ window_manager_window_tree_factory_set_(this, &user_id_tracker_) {
+ user_id_tracker_.AddObserver(this);
+ OnUserIdAdded(user_id_tracker_.active_id());
+}
+
+WindowServer::~WindowServer() {
+ in_destructor_ = true;
+
+ // Destroys the window trees results in querying for the display. Tear down
+ // the displays first so that the trees are notified of the display going
+ // away while the display is still valid.
+ display_manager_->DestroyAllDisplays();
+
+ while (!tree_map_.empty())
+ DestroyTree(tree_map_.begin()->second.get());
+
+ display_manager_.reset();
+}
+
+ServerWindow* WindowServer::CreateServerWindow(
+ const WindowId& id,
+ const std::map<std::string, std::vector<uint8_t>>& properties) {
+ ServerWindow* window = new ServerWindow(this, id, properties);
+ window->AddObserver(this);
+ return window;
+}
+
+ClientSpecificId WindowServer::GetAndAdvanceNextClientId() {
+ const ClientSpecificId id = next_client_id_++;
+ DCHECK_LT(id, next_client_id_);
+ return id;
+}
+
+WindowTree* WindowServer::EmbedAtWindow(
+ ServerWindow* root,
+ const UserId& user_id,
+ mojom::WindowTreeClientPtr client,
+ uint32_t flags,
+ std::unique_ptr<AccessPolicy> access_policy) {
+ std::unique_ptr<WindowTree> tree_ptr(
+ new WindowTree(this, user_id, root, std::move(access_policy)));
+ WindowTree* tree = tree_ptr.get();
+ if (flags & mojom::kEmbedFlagEmbedderInterceptsEvents)
+ tree->set_embedder_intercepts_events();
+
+ mojom::WindowTreePtr window_tree_ptr;
+ mojom::WindowTreeRequest window_tree_request = GetProxy(&window_tree_ptr);
+ std::unique_ptr<WindowTreeBinding> binding =
+ delegate_->CreateWindowTreeBinding(
+ WindowServerDelegate::BindingType::EMBED, this, tree,
+ &window_tree_request, &client);
+ if (!binding) {
+ binding.reset(new ws::DefaultWindowTreeBinding(
+ tree, this, std::move(window_tree_request), std::move(client)));
+ }
+
+ AddTree(std::move(tree_ptr), std::move(binding), std::move(window_tree_ptr));
+ OnTreeMessagedClient(tree->id());
+ return tree;
+}
+
+void WindowServer::AddTree(std::unique_ptr<WindowTree> tree_impl_ptr,
+ std::unique_ptr<WindowTreeBinding> binding,
+ mojom::WindowTreePtr tree_ptr) {
+ CHECK_EQ(0u, tree_map_.count(tree_impl_ptr->id()));
+ WindowTree* tree = tree_impl_ptr.get();
+ tree_map_[tree->id()] = std::move(tree_impl_ptr);
+ tree->Init(std::move(binding), std::move(tree_ptr));
+}
+
+WindowTree* WindowServer::CreateTreeForWindowManager(
+ const UserId& user_id,
+ mojom::WindowTreeRequest window_tree_request,
+ mojom::WindowTreeClientPtr window_tree_client) {
+ std::unique_ptr<WindowTree> window_tree(new WindowTree(
+ this, user_id, nullptr, base::WrapUnique(new WindowManagerAccessPolicy)));
+ std::unique_ptr<WindowTreeBinding> window_tree_binding =
+ delegate_->CreateWindowTreeBinding(
+ WindowServerDelegate::BindingType::WINDOW_MANAGER, this,
+ window_tree.get(), &window_tree_request, &window_tree_client);
+ if (!window_tree_binding) {
+ window_tree_binding.reset(new DefaultWindowTreeBinding(
+ window_tree.get(), this, std::move(window_tree_request),
+ std::move(window_tree_client)));
+ }
+ WindowTree* window_tree_ptr = window_tree.get();
+ AddTree(std::move(window_tree), std::move(window_tree_binding), nullptr);
+ window_tree_ptr->ConfigureWindowManager();
+ return window_tree_ptr;
+}
+
+void WindowServer::DestroyTree(WindowTree* tree) {
+ std::unique_ptr<WindowTree> tree_ptr;
+ {
+ auto iter = tree_map_.find(tree->id());
+ DCHECK(iter != tree_map_.end());
+ tree_ptr = std::move(iter->second);
+ tree_map_.erase(iter);
+ }
+
+ // Notify remaining connections so that they can cleanup.
+ for (auto& pair : tree_map_)
+ pair.second->OnWindowDestroyingTreeImpl(tree);
+
+ // Notify the hosts, taking care to only notify each host once.
+ std::set<Display*> displays_notified;
+ for (auto* root : tree->roots()) {
+ // WindowTree holds its roots as a const, which is right as WindowTree
+ // doesn't need to modify the window. OTOH we do. We could look up the
+ // window using the id to get non-const version, but instead we cast.
+ Display* display =
+ display_manager_->GetDisplayContaining(const_cast<ServerWindow*>(root));
+ if (display && displays_notified.count(display) == 0) {
+ display->OnWillDestroyTree(tree);
+ displays_notified.insert(display);
+ }
+ }
+
+ window_manager_window_tree_factory_set_.DeleteFactoryAssociatedWithTree(tree);
+
+ // Remove any requests from the client that resulted in a call to the window
+ // manager and we haven't gotten a response back yet.
+ std::set<uint32_t> to_remove;
+ for (auto& pair : in_flight_wm_change_map_) {
+ if (pair.second.client_id == tree->id())
+ to_remove.insert(pair.first);
+ }
+ for (uint32_t id : to_remove)
+ in_flight_wm_change_map_.erase(id);
+}
+
+WindowTree* WindowServer::GetTreeWithId(ClientSpecificId client_id) {
+ auto iter = tree_map_.find(client_id);
+ return iter == tree_map_.end() ? nullptr : iter->second.get();
+}
+
+WindowTree* WindowServer::GetTreeWithClientName(
+ const std::string& client_name) {
+ for (const auto& entry : tree_map_) {
+ if (entry.second->name() == client_name)
+ return entry.second.get();
+ }
+ return nullptr;
+}
+
+ServerWindow* WindowServer::GetWindow(const WindowId& id) {
+ // kInvalidClientId is used for Display and WindowManager nodes.
+ if (id.client_id == kInvalidClientId) {
+ for (Display* display : display_manager_->displays()) {
+ ServerWindow* window = display->GetRootWithId(id);
+ if (window)
+ return window;
+ }
+ }
+ WindowTree* tree = GetTreeWithId(id.client_id);
+ return tree ? tree->GetWindow(id) : nullptr;
+}
+
+void WindowServer::SchedulePaint(ServerWindow* window,
+ const gfx::Rect& bounds) {
+ Display* display = display_manager_->GetDisplayContaining(window);
+ if (display)
+ display->SchedulePaint(window, bounds);
+}
+
+void WindowServer::OnTreeMessagedClient(ClientSpecificId id) {
+ if (current_operation_)
+ current_operation_->MarkTreeAsMessaged(id);
+}
+
+bool WindowServer::DidTreeMessageClient(ClientSpecificId id) const {
+ return current_operation_ && current_operation_->DidMessageTree(id);
+}
+
+const WindowTree* WindowServer::GetTreeWithRoot(
+ const ServerWindow* window) const {
+ if (!window)
+ return nullptr;
+ for (auto& pair : tree_map_) {
+ if (pair.second->HasRoot(window))
+ return pair.second.get();
+ }
+ return nullptr;
+}
+
+void WindowServer::OnFirstWindowManagerWindowTreeFactoryReady() {
+ if (display_manager_->has_active_or_pending_displays())
+ return;
+
+ // We've been supplied a WindowManagerFactory and no displays have been
+ // created yet. Treat this as a signal to create a Display.
+ // TODO(sky): we need a better way to determine this, most likely a switch.
+ delegate_->CreateDefaultDisplays();
+}
+
+UserActivityMonitor* WindowServer::GetUserActivityMonitorForUser(
+ const UserId& user_id) {
+ DCHECK_GT(activity_monitor_map_.count(user_id), 0u);
+ return activity_monitor_map_[user_id].get();
+}
+
+bool WindowServer::SetFocusedWindow(ServerWindow* window) {
+ // TODO(sky): this should fail if there is modal dialog active and |window|
+ // is outside that.
+ ServerWindow* currently_focused = GetFocusedWindow();
+ Display* focused_display =
+ currently_focused
+ ? display_manager_->GetDisplayContaining(currently_focused)
+ : nullptr;
+ if (!window)
+ return focused_display ? focused_display->SetFocusedWindow(nullptr) : true;
+
+ Display* display = display_manager_->GetDisplayContaining(window);
+ DCHECK(display); // It's assumed callers do validation before calling this.
+ const bool result = display->SetFocusedWindow(window);
+ // If the focus actually changed, and focus was in another display, then we
+ // need to notify the previously focused display so that it cleans up state
+ // and notifies appropriately.
+ if (result && display->GetFocusedWindow() && display != focused_display &&
+ focused_display) {
+ const bool cleared_focus = focused_display->SetFocusedWindow(nullptr);
+ DCHECK(cleared_focus);
+ }
+ return result;
+}
+
+ServerWindow* WindowServer::GetFocusedWindow() {
+ for (Display* display : display_manager_->displays()) {
+ ServerWindow* focused_window = display->GetFocusedWindow();
+ if (focused_window)
+ return focused_window;
+ }
+ return nullptr;
+}
+
+uint32_t WindowServer::GenerateWindowManagerChangeId(
+ WindowTree* source,
+ uint32_t client_change_id) {
+ const uint32_t wm_change_id = next_wm_change_id_++;
+ in_flight_wm_change_map_[wm_change_id] = {source->id(), client_change_id};
+ return wm_change_id;
+}
+
+void WindowServer::WindowManagerChangeCompleted(
+ uint32_t window_manager_change_id,
+ bool success) {
+ InFlightWindowManagerChange change;
+ if (!GetAndClearInFlightWindowManagerChange(window_manager_change_id,
+ &change)) {
+ return;
+ }
+
+ WindowTree* tree = GetTreeWithId(change.client_id);
+ tree->OnChangeCompleted(change.client_change_id, success);
+}
+
+void WindowServer::WindowManagerCreatedTopLevelWindow(
+ WindowTree* wm_tree,
+ uint32_t window_manager_change_id,
+ const ServerWindow* window) {
+ InFlightWindowManagerChange change;
+ if (!GetAndClearInFlightWindowManagerChange(window_manager_change_id,
+ &change)) {
+ return;
+ }
+ if (!window) {
+ WindowManagerSentBogusMessage();
+ return;
+ }
+
+ WindowTree* tree = GetTreeWithId(change.client_id);
+ // The window manager should have created the window already, and it should
+ // be ready for embedding.
+ if (!tree->IsWaitingForNewTopLevelWindow(window_manager_change_id) ||
+ !window || window->id().client_id != wm_tree->id() ||
+ !window->children().empty() || GetTreeWithRoot(window)) {
+ WindowManagerSentBogusMessage();
+ return;
+ }
+
+ tree->OnWindowManagerCreatedTopLevelWindow(window_manager_change_id,
+ change.client_change_id, window);
+}
+
+void WindowServer::ProcessWindowBoundsChanged(const ServerWindow* window,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {
+ for (auto& pair : tree_map_) {
+ pair.second->ProcessWindowBoundsChanged(window, old_bounds, new_bounds,
+ IsOperationSource(pair.first));
+ }
+}
+
+void WindowServer::ProcessClientAreaChanged(
+ const ServerWindow* window,
+ const gfx::Insets& new_client_area,
+ const std::vector<gfx::Rect>& new_additional_client_areas) {
+ for (auto& pair : tree_map_) {
+ pair.second->ProcessClientAreaChanged(window, new_client_area,
+ new_additional_client_areas,
+ IsOperationSource(pair.first));
+ }
+}
+
+void WindowServer::ProcessLostCapture(const ServerWindow* window) {
+ for (auto& pair : tree_map_)
+ pair.second->ProcessLostCapture(window, IsOperationSource(pair.first));
+}
+
+void WindowServer::ProcessWillChangeWindowHierarchy(
+ const ServerWindow* window,
+ const ServerWindow* new_parent,
+ const ServerWindow* old_parent) {
+ for (auto& pair : tree_map_) {
+ pair.second->ProcessWillChangeWindowHierarchy(
+ window, new_parent, old_parent, IsOperationSource(pair.first));
+ }
+}
+
+void WindowServer::ProcessWindowHierarchyChanged(
+ const ServerWindow* window,
+ const ServerWindow* new_parent,
+ const ServerWindow* old_parent) {
+ for (auto& pair : tree_map_) {
+ pair.second->ProcessWindowHierarchyChanged(window, new_parent, old_parent,
+ IsOperationSource(pair.first));
+ }
+}
+
+void WindowServer::ProcessWindowReorder(const ServerWindow* window,
+ const ServerWindow* relative_window,
+ const mojom::OrderDirection direction) {
+ // We'll probably do a bit of reshuffling when we add a transient window.
+ if ((current_operation_type() == OperationType::ADD_TRANSIENT_WINDOW) ||
+ (current_operation_type() ==
+ OperationType::REMOVE_TRANSIENT_WINDOW_FROM_PARENT)) {
+ return;
+ }
+ for (auto& pair : tree_map_) {
+ pair.second->ProcessWindowReorder(window, relative_window, direction,
+ IsOperationSource(pair.first));
+ }
+}
+
+void WindowServer::ProcessWindowDeleted(const ServerWindow* window) {
+ for (auto& pair : tree_map_)
+ pair.second->ProcessWindowDeleted(window, IsOperationSource(pair.first));
+}
+
+void WindowServer::ProcessWillChangeWindowPredefinedCursor(ServerWindow* window,
+ int32_t cursor_id) {
+ for (auto& pair : tree_map_) {
+ pair.second->ProcessCursorChanged(window, cursor_id,
+ IsOperationSource(pair.first));
+ }
+}
+
+void WindowServer::SendToEventObservers(const ui::Event& event,
+ const UserId& user_id,
+ WindowTree* ignore_tree) {
+ for (auto& pair : tree_map_) {
+ WindowTree* tree = pair.second.get();
+ if (tree->user_id() == user_id && tree != ignore_tree)
+ tree->SendToEventObserver(event);
+ }
+}
+
+void WindowServer::SetPaintCallback(
+ const base::Callback<void(ServerWindow*)>& callback) {
+ DCHECK(delegate_->IsTestConfig()) << "Paint callbacks are expensive, and "
+ << "allowed only in tests.";
+ DCHECK(window_paint_callback_.is_null() || callback.is_null());
+ window_paint_callback_ = callback;
+}
+
+bool WindowServer::GetAndClearInFlightWindowManagerChange(
+ uint32_t window_manager_change_id,
+ InFlightWindowManagerChange* change) {
+ // There are valid reasons as to why we wouldn't know about the id. The
+ // most likely is the client disconnected before the response from the window
+ // manager came back.
+ auto iter = in_flight_wm_change_map_.find(window_manager_change_id);
+ if (iter == in_flight_wm_change_map_.end())
+ return false;
+
+ *change = iter->second;
+ in_flight_wm_change_map_.erase(iter);
+ return true;
+}
+
+void WindowServer::PrepareForOperation(Operation* op) {
+ // Should only ever have one change in flight.
+ CHECK(!current_operation_);
+ current_operation_ = op;
+}
+
+void WindowServer::FinishOperation() {
+ // PrepareForOperation/FinishOperation should be balanced.
+ CHECK(current_operation_);
+ current_operation_ = nullptr;
+}
+
+void WindowServer::UpdateNativeCursorFromMouseLocation(ServerWindow* window) {
+ WindowManagerDisplayRoot* display_root =
+ display_manager_->GetWindowManagerDisplayRoot(window);
+ if (display_root) {
+ EventDispatcher* event_dispatcher =
+ display_root->window_manager_state()->event_dispatcher();
+ event_dispatcher->UpdateCursorProviderByLastKnownLocation();
+ int32_t cursor_id = 0;
+ if (event_dispatcher->GetCurrentMouseCursor(&cursor_id))
+ display_root->display()->UpdateNativeCursor(cursor_id);
+ }
+}
+
+void WindowServer::UpdateNativeCursorIfOver(ServerWindow* window) {
+ WindowManagerDisplayRoot* display_root =
+ display_manager_->GetWindowManagerDisplayRoot(window);
+ if (!display_root)
+ return;
+
+ EventDispatcher* event_dispatcher =
+ display_root->window_manager_state()->event_dispatcher();
+ if (window != event_dispatcher->mouse_cursor_source_window())
+ return;
+
+ event_dispatcher->UpdateNonClientAreaForCurrentWindow();
+ int32_t cursor_id = 0;
+ if (event_dispatcher->GetCurrentMouseCursor(&cursor_id))
+ display_root->display()->UpdateNativeCursor(cursor_id);
+}
+
+mus::SurfacesState* WindowServer::GetSurfacesState() {
+ return surfaces_state_.get();
+}
+
+void WindowServer::OnScheduleWindowPaint(ServerWindow* window) {
+ if (in_destructor_)
+ return;
+
+ SchedulePaint(window, gfx::Rect(window->bounds().size()));
+ if (!window_paint_callback_.is_null())
+ window_paint_callback_.Run(window);
+}
+
+const ServerWindow* WindowServer::GetRootWindow(
+ const ServerWindow* window) const {
+ const Display* display = display_manager_->GetDisplayContaining(window);
+ return display ? display->root_window() : nullptr;
+}
+
+void WindowServer::ScheduleSurfaceDestruction(ServerWindow* window) {
+ Display* display = display_manager_->GetDisplayContaining(window);
+ if (display)
+ display->ScheduleSurfaceDestruction(window);
+}
+
+void WindowServer::OnWindowDestroyed(ServerWindow* window) {
+ ProcessWindowDeleted(window);
+}
+
+void WindowServer::OnWillChangeWindowHierarchy(ServerWindow* window,
+ ServerWindow* new_parent,
+ ServerWindow* old_parent) {
+ if (in_destructor_)
+ return;
+
+ ProcessWillChangeWindowHierarchy(window, new_parent, old_parent);
+}
+
+void WindowServer::OnWindowHierarchyChanged(ServerWindow* window,
+ ServerWindow* new_parent,
+ ServerWindow* old_parent) {
+ if (in_destructor_)
+ return;
+
+ WindowManagerDisplayRoot* display_root =
+ display_manager_->GetWindowManagerDisplayRoot(window);
+ if (display_root)
+ display_root->window_manager_state()
+ ->ReleaseCaptureBlockedByAnyModalWindow();
+
+ ProcessWindowHierarchyChanged(window, new_parent, old_parent);
+
+ // TODO(beng): optimize.
+ if (old_parent)
+ SchedulePaint(old_parent, gfx::Rect(old_parent->bounds().size()));
+ if (new_parent)
+ SchedulePaint(new_parent, gfx::Rect(new_parent->bounds().size()));
+
+ UpdateNativeCursorFromMouseLocation(window);
+}
+
+void WindowServer::OnWindowBoundsChanged(ServerWindow* window,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {
+ if (in_destructor_)
+ return;
+
+ ProcessWindowBoundsChanged(window, old_bounds, new_bounds);
+ if (!window->parent())
+ return;
+
+ SchedulePaint(window->parent(), old_bounds);
+ SchedulePaint(window->parent(), new_bounds);
+
+ UpdateNativeCursorFromMouseLocation(window);
+}
+
+void WindowServer::OnWindowClientAreaChanged(
+ ServerWindow* window,
+ const gfx::Insets& new_client_area,
+ const std::vector<gfx::Rect>& new_additional_client_areas) {
+ if (in_destructor_)
+ return;
+
+ ProcessClientAreaChanged(window, new_client_area,
+ new_additional_client_areas);
+
+ UpdateNativeCursorIfOver(window);
+}
+
+void WindowServer::OnWindowReordered(ServerWindow* window,
+ ServerWindow* relative,
+ mojom::OrderDirection direction) {
+ ProcessWindowReorder(window, relative, direction);
+ if (!in_destructor_)
+ SchedulePaint(window, gfx::Rect(window->bounds().size()));
+ UpdateNativeCursorFromMouseLocation(window);
+}
+
+void WindowServer::OnWillChangeWindowVisibility(ServerWindow* window) {
+ if (in_destructor_)
+ return;
+
+ // Need to repaint if the window was drawn (which means it's in the process of
+ // hiding) or the window is transitioning to drawn.
+ if (window->parent() &&
+ (window->IsDrawn() ||
+ (!window->visible() && window->parent()->IsDrawn()))) {
+ SchedulePaint(window->parent(), window->bounds());
+ }
+
+ for (auto& pair : tree_map_) {
+ pair.second->ProcessWillChangeWindowVisibility(
+ window, IsOperationSource(pair.first));
+ }
+}
+
+void WindowServer::OnWindowOpacityChanged(ServerWindow* window,
+ float old_opacity,
+ float new_opacity) {
+ DCHECK(!in_destructor_);
+
+ for (auto& pair : tree_map_) {
+ pair.second->ProcessWindowOpacityChanged(window, old_opacity, new_opacity,
+ IsOperationSource(pair.first));
+ }
+}
+
+void WindowServer::OnWindowVisibilityChanged(ServerWindow* window) {
+ if (in_destructor_)
+ return;
+
+ WindowManagerDisplayRoot* display_root =
+ display_manager_->GetWindowManagerDisplayRoot(window);
+ if (display_root)
+ display_root->window_manager_state()->ReleaseCaptureBlockedByModalWindow(
+ window);
+}
+
+void WindowServer::OnWindowPredefinedCursorChanged(ServerWindow* window,
+ int32_t cursor_id) {
+ if (in_destructor_)
+ return;
+
+ ProcessWillChangeWindowPredefinedCursor(window, cursor_id);
+
+ UpdateNativeCursorIfOver(window);
+}
+
+void WindowServer::OnWindowNonClientCursorChanged(ServerWindow* window,
+ int32_t cursor_id) {
+ if (in_destructor_)
+ return;
+
+ UpdateNativeCursorIfOver(window);
+}
+
+void WindowServer::OnWindowSharedPropertyChanged(
+ ServerWindow* window,
+ const std::string& name,
+ const std::vector<uint8_t>* new_data) {
+ for (auto& pair : tree_map_) {
+ pair.second->ProcessWindowPropertyChanged(window, name, new_data,
+ IsOperationSource(pair.first));
+ }
+}
+
+void WindowServer::OnWindowTextInputStateChanged(
+ ServerWindow* window,
+ const ui::TextInputState& state) {
+ Display* display = display_manager_->GetDisplayContaining(window);
+ display->UpdateTextInputState(window, state);
+}
+
+void WindowServer::OnTransientWindowAdded(ServerWindow* window,
+ ServerWindow* transient_child) {
+ for (auto& pair : tree_map_) {
+ pair.second->ProcessTransientWindowAdded(window, transient_child,
+ IsOperationSource(pair.first));
+ }
+}
+
+void WindowServer::OnTransientWindowRemoved(ServerWindow* window,
+ ServerWindow* transient_child) {
+ // If we're deleting a window, then this is a superfluous message.
+ if (current_operation_type() == OperationType::DELETE_WINDOW)
+ return;
+ for (auto& pair : tree_map_) {
+ pair.second->ProcessTransientWindowRemoved(window, transient_child,
+ IsOperationSource(pair.first));
+ }
+}
+
+void WindowServer::OnFirstDisplayReady() {
+ delegate_->OnFirstDisplayReady();
+}
+
+void WindowServer::OnNoMoreDisplays() {
+ delegate_->OnNoMoreDisplays();
+}
+
+bool WindowServer::GetFrameDecorationsForUser(
+ const UserId& user_id,
+ mojom::FrameDecorationValuesPtr* values) {
+ WindowManagerState* window_manager_state =
+ window_manager_window_tree_factory_set_.GetWindowManagerStateForUser(
+ user_id);
+ if (!window_manager_state)
+ return false;
+ if (values && window_manager_state->got_frame_decoration_values())
+ *values = window_manager_state->frame_decoration_values().Clone();
+ return window_manager_state->got_frame_decoration_values();
+}
+
+WindowManagerState* WindowServer::GetWindowManagerStateForUser(
+ const UserId& user_id) {
+ return window_manager_window_tree_factory_set_.GetWindowManagerStateForUser(
+ user_id);
+}
+
+void WindowServer::OnActiveUserIdChanged(const UserId& previously_active_id,
+ const UserId& active_id) {}
+
+void WindowServer::OnUserIdAdded(const UserId& id) {
+ activity_monitor_map_[id] = base::MakeUnique<UserActivityMonitor>(nullptr);
+}
+
+void WindowServer::OnUserIdRemoved(const UserId& id) {
+ activity_monitor_map_.erase(id);
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/window_server.h b/chromium/components/mus/ws/window_server.h
new file mode 100644
index 00000000000..0ecbc76ee4c
--- /dev/null
+++ b/chromium/components/mus/ws/window_server.h
@@ -0,0 +1,342 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_WINDOW_SERVER_H_
+#define COMPONENTS_MUS_WS_WINDOW_SERVER_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/mus/public/interfaces/window_manager_window_tree_factory.mojom.h"
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "components/mus/public/interfaces/window_tree_host.mojom.h"
+#include "components/mus/surfaces/surfaces_state.h"
+#include "components/mus/ws/display.h"
+#include "components/mus/ws/display_manager_delegate.h"
+#include "components/mus/ws/ids.h"
+#include "components/mus/ws/operation.h"
+#include "components/mus/ws/server_window_delegate.h"
+#include "components/mus/ws/server_window_observer.h"
+#include "components/mus/ws/user_id_tracker.h"
+#include "components/mus/ws/user_id_tracker_observer.h"
+#include "components/mus/ws/window_manager_window_tree_factory_set.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace mus {
+namespace ws {
+
+class AccessPolicy;
+class DisplayManager;
+class ServerWindow;
+class UserActivityMonitor;
+class WindowManagerState;
+class WindowServerDelegate;
+class WindowTree;
+class WindowTreeBinding;
+
+// WindowServer manages the set of clients of the window server (all the
+// WindowTrees) as well as providing the root of the hierarchy.
+class WindowServer : public ServerWindowDelegate,
+ public ServerWindowObserver,
+ public DisplayManagerDelegate,
+ public UserIdTrackerObserver {
+ public:
+ WindowServer(WindowServerDelegate* delegate,
+ const scoped_refptr<mus::SurfacesState>& surfaces_state);
+ ~WindowServer() override;
+
+ WindowServerDelegate* delegate() { return delegate_; }
+
+ UserIdTracker* user_id_tracker() { return &user_id_tracker_; }
+ const UserIdTracker* user_id_tracker() const { return &user_id_tracker_; }
+
+ DisplayManager* display_manager() { return display_manager_.get(); }
+ const DisplayManager* display_manager() const {
+ return display_manager_.get();
+ }
+
+ // Creates a new ServerWindow. The return value is owned by the caller, but
+ // must be destroyed before WindowServer.
+ ServerWindow* CreateServerWindow(
+ const WindowId& id,
+ const std::map<std::string, std::vector<uint8_t>>& properties);
+
+ // Returns the id for the next WindowTree.
+ ClientSpecificId GetAndAdvanceNextClientId();
+
+ // See description of WindowTree::Embed() for details. This assumes
+ // |transport_window_id| is valid.
+ WindowTree* EmbedAtWindow(ServerWindow* root,
+ const UserId& user_id,
+ mojom::WindowTreeClientPtr client,
+ uint32_t flags,
+ std::unique_ptr<AccessPolicy> access_policy);
+
+ // Adds |tree_impl_ptr| to the set of known trees. Use DestroyTree() to
+ // destroy the tree.
+ void AddTree(std::unique_ptr<WindowTree> tree_impl_ptr,
+ std::unique_ptr<WindowTreeBinding> binding,
+ mojom::WindowTreePtr tree_ptr);
+ WindowTree* CreateTreeForWindowManager(
+ const UserId& user_id,
+ mojom::WindowTreeRequest window_tree_request,
+ mojom::WindowTreeClientPtr window_tree_client);
+ // Invoked when a WindowTree's connection encounters an error.
+ void DestroyTree(WindowTree* tree);
+
+ // Returns the tree by client id.
+ WindowTree* GetTreeWithId(ClientSpecificId client_id);
+
+ WindowTree* GetTreeWithClientName(const std::string& client_name);
+
+ size_t num_trees() const { return tree_map_.size(); }
+
+ // Returns the Window identified by |id|.
+ ServerWindow* GetWindow(const WindowId& id);
+
+ // Schedules a paint for the specified region in the coordinates of |window|.
+ void SchedulePaint(ServerWindow* window, const gfx::Rect& bounds);
+
+ OperationType current_operation_type() const {
+ return current_operation_ ? current_operation_->type()
+ : OperationType::NONE;
+ }
+
+ // Returns true if the specified client issued the current operation.
+ bool IsOperationSource(ClientSpecificId client_id) const {
+ return current_operation_ &&
+ current_operation_->source_tree_id() == client_id;
+ }
+
+ // Invoked when a client messages a client about the change. This is used
+ // to avoid sending ServerChangeIdAdvanced() unnecessarily.
+ void OnTreeMessagedClient(ClientSpecificId id);
+
+ // Returns true if OnTreeMessagedClient() was invoked for id.
+ bool DidTreeMessageClient(ClientSpecificId id) const;
+
+ // Returns the WindowTree that has |id| as a root.
+ WindowTree* GetTreeWithRoot(const ServerWindow* window) {
+ return const_cast<WindowTree*>(
+ const_cast<const WindowServer*>(this)->GetTreeWithRoot(window));
+ }
+ const WindowTree* GetTreeWithRoot(const ServerWindow* window) const;
+
+ void OnFirstWindowManagerWindowTreeFactoryReady();
+
+ UserActivityMonitor* GetUserActivityMonitorForUser(const UserId& user_id);
+
+ WindowManagerWindowTreeFactorySet* window_manager_window_tree_factory_set() {
+ return &window_manager_window_tree_factory_set_;
+ }
+
+ // Sets focus to |window|. Returns true if |window| already has focus, or
+ // focus was successfully changed. Returns |false| if |window| is not a valid
+ // window to receive focus.
+ bool SetFocusedWindow(ServerWindow* window);
+ ServerWindow* GetFocusedWindow();
+
+ // Returns a change id for the window manager that is associated with
+ // |source| and |client_change_id|. When the window manager replies
+ // WindowManagerChangeCompleted() is called to obtain the original source
+ // and client supplied change_id that initiated the called.
+ uint32_t GenerateWindowManagerChangeId(WindowTree* source,
+ uint32_t client_change_id);
+
+ // Called when a response from the window manager is obtained. Calls to
+ // the client that initiated the change with the change id originally
+ // supplied by the client.
+ void WindowManagerChangeCompleted(uint32_t window_manager_change_id,
+ bool success);
+ void WindowManagerCreatedTopLevelWindow(WindowTree* wm_tree,
+ uint32_t window_manager_change_id,
+ const ServerWindow* window);
+
+ // Called when we get an unexpected message from the WindowManager.
+ // TODO(sky): decide what we want to do here.
+ void WindowManagerSentBogusMessage() {}
+
+ // These functions trivially delegate to all WindowTrees, which in
+ // term notify their clients.
+ void ProcessWindowBoundsChanged(const ServerWindow* window,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds);
+ void ProcessClientAreaChanged(
+ const ServerWindow* window,
+ const gfx::Insets& new_client_area,
+ const std::vector<gfx::Rect>& new_additional_client_areas);
+ void ProcessLostCapture(const ServerWindow* window);
+ void ProcessWillChangeWindowHierarchy(const ServerWindow* window,
+ const ServerWindow* new_parent,
+ const ServerWindow* old_parent);
+ void ProcessWindowHierarchyChanged(const ServerWindow* window,
+ const ServerWindow* new_parent,
+ const ServerWindow* old_parent);
+ void ProcessWindowReorder(const ServerWindow* window,
+ const ServerWindow* relative_window,
+ const mojom::OrderDirection direction);
+ void ProcessWindowDeleted(const ServerWindow* window);
+ void ProcessWillChangeWindowPredefinedCursor(ServerWindow* window,
+ int32_t cursor_id);
+
+ // Sends an |event| to all WindowTrees belonging to |user_id| that might be
+ // observing events. Skips |ignore_tree| if it is non-null.
+ void SendToEventObservers(const ui::Event& event,
+ const UserId& user_id,
+ WindowTree* ignore_tree);
+
+ // Sets a callback to be called whenever a ServerWindow is scheduled for
+ // a [re]paint. This should only be called in a test configuration.
+ void SetPaintCallback(const base::Callback<void(ServerWindow*)>& callback);
+
+ private:
+ friend class Operation;
+
+ using WindowTreeMap =
+ std::map<ClientSpecificId, std::unique_ptr<WindowTree>>;
+ using UserActivityMonitorMap =
+ std::map<UserId, std::unique_ptr<UserActivityMonitor>>;
+
+ struct InFlightWindowManagerChange {
+ // Identifies the client that initiated the change.
+ ClientSpecificId client_id;
+
+ // Change id supplied by the client.
+ uint32_t client_change_id;
+ };
+
+ using InFlightWindowManagerChangeMap =
+ std::map<uint32_t, InFlightWindowManagerChange>;
+
+ bool GetAndClearInFlightWindowManagerChange(
+ uint32_t window_manager_change_id,
+ InFlightWindowManagerChange* change);
+
+ // Invoked when a client is about to execute a window server operation.
+ // Subsequently followed by FinishOperation() once the change is done.
+ //
+ // Changes should never nest, meaning each PrepareForOperation() must be
+ // balanced with a call to FinishOperation() with no PrepareForOperation()
+ // in between.
+ void PrepareForOperation(Operation* op);
+
+ // Balances a call to PrepareForOperation().
+ void FinishOperation();
+
+ // Updates the native cursor by figuring out what window is under the mouse
+ // cursor. This is run in response to events that change the bounds or window
+ // hierarchy.
+ void UpdateNativeCursorFromMouseLocation(ServerWindow* window);
+
+ // Updates the native cursor if the cursor is currently inside |window|. This
+ // is run in response to events that change the mouse cursor properties of
+ // |window|.
+ void UpdateNativeCursorIfOver(ServerWindow* window);
+
+ // Overridden from ServerWindowDelegate:
+ mus::SurfacesState* GetSurfacesState() override;
+ void OnScheduleWindowPaint(ServerWindow* window) override;
+ const ServerWindow* GetRootWindow(const ServerWindow* window) const override;
+ void ScheduleSurfaceDestruction(ServerWindow* window) override;
+
+ // Overridden from ServerWindowObserver:
+ void OnWindowDestroyed(ServerWindow* window) override;
+ void OnWillChangeWindowHierarchy(ServerWindow* window,
+ ServerWindow* new_parent,
+ ServerWindow* old_parent) override;
+ void OnWindowHierarchyChanged(ServerWindow* window,
+ ServerWindow* new_parent,
+ ServerWindow* old_parent) override;
+ void OnWindowBoundsChanged(ServerWindow* window,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) override;
+ void OnWindowClientAreaChanged(
+ ServerWindow* window,
+ const gfx::Insets& new_client_area,
+ const std::vector<gfx::Rect>& new_additional_client_areas) override;
+ void OnWindowReordered(ServerWindow* window,
+ ServerWindow* relative,
+ mojom::OrderDirection direction) override;
+ void OnWillChangeWindowVisibility(ServerWindow* window) override;
+ void OnWindowVisibilityChanged(ServerWindow* window) override;
+ void OnWindowOpacityChanged(ServerWindow* window,
+ float old_opacity,
+ float new_opacity) override;
+ void OnWindowSharedPropertyChanged(
+ ServerWindow* window,
+ const std::string& name,
+ const std::vector<uint8_t>* new_data) override;
+ void OnWindowPredefinedCursorChanged(ServerWindow* window,
+ int32_t cursor_id) override;
+ void OnWindowNonClientCursorChanged(ServerWindow* window,
+ int32_t cursor_id) override;
+ void OnWindowTextInputStateChanged(ServerWindow* window,
+ const ui::TextInputState& state) override;
+ void OnTransientWindowAdded(ServerWindow* window,
+ ServerWindow* transient_child) override;
+ void OnTransientWindowRemoved(ServerWindow* window,
+ ServerWindow* transient_child) override;
+
+ // DisplayManagerDelegate:
+ void OnFirstDisplayReady() override;
+ void OnNoMoreDisplays() override;
+ bool GetFrameDecorationsForUser(
+ const UserId& user_id,
+ mojom::FrameDecorationValuesPtr* values) override;
+ WindowManagerState* GetWindowManagerStateForUser(
+ const UserId& user_id) override;
+
+ // UserIdTrackerObserver:
+ void OnActiveUserIdChanged(const UserId& previously_active_id,
+ const UserId& active_id) override;
+ void OnUserIdAdded(const UserId& id) override;
+ void OnUserIdRemoved(const UserId& id) override;
+
+ UserIdTracker user_id_tracker_;
+
+ WindowServerDelegate* delegate_;
+
+ // State for rendering into a Surface.
+ scoped_refptr<mus::SurfacesState> surfaces_state_;
+
+ // ID to use for next WindowTree.
+ ClientSpecificId next_client_id_;
+
+ std::unique_ptr<DisplayManager> display_manager_;
+
+ // Set of WindowTrees.
+ WindowTreeMap tree_map_;
+
+ // If non-null then we're processing a client operation. The Operation is
+ // not owned by us (it's created on the stack by WindowTree).
+ Operation* current_operation_;
+
+ bool in_destructor_;
+
+ // Maps from window manager change id to the client that initiated the
+ // request.
+ InFlightWindowManagerChangeMap in_flight_wm_change_map_;
+
+ // Next id supplied to the window manager.
+ uint32_t next_wm_change_id_;
+
+ base::Callback<void(ServerWindow*)> window_paint_callback_;
+
+ UserActivityMonitorMap activity_monitor_map_;
+
+ WindowManagerWindowTreeFactorySet window_manager_window_tree_factory_set_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowServer);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_WINDOW_SERVER_H_
diff --git a/chromium/components/mus/ws/window_server_delegate.cc b/chromium/components/mus/ws/window_server_delegate.cc
new file mode 100644
index 00000000000..1d9a1045f3c
--- /dev/null
+++ b/chromium/components/mus/ws/window_server_delegate.cc
@@ -0,0 +1,25 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/window_server_delegate.h"
+
+#include "components/mus/ws/window_tree_binding.h"
+
+namespace mus {
+namespace ws {
+
+void WindowServerDelegate::OnFirstDisplayReady() {}
+
+std::unique_ptr<WindowTreeBinding>
+WindowServerDelegate::CreateWindowTreeBinding(
+ BindingType type,
+ ws::WindowServer* window_server,
+ ws::WindowTree* tree,
+ mojom::WindowTreeRequest* tree_request,
+ mojom::WindowTreeClientPtr* client) {
+ return nullptr;
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/window_server_delegate.h b/chromium/components/mus/ws/window_server_delegate.h
new file mode 100644
index 00000000000..62a8db4b07a
--- /dev/null
+++ b/chromium/components/mus/ws/window_server_delegate.h
@@ -0,0 +1,67 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_WINDOW_SERVER_DELEGATE_H_
+#define COMPONENTS_MUS_WS_WINDOW_SERVER_DELEGATE_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "components/mus/common/types.h"
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+
+namespace mus {
+
+namespace mojom {
+class Display;
+class WindowManagerFactory;
+class WindowTree;
+}
+
+namespace ws {
+
+class Display;
+class ServerWindow;
+class WindowServer;
+class WindowTree;
+class WindowTreeBinding;
+
+class WindowServerDelegate {
+ public:
+ enum BindingType {
+ EMBED,
+ WINDOW_MANAGER,
+ };
+
+ // Called if no Displays have been created, but a WindowManagerFactory has
+ // been set.
+ virtual void CreateDefaultDisplays() = 0;
+
+ // Called once when the AcceleratedWidget of a Display is available.
+ virtual void OnFirstDisplayReady();
+
+ virtual void OnNoMoreDisplays() = 0;
+
+ virtual bool IsTestConfig() const = 0;
+
+ // Creates a WindowTreeBinding. Default implementation returns null, which
+ // creates DefaultBinding.
+ virtual std::unique_ptr<WindowTreeBinding> CreateWindowTreeBinding(
+ BindingType type,
+ ws::WindowServer* window_server,
+ ws::WindowTree* tree,
+ mojom::WindowTreeRequest* tree_request,
+ mojom::WindowTreeClientPtr* client);
+
+ protected:
+ virtual ~WindowServerDelegate() {}
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_WINDOW_SERVER_DELEGATE_H_
diff --git a/chromium/components/mus/ws/window_server_test_impl.cc b/chromium/components/mus/ws/window_server_test_impl.cc
new file mode 100644
index 00000000000..a61eb2d2bc9
--- /dev/null
+++ b/chromium/components/mus/ws/window_server_test_impl.cc
@@ -0,0 +1,66 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/window_server_test_impl.h"
+
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/server_window_surface_manager.h"
+#include "components/mus/ws/window_server.h"
+#include "components/mus/ws/window_tree.h"
+
+namespace mus {
+namespace ws {
+
+namespace {
+
+bool WindowHasValidFrame(const ServerWindow* window) {
+ const ServerWindowSurfaceManager* manager = window->surface_manager();
+ return manager &&
+ !manager->GetDefaultSurface()->last_submitted_frame_size().IsEmpty();
+}
+
+} // namespace
+
+WindowServerTestImpl::WindowServerTestImpl(
+ WindowServer* window_server,
+ mojo::InterfaceRequest<WindowServerTest> request)
+ : window_server_(window_server), binding_(this, std::move(request)) {}
+
+WindowServerTestImpl::~WindowServerTestImpl() {}
+
+void WindowServerTestImpl::OnWindowPaint(
+ const std::string& name,
+ const EnsureClientHasDrawnWindowCallback& cb,
+ ServerWindow* window) {
+ WindowTree* tree = window_server_->GetTreeWithClientName(name);
+ if (!tree)
+ return;
+ if (tree->HasRoot(window) && WindowHasValidFrame(window)) {
+ cb.Run(true);
+ window_server_->SetPaintCallback(base::Callback<void(ServerWindow*)>());
+ }
+}
+
+void WindowServerTestImpl::EnsureClientHasDrawnWindow(
+ const mojo::String& client_name,
+ const EnsureClientHasDrawnWindowCallback& callback) {
+ std::string name = client_name.To<std::string>();
+ WindowTree* tree = window_server_->GetTreeWithClientName(name);
+ if (tree) {
+ for (const ServerWindow* window : tree->roots()) {
+ if (WindowHasValidFrame(window)) {
+ callback.Run(true);
+ return;
+ }
+ }
+ }
+
+ window_server_->SetPaintCallback(
+ base::Bind(&WindowServerTestImpl::OnWindowPaint, base::Unretained(this),
+ name, std::move(callback)));
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/window_server_test_impl.h b/chromium/components/mus/ws/window_server_test_impl.h
new file mode 100644
index 00000000000..deacb773c61
--- /dev/null
+++ b/chromium/components/mus/ws/window_server_test_impl.h
@@ -0,0 +1,45 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_WINDOW_SERVER_TEST_IMPL_H_
+#define COMPONENTS_MUS_WS_WINDOW_SERVER_TEST_IMPL_H_
+
+#include "components/mus/public/interfaces/window_server_test.mojom.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+namespace mus {
+namespace ws {
+
+class ServerWindow;
+class WindowServer;
+class WindowTree;
+struct WindowId;
+
+class WindowServerTestImpl : public mojom::WindowServerTest {
+ public:
+ WindowServerTestImpl(WindowServer* server,
+ mojo::InterfaceRequest<WindowServerTest> request);
+
+ private:
+ ~WindowServerTestImpl() override;
+
+ void OnWindowPaint(const std::string& name,
+ const EnsureClientHasDrawnWindowCallback& cb,
+ ServerWindow* window);
+
+ // mojom::WindowServerTest:
+ void EnsureClientHasDrawnWindow(
+ const mojo::String& client_name,
+ const EnsureClientHasDrawnWindowCallback& callback) override;
+
+ WindowServer* window_server_;
+ mojo::StrongBinding<WindowServerTest> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowServerTestImpl);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_WINDOW_SERVER_TEST_IMPL_H_
diff --git a/chromium/components/mus/ws/window_tree.cc b/chromium/components/mus/ws/window_tree.cc
new file mode 100644
index 00000000000..aafdfce96ec
--- /dev/null
+++ b/chromium/components/mus/ws/window_tree.cc
@@ -0,0 +1,1539 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/window_tree.h"
+
+#include <stddef.h>
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
+#include "components/mus/ws/default_access_policy.h"
+#include "components/mus/ws/display.h"
+#include "components/mus/ws/display_manager.h"
+#include "components/mus/ws/event_matcher.h"
+#include "components/mus/ws/focus_controller.h"
+#include "components/mus/ws/operation.h"
+#include "components/mus/ws/platform_display.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/server_window_observer.h"
+#include "components/mus/ws/user_display_manager.h"
+#include "components/mus/ws/window_manager_display_root.h"
+#include "components/mus/ws/window_manager_state.h"
+#include "components/mus/ws/window_server.h"
+#include "components/mus/ws/window_tree_binding.h"
+#include "ui/display/display.h"
+#include "ui/platform_window/mojo/ime_type_converters.h"
+#include "ui/platform_window/text_input_state.h"
+
+using mojo::Array;
+using mojo::InterfaceRequest;
+using mojo::String;
+
+namespace mus {
+namespace ws {
+
+class TargetedEvent : public ServerWindowObserver {
+ public:
+ TargetedEvent(ServerWindow* target, const ui::Event& event)
+ : target_(target), event_(ui::Event::Clone(event)) {
+ target_->AddObserver(this);
+ }
+ ~TargetedEvent() override {
+ if (target_)
+ target_->RemoveObserver(this);
+ }
+
+ ServerWindow* target() { return target_; }
+ std::unique_ptr<ui::Event> TakeEvent() { return std::move(event_); }
+
+ private:
+ // ServerWindowObserver:
+ void OnWindowDestroyed(ServerWindow* window) override {
+ DCHECK_EQ(target_, window);
+ target_->RemoveObserver(this);
+ target_ = nullptr;
+ }
+
+ ServerWindow* target_;
+ std::unique_ptr<ui::Event> event_;
+
+ DISALLOW_COPY_AND_ASSIGN(TargetedEvent);
+};
+
+WindowTree::WindowTree(WindowServer* window_server,
+ const UserId& user_id,
+ ServerWindow* root,
+ std::unique_ptr<AccessPolicy> access_policy)
+ : window_server_(window_server),
+ user_id_(user_id),
+ id_(window_server_->GetAndAdvanceNextClientId()),
+ next_window_id_(1),
+ access_policy_(std::move(access_policy)),
+ event_ack_id_(0),
+ window_manager_internal_(nullptr) {
+ if (root)
+ roots_.insert(root);
+ access_policy_->Init(id_, this);
+}
+
+WindowTree::~WindowTree() {
+ DestroyWindows();
+}
+
+void WindowTree::Init(std::unique_ptr<WindowTreeBinding> binding,
+ mojom::WindowTreePtr tree) {
+ DCHECK(!binding_);
+ binding_ = std::move(binding);
+
+ if (roots_.empty())
+ return;
+
+ std::vector<const ServerWindow*> to_send;
+ CHECK_EQ(1u, roots_.size());
+ const ServerWindow* root = *roots_.begin();
+ GetUnknownWindowsFrom(root, &to_send);
+
+ Display* display = GetDisplay(root);
+ int64_t display_id =
+ display ? display->id() : display::Display::kInvalidDisplayID;
+ const ServerWindow* focused_window =
+ display ? display->GetFocusedWindow() : nullptr;
+ if (focused_window)
+ focused_window = access_policy_->GetWindowForFocusChange(focused_window);
+ ClientWindowId focused_window_id;
+ if (focused_window)
+ IsWindowKnown(focused_window, &focused_window_id);
+
+ const bool drawn = root->parent() && root->parent()->IsDrawn();
+ client()->OnEmbed(id_, WindowToWindowData(to_send.front()), std::move(tree),
+ display_id, focused_window_id.id, drawn);
+}
+
+void WindowTree::ConfigureWindowManager() {
+ DCHECK(!window_manager_internal_);
+ window_manager_internal_ = binding_->GetWindowManager();
+ window_manager_internal_->OnConnect(id_);
+ window_manager_state_.reset(new WindowManagerState(this));
+}
+
+const ServerWindow* WindowTree::GetWindow(const WindowId& id) const {
+ if (id_ == id.client_id) {
+ auto iter = created_window_map_.find(id);
+ return iter == created_window_map_.end() ? nullptr : iter->second;
+ }
+ return window_server_->GetWindow(id);
+}
+
+bool WindowTree::IsWindowKnown(const ServerWindow* window,
+ ClientWindowId* id) const {
+ if (!window)
+ return false;
+ auto iter = window_id_to_client_id_map_.find(window->id());
+ if (iter == window_id_to_client_id_map_.end())
+ return false;
+ if (id)
+ *id = iter->second;
+ return true;
+}
+
+bool WindowTree::HasRoot(const ServerWindow* window) const {
+ return roots_.count(window) > 0;
+}
+
+const ServerWindow* WindowTree::GetWindowByClientId(
+ const ClientWindowId& id) const {
+ auto iter = client_id_to_window_id_map_.find(id);
+ return iter == client_id_to_window_id_map_.end() ? nullptr
+ : GetWindow(iter->second);
+}
+
+const Display* WindowTree::GetDisplay(const ServerWindow* window) const {
+ return window ? display_manager()->GetDisplayContaining(window) : nullptr;
+}
+
+const WindowManagerDisplayRoot* WindowTree::GetWindowManagerDisplayRoot(
+ const ServerWindow* window) const {
+ return window ? display_manager()->GetWindowManagerDisplayRoot(window)
+ : nullptr;
+}
+
+DisplayManager* WindowTree::display_manager() {
+ return window_server_->display_manager();
+}
+
+const DisplayManager* WindowTree::display_manager() const {
+ return window_server_->display_manager();
+}
+
+void WindowTree::AddRootForWindowManager(const ServerWindow* root) {
+ DCHECK(window_manager_internal_);
+ const ClientWindowId client_window_id(WindowIdToTransportId(root->id()));
+ DCHECK_EQ(0u, client_id_to_window_id_map_.count(client_window_id));
+ client_id_to_window_id_map_[client_window_id] = root->id();
+ window_id_to_client_id_map_[root->id()] = client_window_id;
+ roots_.insert(root);
+
+ Display* display = GetDisplay(root);
+ DCHECK(display);
+
+ window_manager_internal_->WmNewDisplayAdded(display->ToMojomDisplay(),
+ WindowToWindowData(root),
+ root->parent()->IsDrawn());
+}
+
+void WindowTree::OnWindowDestroyingTreeImpl(WindowTree* tree) {
+ if (window_manager_state_)
+ window_manager_state_->OnWillDestroyTree(tree);
+
+ if (event_source_wms_ && event_source_wms_->window_tree() == tree)
+ event_source_wms_ = nullptr;
+
+ // Notify our client if |tree| was embedded in any of our views.
+ for (const auto* tree_root : tree->roots_) {
+ const bool owns_tree_root = tree_root->id().client_id == id_;
+ if (owns_tree_root) {
+ client()->OnEmbeddedAppDisconnected(
+ ClientWindowIdForWindow(tree_root).id);
+ }
+ }
+}
+
+void WindowTree::NotifyChangeCompleted(
+ uint32_t change_id,
+ mojom::WindowManagerErrorCode error_code) {
+ client()->OnChangeCompleted(
+ change_id, error_code == mojom::WindowManagerErrorCode::SUCCESS);
+}
+
+bool WindowTree::SetCapture(const ClientWindowId& client_window_id) {
+ ServerWindow* window = GetWindowByClientId(client_window_id);
+ WindowManagerDisplayRoot* display_root = GetWindowManagerDisplayRoot(window);
+ ServerWindow* current_capture_window =
+ display_root ? display_root->window_manager_state()->capture_window()
+ : nullptr;
+ if (window && window->IsDrawn() && display_root &&
+ display_root->window_manager_state()->IsActive() &&
+ access_policy_->CanSetCapture(window) &&
+ (!current_capture_window ||
+ access_policy_->CanSetCapture(current_capture_window))) {
+ return display_root->window_manager_state()->SetCapture(window, id_);
+ }
+ return false;
+}
+
+bool WindowTree::NewWindow(
+ const ClientWindowId& client_window_id,
+ const std::map<std::string, std::vector<uint8_t>>& properties) {
+ if (!IsValidIdForNewWindow(client_window_id))
+ return false;
+ const WindowId window_id = GenerateNewWindowId();
+ DCHECK(!GetWindow(window_id));
+ ServerWindow* window =
+ window_server_->CreateServerWindow(window_id, properties);
+ created_window_map_[window_id] = window;
+ client_id_to_window_id_map_[client_window_id] = window_id;
+ window_id_to_client_id_map_[window_id] = client_window_id;
+ return true;
+}
+
+bool WindowTree::AddWindow(const ClientWindowId& parent_id,
+ const ClientWindowId& child_id) {
+ ServerWindow* parent = GetWindowByClientId(parent_id);
+ ServerWindow* child = GetWindowByClientId(child_id);
+ if (parent && child && child->parent() != parent &&
+ !child->Contains(parent) && access_policy_->CanAddWindow(parent, child)) {
+ Operation op(this, window_server_, OperationType::ADD_WINDOW);
+ parent->Add(child);
+ return true;
+ }
+ return false;
+}
+
+bool WindowTree::AddTransientWindow(const ClientWindowId& window_id,
+ const ClientWindowId& transient_window_id) {
+ ServerWindow* window = GetWindowByClientId(window_id);
+ ServerWindow* transient_window = GetWindowByClientId(transient_window_id);
+ if (window && transient_window && !transient_window->Contains(window) &&
+ access_policy_->CanAddTransientWindow(window, transient_window)) {
+ Operation op(this, window_server_, OperationType::ADD_TRANSIENT_WINDOW);
+ return window->AddTransientWindow(transient_window);
+ }
+ return false;
+}
+
+bool WindowTree::SetModal(const ClientWindowId& window_id) {
+ ServerWindow* window = GetWindowByClientId(window_id);
+ if (window && access_policy_->CanSetModal(window)) {
+ WindowManagerDisplayRoot* display_root =
+ GetWindowManagerDisplayRoot(window);
+ if (window->transient_parent()) {
+ window->SetModal();
+ } else if (user_id_ != InvalidUserId()) {
+ if (display_root)
+ display_root->window_manager_state()->AddSystemModalWindow(window);
+ } else {
+ return false;
+ }
+ if (display_root)
+ display_root->window_manager_state()->ReleaseCaptureBlockedByModalWindow(
+ window);
+ return true;
+ }
+ return false;
+}
+
+std::vector<const ServerWindow*> WindowTree::GetWindowTree(
+ const ClientWindowId& window_id) const {
+ const ServerWindow* window = GetWindowByClientId(window_id);
+ std::vector<const ServerWindow*> windows;
+ if (window)
+ GetWindowTreeImpl(window, &windows);
+ return windows;
+}
+
+bool WindowTree::SetWindowVisibility(const ClientWindowId& window_id,
+ bool visible) {
+ ServerWindow* window = GetWindowByClientId(window_id);
+ if (!window || !access_policy_->CanChangeWindowVisibility(window))
+ return false;
+ if (window->visible() == visible)
+ return true;
+ Operation op(this, window_server_, OperationType::SET_WINDOW_VISIBILITY);
+ window->SetVisible(visible);
+ return true;
+}
+
+bool WindowTree::SetWindowOpacity(const ClientWindowId& window_id,
+ float opacity) {
+ ServerWindow* window = GetWindowByClientId(window_id);
+ if (!window || !access_policy_->CanChangeWindowOpacity(window))
+ return false;
+ if (window->opacity() == opacity)
+ return true;
+ Operation op(this, window_server_, OperationType::SET_WINDOW_OPACITY);
+ window->SetOpacity(opacity);
+ return true;
+}
+
+bool WindowTree::SetFocus(const ClientWindowId& window_id) {
+ ServerWindow* window = GetWindowByClientId(window_id);
+ ServerWindow* currently_focused = window_server_->GetFocusedWindow();
+ if (!currently_focused && !window) {
+ DVLOG(1) << "SetFocus failure, no focused window to clear.";
+ return false;
+ }
+
+ Display* display = GetDisplay(window);
+ if (window && (!display || !window->can_focus() || !window->IsDrawn())) {
+ DVLOG(1) << "SetFocus failure, window cannot be focused.";
+ return false;
+ }
+
+ if (!access_policy_->CanSetFocus(window)) {
+ DVLOG(1) << "SetFocus failure, blocked by access policy.";
+ return false;
+ }
+
+ Operation op(this, window_server_, OperationType::SET_FOCUS);
+ bool success = window_server_->SetFocusedWindow(window);
+ if (!success) {
+ DVLOG(1) << "SetFocus failure, could not SetFocusedWindow.";
+ }
+ return success;
+}
+
+bool WindowTree::Embed(const ClientWindowId& window_id,
+ mojom::WindowTreeClientPtr client,
+ uint32_t flags) {
+ if (!client || !CanEmbed(window_id))
+ return false;
+ ServerWindow* window = GetWindowByClientId(window_id);
+ PrepareForEmbed(window);
+ // When embedding we don't know the user id of where the TreeClient came
+ // from. Use an invalid id, which limits what the client is able to do.
+ window_server_->EmbedAtWindow(window, InvalidUserId(), std::move(client),
+ flags,
+ base::WrapUnique(new DefaultAccessPolicy));
+ return true;
+}
+
+void WindowTree::DispatchInputEvent(ServerWindow* target,
+ const ui::Event& event) {
+ if (event_ack_id_) {
+ // This is currently waiting for an event ack. Add it to the queue.
+ event_queue_.push(base::WrapUnique(new TargetedEvent(target, event)));
+ // TODO(sad): If the |event_queue_| grows too large, then this should notify
+ // Display, so that it can stop sending events.
+ return;
+ }
+
+ // If there are events in the queue, then store this new event in the queue,
+ // and dispatch the latest event from the queue instead that still has a live
+ // target.
+ if (!event_queue_.empty()) {
+ event_queue_.push(base::WrapUnique(new TargetedEvent(target, event)));
+ return;
+ }
+
+ DispatchInputEventImpl(target, event);
+}
+
+bool WindowTree::IsWaitingForNewTopLevelWindow(uint32_t wm_change_id) {
+ return waiting_for_top_level_window_info_ &&
+ waiting_for_top_level_window_info_->wm_change_id == wm_change_id;
+}
+
+void WindowTree::OnWindowManagerCreatedTopLevelWindow(
+ uint32_t wm_change_id,
+ uint32_t client_change_id,
+ const ServerWindow* window) {
+ DCHECK(IsWaitingForNewTopLevelWindow(wm_change_id));
+ std::unique_ptr<WaitingForTopLevelWindowInfo>
+ waiting_for_top_level_window_info(
+ std::move(waiting_for_top_level_window_info_));
+ binding_->SetIncomingMethodCallProcessingPaused(false);
+ // We were paused, so the id should still be valid.
+ DCHECK(IsValidIdForNewWindow(
+ waiting_for_top_level_window_info->client_window_id));
+ client_id_to_window_id_map_[waiting_for_top_level_window_info
+ ->client_window_id] = window->id();
+ window_id_to_client_id_map_[window->id()] =
+ waiting_for_top_level_window_info->client_window_id;
+ roots_.insert(window);
+ Display* display = GetDisplay(window);
+ int64_t display_id =
+ display ? display->id() : display::Display::kInvalidDisplayID;
+ const bool drawn = window->parent() && window->parent()->IsDrawn();
+ client()->OnTopLevelCreated(client_change_id, WindowToWindowData(window),
+ display_id, drawn);
+}
+
+void WindowTree::AddActivationParent(const ClientWindowId& window_id) {
+ ServerWindow* window = GetWindowByClientId(window_id);
+ if (window) {
+ Display* display = GetDisplay(window);
+ if (display)
+ display->AddActivationParent(window);
+ else
+ DVLOG(1) << "AddActivationParent window not associated with display";
+ } else {
+ DVLOG(1) << "AddActivationParent supplied invalid window id";
+ }
+}
+
+void WindowTree::OnChangeCompleted(uint32_t change_id, bool success) {
+ client()->OnChangeCompleted(change_id, success);
+}
+
+void WindowTree::OnAccelerator(uint32_t accelerator_id,
+ const ui::Event& event) {
+ DCHECK(window_manager_internal_);
+ // TODO(moshayedi): crbug.com/617167. Don't clone even once we map
+ // mojom::Event directly to ui::Event.
+ window_manager_internal_->OnAccelerator(accelerator_id,
+ ui::Event::Clone(event));
+}
+
+void WindowTree::ClientJankinessChanged(WindowTree* tree) {
+ tree->janky_ = !tree->janky_;
+ if (window_manager_internal_) {
+ window_manager_internal_->WmClientJankinessChanged(
+ tree->id(), tree->janky());
+ }
+}
+
+void WindowTree::ProcessWindowBoundsChanged(const ServerWindow* window,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds,
+ bool originated_change) {
+ ClientWindowId client_window_id;
+ if (originated_change || !IsWindowKnown(window, &client_window_id))
+ return;
+ client()->OnWindowBoundsChanged(client_window_id.id, old_bounds, new_bounds);
+}
+
+void WindowTree::ProcessClientAreaChanged(
+ const ServerWindow* window,
+ const gfx::Insets& new_client_area,
+ const std::vector<gfx::Rect>& new_additional_client_areas,
+ bool originated_change) {
+ ClientWindowId client_window_id;
+ if (originated_change || !IsWindowKnown(window, &client_window_id))
+ return;
+ client()->OnClientAreaChanged(
+ client_window_id.id, new_client_area,
+ std::vector<gfx::Rect>(new_additional_client_areas));
+}
+
+void WindowTree::ProcessWillChangeWindowHierarchy(
+ const ServerWindow* window,
+ const ServerWindow* new_parent,
+ const ServerWindow* old_parent,
+ bool originated_change) {
+ if (originated_change)
+ return;
+
+ const bool old_drawn = window->IsDrawn();
+ const bool new_drawn =
+ window->visible() && new_parent && new_parent->IsDrawn();
+ if (old_drawn == new_drawn)
+ return;
+
+ NotifyDrawnStateChanged(window, new_drawn);
+}
+
+void WindowTree::ProcessWindowPropertyChanged(
+ const ServerWindow* window,
+ const std::string& name,
+ const std::vector<uint8_t>* new_data,
+ bool originated_change) {
+ if (originated_change)
+ return;
+
+ ClientWindowId client_window_id;
+ if (!IsWindowKnown(window, &client_window_id))
+ return;
+
+ Array<uint8_t> data(nullptr);
+ if (new_data)
+ data = Array<uint8_t>::From(*new_data);
+
+ client()->OnWindowSharedPropertyChanged(client_window_id.id, String(name),
+ std::move(data));
+}
+
+void WindowTree::ProcessWindowHierarchyChanged(const ServerWindow* window,
+ const ServerWindow* new_parent,
+ const ServerWindow* old_parent,
+ bool originated_change) {
+ const bool knows_new = new_parent && IsWindowKnown(new_parent);
+ if (originated_change && !IsWindowKnown(window) && knows_new) {
+ std::vector<const ServerWindow*> unused;
+ GetUnknownWindowsFrom(window, &unused);
+ }
+ if (originated_change || (window_server_->current_operation_type() ==
+ OperationType::DELETE_WINDOW) ||
+ (window_server_->current_operation_type() == OperationType::EMBED) ||
+ window_server_->DidTreeMessageClient(id_)) {
+ return;
+ }
+
+ if (!access_policy_->ShouldNotifyOnHierarchyChange(window, &new_parent,
+ &old_parent)) {
+ return;
+ }
+ // Inform the client of any new windows and update the set of windows we know
+ // about.
+ std::vector<const ServerWindow*> to_send;
+ if (!IsWindowKnown(window))
+ GetUnknownWindowsFrom(window, &to_send);
+ const bool knows_old = old_parent && IsWindowKnown(old_parent);
+ if (!knows_old && !knows_new)
+ return;
+
+ const ClientWindowId new_parent_client_window_id =
+ knows_new ? ClientWindowIdForWindow(new_parent) : ClientWindowId();
+ const ClientWindowId old_parent_client_window_id =
+ knows_old ? ClientWindowIdForWindow(old_parent) : ClientWindowId();
+ const ClientWindowId client_window_id =
+ window ? ClientWindowIdForWindow(window) : ClientWindowId();
+ client()->OnWindowHierarchyChanged(
+ client_window_id.id, old_parent_client_window_id.id,
+ new_parent_client_window_id.id, WindowsToWindowDatas(to_send));
+ window_server_->OnTreeMessagedClient(id_);
+}
+
+void WindowTree::ProcessWindowReorder(const ServerWindow* window,
+ const ServerWindow* relative_window,
+ mojom::OrderDirection direction,
+ bool originated_change) {
+ DCHECK_EQ(window->parent(), relative_window->parent());
+ ClientWindowId client_window_id, relative_client_window_id;
+ if (originated_change || !IsWindowKnown(window, &client_window_id) ||
+ !IsWindowKnown(relative_window, &relative_client_window_id) ||
+ window_server_->DidTreeMessageClient(id_))
+ return;
+
+ // Do not notify ordering changes of the root windows, since the client
+ // doesn't know about the ancestors of the roots, and so can't do anything
+ // about this ordering change of the root.
+ if (HasRoot(window) || HasRoot(relative_window))
+ return;
+
+ client()->OnWindowReordered(client_window_id.id, relative_client_window_id.id,
+ direction);
+ window_server_->OnTreeMessagedClient(id_);
+}
+
+void WindowTree::ProcessWindowDeleted(const ServerWindow* window,
+ bool originated_change) {
+ if (window->id().client_id == id_)
+ created_window_map_.erase(window->id());
+
+ ClientWindowId client_window_id;
+ if (!IsWindowKnown(window, &client_window_id))
+ return;
+
+ if (HasRoot(window))
+ RemoveRoot(window, RemoveRootReason::DELETED);
+ else
+ RemoveFromMaps(window);
+
+ if (originated_change)
+ return;
+
+ client()->OnWindowDeleted(client_window_id.id);
+ window_server_->OnTreeMessagedClient(id_);
+}
+
+void WindowTree::ProcessWillChangeWindowVisibility(const ServerWindow* window,
+ bool originated_change) {
+ if (originated_change)
+ return;
+
+ ClientWindowId client_window_id;
+ if (IsWindowKnown(window, &client_window_id)) {
+ client()->OnWindowVisibilityChanged(client_window_id.id,
+ !window->visible());
+ return;
+ }
+
+ bool window_target_drawn_state;
+ if (window->visible()) {
+ // Window is being hidden, won't be drawn.
+ window_target_drawn_state = false;
+ } else {
+ // Window is being shown. Window will be drawn if its parent is drawn.
+ window_target_drawn_state = window->parent() && window->parent()->IsDrawn();
+ }
+
+ NotifyDrawnStateChanged(window, window_target_drawn_state);
+}
+
+void WindowTree::ProcessWindowOpacityChanged(const ServerWindow* window,
+ float old_opacity,
+ float new_opacity,
+ bool originated_change) {
+ if (originated_change)
+ return;
+
+ ClientWindowId client_window_id;
+ if (IsWindowKnown(window, &client_window_id)) {
+ client()->OnWindowOpacityChanged(client_window_id.id, old_opacity,
+ new_opacity);
+ }
+}
+
+void WindowTree::ProcessCursorChanged(const ServerWindow* window,
+ int32_t cursor_id,
+ bool originated_change) {
+ if (originated_change)
+ return;
+ ClientWindowId client_window_id;
+ if (!IsWindowKnown(window, &client_window_id))
+ return;
+
+ client()->OnWindowPredefinedCursorChanged(client_window_id.id,
+ mojom::Cursor(cursor_id));
+}
+
+void WindowTree::ProcessFocusChanged(const ServerWindow* old_focused_window,
+ const ServerWindow* new_focused_window) {
+ if (window_server_->current_operation_type() == OperationType::SET_FOCUS &&
+ window_server_->IsOperationSource(id_)) {
+ return;
+ }
+ const ServerWindow* window =
+ new_focused_window
+ ? access_policy_->GetWindowForFocusChange(new_focused_window)
+ : nullptr;
+ ClientWindowId client_window_id;
+ // If the window isn't known we'll supply null, which is ok.
+ IsWindowKnown(window, &client_window_id);
+ client()->OnWindowFocused(client_window_id.id);
+}
+
+void WindowTree::ProcessTransientWindowAdded(
+ const ServerWindow* window,
+ const ServerWindow* transient_window,
+ bool originated_change) {
+ if (originated_change)
+ return;
+
+ ClientWindowId client_window_id, transient_client_window_id;
+ if (!IsWindowKnown(window, &client_window_id) ||
+ !IsWindowKnown(transient_window, &transient_client_window_id)) {
+ return;
+ }
+ client()->OnTransientWindowAdded(client_window_id.id,
+ transient_client_window_id.id);
+}
+
+void WindowTree::ProcessTransientWindowRemoved(
+ const ServerWindow* window,
+ const ServerWindow* transient_window,
+ bool originated_change) {
+ if (originated_change)
+ return;
+ ClientWindowId client_window_id, transient_client_window_id;
+ if (!IsWindowKnown(window, &client_window_id) ||
+ !IsWindowKnown(transient_window, &transient_client_window_id)) {
+ return;
+ }
+ client()->OnTransientWindowRemoved(client_window_id.id,
+ transient_client_window_id.id);
+}
+
+bool WindowTree::ShouldRouteToWindowManager(const ServerWindow* window) const {
+ if (window_manager_state_)
+ return false; // We are the window manager, don't route to ourself.
+
+ // If the client created this window, then do not route it through the WM.
+ if (window->id().client_id == id_)
+ return false;
+
+ // If the client did not create the window, then it must be the root of the
+ // client. If not, that means the client should not know about this window,
+ // and so do not route the request to the WM.
+ if (roots_.count(window) == 0)
+ return false;
+
+ // The WindowManager is attached to the root of the Display, if there isn't a
+ // WindowManager attached no need to route to it.
+ const WindowManagerDisplayRoot* display_root =
+ GetWindowManagerDisplayRoot(window);
+ if (!display_root)
+ return false;
+
+ // Route to the windowmanager if the windowmanager created the window.
+ return display_root->window_manager_state()->window_tree()->id() ==
+ window->id().client_id;
+}
+
+void WindowTree::ProcessLostCapture(const ServerWindow* old_capture_window,
+ bool originated_change) {
+ if ((originated_change &&
+ window_server_->current_operation_type() ==
+ OperationType::RELEASE_CAPTURE) ||
+ !IsWindowKnown(old_capture_window)) {
+ return;
+ }
+ client()->OnLostCapture(WindowIdToTransportId(old_capture_window->id()));
+}
+
+ClientWindowId WindowTree::ClientWindowIdForWindow(
+ const ServerWindow* window) const {
+ auto iter = window_id_to_client_id_map_.find(window->id());
+ DCHECK(iter != window_id_to_client_id_map_.end());
+ return iter->second;
+}
+
+bool WindowTree::IsValidIdForNewWindow(const ClientWindowId& id) const {
+ // Reserve 0 to indicate a null window.
+ return client_id_to_window_id_map_.count(id) == 0u &&
+ access_policy_->IsValidIdForNewWindow(id) && id != ClientWindowId();
+}
+
+WindowId WindowTree::GenerateNewWindowId() {
+ // TODO(sky): deal with wrapping and uniqueness.
+ return WindowId(id_, next_window_id_++);
+}
+
+bool WindowTree::CanReorderWindow(const ServerWindow* window,
+ const ServerWindow* relative_window,
+ mojom::OrderDirection direction) const {
+ if (!window || !relative_window)
+ return false;
+
+ if (!window->parent() || window->parent() != relative_window->parent())
+ return false;
+
+ if (!access_policy_->CanReorderWindow(window, relative_window, direction))
+ return false;
+
+ std::vector<const ServerWindow*> children = window->parent()->GetChildren();
+ const size_t child_i =
+ std::find(children.begin(), children.end(), window) - children.begin();
+ const size_t target_i =
+ std::find(children.begin(), children.end(), relative_window) -
+ children.begin();
+ if ((direction == mojom::OrderDirection::ABOVE && child_i == target_i + 1) ||
+ (direction == mojom::OrderDirection::BELOW && child_i + 1 == target_i)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool WindowTree::DeleteWindowImpl(WindowTree* source, ServerWindow* window) {
+ DCHECK(window);
+ DCHECK_EQ(window->id().client_id, id_);
+ Operation op(source, window_server_, OperationType::DELETE_WINDOW);
+ delete window;
+ return true;
+}
+
+void WindowTree::GetUnknownWindowsFrom(
+ const ServerWindow* window,
+ std::vector<const ServerWindow*>* windows) {
+ if (IsWindowKnown(window) || !access_policy_->CanGetWindowTree(window))
+ return;
+ windows->push_back(window);
+ // There are two cases where this gets hit:
+ // . During init, in which case using the window id as the client id is
+ // fine.
+ // . When a window is moved to a parent of a window we know about. This is
+ // only encountered for the WM or embed roots. We assume such clients want
+ // to see the real id of the window and are only created ClientWindowIds
+ // with the client_id.
+ const ClientWindowId client_window_id(WindowIdToTransportId(window->id()));
+ DCHECK_EQ(0u, client_id_to_window_id_map_.count(client_window_id));
+ client_id_to_window_id_map_[client_window_id] = window->id();
+ window_id_to_client_id_map_[window->id()] = client_window_id;
+ if (!access_policy_->CanDescendIntoWindowForWindowTree(window))
+ return;
+ std::vector<const ServerWindow*> children(window->GetChildren());
+ for (size_t i = 0; i < children.size(); ++i)
+ GetUnknownWindowsFrom(children[i], windows);
+}
+
+bool WindowTree::RemoveFromMaps(const ServerWindow* window) {
+ auto iter = window_id_to_client_id_map_.find(window->id());
+ if (iter == window_id_to_client_id_map_.end())
+ return false;
+
+ client_id_to_window_id_map_.erase(iter->second);
+ window_id_to_client_id_map_.erase(iter);
+ return true;
+}
+
+void WindowTree::RemoveFromKnown(const ServerWindow* window,
+ std::vector<ServerWindow*>* local_windows) {
+ if (window->id().client_id == id_) {
+ if (local_windows)
+ local_windows->push_back(GetWindow(window->id()));
+ return;
+ }
+
+ RemoveFromMaps(window);
+
+ std::vector<const ServerWindow*> children = window->GetChildren();
+ for (size_t i = 0; i < children.size(); ++i)
+ RemoveFromKnown(children[i], local_windows);
+}
+
+void WindowTree::RemoveRoot(const ServerWindow* window,
+ RemoveRootReason reason) {
+ DCHECK(roots_.count(window) > 0);
+ roots_.erase(window);
+
+ const ClientWindowId client_window_id(ClientWindowIdForWindow(window));
+
+ // No need to do anything if we created the window.
+ if (window->id().client_id == id_)
+ return;
+
+ if (reason == RemoveRootReason::EMBED) {
+ client()->OnUnembed(client_window_id.id);
+ client()->OnWindowDeleted(client_window_id.id);
+ window_server_->OnTreeMessagedClient(id_);
+ }
+
+ // This client no longer knows about the window. Unparent any windows that
+ // were parented to windows in the root.
+ std::vector<ServerWindow*> local_windows;
+ RemoveFromKnown(window, &local_windows);
+ for (size_t i = 0; i < local_windows.size(); ++i)
+ local_windows[i]->parent()->Remove(local_windows[i]);
+}
+
+Array<mojom::WindowDataPtr> WindowTree::WindowsToWindowDatas(
+ const std::vector<const ServerWindow*>& windows) {
+ Array<mojom::WindowDataPtr> array(windows.size());
+ for (size_t i = 0; i < windows.size(); ++i)
+ array[i] = WindowToWindowData(windows[i]);
+ return array;
+}
+
+mojom::WindowDataPtr WindowTree::WindowToWindowData(
+ const ServerWindow* window) {
+ DCHECK(IsWindowKnown(window));
+ const ServerWindow* parent = window->parent();
+ // If the parent isn't known, it means the parent is not visible to us (not
+ // in roots), and should not be sent over.
+ if (parent && !IsWindowKnown(parent))
+ parent = nullptr;
+ mojom::WindowDataPtr window_data(mojom::WindowData::New());
+ window_data->parent_id =
+ parent ? ClientWindowIdForWindow(parent).id : ClientWindowId().id;
+ window_data->window_id =
+ window ? ClientWindowIdForWindow(window).id : ClientWindowId().id;
+ window_data->bounds = window->bounds();
+ window_data->properties =
+ mojo::Map<String, Array<uint8_t>>::From(window->properties());
+ window_data->visible = window->visible();
+ return window_data;
+}
+
+void WindowTree::GetWindowTreeImpl(
+ const ServerWindow* window,
+ std::vector<const ServerWindow*>* windows) const {
+ DCHECK(window);
+
+ if (!access_policy_->CanGetWindowTree(window))
+ return;
+
+ windows->push_back(window);
+
+ if (!access_policy_->CanDescendIntoWindowForWindowTree(window))
+ return;
+
+ std::vector<const ServerWindow*> children(window->GetChildren());
+ for (size_t i = 0; i < children.size(); ++i)
+ GetWindowTreeImpl(children[i], windows);
+}
+
+void WindowTree::NotifyDrawnStateChanged(const ServerWindow* window,
+ bool new_drawn_value) {
+ // Even though we don't know about window, it may be an ancestor of our root,
+ // in which case the change may effect our roots drawn state.
+ if (roots_.empty())
+ return;
+
+ for (auto* root : roots_) {
+ if (window->Contains(root) && (new_drawn_value != root->IsDrawn())) {
+ client()->OnWindowParentDrawnStateChanged(
+ ClientWindowIdForWindow(root).id, new_drawn_value);
+ }
+ }
+}
+
+void WindowTree::DestroyWindows() {
+ if (created_window_map_.empty())
+ return;
+
+ Operation op(this, window_server_, OperationType::DELETE_WINDOW);
+ // If we get here from the destructor we're not going to get
+ // ProcessWindowDeleted(). Copy the map and delete from the copy so that we
+ // don't have to worry about whether |created_window_map_| changes or not.
+ base::hash_map<WindowId, ServerWindow*> created_window_map_copy;
+ created_window_map_.swap(created_window_map_copy);
+ // A sibling can be a transient parent of another window so we detach windows
+ // from their transient parents to avoid double deletes.
+ for (auto& pair : created_window_map_copy) {
+ ServerWindow* transient_parent = pair.second->transient_parent();
+ if (transient_parent)
+ transient_parent->RemoveTransientWindow(pair.second);
+ }
+ STLDeleteValues(&created_window_map_copy);
+}
+
+bool WindowTree::CanEmbed(const ClientWindowId& window_id) const {
+ const ServerWindow* window = GetWindowByClientId(window_id);
+ return window && access_policy_->CanEmbed(window);
+}
+
+void WindowTree::PrepareForEmbed(ServerWindow* window) {
+ DCHECK(window);
+
+ // Only allow a node to be the root for one client.
+ WindowTree* existing_owner = window_server_->GetTreeWithRoot(window);
+
+ Operation op(this, window_server_, OperationType::EMBED);
+ RemoveChildrenAsPartOfEmbed(window);
+ if (existing_owner) {
+ // Never message the originating client.
+ window_server_->OnTreeMessagedClient(id_);
+ existing_owner->RemoveRoot(window, RemoveRootReason::EMBED);
+ }
+}
+
+void WindowTree::RemoveChildrenAsPartOfEmbed(ServerWindow* window) {
+ CHECK(window);
+ std::vector<ServerWindow*> children = window->GetChildren();
+ for (size_t i = 0; i < children.size(); ++i)
+ window->Remove(children[i]);
+}
+
+void WindowTree::DispatchInputEventImpl(ServerWindow* target,
+ const ui::Event& event) {
+ DCHECK(!event_ack_id_);
+ // We do not want to create a sequential id for each event, because that can
+ // leak some information to the client. So instead, manufacture the id
+ // randomly.
+ // TODO(moshayedi): Find a faster way to generate ids.
+ event_ack_id_ = 0x1000000 | (rand() & 0xffffff);
+ WindowManagerDisplayRoot* display_root = GetWindowManagerDisplayRoot(target);
+ DCHECK(display_root);
+ event_source_wms_ = display_root->window_manager_state();
+ // Should only get events from windows attached to a host.
+ DCHECK(event_source_wms_);
+ bool matched_observer =
+ event_observer_matcher_ && event_observer_matcher_->MatchesEvent(event);
+ client()->OnWindowInputEvent(
+ event_ack_id_, ClientWindowIdForWindow(target).id,
+ ui::Event::Clone(event), matched_observer ? event_observer_id_ : 0);
+}
+
+void WindowTree::SendToEventObserver(const ui::Event& event) {
+ if (event_observer_matcher_ && event_observer_matcher_->MatchesEvent(event))
+ client()->OnEventObserved(ui::Event::Clone(event), event_observer_id_);
+}
+
+void WindowTree::NewWindow(
+ uint32_t change_id,
+ Id transport_window_id,
+ mojo::Map<mojo::String, mojo::Array<uint8_t>> transport_properties) {
+ std::map<std::string, std::vector<uint8_t>> properties;
+ if (!transport_properties.is_null()) {
+ properties =
+ transport_properties.To<std::map<std::string, std::vector<uint8_t>>>();
+ }
+ client()->OnChangeCompleted(
+ change_id, NewWindow(ClientWindowId(transport_window_id), properties));
+}
+
+void WindowTree::NewTopLevelWindow(
+ uint32_t change_id,
+ Id transport_window_id,
+ mojo::Map<mojo::String, mojo::Array<uint8_t>> transport_properties) {
+ DCHECK(!waiting_for_top_level_window_info_);
+ const ClientWindowId client_window_id(transport_window_id);
+ // TODO(sky): need a way for client to provide context to figure out display.
+ Display* display = display_manager()->displays().empty()
+ ? nullptr
+ : *(display_manager()->displays().begin());
+ // TODO(sky): move checks to accesspolicy.
+ WindowManagerDisplayRoot* display_root =
+ display && user_id_ != InvalidUserId()
+ ? display->GetWindowManagerDisplayRootForUser(user_id_)
+ : nullptr;
+ if (!display_root ||
+ display_root->window_manager_state()->window_tree() == this ||
+ !IsValidIdForNewWindow(client_window_id)) {
+ client()->OnChangeCompleted(change_id, false);
+ return;
+ }
+
+ // The server creates the real window. Any further messages from the client
+ // may try to alter the window. Pause incoming messages so that we know we
+ // can't get a message for a window before the window is created. Once the
+ // window is created we'll resume processing.
+ binding_->SetIncomingMethodCallProcessingPaused(true);
+
+ const uint32_t wm_change_id =
+ window_server_->GenerateWindowManagerChangeId(this, change_id);
+
+ waiting_for_top_level_window_info_.reset(
+ new WaitingForTopLevelWindowInfo(client_window_id, wm_change_id));
+
+ display_root->window_manager_state()
+ ->window_tree()
+ ->window_manager_internal_->WmCreateTopLevelWindow(
+ wm_change_id, id_, std::move(transport_properties));
+}
+
+void WindowTree::DeleteWindow(uint32_t change_id, Id transport_window_id) {
+ ServerWindow* window =
+ GetWindowByClientId(ClientWindowId(transport_window_id));
+ bool success = false;
+ bool should_close = window && (access_policy_->CanDeleteWindow(window) ||
+ ShouldRouteToWindowManager(window));
+ if (should_close) {
+ WindowTree* tree =
+ window_server_->GetTreeWithId(window->id().client_id);
+ success = tree && tree->DeleteWindowImpl(this, window);
+ }
+ client()->OnChangeCompleted(change_id, success);
+}
+
+void WindowTree::AddWindow(uint32_t change_id, Id parent_id, Id child_id) {
+ client()->OnChangeCompleted(change_id, AddWindow(ClientWindowId(parent_id),
+ ClientWindowId(child_id)));
+}
+
+void WindowTree::RemoveWindowFromParent(uint32_t change_id, Id window_id) {
+ bool success = false;
+ ServerWindow* window = GetWindowByClientId(ClientWindowId(window_id));
+ if (window && window->parent() &&
+ access_policy_->CanRemoveWindowFromParent(window)) {
+ success = true;
+ Operation op(this, window_server_,
+ OperationType::REMOVE_WINDOW_FROM_PARENT);
+ window->parent()->Remove(window);
+ }
+ client()->OnChangeCompleted(change_id, success);
+}
+
+void WindowTree::AddTransientWindow(uint32_t change_id,
+ Id window,
+ Id transient_window) {
+ client()->OnChangeCompleted(
+ change_id, AddTransientWindow(ClientWindowId(window),
+ ClientWindowId(transient_window)));
+}
+
+void WindowTree::RemoveTransientWindowFromParent(uint32_t change_id,
+ Id transient_window_id) {
+ bool success = false;
+ ServerWindow* transient_window =
+ GetWindowByClientId(ClientWindowId(transient_window_id));
+ if (transient_window && transient_window->transient_parent() &&
+ access_policy_->CanRemoveTransientWindowFromParent(transient_window)) {
+ success = true;
+ Operation op(this, window_server_,
+ OperationType::REMOVE_TRANSIENT_WINDOW_FROM_PARENT);
+ transient_window->transient_parent()->RemoveTransientWindow(
+ transient_window);
+ }
+ client()->OnChangeCompleted(change_id, success);
+}
+
+void WindowTree::SetModal(uint32_t change_id, Id window_id) {
+ client()->OnChangeCompleted(change_id, SetModal(ClientWindowId(window_id)));
+}
+
+void WindowTree::ReorderWindow(uint32_t change_id,
+ Id window_id,
+ Id relative_window_id,
+ mojom::OrderDirection direction) {
+ bool success = false;
+ ServerWindow* window = GetWindowByClientId(ClientWindowId(window_id));
+ ServerWindow* relative_window =
+ GetWindowByClientId(ClientWindowId(relative_window_id));
+ if (CanReorderWindow(window, relative_window, direction)) {
+ success = true;
+ Operation op(this, window_server_, OperationType::REORDER_WINDOW);
+ window->Reorder(relative_window, direction);
+ window_server_->ProcessWindowReorder(window, relative_window, direction);
+ }
+ client()->OnChangeCompleted(change_id, success);
+}
+
+void WindowTree::GetWindowTree(
+ Id window_id,
+ const base::Callback<void(Array<mojom::WindowDataPtr>)>& callback) {
+ std::vector<const ServerWindow*> windows(
+ GetWindowTree(ClientWindowId(window_id)));
+ callback.Run(WindowsToWindowDatas(windows));
+}
+
+void WindowTree::SetCapture(uint32_t change_id, Id window_id) {
+ client()->OnChangeCompleted(change_id, SetCapture(ClientWindowId(window_id)));
+}
+
+void WindowTree::ReleaseCapture(uint32_t change_id, Id window_id) {
+ ServerWindow* window = GetWindowByClientId(ClientWindowId(window_id));
+ WindowManagerDisplayRoot* display_root = GetWindowManagerDisplayRoot(window);
+ ServerWindow* current_capture_window =
+ display_root ? display_root->window_manager_state()->capture_window()
+ : nullptr;
+ bool success = window && display_root &&
+ display_root->window_manager_state()->IsActive() &&
+ (!current_capture_window ||
+ access_policy_->CanSetCapture(current_capture_window)) &&
+ window == current_capture_window;
+ if (success) {
+ Operation op(this, window_server_, OperationType::RELEASE_CAPTURE);
+ success = display_root->window_manager_state()->SetCapture(
+ nullptr, kInvalidClientId);
+ }
+ client()->OnChangeCompleted(change_id, success);
+}
+
+void WindowTree::SetEventObserver(mojom::EventMatcherPtr matcher,
+ uint32_t observer_id) {
+ if (matcher.is_null() || observer_id == 0) {
+ // Clear any existing event observer.
+ event_observer_matcher_.reset();
+ event_observer_id_ = 0;
+ return;
+ }
+
+ // Do not allow key events to be observed, as a compromised app could register
+ // itself as an event observer and spy on keystrokes to another app.
+ if (!matcher->type_matcher) {
+ DVLOG(1) << "SetEventObserver must specify an event type.";
+ return;
+ }
+ const ui::mojom::EventType event_type_whitelist[] = {
+ ui::mojom::EventType::POINTER_CANCEL, ui::mojom::EventType::POINTER_DOWN,
+ ui::mojom::EventType::POINTER_MOVE, ui::mojom::EventType::POINTER_UP,
+ ui::mojom::EventType::MOUSE_EXIT, ui::mojom::EventType::WHEEL,
+ };
+ bool allowed = false;
+ for (ui::mojom::EventType event_type : event_type_whitelist) {
+ if (matcher->type_matcher->type == event_type) {
+ allowed = true;
+ break;
+ }
+ }
+ if (!allowed) {
+ DVLOG(1) << "SetEventObserver event type not allowed";
+ return;
+ }
+
+ event_observer_matcher_.reset(new EventMatcher(*matcher));
+ event_observer_id_ = observer_id;
+}
+
+void WindowTree::SetWindowBounds(uint32_t change_id,
+ Id window_id,
+ const gfx::Rect& bounds) {
+ ServerWindow* window = GetWindowByClientId(ClientWindowId(window_id));
+ if (window && ShouldRouteToWindowManager(window)) {
+ const uint32_t wm_change_id =
+ window_server_->GenerateWindowManagerChangeId(this, change_id);
+ // |window_id| may be a client id, use the id from the window to ensure
+ // the windowmanager doesn't get an id it doesn't know about.
+ WindowManagerDisplayRoot* display_root =
+ GetWindowManagerDisplayRoot(window);
+ WindowTree* wm_tree = display_root->window_manager_state()->window_tree();
+ wm_tree->window_manager_internal_->WmSetBounds(
+ wm_change_id, wm_tree->ClientWindowIdForWindow(window).id,
+ std::move(bounds));
+ return;
+ }
+
+ // Only the owner of the window can change the bounds.
+ bool success = window && access_policy_->CanSetWindowBounds(window);
+ if (success) {
+ Operation op(this, window_server_, OperationType::SET_WINDOW_BOUNDS);
+ window->SetBounds(bounds);
+ }
+ client()->OnChangeCompleted(change_id, success);
+}
+
+void WindowTree::SetWindowVisibility(uint32_t change_id,
+ Id transport_window_id,
+ bool visible) {
+ client()->OnChangeCompleted(
+ change_id,
+ SetWindowVisibility(ClientWindowId(transport_window_id), visible));
+}
+
+void WindowTree::SetWindowProperty(uint32_t change_id,
+ Id transport_window_id,
+ const mojo::String& name,
+ mojo::Array<uint8_t> value) {
+ ServerWindow* window =
+ GetWindowByClientId(ClientWindowId(transport_window_id));
+ if (window && ShouldRouteToWindowManager(window)) {
+ const uint32_t wm_change_id =
+ window_server_->GenerateWindowManagerChangeId(this, change_id);
+ WindowManagerDisplayRoot* display_root =
+ GetWindowManagerDisplayRoot(window);
+ WindowTree* wm_tree = display_root->window_manager_state()->window_tree();
+ wm_tree->window_manager_internal_->WmSetProperty(
+ wm_change_id, wm_tree->ClientWindowIdForWindow(window).id, name,
+ std::move(value));
+ return;
+ }
+ const bool success = window && access_policy_->CanSetWindowProperties(window);
+ if (success) {
+ Operation op(this, window_server_, OperationType::SET_WINDOW_PROPERTY);
+ if (value.is_null()) {
+ window->SetProperty(name, nullptr);
+ } else {
+ std::vector<uint8_t> data = value.To<std::vector<uint8_t>>();
+ window->SetProperty(name, &data);
+ }
+ }
+ client()->OnChangeCompleted(change_id, success);
+}
+
+void WindowTree::SetWindowOpacity(uint32_t change_id,
+ Id window_id,
+ float opacity) {
+ client()->OnChangeCompleted(
+ change_id, SetWindowOpacity(ClientWindowId(window_id), opacity));
+}
+
+void WindowTree::AttachSurface(Id transport_window_id,
+ mojom::SurfaceType type,
+ mojo::InterfaceRequest<mojom::Surface> surface,
+ mojom::SurfaceClientPtr client) {
+ ServerWindow* window =
+ GetWindowByClientId(ClientWindowId(transport_window_id));
+ const bool success =
+ window && access_policy_->CanSetWindowSurface(window, type);
+ if (!success)
+ return;
+ window->CreateSurface(type, std::move(surface), std::move(client));
+}
+
+void WindowTree::SetWindowTextInputState(Id transport_window_id,
+ mojo::TextInputStatePtr state) {
+ ServerWindow* window =
+ GetWindowByClientId(ClientWindowId(transport_window_id));
+ bool success = window && access_policy_->CanSetWindowTextInputState(window);
+ if (success)
+ window->SetTextInputState(state.To<ui::TextInputState>());
+}
+
+void WindowTree::SetImeVisibility(Id transport_window_id,
+ bool visible,
+ mojo::TextInputStatePtr state) {
+ ServerWindow* window =
+ GetWindowByClientId(ClientWindowId(transport_window_id));
+ bool success = window && access_policy_->CanSetWindowTextInputState(window);
+ if (success) {
+ if (!state.is_null())
+ window->SetTextInputState(state.To<ui::TextInputState>());
+
+ Display* display = GetDisplay(window);
+ if (display)
+ display->SetImeVisibility(window, visible);
+ }
+}
+
+void WindowTree::OnWindowInputEventAck(uint32_t event_id,
+ mojom::EventResult result) {
+ if (event_ack_id_ == 0 || event_id != event_ack_id_) {
+ // TODO(sad): Something bad happened. Kill the client?
+ NOTIMPLEMENTED() << "Wrong event acked.";
+ }
+ event_ack_id_ = 0;
+
+ if (janky_)
+ event_source_wms_->window_tree()->ClientJankinessChanged(this);
+
+ WindowManagerState* event_source_wms = event_source_wms_;
+ event_source_wms_ = nullptr;
+ if (event_source_wms)
+ event_source_wms->OnEventAck(this, result);
+
+ if (!event_queue_.empty()) {
+ DCHECK(!event_ack_id_);
+ ServerWindow* target = nullptr;
+ std::unique_ptr<ui::Event> event;
+ do {
+ std::unique_ptr<TargetedEvent> targeted_event =
+ std::move(event_queue_.front());
+ event_queue_.pop();
+ target = targeted_event->target();
+ event = targeted_event->TakeEvent();
+ } while (!event_queue_.empty() && !GetDisplay(target));
+ if (target)
+ DispatchInputEventImpl(target, *event);
+ }
+}
+
+void WindowTree::SetClientArea(
+ Id transport_window_id,
+ const gfx::Insets& insets,
+ mojo::Array<gfx::Rect> transport_additional_client_areas) {
+ ServerWindow* window =
+ GetWindowByClientId(ClientWindowId(transport_window_id));
+ if (!window || !access_policy_->CanSetClientArea(window))
+ return;
+
+ std::vector<gfx::Rect> additional_client_areas =
+ transport_additional_client_areas.To<std::vector<gfx::Rect>>();
+ window->SetClientArea(insets, additional_client_areas);
+}
+
+void WindowTree::SetHitTestMask(Id transport_window_id, const gfx::Rect& mask) {
+ ServerWindow* window =
+ GetWindowByClientId(ClientWindowId(transport_window_id));
+ if (!window || !access_policy_->CanSetHitTestMask(window)) {
+ DVLOG(1) << "SetHitTestMask failed";
+ return;
+ }
+
+ if (!mask.IsEmpty())
+ window->SetHitTestMask(mask);
+ else
+ window->ClearHitTestMask();
+}
+
+void WindowTree::Embed(Id transport_window_id,
+ mojom::WindowTreeClientPtr client,
+ uint32_t flags,
+ const EmbedCallback& callback) {
+ callback.Run(
+ Embed(ClientWindowId(transport_window_id), std::move(client), flags));
+}
+
+void WindowTree::SetFocus(uint32_t change_id, Id transport_window_id) {
+ client()->OnChangeCompleted(change_id,
+ SetFocus(ClientWindowId(transport_window_id)));
+}
+
+void WindowTree::SetCanFocus(Id transport_window_id, bool can_focus) {
+ ServerWindow* window =
+ GetWindowByClientId(ClientWindowId(transport_window_id));
+ // TODO(sky): there should be an else case (it shouldn't route to wm and
+ // policy allows, then set_can_focus).
+ if (window && ShouldRouteToWindowManager(window))
+ window->set_can_focus(can_focus);
+}
+
+void WindowTree::SetPredefinedCursor(uint32_t change_id,
+ Id transport_window_id,
+ mus::mojom::Cursor cursor_id) {
+ ServerWindow* window =
+ GetWindowByClientId(ClientWindowId(transport_window_id));
+
+ // Only the owner of the window can change the bounds.
+ bool success = window && access_policy_->CanSetCursorProperties(window);
+ if (success) {
+ Operation op(this, window_server_,
+ OperationType::SET_WINDOW_PREDEFINED_CURSOR);
+ window->SetPredefinedCursor(cursor_id);
+ }
+ client()->OnChangeCompleted(change_id, success);
+}
+
+void WindowTree::GetWindowManagerClient(
+ mojo::AssociatedInterfaceRequest<mojom::WindowManagerClient> internal) {
+ if (!access_policy_->CanSetWindowManager() || !window_manager_internal_ ||
+ window_manager_internal_client_binding_) {
+ return;
+ }
+ window_manager_internal_client_binding_.reset(
+ new mojo::AssociatedBinding<mojom::WindowManagerClient>(
+ this, std::move(internal)));
+}
+
+void WindowTree::GetCursorLocationMemory(
+ const GetCursorLocationMemoryCallback& callback) {
+ callback.Run(
+ window_server_->display_manager()->GetUserDisplayManager(user_id_)->
+ GetCursorLocationMemory());
+}
+
+void WindowTree::AddAccelerator(uint32_t id,
+ mojom::EventMatcherPtr event_matcher,
+ const AddAcceleratorCallback& callback) {
+ DCHECK(window_manager_state_);
+ const bool success =
+ window_manager_state_->event_dispatcher()->AddAccelerator(
+ id, std::move(event_matcher));
+ callback.Run(success);
+}
+
+void WindowTree::RemoveAccelerator(uint32_t id) {
+ window_manager_state_->event_dispatcher()->RemoveAccelerator(id);
+}
+
+void WindowTree::AddActivationParent(Id transport_window_id) {
+ AddActivationParent(ClientWindowId(transport_window_id));
+}
+
+void WindowTree::RemoveActivationParent(Id transport_window_id) {
+ ServerWindow* window =
+ GetWindowByClientId(ClientWindowId(transport_window_id));
+ if (window) {
+ Display* display = GetDisplay(window);
+ if (display)
+ display->RemoveActivationParent(window);
+ else
+ DVLOG(1) << "RemoveActivationParent window not associated with display";
+ } else {
+ DVLOG(1) << "RemoveActivationParent supplied invalid window id";
+ }
+}
+
+void WindowTree::ActivateNextWindow() {
+ DCHECK(window_manager_state_);
+ if (window_server_->user_id_tracker()->active_id() != user_id_)
+ return;
+
+ ServerWindow* focused_window = window_server_->GetFocusedWindow();
+ if (focused_window) {
+ WindowManagerDisplayRoot* display_root =
+ GetWindowManagerDisplayRoot(focused_window);
+ if (display_root->window_manager_state() != window_manager_state_.get()) {
+ // We aren't active.
+ return;
+ }
+ display_root->display()->ActivateNextWindow();
+ return;
+ }
+ // Use the first display.
+ std::set<Display*> displays = window_server_->display_manager()->displays();
+ if (displays.empty())
+ return;
+
+ (*displays.begin())->ActivateNextWindow();
+}
+
+void WindowTree::SetUnderlaySurfaceOffsetAndExtendedHitArea(
+ Id window_id,
+ int32_t x_offset,
+ int32_t y_offset,
+ const gfx::Insets& hit_area) {
+ ServerWindow* window = GetWindowByClientId(ClientWindowId(window_id));
+ if (!window)
+ return;
+
+ window->SetUnderlayOffset(gfx::Vector2d(x_offset, y_offset));
+ window->set_extended_hit_test_region(hit_area);
+}
+
+void WindowTree::WmResponse(uint32_t change_id, bool response) {
+ window_server_->WindowManagerChangeCompleted(change_id, response);
+}
+
+void WindowTree::WmRequestClose(Id transport_window_id) {
+ ServerWindow* window =
+ GetWindowByClientId(ClientWindowId(transport_window_id));
+ WindowTree* tree = window_server_->GetTreeWithRoot(window);
+ if (tree && tree != this) {
+ tree->client()->RequestClose(tree->ClientWindowIdForWindow(window).id);
+ }
+ // TODO(sky): think about what else case means.
+}
+
+void WindowTree::WmSetFrameDecorationValues(
+ mojom::FrameDecorationValuesPtr values) {
+ DCHECK(window_manager_state_);
+ window_manager_state_->SetFrameDecorationValues(std::move(values));
+}
+
+void WindowTree::WmSetNonClientCursor(uint32_t window_id,
+ mojom::Cursor cursor_id) {
+ DCHECK(window_manager_state_);
+ ServerWindow* window = GetWindowByClientId(ClientWindowId(window_id));
+ if (window) {
+ window->SetNonClientCursor(cursor_id);
+ } else {
+ DVLOG(1) << "trying to update non-client cursor of invalid window";
+ }
+}
+
+void WindowTree::OnWmCreatedTopLevelWindow(uint32_t change_id,
+ Id transport_window_id) {
+ ServerWindow* window =
+ GetWindowByClientId(ClientWindowId(transport_window_id));
+ if (window && window->id().client_id != id_) {
+ DVLOG(1) << "OnWmCreatedTopLevelWindow supplied invalid window id";
+ window_server_->WindowManagerSentBogusMessage();
+ window = nullptr;
+ }
+ window_server_->WindowManagerCreatedTopLevelWindow(this, change_id, window);
+}
+
+bool WindowTree::HasRootForAccessPolicy(const ServerWindow* window) const {
+ return HasRoot(window);
+}
+
+bool WindowTree::IsWindowKnownForAccessPolicy(
+ const ServerWindow* window) const {
+ return IsWindowKnown(window);
+}
+
+bool WindowTree::IsWindowRootOfAnotherTreeForAccessPolicy(
+ const ServerWindow* window) const {
+ WindowTree* tree = window_server_->GetTreeWithRoot(window);
+ return tree && tree != this;
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/window_tree.h b/chromium/components/mus/ws/window_tree.h
new file mode 100644
index 00000000000..0e804f355a1
--- /dev/null
+++ b/chromium/components/mus/ws/window_tree.h
@@ -0,0 +1,497 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_WINDOW_TREE_H_
+#define COMPONENTS_MUS_WS_WINDOW_TREE_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <queue>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/containers/hash_tables.h"
+#include "base/macros.h"
+#include "cc/ipc/surface_id.mojom.h"
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "components/mus/ws/access_policy_delegate.h"
+#include "components/mus/ws/ids.h"
+#include "components/mus/ws/user_id.h"
+#include "components/mus/ws/window_tree_binding.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
+
+namespace gfx {
+class Insets;
+class Rect;
+}
+
+namespace ui {
+class Event;
+}
+
+namespace mus {
+namespace ws {
+
+class AccessPolicy;
+class DisplayManager;
+class Display;
+class EventMatcher;
+class ServerWindow;
+class TargetedEvent;
+class WindowManagerDisplayRoot;
+class WindowManagerState;
+class WindowServer;
+class WindowTreeTest;
+
+namespace test {
+class WindowTreeTestApi;
+}
+
+// WindowTree represents a view onto portions of the window tree. The parts of
+// the tree exposed to the client start at the root windows. A WindowTree may
+// have any number of roots (including none). A WindowTree may not have
+// visibility of all the descendants of its roots.
+//
+// WindowTree notifies its client as changes happen to windows exposed to the
+// the client.
+//
+// See ids.h for details on how WindowTree handles identity of windows.
+class WindowTree : public mojom::WindowTree,
+ public AccessPolicyDelegate,
+ public mojom::WindowManagerClient {
+ public:
+ WindowTree(WindowServer* window_server,
+ const UserId& user_id,
+ ServerWindow* root,
+ std::unique_ptr<AccessPolicy> access_policy);
+ ~WindowTree() override;
+
+ void Init(std::unique_ptr<WindowTreeBinding> binding,
+ mojom::WindowTreePtr tree);
+
+ // Called if this WindowTree hosts a WindowManager.
+ void ConfigureWindowManager();
+
+ ClientSpecificId id() const { return id_; }
+
+ void set_embedder_intercepts_events() { embedder_intercepts_events_ = true; }
+ bool embedder_intercepts_events() const {
+ return embedder_intercepts_events_;
+ }
+
+ const UserId& user_id() const { return user_id_; }
+
+ mojom::WindowTreeClient* client() { return binding_->client(); }
+
+ // Returns the Window with the specified id.
+ ServerWindow* GetWindow(const WindowId& id) {
+ return const_cast<ServerWindow*>(
+ const_cast<const WindowTree*>(this)->GetWindow(id));
+ }
+ const ServerWindow* GetWindow(const WindowId& id) const;
+
+ // Returns the Window with the specified client id *only* if known to this
+ // client, returns null if not known.
+ ServerWindow* GetWindowByClientId(const ClientWindowId& id) {
+ return const_cast<ServerWindow*>(
+ const_cast<const WindowTree*>(this)->GetWindowByClientId(id));
+ }
+ const ServerWindow* GetWindowByClientId(const ClientWindowId& id) const;
+
+ bool IsWindowKnown(const ServerWindow* window) const {
+ return IsWindowKnown(window, nullptr);
+ }
+ // Returns whether |window| is known to this tree. If |window| is known and
+ // |client_window_id| is non-null |client_window_id| is set to the
+ // ClientWindowId of the window.
+ bool IsWindowKnown(const ServerWindow* window,
+ ClientWindowId* client_window_id) const;
+
+ // Returns true if |window| is one of this trees roots.
+ bool HasRoot(const ServerWindow* window) const;
+
+ std::set<const ServerWindow*> roots() { return roots_; }
+
+ void set_name(const std::string& name) { name_ = name; }
+ const std::string& name() const { return name_; }
+
+ bool janky() const { return janky_; }
+
+ const Display* GetDisplay(const ServerWindow* window) const;
+ Display* GetDisplay(const ServerWindow* window) {
+ return const_cast<Display*>(
+ const_cast<const WindowTree*>(this)->GetDisplay(window));
+ }
+
+ const WindowManagerDisplayRoot* GetWindowManagerDisplayRoot(
+ const ServerWindow* window) const;
+ WindowManagerDisplayRoot* GetWindowManagerDisplayRoot(
+ const ServerWindow* window) {
+ return const_cast<WindowManagerDisplayRoot*>(
+ const_cast<const WindowTree*>(this)->GetWindowManagerDisplayRoot(
+ window));
+ }
+ WindowManagerState* window_manager_state() {
+ return window_manager_state_.get();
+ }
+
+ DisplayManager* display_manager();
+ const DisplayManager* display_manager() const;
+
+ WindowServer* window_server() { return window_server_; }
+ const WindowServer* window_server() const { return window_server_; }
+
+ // Adds a new root to this tree. This is only valid for window managers.
+ void AddRootForWindowManager(const ServerWindow* root);
+
+ // Invoked when a tree is about to be destroyed.
+ void OnWindowDestroyingTreeImpl(WindowTree* tree);
+
+ // These functions are synchronous variants of those defined in the mojom. The
+ // WindowTree implementations all call into these. See the mojom for details.
+ bool SetCapture(const ClientWindowId& client_window_id);
+ bool NewWindow(const ClientWindowId& client_window_id,
+ const std::map<std::string, std::vector<uint8_t>>& properties);
+ bool AddWindow(const ClientWindowId& parent_id,
+ const ClientWindowId& child_id);
+ bool AddTransientWindow(const ClientWindowId& window_id,
+ const ClientWindowId& transient_window_id);
+ bool SetModal(const ClientWindowId& window_id);
+ std::vector<const ServerWindow*> GetWindowTree(
+ const ClientWindowId& window_id) const;
+ bool SetWindowVisibility(const ClientWindowId& window_id, bool visible);
+ bool SetWindowOpacity(const ClientWindowId& window_id, float opacity);
+ bool SetFocus(const ClientWindowId& window_id);
+ bool Embed(const ClientWindowId& window_id,
+ mojom::WindowTreeClientPtr client,
+ uint32_t flags);
+ void DispatchInputEvent(ServerWindow* target, const ui::Event& event);
+
+ bool IsWaitingForNewTopLevelWindow(uint32_t wm_change_id);
+ void OnWindowManagerCreatedTopLevelWindow(uint32_t wm_change_id,
+ uint32_t client_change_id,
+ const ServerWindow* window);
+ void AddActivationParent(const ClientWindowId& window_id);
+
+ // Calls through to the client.
+ void OnChangeCompleted(uint32_t change_id, bool success);
+ void OnAccelerator(uint32_t accelerator_id, const ui::Event& event);
+
+ // Called when |tree|'s jankiness changes (see janky_ for definition).
+ // Notifies the window manager client so it can update UI for the affected
+ // window(s).
+ void ClientJankinessChanged(WindowTree* tree);
+
+ // The following methods are invoked after the corresponding change has been
+ // processed. They do the appropriate bookkeeping and update the client as
+ // necessary.
+ void ProcessWindowBoundsChanged(const ServerWindow* window,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds,
+ bool originated_change);
+ void ProcessClientAreaChanged(
+ const ServerWindow* window,
+ const gfx::Insets& new_client_area,
+ const std::vector<gfx::Rect>& new_additional_client_areas,
+ bool originated_change);
+ void ProcessWillChangeWindowHierarchy(const ServerWindow* window,
+ const ServerWindow* new_parent,
+ const ServerWindow* old_parent,
+ bool originated_change);
+ void ProcessWindowPropertyChanged(const ServerWindow* window,
+ const std::string& name,
+ const std::vector<uint8_t>* new_data,
+ bool originated_change);
+ void ProcessWindowHierarchyChanged(const ServerWindow* window,
+ const ServerWindow* new_parent,
+ const ServerWindow* old_parent,
+ bool originated_change);
+ void ProcessWindowReorder(const ServerWindow* window,
+ const ServerWindow* relative_window,
+ mojom::OrderDirection direction,
+ bool originated_change);
+ void ProcessWindowDeleted(const ServerWindow* window, bool originated_change);
+ void ProcessWillChangeWindowVisibility(const ServerWindow* window,
+ bool originated_change);
+ void ProcessWindowOpacityChanged(const ServerWindow* window,
+ float old_opacity,
+ float new_opacity,
+ bool originated_change);
+ void ProcessCursorChanged(const ServerWindow* window,
+ int32_t cursor_id,
+ bool originated_change);
+ void ProcessFocusChanged(const ServerWindow* old_focused_window,
+ const ServerWindow* new_focused_window);
+ void ProcessLostCapture(const ServerWindow* old_lost_capture,
+ bool originated_change);
+ void ProcessTransientWindowAdded(const ServerWindow* window,
+ const ServerWindow* transient_window,
+ bool originated_change);
+ void ProcessTransientWindowRemoved(const ServerWindow* window,
+ const ServerWindow* transient_window,
+ bool originated_change);
+
+ // Sends this event to the client if it matches an active event observer.
+ void SendToEventObserver(const ui::Event& event);
+
+ private:
+ friend class test::WindowTreeTestApi;
+
+ struct WaitingForTopLevelWindowInfo {
+ WaitingForTopLevelWindowInfo(const ClientWindowId& client_window_id,
+ uint32_t wm_change_id)
+ : client_window_id(client_window_id), wm_change_id(wm_change_id) {}
+ ~WaitingForTopLevelWindowInfo() {}
+
+ // Id supplied from the client.
+ ClientWindowId client_window_id;
+
+ // Change id we created for the window manager.
+ uint32_t wm_change_id;
+ };
+
+ enum class RemoveRootReason {
+ // The window is being removed.
+ DELETED,
+
+ // Another client is being embedded in the window.
+ EMBED,
+ };
+
+ bool ShouldRouteToWindowManager(const ServerWindow* window) const;
+
+ ClientWindowId ClientWindowIdForWindow(const ServerWindow* window) const;
+
+ // Returns true if |id| is a valid WindowId for a new window.
+ bool IsValidIdForNewWindow(const ClientWindowId& id) const;
+
+ WindowId GenerateNewWindowId();
+
+ // These functions return true if the corresponding mojom function is allowed
+ // for this tree.
+ bool CanReorderWindow(const ServerWindow* window,
+ const ServerWindow* relative_window,
+ mojom::OrderDirection direction) const;
+
+ // Deletes a window owned by this tree. Returns true on success. |source| is
+ // the tree that originated the change.
+ bool DeleteWindowImpl(WindowTree* source, ServerWindow* window);
+
+ // If |window| is known does nothing. Otherwise adds |window| to |windows|,
+ // marks |window| as known and recurses.
+ void GetUnknownWindowsFrom(const ServerWindow* window,
+ std::vector<const ServerWindow*>* windows);
+
+ // Removes |window| from the appropriate maps. If |window| is known to this
+ // client true is returned.
+ bool RemoveFromMaps(const ServerWindow* window);
+
+ // Removes |window| and all its descendants from the necessary maps. This
+ // does not recurse through windows that were created by this tree. All
+ // windows owned by this tree are added to |local_windows|.
+ void RemoveFromKnown(const ServerWindow* window,
+ std::vector<ServerWindow*>* local_windows);
+
+ // Removes a root from set of roots of this tree. This does not remove
+ // the window from the window tree, only from the set of roots.
+ void RemoveRoot(const ServerWindow* window, RemoveRootReason reason);
+
+ // Converts Window(s) to WindowData(s) for transport. This assumes all the
+ // windows are valid for the client. The parent of windows the client is not
+ // allowed to see are set to NULL (in the returned WindowData(s)).
+ mojo::Array<mojom::WindowDataPtr> WindowsToWindowDatas(
+ const std::vector<const ServerWindow*>& windows);
+ mojom::WindowDataPtr WindowToWindowData(const ServerWindow* window);
+
+ // Implementation of GetWindowTree(). Adds |window| to |windows| and recurses
+ // if CanDescendIntoWindowForWindowTree() returns true.
+ void GetWindowTreeImpl(const ServerWindow* window,
+ std::vector<const ServerWindow*>* windows) const;
+
+ // Notify the client if the drawn state of any of the roots changes.
+ // |window| is the window that is changing to the drawn state
+ // |new_drawn_value|.
+ void NotifyDrawnStateChanged(const ServerWindow* window,
+ bool new_drawn_value);
+
+ // Deletes all Windows we own.
+ void DestroyWindows();
+
+ bool CanEmbed(const ClientWindowId& window_id) const;
+ void PrepareForEmbed(ServerWindow* window);
+ void RemoveChildrenAsPartOfEmbed(ServerWindow* window);
+
+ void DispatchInputEventImpl(ServerWindow* target, const ui::Event& event);
+
+ // Calls OnChangeCompleted() on the client.
+ void NotifyChangeCompleted(uint32_t change_id,
+ mojom::WindowManagerErrorCode error_code);
+
+ // WindowTree:
+ void NewWindow(uint32_t change_id,
+ Id transport_window_id,
+ mojo::Map<mojo::String, mojo::Array<uint8_t>>
+ transport_properties) override;
+ void NewTopLevelWindow(uint32_t change_id,
+ Id transport_window_id,
+ mojo::Map<mojo::String, mojo::Array<uint8_t>>
+ transport_properties) override;
+ void DeleteWindow(uint32_t change_id, Id transport_window_id) override;
+ void AddWindow(uint32_t change_id, Id parent_id, Id child_id) override;
+ void RemoveWindowFromParent(uint32_t change_id, Id window_id) override;
+ void AddTransientWindow(uint32_t change_id,
+ Id window,
+ Id transient_window) override;
+ void RemoveTransientWindowFromParent(uint32_t change_id,
+ Id transient_window_id) override;
+ void SetModal(uint32_t change_id, Id window_id) override;
+ void ReorderWindow(uint32_t change_Id,
+ Id window_id,
+ Id relative_window_id,
+ mojom::OrderDirection direction) override;
+ void GetWindowTree(
+ Id window_id,
+ const base::Callback<void(mojo::Array<mojom::WindowDataPtr>)>& callback)
+ override;
+ void SetCapture(uint32_t change_id, Id window_id) override;
+ void ReleaseCapture(uint32_t change_id, Id window_id) override;
+ void SetEventObserver(mojom::EventMatcherPtr matcher,
+ uint32_t observer_id) override;
+ void SetWindowBounds(uint32_t change_id,
+ Id window_id,
+ const gfx::Rect& bounds) override;
+ void SetWindowVisibility(uint32_t change_id,
+ Id window_id,
+ bool visible) override;
+ void SetWindowProperty(uint32_t change_id,
+ Id transport_window_id,
+ const mojo::String& name,
+ mojo::Array<uint8_t> value) override;
+ void SetWindowOpacity(uint32_t change_id,
+ Id window_id,
+ float opacity) override;
+ void AttachSurface(Id transport_window_id,
+ mojom::SurfaceType type,
+ mojo::InterfaceRequest<mojom::Surface> surface,
+ mojom::SurfaceClientPtr client) override;
+ void Embed(Id transport_window_id,
+ mojom::WindowTreeClientPtr client,
+ uint32_t flags,
+ const EmbedCallback& callback) override;
+ void SetFocus(uint32_t change_id, Id transport_window_id) override;
+ void SetCanFocus(Id transport_window_id, bool can_focus) override;
+ void SetPredefinedCursor(uint32_t change_id,
+ Id transport_window_id,
+ mus::mojom::Cursor cursor_id) override;
+ void SetWindowTextInputState(Id transport_window_id,
+ mojo::TextInputStatePtr state) override;
+ void SetImeVisibility(Id transport_window_id,
+ bool visible,
+ mojo::TextInputStatePtr state) override;
+ void OnWindowInputEventAck(uint32_t event_id,
+ mojom::EventResult result) override;
+ void SetClientArea(
+ Id transport_window_id,
+ const gfx::Insets& insets,
+ mojo::Array<gfx::Rect> transport_additional_client_areas) override;
+ void SetHitTestMask(Id transport_window_id, const gfx::Rect& mask) override;
+ void GetWindowManagerClient(
+ mojo::AssociatedInterfaceRequest<mojom::WindowManagerClient> internal)
+ override;
+ void GetCursorLocationMemory(const GetCursorLocationMemoryCallback& callback)
+ override;
+
+ // mojom::WindowManagerClient:
+ void AddAccelerator(uint32_t id,
+ mojom::EventMatcherPtr event_matcher,
+ const AddAcceleratorCallback& callback) override;
+ void RemoveAccelerator(uint32_t id) override;
+ void AddActivationParent(Id transport_window_id) override;
+ void RemoveActivationParent(Id transport_window_id) override;
+ void ActivateNextWindow() override;
+ void SetUnderlaySurfaceOffsetAndExtendedHitArea(
+ Id window_id,
+ int32_t x_offset,
+ int32_t y_offset,
+ const gfx::Insets& hit_area) override;
+ void WmResponse(uint32_t change_id, bool response) override;
+ void WmRequestClose(Id transport_window_id) override;
+ void WmSetFrameDecorationValues(
+ mojom::FrameDecorationValuesPtr values) override;
+ void WmSetNonClientCursor(uint32_t window_id,
+ mojom::Cursor cursor_id) override;
+ void OnWmCreatedTopLevelWindow(uint32_t change_id,
+ Id transport_window_id) override;
+
+ // AccessPolicyDelegate:
+ bool HasRootForAccessPolicy(const ServerWindow* window) const override;
+ bool IsWindowKnownForAccessPolicy(const ServerWindow* window) const override;
+ bool IsWindowRootOfAnotherTreeForAccessPolicy(
+ const ServerWindow* window) const override;
+
+ WindowServer* window_server_;
+
+ UserId user_id_;
+
+ // Id of this tree as assigned by WindowServer.
+ const ClientSpecificId id_;
+ std::string name_;
+
+ ClientSpecificId next_window_id_;
+
+ std::unique_ptr<WindowTreeBinding> binding_;
+
+ std::unique_ptr<mus::ws::AccessPolicy> access_policy_;
+
+ // The roots, or embed points, of this tree. A WindowTree may have any
+ // number of roots, including 0.
+ std::set<const ServerWindow*> roots_;
+
+ // The windows created by this tree. This tree owns these objects.
+ base::hash_map<WindowId, ServerWindow*> created_window_map_;
+
+ // The client is allowed to assign ids. These two maps providing the mapping
+ // from the ids native to the server (WindowId) to those understood by the
+ // client (ClientWindowId).
+ base::hash_map<ClientWindowId, WindowId> client_id_to_window_id_map_;
+ base::hash_map<WindowId, ClientWindowId> window_id_to_client_id_map_;
+
+ uint32_t event_ack_id_;
+
+ // A client is considered janky if it hasn't ACK'ed input events within a
+ // reasonable timeframe.
+ bool janky_ = false;
+
+ // Set when the client is using SetEventObserver() to observe events,
+ // otherwise null.
+ std::unique_ptr<EventMatcher> event_observer_matcher_;
+
+ // The ID supplied by the client for the current event observer.
+ uint32_t event_observer_id_ = 0;
+
+ // WindowManager the current event came from.
+ WindowManagerState* event_source_wms_ = nullptr;
+
+ std::queue<std::unique_ptr<TargetedEvent>> event_queue_;
+
+ std::unique_ptr<mojo::AssociatedBinding<mojom::WindowManagerClient>>
+ window_manager_internal_client_binding_;
+ mojom::WindowManager* window_manager_internal_;
+ std::unique_ptr<WindowManagerState> window_manager_state_;
+
+ std::unique_ptr<WaitingForTopLevelWindowInfo>
+ waiting_for_top_level_window_info_;
+ bool embedder_intercepts_events_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowTree);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_WINDOW_TREE_H_
diff --git a/chromium/components/mus/ws/window_tree_binding.cc b/chromium/components/mus/ws/window_tree_binding.cc
new file mode 100644
index 00000000000..6f64a9f895f
--- /dev/null
+++ b/chromium/components/mus/ws/window_tree_binding.cc
@@ -0,0 +1,62 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/window_tree_binding.h"
+
+#include "base/bind.h"
+#include "components/mus/ws/window_server.h"
+#include "components/mus/ws/window_tree.h"
+
+namespace mus {
+namespace ws {
+
+WindowTreeBinding::WindowTreeBinding(mojom::WindowTreeClient* client)
+ : client_(client) {}
+
+WindowTreeBinding::~WindowTreeBinding() {}
+
+DefaultWindowTreeBinding::DefaultWindowTreeBinding(
+ WindowTree* tree,
+ WindowServer* window_server,
+ mojom::WindowTreeRequest service_request,
+ mojom::WindowTreeClientPtr client)
+ : WindowTreeBinding(client.get()),
+ binding_(tree, std::move(service_request)),
+ client_(std::move(client)) {
+ // Both |window_server| and |tree| outlive us.
+ binding_.set_connection_error_handler(
+ base::Bind(&WindowServer::DestroyTree, base::Unretained(window_server),
+ base::Unretained(tree)));
+}
+
+DefaultWindowTreeBinding::DefaultWindowTreeBinding(
+ WindowTree* tree,
+ mojom::WindowTreeClientPtr client)
+ : WindowTreeBinding(client.get()),
+ binding_(tree),
+ client_(std::move(client)) {}
+
+DefaultWindowTreeBinding::~DefaultWindowTreeBinding() {}
+
+void DefaultWindowTreeBinding::SetIncomingMethodCallProcessingPaused(
+ bool paused) {
+ if (paused)
+ binding_.PauseIncomingMethodCallProcessing();
+ else
+ binding_.ResumeIncomingMethodCallProcessing();
+}
+
+mojom::WindowTreePtr DefaultWindowTreeBinding::CreateInterfacePtrAndBind() {
+ DCHECK(!binding_.is_bound());
+ return binding_.CreateInterfacePtrAndBind();
+}
+
+mojom::WindowManager* DefaultWindowTreeBinding::GetWindowManager() {
+ client_->GetWindowManager(
+ GetProxy(&window_manager_internal_, client_.associated_group()));
+ return window_manager_internal_.get();
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/window_tree_binding.h b/chromium/components/mus/ws/window_tree_binding.h
new file mode 100644
index 00000000000..a9472b3566c
--- /dev/null
+++ b/chromium/components/mus/ws/window_tree_binding.h
@@ -0,0 +1,72 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_WINDOW_TREE_BINDING_H_
+#define COMPONENTS_MUS_WS_WINDOW_TREE_BINDING_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace mus {
+namespace ws {
+
+class WindowServer;
+class WindowTree;
+
+// WindowTreeBinding manages the binding between a WindowTree and its
+// WindowTreeClient. WindowTreeBinding exists so that a mock implementation
+// of WindowTreeClient can be injected for tests. WindowTree owns its
+// associated WindowTreeBinding.
+class WindowTreeBinding {
+ public:
+ explicit WindowTreeBinding(mojom::WindowTreeClient* client);
+ virtual ~WindowTreeBinding();
+
+ mojom::WindowTreeClient* client() { return client_; }
+
+ // Obtains a new WindowManager. This should only be called once.
+ virtual mojom::WindowManager* GetWindowManager() = 0;
+
+ virtual void SetIncomingMethodCallProcessingPaused(bool paused) = 0;
+
+ private:
+ mojom::WindowTreeClient* client_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowTreeBinding);
+};
+
+// Bindings implementation of WindowTreeBinding.
+class DefaultWindowTreeBinding : public WindowTreeBinding {
+ public:
+ DefaultWindowTreeBinding(WindowTree* tree,
+ WindowServer* window_server,
+ mojom::WindowTreeRequest service_request,
+ mojom::WindowTreeClientPtr client);
+ DefaultWindowTreeBinding(WindowTree* tree,
+ mojom::WindowTreeClientPtr client);
+ ~DefaultWindowTreeBinding() override;
+
+ // Use when created with the constructor that does not take a
+ // WindowTreeRequest.
+ mojom::WindowTreePtr CreateInterfacePtrAndBind();
+
+ // WindowTreeBinding:
+ mojom::WindowManager* GetWindowManager() override;
+ void SetIncomingMethodCallProcessingPaused(bool paused) override;
+
+ private:
+ mojo::Binding<mojom::WindowTree> binding_;
+ mojom::WindowTreeClientPtr client_;
+ mojom::WindowManagerAssociatedPtr window_manager_internal_;
+
+ DISALLOW_COPY_AND_ASSIGN(DefaultWindowTreeBinding);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_WINDOW_TREE_BINDING_H_
diff --git a/chromium/components/mus/ws/window_tree_client_unittest.cc b/chromium/components/mus/ws/window_tree_client_unittest.cc
new file mode 100644
index 00000000000..34169dd2152
--- /dev/null
+++ b/chromium/components/mus/ws/window_tree_client_unittest.cc
@@ -0,0 +1,2042 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "components/mus/public/cpp/tests/window_server_shelltest_base.h"
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "components/mus/public/interfaces/window_tree_host.mojom.h"
+#include "components/mus/ws/ids.h"
+#include "components/mus/ws/test_change_tracker.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "services/shell/public/cpp/shell_test.h"
+
+using mojo::Array;
+using shell::Connection;
+using mojo::InterfaceRequest;
+using shell::ShellClient;
+using mojo::String;
+using mus::mojom::WindowDataPtr;
+using mus::mojom::WindowTree;
+using mus::mojom::WindowTreeClient;
+
+namespace mus {
+namespace ws {
+namespace test {
+
+namespace {
+
+// Creates an id used for transport from the specified parameters.
+Id BuildWindowId(ClientSpecificId client_id,
+ ClientSpecificId window_id) {
+ return (client_id << 16) | window_id;
+}
+
+// Callback function from WindowTree functions.
+// ----------------------------------
+
+void WindowTreeResultCallback(base::RunLoop* run_loop,
+ std::vector<TestWindow>* windows,
+ Array<WindowDataPtr> results) {
+ WindowDatasToTestWindows(results, windows);
+ run_loop->Quit();
+}
+
+void EmbedCallbackImpl(base::RunLoop* run_loop,
+ bool* result_cache,
+ bool result) {
+ *result_cache = result;
+ run_loop->Quit();
+}
+
+// -----------------------------------------------------------------------------
+
+bool EmbedUrl(shell::Connector* connector,
+ WindowTree* tree,
+ const String& url,
+ Id root_id) {
+ bool result = false;
+ base::RunLoop run_loop;
+ {
+ mojom::WindowTreeClientPtr client;
+ connector->ConnectToInterface(url.get(), &client);
+ const uint32_t embed_flags = 0;
+ tree->Embed(root_id, std::move(client), embed_flags,
+ base::Bind(&EmbedCallbackImpl, &run_loop, &result));
+ }
+ run_loop.Run();
+ return result;
+}
+
+bool Embed(WindowTree* tree, Id root_id, mojom::WindowTreeClientPtr client) {
+ bool result = false;
+ base::RunLoop run_loop;
+ {
+ const uint32_t embed_flags = 0;
+ tree->Embed(root_id, std::move(client), embed_flags,
+ base::Bind(&EmbedCallbackImpl, &run_loop, &result));
+ }
+ run_loop.Run();
+ return result;
+}
+
+void GetWindowTree(WindowTree* tree,
+ Id window_id,
+ std::vector<TestWindow>* windows) {
+ base::RunLoop run_loop;
+ tree->GetWindowTree(
+ window_id, base::Bind(&WindowTreeResultCallback, &run_loop, windows));
+ run_loop.Run();
+}
+
+// Utility functions -----------------------------------------------------------
+
+const Id kNullParentId = 0;
+std::string IdToString(Id id) {
+ return (id == kNullParentId) ? "null" : base::StringPrintf(
+ "%d,%d", HiWord(id), LoWord(id));
+}
+
+std::string WindowParentToString(Id window, Id parent) {
+ return base::StringPrintf("window=%s parent=%s", IdToString(window).c_str(),
+ IdToString(parent).c_str());
+}
+
+// -----------------------------------------------------------------------------
+
+// A WindowTreeClient implementation that logs all changes to a tracker.
+class TestWindowTreeClient : public mojom::WindowTreeClient,
+ public TestChangeTracker::Delegate,
+ public mojom::WindowManager {
+ public:
+ TestWindowTreeClient()
+ : binding_(this),
+ client_id_(0),
+ root_window_id_(0),
+ // Start with a random large number so tests can use lower ids if they
+ // want.
+ next_change_id_(10000),
+ waiting_change_id_(0),
+ on_change_completed_result_(false),
+ track_root_bounds_changes_(false) {
+ tracker_.set_delegate(this);
+ }
+
+ void Bind(mojo::InterfaceRequest<mojom::WindowTreeClient> request) {
+ binding_.Bind(std::move(request));
+ }
+
+ mojom::WindowTree* tree() { return tree_.get(); }
+ TestChangeTracker* tracker() { return &tracker_; }
+ Id root_window_id() const { return root_window_id_; }
+
+ // Sets whether changes to the bounds of the root should be tracked. Normally
+ // they are ignored (as during startup we often times get random size
+ // changes).
+ void set_track_root_bounds_changes(bool value) {
+ track_root_bounds_changes_ = value;
+ }
+
+ // Runs a nested MessageLoop until |count| changes (calls to
+ // WindowTreeClient functions) have been received.
+ void WaitForChangeCount(size_t count) {
+ if (tracker_.changes()->size() >= count)
+ return;
+
+ ASSERT_TRUE(wait_state_.get() == nullptr);
+ wait_state_.reset(new WaitState);
+ wait_state_->change_count = count;
+ wait_state_->run_loop.Run();
+ wait_state_.reset();
+ }
+
+ uint32_t GetAndAdvanceChangeId() { return next_change_id_++; }
+
+ // Runs a nested MessageLoop until OnEmbed() has been encountered.
+ void WaitForOnEmbed() {
+ if (tree_)
+ return;
+ embed_run_loop_.reset(new base::RunLoop);
+ embed_run_loop_->Run();
+ embed_run_loop_.reset();
+ }
+
+ bool WaitForChangeCompleted(uint32_t id) {
+ waiting_change_id_ = id;
+ change_completed_run_loop_.reset(new base::RunLoop);
+ change_completed_run_loop_->Run();
+ return on_change_completed_result_;
+ }
+
+ bool DeleteWindow(Id id) {
+ const uint32_t change_id = GetAndAdvanceChangeId();
+ tree()->DeleteWindow(change_id, id);
+ return WaitForChangeCompleted(change_id);
+ }
+
+ bool AddWindow(Id parent, Id child) {
+ const uint32_t change_id = GetAndAdvanceChangeId();
+ tree()->AddWindow(change_id, parent, child);
+ return WaitForChangeCompleted(change_id);
+ }
+
+ bool RemoveWindowFromParent(Id window_id) {
+ const uint32_t change_id = GetAndAdvanceChangeId();
+ tree()->RemoveWindowFromParent(change_id, window_id);
+ return WaitForChangeCompleted(change_id);
+ }
+
+ bool ReorderWindow(Id window_id,
+ Id relative_window_id,
+ mojom::OrderDirection direction) {
+ const uint32_t change_id = GetAndAdvanceChangeId();
+ tree()->ReorderWindow(change_id, window_id, relative_window_id, direction);
+ return WaitForChangeCompleted(change_id);
+ }
+
+ // Waits for all messages to be received by |ws|. This is done by attempting
+ // to create a bogus window. When we get the response we know all messages
+ // have been processed.
+ bool WaitForAllMessages() {
+ return NewWindowWithCompleteId(WindowIdToTransportId(InvalidWindowId())) ==
+ 0;
+ }
+
+ Id NewWindow(ClientSpecificId window_id) {
+ return NewWindowWithCompleteId(BuildWindowId(client_id_, window_id));
+ }
+
+ // Generally you want NewWindow(), but use this if you need to test given
+ // a complete window id (NewWindow() ors with the client id).
+ Id NewWindowWithCompleteId(Id id) {
+ mojo::Map<mojo::String, mojo::Array<uint8_t>> properties;
+ const uint32_t change_id = GetAndAdvanceChangeId();
+ tree()->NewWindow(change_id, id, std::move(properties));
+ return WaitForChangeCompleted(change_id) ? id : 0;
+ }
+
+ bool SetWindowProperty(Id window_id,
+ const std::string& name,
+ const std::vector<uint8_t>* data) {
+ Array<uint8_t> mojo_data(nullptr);
+ if (data)
+ mojo_data = Array<uint8_t>::From(*data);
+ const uint32_t change_id = GetAndAdvanceChangeId();
+ tree()->SetWindowProperty(change_id, window_id, name, std::move(mojo_data));
+ return WaitForChangeCompleted(change_id);
+ }
+
+ bool SetPredefinedCursor(Id window_id, mojom::Cursor cursor) {
+ const uint32_t change_id = GetAndAdvanceChangeId();
+ tree()->SetPredefinedCursor(change_id, window_id, cursor);
+ return WaitForChangeCompleted(change_id);
+ }
+
+ bool SetWindowVisibility(Id window_id, bool visible) {
+ const uint32_t change_id = GetAndAdvanceChangeId();
+ tree()->SetWindowVisibility(change_id, window_id, visible);
+ return WaitForChangeCompleted(change_id);
+ }
+
+ bool SetWindowOpacity(Id window_id, float opacity) {
+ const uint32_t change_id = GetAndAdvanceChangeId();
+ tree()->SetWindowOpacity(change_id, window_id, opacity);
+ return WaitForChangeCompleted(change_id);
+ }
+
+ private:
+ // Used when running a nested MessageLoop.
+ struct WaitState {
+ WaitState() : change_count(0) {}
+
+ // Number of changes waiting for.
+ size_t change_count;
+ base::RunLoop run_loop;
+ };
+
+ // TestChangeTracker::Delegate:
+ void OnChangeAdded() override {
+ if (wait_state_.get() &&
+ tracker_.changes()->size() >= wait_state_->change_count) {
+ wait_state_->run_loop.Quit();
+ }
+ }
+
+ // WindowTreeClient:
+ void OnEmbed(ClientSpecificId client_id,
+ WindowDataPtr root,
+ mojom::WindowTreePtr tree,
+ int64_t display_id,
+ Id focused_window_id,
+ bool drawn) override {
+ // TODO(sky): add coverage of |focused_window_id|.
+ ASSERT_TRUE(root);
+ root_window_id_ = root->window_id;
+ tree_ = std::move(tree);
+ client_id_ = client_id;
+ tracker()->OnEmbed(client_id, std::move(root), drawn);
+ if (embed_run_loop_)
+ embed_run_loop_->Quit();
+ }
+ void OnEmbeddedAppDisconnected(Id window_id) override {
+ tracker()->OnEmbeddedAppDisconnected(window_id);
+ }
+ void OnUnembed(Id window_id) override { tracker()->OnUnembed(window_id); }
+ void OnLostCapture(Id window_id) override {
+ tracker()->OnLostCapture(window_id);
+ }
+ void OnTopLevelCreated(uint32_t change_id,
+ mojom::WindowDataPtr data,
+ int64_t display_id,
+ bool drawn) override {
+ tracker()->OnTopLevelCreated(change_id, std::move(data), drawn);
+ }
+ void OnWindowBoundsChanged(Id window_id,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) override {
+ // The bounds of the root may change during startup on Android at random
+ // times. As this doesn't matter, and shouldn't impact test exepctations,
+ // it is ignored.
+ if (window_id == root_window_id_ && !track_root_bounds_changes_)
+ return;
+ tracker()->OnWindowBoundsChanged(window_id, old_bounds, new_bounds);
+ }
+ void OnClientAreaChanged(
+ uint32_t window_id,
+ const gfx::Insets& new_client_area,
+ mojo::Array<gfx::Rect> new_additional_client_areas) override {}
+ void OnTransientWindowAdded(uint32_t window_id,
+ uint32_t transient_window_id) override {
+ tracker()->OnTransientWindowAdded(window_id, transient_window_id);
+ }
+ void OnTransientWindowRemoved(uint32_t window_id,
+ uint32_t transient_window_id) override {
+ tracker()->OnTransientWindowRemoved(window_id, transient_window_id);
+ }
+ void OnWindowHierarchyChanged(Id window,
+ Id old_parent,
+ Id new_parent,
+ Array<WindowDataPtr> windows) override {
+ tracker()->OnWindowHierarchyChanged(window, old_parent, new_parent,
+ std::move(windows));
+ }
+ void OnWindowReordered(Id window_id,
+ Id relative_window_id,
+ mojom::OrderDirection direction) override {
+ tracker()->OnWindowReordered(window_id, relative_window_id, direction);
+ }
+ void OnWindowDeleted(Id window) override {
+ tracker()->OnWindowDeleted(window);
+ }
+ void OnWindowVisibilityChanged(uint32_t window, bool visible) override {
+ tracker()->OnWindowVisibilityChanged(window, visible);
+ }
+ void OnWindowOpacityChanged(uint32_t window,
+ float old_opacity,
+ float new_opacity) override {
+ tracker()->OnWindowOpacityChanged(window, new_opacity);
+ }
+ void OnWindowParentDrawnStateChanged(uint32_t window, bool drawn) override {
+ tracker()->OnWindowParentDrawnStateChanged(window, drawn);
+ }
+ void OnWindowInputEvent(uint32_t event_id,
+ Id window_id,
+ std::unique_ptr<ui::Event> event,
+ uint32_t event_observer_id) override {
+ // Ack input events to clear the state on the server. These can be received
+ // during test startup. X11Window::DispatchEvent sends a synthetic move
+ // event to notify of entry.
+ tree()->OnWindowInputEventAck(event_id, mojom::EventResult::HANDLED);
+ // Don't log input events as none of the tests care about them and they
+ // may come in at random points.
+ }
+ void OnEventObserved(std::unique_ptr<ui::Event>,
+ uint32_t event_observer_id) override {}
+ void OnWindowSharedPropertyChanged(uint32_t window,
+ const String& name,
+ Array<uint8_t> new_data) override {
+ tracker_.OnWindowSharedPropertyChanged(window, name, std::move(new_data));
+ }
+ // TODO(sky): add testing coverage.
+ void OnWindowFocused(uint32_t focused_window_id) override {}
+ void OnWindowPredefinedCursorChanged(uint32_t window_id,
+ mojom::Cursor cursor_id) override {
+ tracker_.OnWindowPredefinedCursorChanged(window_id, cursor_id);
+ }
+ void OnChangeCompleted(uint32_t change_id, bool success) override {
+ if (waiting_change_id_ == change_id && change_completed_run_loop_) {
+ on_change_completed_result_ = success;
+ change_completed_run_loop_->Quit();
+ }
+ }
+ void RequestClose(uint32_t window_id) override {}
+ void GetWindowManager(mojo::AssociatedInterfaceRequest<mojom::WindowManager>
+ internal) override {
+ window_manager_binding_.reset(
+ new mojo::AssociatedBinding<mojom::WindowManager>(this,
+ std::move(internal)));
+ tree_->GetWindowManagerClient(
+ GetProxy(&window_manager_client_, tree_.associated_group()));
+ }
+
+ // mojom::WindowManager:
+ void OnConnect(uint16_t client_id) override {}
+ void WmNewDisplayAdded(mojom::DisplayPtr display,
+ mojom::WindowDataPtr root_data,
+ bool drawn) override {
+ NOTIMPLEMENTED();
+ }
+ void WmSetBounds(uint32_t change_id,
+ uint32_t window_id,
+ const gfx::Rect& bounds) override {
+ window_manager_client_->WmResponse(change_id, false);
+ }
+ void WmSetProperty(uint32_t change_id,
+ uint32_t window_id,
+ const mojo::String& name,
+ mojo::Array<uint8_t> value) override {
+ window_manager_client_->WmResponse(change_id, false);
+ }
+ void WmCreateTopLevelWindow(
+ uint32_t change_id,
+ ClientSpecificId requesting_client_id,
+ mojo::Map<mojo::String, mojo::Array<uint8_t>> properties) override {
+ NOTIMPLEMENTED();
+ }
+ void WmClientJankinessChanged(ClientSpecificId client_id,
+ bool janky) override {
+ NOTIMPLEMENTED();
+ }
+ void OnAccelerator(uint32_t id, std::unique_ptr<ui::Event> event) override {
+ NOTIMPLEMENTED();
+ }
+
+ TestChangeTracker tracker_;
+
+ mojom::WindowTreePtr tree_;
+
+ // If non-null we're waiting for OnEmbed() using this RunLoop.
+ std::unique_ptr<base::RunLoop> embed_run_loop_;
+
+ // If non-null we're waiting for a certain number of change notifications to
+ // be encountered.
+ std::unique_ptr<WaitState> wait_state_;
+
+ mojo::Binding<WindowTreeClient> binding_;
+ Id client_id_;
+ Id root_window_id_;
+ uint32_t next_change_id_;
+ uint32_t waiting_change_id_;
+ bool on_change_completed_result_;
+ bool track_root_bounds_changes_;
+ std::unique_ptr<base::RunLoop> change_completed_run_loop_;
+
+ std::unique_ptr<mojo::AssociatedBinding<mojom::WindowManager>>
+ window_manager_binding_;
+ mojom::WindowManagerClientAssociatedPtr window_manager_client_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestWindowTreeClient);
+};
+
+// -----------------------------------------------------------------------------
+
+// InterfaceFactory for vending TestWindowTreeClients.
+class WindowTreeClientFactory
+ : public shell::InterfaceFactory<WindowTreeClient> {
+ public:
+ WindowTreeClientFactory() {}
+ ~WindowTreeClientFactory() override {}
+
+ // Runs a nested MessageLoop until a new instance has been created.
+ std::unique_ptr<TestWindowTreeClient> WaitForInstance() {
+ if (!client_impl_.get()) {
+ DCHECK(!run_loop_);
+ run_loop_.reset(new base::RunLoop);
+ run_loop_->Run();
+ run_loop_.reset();
+ }
+ return std::move(client_impl_);
+ }
+
+ private:
+ // InterfaceFactory<WindowTreeClient>:
+ void Create(Connection* connection,
+ InterfaceRequest<WindowTreeClient> request) override {
+ client_impl_.reset(new TestWindowTreeClient());
+ client_impl_->Bind(std::move(request));
+ if (run_loop_.get())
+ run_loop_->Quit();
+ }
+
+ std::unique_ptr<TestWindowTreeClient> client_impl_;
+ std::unique_ptr<base::RunLoop> run_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowTreeClientFactory);
+};
+
+} // namespace
+
+class WindowTreeClientTest : public WindowServerShellTestBase {
+ public:
+ WindowTreeClientTest()
+ : client_id_1_(0), client_id_2_(0), root_window_id_(0) {}
+
+ ~WindowTreeClientTest() override {}
+
+ protected:
+ // Returns the changes from the various clients.
+ std::vector<Change>* changes1() { return wt_client1_->tracker()->changes(); }
+ std::vector<Change>* changes2() { return wt_client2_->tracker()->changes(); }
+ std::vector<Change>* changes3() { return wt_client3_->tracker()->changes(); }
+
+ // Various clients. |wt1()|, being the first client, has special permissions
+ // (it's treated as the window manager).
+ WindowTree* wt1() { return wt_client1_->tree(); }
+ WindowTree* wt2() { return wt_client2_->tree(); }
+ WindowTree* wt3() { return wt_client3_->tree(); }
+
+ TestWindowTreeClient* wt_client1() { return wt_client1_.get(); }
+ TestWindowTreeClient* wt_client2() { return wt_client2_.get(); }
+ TestWindowTreeClient* wt_client3() { return wt_client3_.get(); }
+
+ Id root_window_id() const { return root_window_id_; }
+
+ int client_id_1() const { return client_id_1_; }
+ int client_id_2() const { return client_id_2_; }
+
+ void EstablishSecondClientWithRoot(Id root_id) {
+ ASSERT_TRUE(wt_client2_.get() == nullptr);
+ wt_client2_ =
+ EstablishClientViaEmbed(wt1(), root_id, &client_id_2_);
+ ASSERT_GT(client_id_2_, 0);
+ ASSERT_TRUE(wt_client2_.get() != nullptr);
+ }
+
+ void EstablishSecondClient(bool create_initial_window) {
+ Id window_1_1 = 0;
+ if (create_initial_window) {
+ window_1_1 = wt_client1()->NewWindow(1);
+ ASSERT_TRUE(window_1_1);
+ }
+ ASSERT_NO_FATAL_FAILURE(
+ EstablishSecondClientWithRoot(BuildWindowId(client_id_1(), 1)));
+
+ if (create_initial_window) {
+ EXPECT_EQ("[" + WindowParentToString(window_1_1, kNullParentId) + "]",
+ ChangeWindowDescription(*changes2()));
+ }
+ }
+
+ void EstablishThirdClient(WindowTree* owner, Id root_id) {
+ ASSERT_TRUE(wt_client3_.get() == nullptr);
+ wt_client3_ = EstablishClientViaEmbed(owner, root_id, nullptr);
+ ASSERT_TRUE(wt_client3_.get() != nullptr);
+ }
+
+ std::unique_ptr<TestWindowTreeClient> WaitForWindowTreeClient() {
+ return client_factory_->WaitForInstance();
+ }
+
+ // Establishes a new client by way of Embed() on the specified WindowTree.
+ std::unique_ptr<TestWindowTreeClient> EstablishClientViaEmbed(
+ WindowTree* owner,
+ Id root_id,
+ int* client_id) {
+ return EstablishClientViaEmbedWithPolicyBitmask(owner, root_id, client_id);
+ }
+
+ std::unique_ptr<TestWindowTreeClient>
+ EstablishClientViaEmbedWithPolicyBitmask(WindowTree* owner,
+ Id root_id,
+ int* client_id) {
+ if (!EmbedUrl(connector(), owner, test_name(), root_id)) {
+ ADD_FAILURE() << "Embed() failed";
+ return nullptr;
+ }
+ std::unique_ptr<TestWindowTreeClient> client =
+ client_factory_->WaitForInstance();
+ if (!client.get()) {
+ ADD_FAILURE() << "WaitForInstance failed";
+ return nullptr;
+ }
+ client->WaitForOnEmbed();
+
+ EXPECT_EQ("OnEmbed",
+ SingleChangeToDescription(*client->tracker()->changes()));
+ if (client_id)
+ *client_id = (*client->tracker()->changes())[0].client_id;
+ return client;
+ }
+
+ // WindowServerShellTestBase:
+ bool AcceptConnection(shell::Connection* connection) override {
+ connection->AddInterface(client_factory_.get());
+ return true;
+ }
+
+ void SetUp() override {
+ client_factory_.reset(new WindowTreeClientFactory());
+
+ WindowServerShellTestBase::SetUp();
+
+ mojom::WindowTreeHostFactoryPtr factory;
+ connector()->ConnectToInterface("mojo:mus", &factory);
+
+ mojom::WindowTreeClientPtr tree_client_ptr;
+ wt_client1_.reset(new TestWindowTreeClient());
+ wt_client1_->Bind(GetProxy(&tree_client_ptr));
+
+ factory->CreateWindowTreeHost(GetProxy(&host_),
+ std::move(tree_client_ptr));
+
+ // Next we should get an embed call on the "window manager" client.
+ wt_client1_->WaitForOnEmbed();
+
+ ASSERT_EQ(1u, changes1()->size());
+ EXPECT_EQ(CHANGE_TYPE_EMBED, (*changes1())[0].type);
+ // All these tests assume 1 for the client id. The only real assertion here
+ // is the client id is not zero, but adding this as rest of code here
+ // assumes 1.
+ ASSERT_GT((*changes1())[0].client_id, 0);
+ client_id_1_ = (*changes1())[0].client_id;
+ ASSERT_FALSE((*changes1())[0].windows.empty());
+ root_window_id_ = (*changes1())[0].windows[0].window_id;
+ ASSERT_EQ(root_window_id_, wt_client1_->root_window_id());
+ changes1()->clear();
+ }
+
+ void TearDown() override {
+ // Destroy these before the message loop is destroyed (happens in
+ // WindowServerShellTestBase::TearDown).
+ wt_client1_.reset();
+ wt_client2_.reset();
+ wt_client3_.reset();
+ client_factory_.reset();
+ WindowServerShellTestBase::TearDown();
+ }
+
+ std::unique_ptr<TestWindowTreeClient> wt_client1_;
+ std::unique_ptr<TestWindowTreeClient> wt_client2_;
+ std::unique_ptr<TestWindowTreeClient> wt_client3_;
+
+ mojom::WindowTreeHostPtr host_;
+
+ private:
+ std::unique_ptr<WindowTreeClientFactory> client_factory_;
+ int client_id_1_;
+ int client_id_2_;
+ Id root_window_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowTreeClientTest);
+};
+
+// Verifies two clients get different ids.
+TEST_F(WindowTreeClientTest, TwoClientsGetDifferentClientIds) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(true));
+
+ ASSERT_EQ(1u, changes2()->size());
+ ASSERT_NE(client_id_1(), client_id_2());
+}
+
+// Verifies when Embed() is invoked any child windows are removed.
+TEST_F(WindowTreeClientTest, WindowsRemovedWhenEmbedding) {
+ // Two windows 1 and 2. 2 is parented to 1.
+ Id window_1_1 = wt_client1()->NewWindow(1);
+ ASSERT_TRUE(window_1_1);
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
+
+ Id window_1_2 = wt_client1()->NewWindow(2);
+ ASSERT_TRUE(window_1_2);
+ ASSERT_TRUE(wt_client1()->AddWindow(window_1_1, window_1_2));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(false));
+ ASSERT_EQ(1u, changes2()->size());
+ ASSERT_EQ(1u, (*changes2())[0].windows.size());
+ EXPECT_EQ("[" + WindowParentToString(window_1_1, kNullParentId) + "]",
+ ChangeWindowDescription(*changes2()));
+
+ // Embed() removed window 2.
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt1(), window_1_2, &windows);
+ EXPECT_EQ(WindowParentToString(window_1_2, kNullParentId),
+ SingleWindowDescription(windows));
+ }
+
+ // ws2 should not see window 2.
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt2(), window_1_1, &windows);
+ EXPECT_EQ(WindowParentToString(window_1_1, kNullParentId),
+ SingleWindowDescription(windows));
+ }
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt2(), window_1_2, &windows);
+ EXPECT_TRUE(windows.empty());
+ }
+
+ // Windows 3 and 4 in client 2.
+ Id window_2_3 = wt_client2()->NewWindow(3);
+ Id window_2_4 = wt_client2()->NewWindow(4);
+ ASSERT_TRUE(window_2_3);
+ ASSERT_TRUE(window_2_4);
+ ASSERT_TRUE(wt_client2()->AddWindow(window_2_3, window_2_4));
+
+ // Client 3 rooted at 2.
+ ASSERT_NO_FATAL_FAILURE(EstablishThirdClient(wt2(), window_2_3));
+
+ // Window 4 should no longer have a parent.
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt2(), window_2_3, &windows);
+ EXPECT_EQ(WindowParentToString(window_2_3, kNullParentId),
+ SingleWindowDescription(windows));
+
+ windows.clear();
+ GetWindowTree(wt2(), window_2_4, &windows);
+ EXPECT_EQ(WindowParentToString(window_2_4, kNullParentId),
+ SingleWindowDescription(windows));
+ }
+
+ // And window 4 should not be visible to client 3.
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt3(), window_2_3, &windows);
+ EXPECT_EQ("no windows", SingleWindowDescription(windows));
+ }
+}
+
+// Verifies once Embed() has been invoked the parent client can't see any
+// children.
+TEST_F(WindowTreeClientTest, CantAccessChildrenOfEmbeddedWindow) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(true));
+
+ Id window_1_1 = BuildWindowId(client_id_1(), 1);
+ Id window_2_2 = wt_client2()->NewWindow(2);
+ ASSERT_TRUE(window_2_2);
+ ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_2));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishThirdClient(wt2(), window_2_2));
+
+ Id window_3_3 = wt_client3()->NewWindow(3);
+ ASSERT_TRUE(window_3_3);
+ ASSERT_TRUE(
+ wt_client3()->AddWindow(wt_client3()->root_window_id(), window_3_3));
+
+ // Even though 3 is a child of 2 client 2 can't see 3 as it's from a
+ // different client.
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt2(), window_2_2, &windows);
+ EXPECT_EQ(WindowParentToString(window_2_2, window_1_1),
+ SingleWindowDescription(windows));
+ }
+
+ // Client 2 shouldn't be able to get window 3 at all.
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt2(), window_3_3, &windows);
+ EXPECT_TRUE(windows.empty());
+ }
+
+ // Client 1 should be able to see it all (its the root).
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt1(), window_1_1, &windows);
+ ASSERT_EQ(3u, windows.size());
+ EXPECT_EQ(WindowParentToString(window_1_1, kNullParentId),
+ windows[0].ToString());
+ // NOTE: we expect a match of WindowParentToString(window_2_2, window_1_1),
+ // but the ids are in the id space of client2, which is not the same as
+ // the id space of wt1().
+ EXPECT_EQ("window=2,1 parent=1,1", windows[1].ToString());
+ // Same thing here, we really want to test for
+ // WindowParentToString(window_3_3, window_2_2).
+ EXPECT_EQ("window=3,1 parent=2,1", windows[2].ToString());
+ }
+}
+
+// Verifies once Embed() has been invoked the parent can't mutate the children.
+TEST_F(WindowTreeClientTest, CantModifyChildrenOfEmbeddedWindow) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(true));
+
+ Id window_1_1 = BuildWindowId(client_id_1(), 1);
+ Id window_2_1 = wt_client2()->NewWindow(1);
+ ASSERT_TRUE(window_2_1);
+ ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_1));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishThirdClient(wt2(), window_2_1));
+
+ Id window_2_2 = wt_client2()->NewWindow(2);
+ ASSERT_TRUE(window_2_2);
+ // Client 2 shouldn't be able to add anything to the window anymore.
+ ASSERT_FALSE(wt_client2()->AddWindow(window_2_1, window_2_2));
+
+ // Create window 3 in client 3 and add it to window 3.
+ Id window_3_1 = wt_client3()->NewWindow(1);
+ ASSERT_TRUE(window_3_1);
+ ASSERT_TRUE(wt_client3()->AddWindow(window_2_1, window_3_1));
+
+ // Client 2 shouldn't be able to remove window 3.
+ ASSERT_FALSE(wt_client2()->RemoveWindowFromParent(window_3_1));
+}
+
+// Verifies client gets a valid id.
+TEST_F(WindowTreeClientTest, NewWindow) {
+ Id window_1_1 = wt_client1()->NewWindow(1);
+ ASSERT_TRUE(window_1_1);
+ EXPECT_TRUE(changes1()->empty());
+
+ // Can't create a window with the same id.
+ ASSERT_EQ(0u, wt_client1()->NewWindowWithCompleteId(window_1_1));
+ EXPECT_TRUE(changes1()->empty());
+
+ // Can't create a window with a bogus client id.
+ ASSERT_EQ(0u, wt_client1()->NewWindowWithCompleteId(
+ BuildWindowId(client_id_1() + 1, 1)));
+ EXPECT_TRUE(changes1()->empty());
+}
+
+// Verifies AddWindow fails when window is already in position.
+TEST_F(WindowTreeClientTest, AddWindowWithNoChange) {
+ // Create the embed point now so that the ids line up.
+ ASSERT_TRUE(wt_client1()->NewWindow(1));
+ Id window_1_2 = wt_client1()->NewWindow(2);
+ Id window_1_3 = wt_client1()->NewWindow(3);
+ ASSERT_TRUE(window_1_2);
+ ASSERT_TRUE(window_1_3);
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(false));
+
+ // Make 3 a child of 2.
+ ASSERT_TRUE(wt_client1()->AddWindow(window_1_2, window_1_3));
+
+ // Try again, this should fail.
+ EXPECT_FALSE(wt_client1()->AddWindow(window_1_2, window_1_3));
+}
+
+// Verifies AddWindow fails when window is already in position.
+TEST_F(WindowTreeClientTest, AddAncestorFails) {
+ // Create the embed point now so that the ids line up.
+ ASSERT_TRUE(wt_client1()->NewWindow(1));
+ Id window_1_2 = wt_client1()->NewWindow(2);
+ Id window_1_3 = wt_client1()->NewWindow(3);
+ ASSERT_TRUE(window_1_2);
+ ASSERT_TRUE(window_1_3);
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(false));
+
+ // Make 3 a child of 2.
+ ASSERT_TRUE(wt_client1()->AddWindow(window_1_2, window_1_3));
+
+ // Try to make 2 a child of 3, this should fail since 2 is an ancestor of 3.
+ EXPECT_FALSE(wt_client1()->AddWindow(window_1_3, window_1_2));
+}
+
+// Verifies adding to root sends right notifications.
+TEST_F(WindowTreeClientTest, AddToRoot) {
+ // Create the embed point now so that the ids line up.
+ Id window_1_1 = wt_client1()->NewWindow(1);
+ ASSERT_TRUE(window_1_1);
+ Id window_1_21 = wt_client1()->NewWindow(21);
+ Id window_1_3 = wt_client1()->NewWindow(3);
+ ASSERT_TRUE(window_1_21);
+ ASSERT_TRUE(window_1_3);
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(false));
+ changes2()->clear();
+
+ // Make 3 a child of 21.
+ ASSERT_TRUE(wt_client1()->AddWindow(window_1_21, window_1_3));
+
+ // Make 21 a child of 1.
+ ASSERT_TRUE(wt_client1()->AddWindow(window_1_1, window_1_21));
+
+ // Client 2 should not be told anything (because the window is from a
+ // different client). Create a window to ensure we got a response from
+ // the server.
+ ASSERT_TRUE(wt_client2()->NewWindow(100));
+ EXPECT_TRUE(changes2()->empty());
+}
+
+// Verifies HierarchyChanged is correctly sent for various adds/removes.
+TEST_F(WindowTreeClientTest, WindowHierarchyChangedWindows) {
+ // Create the embed point now so that the ids line up.
+ Id window_1_1 = wt_client1()->NewWindow(1);
+ // 1,2->1,11.
+ Id window_1_2 = wt_client1()->NewWindow(2);
+ ASSERT_TRUE(window_1_2);
+ ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_2, true));
+ Id window_1_11 = wt_client1()->NewWindow(11);
+ ASSERT_TRUE(window_1_11);
+ ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_11, true));
+ ASSERT_TRUE(wt_client1()->AddWindow(window_1_2, window_1_11));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(false));
+ ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_1, true));
+
+ ASSERT_TRUE(wt_client2()->WaitForAllMessages());
+ changes2()->clear();
+
+ // 1,1->1,2->1,11
+ {
+ // Client 2 should not get anything (1,2 is from another client).
+ ASSERT_TRUE(wt_client1()->AddWindow(window_1_1, window_1_2));
+ ASSERT_TRUE(wt_client2()->WaitForAllMessages());
+ EXPECT_TRUE(changes2()->empty());
+ }
+
+ // 0,1->1,1->1,2->1,11.
+ {
+ // Client 2 is now connected to the root, so it should have gotten a drawn
+ // notification.
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
+ wt_client2_->WaitForChangeCount(1u);
+ EXPECT_EQ(
+ "DrawnStateChanged window=" + IdToString(window_1_1) + " drawn=true",
+ SingleChangeToDescription(*changes2()));
+ }
+
+ // 1,1->1,2->1,11.
+ {
+ // Client 2 is no longer connected to the root, should get drawn state
+ // changed.
+ changes2()->clear();
+ ASSERT_TRUE(wt_client1()->RemoveWindowFromParent(window_1_1));
+ wt_client2_->WaitForChangeCount(1);
+ EXPECT_EQ(
+ "DrawnStateChanged window=" + IdToString(window_1_1) + " drawn=false",
+ SingleChangeToDescription(*changes2()));
+ }
+
+ // 1,1->1,2->1,11->1,111.
+ Id window_1_111 = wt_client1()->NewWindow(111);
+ ASSERT_TRUE(window_1_111);
+ ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_111, true));
+ {
+ changes2()->clear();
+ ASSERT_TRUE(wt_client1()->AddWindow(window_1_11, window_1_111));
+ ASSERT_TRUE(wt_client2()->WaitForAllMessages());
+ EXPECT_TRUE(changes2()->empty());
+ }
+
+ // 0,1->1,1->1,2->1,11->1,111
+ {
+ changes2()->clear();
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
+ wt_client2_->WaitForChangeCount(1);
+ EXPECT_EQ(
+ "DrawnStateChanged window=" + IdToString(window_1_1) + " drawn=true",
+ SingleChangeToDescription(*changes2()));
+ }
+}
+
+TEST_F(WindowTreeClientTest, WindowHierarchyChangedAddingKnownToUnknown) {
+ // Create the following structure: root -> 1 -> 11 and 2->21 (2 has no
+ // parent).
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(true));
+ Id window_1_1 = BuildWindowId(client_id_1(), 1);
+
+ Id window_2_11 = wt_client2()->NewWindow(11);
+ Id window_2_2 = wt_client2()->NewWindow(2);
+ Id window_2_21 = wt_client2()->NewWindow(21);
+ ASSERT_TRUE(window_2_11);
+ ASSERT_TRUE(window_2_2);
+ ASSERT_TRUE(window_2_21);
+
+ // Set up the hierarchy.
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
+ ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_11));
+ ASSERT_TRUE(wt_client2()->AddWindow(window_2_2, window_2_21));
+
+ // Remove 11, should result in a hierarchy change for the root.
+ {
+ changes1()->clear();
+ ASSERT_TRUE(wt_client2()->RemoveWindowFromParent(window_2_11));
+
+ wt_client1_->WaitForChangeCount(1);
+ // 2,1 should be IdToString(window_2_11), but window_2_11 is in the id
+ // space of client2, not client1.
+ EXPECT_EQ("HierarchyChanged window=2,1 old_parent=" +
+ IdToString(window_1_1) + " new_parent=null",
+ SingleChangeToDescription(*changes1()));
+ }
+
+ // Add 2 to 1.
+ {
+ changes1()->clear();
+ ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_2));
+ wt_client1_->WaitForChangeCount(1);
+ EXPECT_EQ("HierarchyChanged window=" + IdToString(window_2_2) +
+ " old_parent=null new_parent=" + IdToString(window_1_1),
+ SingleChangeToDescription(*changes1()));
+ // "window=2,3 parent=2,2]" should be,
+ // WindowParentToString(window_2_21, window_2_2), but isn't because of
+ // differing id spaces.
+ EXPECT_EQ("[" + WindowParentToString(window_2_2, window_1_1) +
+ "],[window=2,3 parent=2,2]",
+ ChangeWindowDescription(*changes1()));
+ }
+}
+
+TEST_F(WindowTreeClientTest, ReorderWindow) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(true));
+
+ Id window_2_1 = wt_client2()->NewWindow(1);
+ Id window_2_2 = wt_client2()->NewWindow(2);
+ Id window_2_3 = wt_client2()->NewWindow(3);
+ Id window_1_4 = wt_client1()->NewWindow(4); // Peer to 1,1
+ Id window_1_5 = wt_client1()->NewWindow(5); // Peer to 1,1
+ Id window_2_6 = wt_client2()->NewWindow(6); // Child of 1,2.
+ Id window_2_7 = wt_client2()->NewWindow(7); // Unparented.
+ Id window_2_8 = wt_client2()->NewWindow(8); // Unparented.
+ ASSERT_TRUE(window_2_1);
+ ASSERT_TRUE(window_2_2);
+ ASSERT_TRUE(window_2_3);
+ ASSERT_TRUE(window_1_4);
+ ASSERT_TRUE(window_1_5);
+ ASSERT_TRUE(window_2_6);
+ ASSERT_TRUE(window_2_7);
+ ASSERT_TRUE(window_2_8);
+
+ ASSERT_TRUE(wt_client2()->AddWindow(window_2_1, window_2_2));
+ ASSERT_TRUE(wt_client2()->AddWindow(window_2_2, window_2_6));
+ ASSERT_TRUE(wt_client2()->AddWindow(window_2_1, window_2_3));
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_4));
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_5));
+ ASSERT_TRUE(
+ wt_client2()->AddWindow(BuildWindowId(client_id_1(), 1), window_2_1));
+
+ {
+ changes1()->clear();
+ ASSERT_TRUE(wt_client2()->ReorderWindow(window_2_2, window_2_3,
+ mojom::OrderDirection::ABOVE));
+
+ wt_client1_->WaitForChangeCount(1);
+ EXPECT_EQ("Reordered window=" + IdToString(window_2_2) + " relative=" +
+ IdToString(window_2_3) + " direction=above",
+ SingleChangeToDescription(*changes1()));
+ }
+
+ {
+ changes1()->clear();
+ ASSERT_TRUE(wt_client2()->ReorderWindow(window_2_2, window_2_3,
+ mojom::OrderDirection::BELOW));
+
+ wt_client1_->WaitForChangeCount(1);
+ EXPECT_EQ("Reordered window=" + IdToString(window_2_2) + " relative=" +
+ IdToString(window_2_3) + " direction=below",
+ SingleChangeToDescription(*changes1()));
+ }
+
+ // view2 is already below view3.
+ EXPECT_FALSE(wt_client2()->ReorderWindow(window_2_2, window_2_3,
+ mojom::OrderDirection::BELOW));
+
+ // view4 & 5 are unknown to client 2.
+ EXPECT_FALSE(wt_client2()->ReorderWindow(window_1_4, window_1_5,
+ mojom::OrderDirection::ABOVE));
+
+ // view6 & view3 have different parents.
+ EXPECT_FALSE(wt_client1()->ReorderWindow(window_2_3, window_2_6,
+ mojom::OrderDirection::ABOVE));
+
+ // Non-existent window-ids
+ EXPECT_FALSE(wt_client1()->ReorderWindow(BuildWindowId(client_id_1(), 27),
+ BuildWindowId(client_id_1(), 28),
+ mojom::OrderDirection::ABOVE));
+
+ // view7 & view8 are un-parented.
+ EXPECT_FALSE(wt_client1()->ReorderWindow(window_2_7, window_2_8,
+ mojom::OrderDirection::ABOVE));
+}
+
+// Verifies DeleteWindow works.
+TEST_F(WindowTreeClientTest, DeleteWindow) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(true));
+ Id window_1_1 = BuildWindowId(client_id_1(), 1);
+ Id window_2_1 = wt_client2()->NewWindow(1);
+ ASSERT_TRUE(window_2_1);
+
+ // Make 2 a child of 1.
+ {
+ changes1()->clear();
+ ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_1));
+ wt_client1_->WaitForChangeCount(1);
+ EXPECT_EQ("HierarchyChanged window=" + IdToString(window_2_1) +
+ " old_parent=null new_parent=" + IdToString(window_1_1),
+ SingleChangeToDescription(*changes1()));
+ }
+
+ // Delete 2.
+ {
+ changes1()->clear();
+ changes2()->clear();
+ ASSERT_TRUE(wt_client2()->DeleteWindow(window_2_1));
+ EXPECT_TRUE(changes2()->empty());
+
+ wt_client1_->WaitForChangeCount(1);
+ EXPECT_EQ("WindowDeleted window=" + IdToString(window_2_1),
+ SingleChangeToDescription(*changes1()));
+ }
+}
+
+// Verifies DeleteWindow isn't allowed from a separate client.
+TEST_F(WindowTreeClientTest, DeleteWindowFromAnotherClientDisallowed) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(true));
+ EXPECT_FALSE(wt_client2()->DeleteWindow(BuildWindowId(client_id_1(), 1)));
+}
+
+// Verifies if a window was deleted and then reused that other clients are
+// properly notified.
+TEST_F(WindowTreeClientTest, ReuseDeletedWindowId) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(true));
+ Id window_1_1 = BuildWindowId(client_id_1(), 1);
+ Id window_2_1 = wt_client2()->NewWindow(1);
+ ASSERT_TRUE(window_2_1);
+
+ // Add 2 to 1.
+ {
+ changes1()->clear();
+ ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_1));
+ wt_client1_->WaitForChangeCount(1);
+ EXPECT_EQ("HierarchyChanged window=" + IdToString(window_2_1) +
+ " old_parent=null new_parent=" + IdToString(window_1_1),
+ SingleChangeToDescription(*changes1()));
+ EXPECT_EQ("[" + WindowParentToString(window_2_1, window_1_1) + "]",
+ ChangeWindowDescription(*changes1()));
+ }
+
+ // Delete 2.
+ {
+ changes1()->clear();
+ ASSERT_TRUE(wt_client2()->DeleteWindow(window_2_1));
+
+ wt_client1_->WaitForChangeCount(1);
+ EXPECT_EQ("WindowDeleted window=" + IdToString(window_2_1),
+ SingleChangeToDescription(*changes1()));
+ }
+
+ // Create 2 again, and add it back to 1. Should get the same notification.
+ window_2_1 = wt_client2()->NewWindow(2);
+ ASSERT_TRUE(window_2_1);
+ {
+ changes1()->clear();
+ ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_1));
+
+ wt_client1_->WaitForChangeCount(1);
+ EXPECT_EQ("HierarchyChanged window=" + IdToString(window_2_1) +
+ " old_parent=null new_parent=" + IdToString(window_1_1),
+ SingleChangeToDescription(*changes1()));
+ EXPECT_EQ("[" + WindowParentToString(window_2_1, window_1_1) + "]",
+ ChangeWindowDescription(*changes1()));
+ }
+}
+
+// Assertions for GetWindowTree.
+TEST_F(WindowTreeClientTest, GetWindowTree) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(true));
+ Id window_1_1 = BuildWindowId(client_id_1(), 1);
+
+ // Create 11 in first client and make it a child of 1.
+ Id window_1_11 = wt_client1()->NewWindow(11);
+ ASSERT_TRUE(window_1_11);
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
+ ASSERT_TRUE(wt_client1()->AddWindow(window_1_1, window_1_11));
+
+ // Create two windows in second client, 2 and 3, both children of 1.
+ Id window_2_1 = wt_client2()->NewWindow(1);
+ Id window_2_2 = wt_client2()->NewWindow(2);
+ ASSERT_TRUE(window_2_1);
+ ASSERT_TRUE(window_2_2);
+ ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_1));
+ ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_2));
+
+ // Verifies GetWindowTree() on the root. The root client sees all.
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt1(), root_window_id(), &windows);
+ ASSERT_EQ(5u, windows.size());
+ EXPECT_EQ(WindowParentToString(root_window_id(), kNullParentId),
+ windows[0].ToString());
+ EXPECT_EQ(WindowParentToString(window_1_1, root_window_id()),
+ windows[1].ToString());
+ EXPECT_EQ(WindowParentToString(window_1_11, window_1_1),
+ windows[2].ToString());
+ EXPECT_EQ(WindowParentToString(window_2_1, window_1_1),
+ windows[3].ToString());
+ EXPECT_EQ(WindowParentToString(window_2_2, window_1_1),
+ windows[4].ToString());
+ }
+
+ // Verifies GetWindowTree() on the window 1,1 from wt2(). wt2() sees 1,1 as
+ // 1,1
+ // is wt2()'s root and wt2() sees all the windows it created.
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt2(), window_1_1, &windows);
+ ASSERT_EQ(3u, windows.size());
+ EXPECT_EQ(WindowParentToString(window_1_1, kNullParentId),
+ windows[0].ToString());
+ EXPECT_EQ(WindowParentToString(window_2_1, window_1_1),
+ windows[1].ToString());
+ EXPECT_EQ(WindowParentToString(window_2_2, window_1_1),
+ windows[2].ToString());
+ }
+
+ // Client 2 shouldn't be able to get the root tree.
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt2(), root_window_id(), &windows);
+ ASSERT_EQ(0u, windows.size());
+ }
+}
+
+TEST_F(WindowTreeClientTest, SetWindowBounds) {
+ Id window_1_1 = wt_client1()->NewWindow(1);
+ ASSERT_TRUE(window_1_1);
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(false));
+
+ changes2()->clear();
+
+ wt_client2_->set_track_root_bounds_changes(true);
+
+ wt1()->SetWindowBounds(10, window_1_1, gfx::Rect(0, 0, 100, 100));
+ ASSERT_TRUE(wt_client1()->WaitForChangeCompleted(10));
+
+ wt_client2_->WaitForChangeCount(1);
+ EXPECT_EQ("BoundsChanged window=" + IdToString(window_1_1) +
+ " old_bounds=0,0 0x0 new_bounds=0,0 100x100",
+ SingleChangeToDescription(*changes2()));
+
+ // Should not be possible to change the bounds of a window created by another
+ // client.
+ wt2()->SetWindowBounds(11, window_1_1, gfx::Rect(0, 0, 0, 0));
+ ASSERT_FALSE(wt_client2()->WaitForChangeCompleted(11));
+}
+
+// Verify AddWindow fails when trying to manipulate windows in other roots.
+TEST_F(WindowTreeClientTest, CantMoveWindowsFromOtherRoot) {
+ // Create 1 and 2 in the first client.
+ Id window_1_1 = wt_client1()->NewWindow(1);
+ Id window_1_2 = wt_client1()->NewWindow(2);
+ ASSERT_TRUE(window_1_1);
+ ASSERT_TRUE(window_1_2);
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(false));
+
+ // Try to move 2 to be a child of 1 from client 2. This should fail as 2
+ // should not be able to access 1.
+ ASSERT_FALSE(wt_client2()->AddWindow(window_1_1, window_1_2));
+
+ // Try to reparent 1 to the root. A client is not allowed to reparent its
+ // roots.
+ ASSERT_FALSE(wt_client2()->AddWindow(root_window_id(), window_1_1));
+}
+
+// Verify RemoveWindowFromParent fails for windows that are descendants of the
+// roots.
+TEST_F(WindowTreeClientTest, CantRemoveWindowsInOtherRoots) {
+ // Create 1 and 2 in the first client and parent both to the root.
+ Id window_1_1 = wt_client1()->NewWindow(1);
+ Id window_1_2 = wt_client1()->NewWindow(2);
+ ASSERT_TRUE(window_1_1);
+ ASSERT_TRUE(window_1_2);
+
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_2));
+
+ // Establish the second client and give it the root 1.
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(false));
+
+ // Client 2 should not be able to remove window 2 or 1 from its parent.
+ ASSERT_FALSE(wt_client2()->RemoveWindowFromParent(window_1_2));
+ ASSERT_FALSE(wt_client2()->RemoveWindowFromParent(window_1_1));
+
+ // Create windows 10 and 11 in 2.
+ Id window_2_10 = wt_client2()->NewWindow(10);
+ Id window_2_11 = wt_client2()->NewWindow(11);
+ ASSERT_TRUE(window_2_10);
+ ASSERT_TRUE(window_2_11);
+
+ // Parent 11 to 10.
+ ASSERT_TRUE(wt_client2()->AddWindow(window_2_10, window_2_11));
+ // Remove 11 from 10.
+ ASSERT_TRUE(wt_client2()->RemoveWindowFromParent(window_2_11));
+
+ // Verify nothing was actually removed.
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt1(), root_window_id(), &windows);
+ ASSERT_EQ(3u, windows.size());
+ EXPECT_EQ(WindowParentToString(root_window_id(), kNullParentId),
+ windows[0].ToString());
+ EXPECT_EQ(WindowParentToString(window_1_1, root_window_id()),
+ windows[1].ToString());
+ EXPECT_EQ(WindowParentToString(window_1_2, root_window_id()),
+ windows[2].ToString());
+ }
+}
+
+// Verify GetWindowTree fails for windows that are not descendants of the roots.
+TEST_F(WindowTreeClientTest, CantGetWindowTreeOfOtherRoots) {
+ // Create 1 and 2 in the first client and parent both to the root.
+ Id window_1_1 = wt_client1()->NewWindow(1);
+ Id window_1_2 = wt_client1()->NewWindow(2);
+ ASSERT_TRUE(window_1_1);
+ ASSERT_TRUE(window_1_2);
+
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_2));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(false));
+
+ std::vector<TestWindow> windows;
+
+ // Should get nothing for the root.
+ GetWindowTree(wt2(), root_window_id(), &windows);
+ ASSERT_TRUE(windows.empty());
+
+ // Should get nothing for window 2.
+ GetWindowTree(wt2(), window_1_2, &windows);
+ ASSERT_TRUE(windows.empty());
+
+ // Should get window 1 if asked for.
+ GetWindowTree(wt2(), window_1_1, &windows);
+ ASSERT_EQ(1u, windows.size());
+ EXPECT_EQ(WindowParentToString(window_1_1, kNullParentId),
+ windows[0].ToString());
+}
+
+TEST_F(WindowTreeClientTest, EmbedWithSameWindowId) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(true));
+ changes2()->clear();
+
+ Id window_1_1 = BuildWindowId(client_id_1(), 1);
+ ASSERT_NO_FATAL_FAILURE(EstablishThirdClient(wt1(), window_1_1));
+
+ // Client 2 should have been told of the unembed and delete.
+ {
+ wt_client2_->WaitForChangeCount(2);
+ EXPECT_EQ("OnUnembed window=" + IdToString(window_1_1),
+ ChangesToDescription1(*changes2())[0]);
+ EXPECT_EQ("WindowDeleted window=" + IdToString(window_1_1),
+ ChangesToDescription1(*changes2())[1]);
+ }
+
+ // Client 2 has no root. Verify it can't see window 1,1 anymore.
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt2(), window_1_1, &windows);
+ EXPECT_TRUE(windows.empty());
+ }
+}
+
+TEST_F(WindowTreeClientTest, EmbedWithSameWindowId2) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(true));
+ Id window_1_1 = BuildWindowId(client_id_1(), 1);
+ changes2()->clear();
+
+ ASSERT_NO_FATAL_FAILURE(EstablishThirdClient(wt1(), window_1_1));
+
+ // Client 2 should have been told about the unembed and delete.
+ wt_client2_->WaitForChangeCount(2);
+ changes2()->clear();
+
+ // Create a window in the third client and parent it to the root.
+ Id window_3_1 = wt_client3()->NewWindow(1);
+ ASSERT_TRUE(window_3_1);
+ ASSERT_TRUE(wt_client3()->AddWindow(window_1_1, window_3_1));
+
+ // Client 1 should have been told about the add (it owns the window).
+ {
+ wt_client1_->WaitForChangeCount(1);
+ EXPECT_EQ("HierarchyChanged window=" + IdToString(window_3_1) +
+ " old_parent=null new_parent=" + IdToString(window_1_1),
+ SingleChangeToDescription(*changes1()));
+ }
+
+ // Embed 1,1 again.
+ {
+ changes3()->clear();
+
+ // We should get a new client for the new embedding.
+ std::unique_ptr<TestWindowTreeClient> client4(
+ EstablishClientViaEmbed(wt1(), window_1_1, nullptr));
+ ASSERT_TRUE(client4.get());
+ EXPECT_EQ("[" + WindowParentToString(window_1_1, kNullParentId) + "]",
+ ChangeWindowDescription(*client4->tracker()->changes()));
+
+ // And 3 should get an unembed and delete.
+ wt_client3_->WaitForChangeCount(2);
+ EXPECT_EQ("OnUnembed window=" + IdToString(window_1_1),
+ ChangesToDescription1(*changes3())[0]);
+ EXPECT_EQ("WindowDeleted window=" + IdToString(window_1_1),
+ ChangesToDescription1(*changes3())[1]);
+ }
+
+ // wt3() has no root. Verify it can't see window 1,1 anymore.
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt3(), window_1_1, &windows);
+ EXPECT_TRUE(windows.empty());
+ }
+
+ // Verify 3,1 is no longer parented to 1,1. We have to do this from 1,1 as
+ // wt3() can no longer see 1,1.
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt1(), window_1_1, &windows);
+ ASSERT_EQ(1u, windows.size());
+ EXPECT_EQ(WindowParentToString(window_1_1, kNullParentId),
+ windows[0].ToString());
+ }
+
+ // Verify wt3() can still see the window it created 3,1.
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt3(), window_3_1, &windows);
+ ASSERT_EQ(1u, windows.size());
+ EXPECT_EQ(WindowParentToString(window_3_1, kNullParentId),
+ windows[0].ToString());
+ }
+}
+
+// Assertions for SetWindowVisibility.
+TEST_F(WindowTreeClientTest, SetWindowVisibility) {
+ // Create 1 and 2 in the first client and parent both to the root.
+ Id window_1_1 = wt_client1()->NewWindow(1);
+ Id window_1_2 = wt_client1()->NewWindow(2);
+ ASSERT_TRUE(window_1_1);
+ ASSERT_TRUE(window_1_2);
+
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt1(), root_window_id(), &windows);
+ ASSERT_EQ(2u, windows.size());
+ EXPECT_EQ(
+ WindowParentToString(root_window_id(), kNullParentId) + " visible=true",
+ windows[0].ToString2());
+ EXPECT_EQ(
+ WindowParentToString(window_1_1, root_window_id()) + " visible=false",
+ windows[1].ToString2());
+ }
+
+ // Show all the windows.
+ ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_1, true));
+ ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_2, true));
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt1(), root_window_id(), &windows);
+ ASSERT_EQ(2u, windows.size());
+ EXPECT_EQ(
+ WindowParentToString(root_window_id(), kNullParentId) + " visible=true",
+ windows[0].ToString2());
+ EXPECT_EQ(
+ WindowParentToString(window_1_1, root_window_id()) + " visible=true",
+ windows[1].ToString2());
+ }
+
+ // Hide 1.
+ ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_1, false));
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt1(), window_1_1, &windows);
+ ASSERT_EQ(1u, windows.size());
+ EXPECT_EQ(
+ WindowParentToString(window_1_1, root_window_id()) + " visible=false",
+ windows[0].ToString2());
+ }
+
+ // Attach 2 to 1.
+ ASSERT_TRUE(wt_client1()->AddWindow(window_1_1, window_1_2));
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt1(), window_1_1, &windows);
+ ASSERT_EQ(2u, windows.size());
+ EXPECT_EQ(
+ WindowParentToString(window_1_1, root_window_id()) + " visible=false",
+ windows[0].ToString2());
+ EXPECT_EQ(WindowParentToString(window_1_2, window_1_1) + " visible=true",
+ windows[1].ToString2());
+ }
+
+ // Show 1.
+ ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_1, true));
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt1(), window_1_1, &windows);
+ ASSERT_EQ(2u, windows.size());
+ EXPECT_EQ(
+ WindowParentToString(window_1_1, root_window_id()) + " visible=true",
+ windows[0].ToString2());
+ EXPECT_EQ(WindowParentToString(window_1_2, window_1_1) + " visible=true",
+ windows[1].ToString2());
+ }
+}
+
+// Test that we hear the cursor change in other clients.
+TEST_F(WindowTreeClientTest, SetCursor) {
+ // Get a second client to listen in.
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(true));
+ Id window_1_1 = BuildWindowId(client_id_1(), 1);
+ changes2()->clear();
+
+ ASSERT_TRUE(
+ wt_client1()->SetPredefinedCursor(window_1_1, mojom::Cursor::IBEAM));
+ wt_client2_->WaitForChangeCount(1u);
+
+ EXPECT_EQ("CursorChanged id=" + IdToString(window_1_1) + " cursor_id=4",
+ SingleChangeToDescription(*changes2()));
+}
+
+// Assertions for SetWindowVisibility sending notifications.
+TEST_F(WindowTreeClientTest, SetWindowVisibilityNotifications) {
+ // Create 1,1 and 1,2. 1,2 is made a child of 1,1 and 1,1 a child of the root.
+ Id window_1_1 = wt_client1()->NewWindow(1);
+ ASSERT_TRUE(window_1_1);
+ ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_1, true));
+ // Setting to the same value should return true.
+ EXPECT_TRUE(wt_client1()->SetWindowVisibility(window_1_1, true));
+
+ Id window_1_2 = wt_client1()->NewWindow(2);
+ ASSERT_TRUE(window_1_2);
+ ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_2, true));
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
+ ASSERT_TRUE(wt_client1()->AddWindow(window_1_1, window_1_2));
+
+ // Establish the second client at 1,2.
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClientWithRoot(window_1_2));
+
+ // Add 2,3 as a child of 1,2.
+ Id window_2_1 = wt_client2()->NewWindow(1);
+ ASSERT_TRUE(window_2_1);
+ ASSERT_TRUE(wt_client2()->SetWindowVisibility(window_2_1, true));
+ ASSERT_TRUE(wt_client2()->AddWindow(window_1_2, window_2_1));
+ ASSERT_TRUE(wt_client1()->WaitForAllMessages());
+
+ changes2()->clear();
+ // Hide 1,2 from client 1. Client 2 should see this.
+ ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_2, false));
+ {
+ wt_client2_->WaitForChangeCount(1);
+ EXPECT_EQ(
+ "VisibilityChanged window=" + IdToString(window_1_2) + " visible=false",
+ SingleChangeToDescription(*changes2()));
+ }
+
+ changes1()->clear();
+ // Show 1,2 from client 2, client 1 should be notified.
+ ASSERT_TRUE(wt_client2()->SetWindowVisibility(window_1_2, true));
+ {
+ wt_client1_->WaitForChangeCount(1);
+ EXPECT_EQ(
+ "VisibilityChanged window=" + IdToString(window_1_2) + " visible=true",
+ SingleChangeToDescription(*changes1()));
+ }
+
+ changes2()->clear();
+ // Hide 1,1, client 2 should be told the draw state changed.
+ ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_1, false));
+ {
+ wt_client2_->WaitForChangeCount(1);
+ EXPECT_EQ(
+ "DrawnStateChanged window=" + IdToString(window_1_2) + " drawn=false",
+ SingleChangeToDescription(*changes2()));
+ }
+
+ changes2()->clear();
+ // Show 1,1 from client 1. Client 2 should see this.
+ ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_1, true));
+ {
+ wt_client2_->WaitForChangeCount(1);
+ EXPECT_EQ(
+ "DrawnStateChanged window=" + IdToString(window_1_2) + " drawn=true",
+ SingleChangeToDescription(*changes2()));
+ }
+
+ // Change visibility of 2,3, client 1 should see this.
+ changes1()->clear();
+ ASSERT_TRUE(wt_client2()->SetWindowVisibility(window_2_1, false));
+ {
+ wt_client1_->WaitForChangeCount(1);
+ EXPECT_EQ(
+ "VisibilityChanged window=" + IdToString(window_2_1) + " visible=false",
+ SingleChangeToDescription(*changes1()));
+ }
+
+ changes2()->clear();
+ // Remove 1,1 from the root, client 2 should see drawn state changed.
+ ASSERT_TRUE(wt_client1()->RemoveWindowFromParent(window_1_1));
+ {
+ wt_client2_->WaitForChangeCount(1);
+ EXPECT_EQ(
+ "DrawnStateChanged window=" + IdToString(window_1_2) + " drawn=false",
+ SingleChangeToDescription(*changes2()));
+ }
+
+ changes2()->clear();
+ // Add 1,1 back to the root, client 2 should see drawn state changed.
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
+ {
+ wt_client2_->WaitForChangeCount(1);
+ EXPECT_EQ(
+ "DrawnStateChanged window=" + IdToString(window_1_2) + " drawn=true",
+ SingleChangeToDescription(*changes2()));
+ }
+}
+
+// Assertions for SetWindowVisibility sending notifications.
+TEST_F(WindowTreeClientTest, SetWindowVisibilityNotifications2) {
+ // Create 1,1 and 1,2. 1,2 is made a child of 1,1 and 1,1 a child of the root.
+ Id window_1_1 = wt_client1()->NewWindow(1);
+ ASSERT_TRUE(window_1_1);
+ ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_1, true));
+ Id window_1_2 = wt_client1()->NewWindow(2);
+ ASSERT_TRUE(window_1_2);
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
+ ASSERT_TRUE(wt_client1()->AddWindow(window_1_1, window_1_2));
+
+ // Establish the second client at 1,2.
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClientWithRoot(window_1_2));
+ EXPECT_EQ("OnEmbed drawn=true", SingleChangeToDescription2(*changes2()));
+ changes2()->clear();
+
+ // Show 1,2 from client 1. Client 2 should see this.
+ ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_2, true));
+ {
+ wt_client2_->WaitForChangeCount(1);
+ EXPECT_EQ(
+ "VisibilityChanged window=" + IdToString(window_1_2) + " visible=true",
+ SingleChangeToDescription(*changes2()));
+ }
+}
+
+// Assertions for SetWindowVisibility sending notifications.
+TEST_F(WindowTreeClientTest, SetWindowVisibilityNotifications3) {
+ // Create 1,1 and 1,2. 1,2 is made a child of 1,1 and 1,1 a child of the root.
+ Id window_1_1 = wt_client1()->NewWindow(1);
+ ASSERT_TRUE(window_1_1);
+ Id window_1_2 = wt_client1()->NewWindow(2);
+ ASSERT_TRUE(window_1_2);
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
+ ASSERT_TRUE(wt_client1()->AddWindow(window_1_1, window_1_2));
+
+ // Establish the second client at 1,2.
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClientWithRoot(window_1_2));
+ EXPECT_EQ("OnEmbed drawn=false", SingleChangeToDescription2(*changes2()));
+ changes2()->clear();
+
+ // Show 1,1, drawn should be true for 1,2 (as that is all the child sees).
+ ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_1, true));
+ {
+ wt_client2_->WaitForChangeCount(1);
+ EXPECT_EQ(
+ "DrawnStateChanged window=" + IdToString(window_1_2) + " drawn=true",
+ SingleChangeToDescription(*changes2()));
+ }
+ changes2()->clear();
+
+ // Show 1,2, visible should be true.
+ ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_2, true));
+ {
+ wt_client2_->WaitForChangeCount(1);
+ EXPECT_EQ(
+ "VisibilityChanged window=" + IdToString(window_1_2) + " visible=true",
+ SingleChangeToDescription(*changes2()));
+ }
+}
+
+// Tests that when opacity is set on a window, that the calling client is not
+// notified, however children are. Also that setting the same opacity is
+// rejected and no on eis notifiyed.
+TEST_F(WindowTreeClientTest, SetOpacityNotifications) {
+ Id window_1_1 = wt_client1()->NewWindow(1);
+ ASSERT_TRUE(window_1_1);
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClientWithRoot(window_1_1));
+ Id window_2_1 = wt_client2()->NewWindow(1);
+ ASSERT_TRUE(window_2_1);
+ ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_1));
+ ASSERT_TRUE(wt_client1()->WaitForAllMessages());
+
+ changes1()->clear();
+ changes2()->clear();
+ // Change opacity, no notification for calling client.
+ ASSERT_TRUE(wt_client1()->SetWindowOpacity(window_1_1, 0.5f));
+ EXPECT_TRUE(changes1()->empty());
+ wt_client2()->WaitForChangeCount(1);
+ EXPECT_EQ(
+ "OpacityChanged window_id=" + IdToString(window_1_1) + " opacity=0.50",
+ SingleChangeToDescription(*changes2()));
+
+ changes2()->clear();
+ // Attempting to set the same opacity should succeed, but no notification as
+ // there was no actual change.
+ ASSERT_TRUE(wt_client1()->SetWindowOpacity(window_1_1, 0.5f));
+ EXPECT_TRUE(changes1()->empty());
+ wt_client2()->WaitForAllMessages();
+ EXPECT_TRUE(changes2()->empty());
+}
+
+TEST_F(WindowTreeClientTest, SetWindowProperty) {
+ Id window_1_1 = wt_client1()->NewWindow(1);
+ ASSERT_TRUE(window_1_1);
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(false));
+ changes2()->clear();
+
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt1(), root_window_id(), &windows);
+ ASSERT_EQ(2u, windows.size());
+ EXPECT_EQ(root_window_id(), windows[0].window_id);
+ EXPECT_EQ(window_1_1, windows[1].window_id);
+ ASSERT_EQ(0u, windows[1].properties.size());
+ }
+
+ // Set properties on 1.
+ changes2()->clear();
+ std::vector<uint8_t> one(1, '1');
+ ASSERT_TRUE(wt_client1()->SetWindowProperty(window_1_1, "one", &one));
+ {
+ wt_client2_->WaitForChangeCount(1);
+ EXPECT_EQ(
+ "PropertyChanged window=" + IdToString(window_1_1) + " key=one value=1",
+ SingleChangeToDescription(*changes2()));
+ }
+
+ // Test that our properties exist in the window tree
+ {
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt1(), window_1_1, &windows);
+ ASSERT_EQ(1u, windows.size());
+ ASSERT_EQ(1u, windows[0].properties.size());
+ EXPECT_EQ(one, windows[0].properties["one"]);
+ }
+
+ changes2()->clear();
+ // Set back to null.
+ ASSERT_TRUE(wt_client1()->SetWindowProperty(window_1_1, "one", NULL));
+ {
+ wt_client2_->WaitForChangeCount(1);
+ EXPECT_EQ("PropertyChanged window=" + IdToString(window_1_1) +
+ " key=one value=NULL",
+ SingleChangeToDescription(*changes2()));
+ }
+}
+
+TEST_F(WindowTreeClientTest, OnEmbeddedAppDisconnected) {
+ // Create client 2 and 3.
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(true));
+ Id window_1_1 = BuildWindowId(client_id_1(), 1);
+ Id window_2_1 = wt_client2()->NewWindow(1);
+ ASSERT_TRUE(window_2_1);
+ ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_1));
+ changes2()->clear();
+ ASSERT_NO_FATAL_FAILURE(EstablishThirdClient(wt2(), window_2_1));
+
+ // Client 1 should get a hierarchy change for window_2_1.
+ wt_client1_->WaitForChangeCount(1);
+ changes1()->clear();
+
+ // Close client 3. Client 2 (which had previously embedded 3) should
+ // be notified of this.
+ wt_client3_.reset();
+ wt_client2_->WaitForChangeCount(1);
+ EXPECT_EQ("OnEmbeddedAppDisconnected window=" + IdToString(window_2_1),
+ SingleChangeToDescription(*changes2()));
+
+ // The closing is only interesting to the root that did the embedding. Other
+ // clients should not be notified of this.
+ wt_client1_->WaitForAllMessages();
+ EXPECT_TRUE(changes1()->empty());
+}
+
+// Verifies when the parent of an Embed() is destroyed the embedded app gets
+// a WindowDeleted (and doesn't trigger a DCHECK).
+TEST_F(WindowTreeClientTest, OnParentOfEmbedDisconnects) {
+ // Create client 2 and 3.
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(true));
+ Id window_1_1 = BuildWindowId(client_id_1(), 1);
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
+ Id window_2_1 = wt_client2()->NewWindow(1);
+ Id window_2_2 = wt_client2()->NewWindow(2);
+ ASSERT_TRUE(window_2_1);
+ ASSERT_TRUE(window_2_2);
+ ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_1));
+ ASSERT_TRUE(wt_client2()->AddWindow(window_2_1, window_2_2));
+ changes2()->clear();
+ ASSERT_NO_FATAL_FAILURE(EstablishThirdClient(wt2(), window_2_2));
+ changes3()->clear();
+
+ // Close client 2. Client 3 should get a delete (for its root).
+ wt_client2_.reset();
+ wt_client3_->WaitForChangeCount(1);
+ EXPECT_EQ("WindowDeleted window=" + IdToString(window_2_2),
+ SingleChangeToDescription(*changes3()));
+}
+
+// Verifies WindowTreeImpl doesn't incorrectly erase from its internal
+// map when a window from another client with the same window_id is removed.
+TEST_F(WindowTreeClientTest, DontCleanMapOnDestroy) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(true));
+ Id window_1_1 = BuildWindowId(client_id_1(), 1);
+ ASSERT_TRUE(wt_client2()->NewWindow(1));
+ changes1()->clear();
+ wt_client2_.reset();
+ wt_client1_->WaitForChangeCount(1);
+ EXPECT_EQ("OnEmbeddedAppDisconnected window=" + IdToString(window_1_1),
+ SingleChangeToDescription(*changes1()));
+ std::vector<TestWindow> windows;
+ GetWindowTree(wt1(), window_1_1, &windows);
+ EXPECT_FALSE(windows.empty());
+}
+
+// Verifies Embed() works when supplying a WindowTreeClient.
+TEST_F(WindowTreeClientTest, EmbedSupplyingWindowTreeClient) {
+ ASSERT_TRUE(wt_client1()->NewWindow(1));
+
+ TestWindowTreeClient client2;
+ mojom::WindowTreeClientPtr client2_ptr;
+ mojo::Binding<WindowTreeClient> client2_binding(&client2, &client2_ptr);
+ ASSERT_TRUE(Embed(wt1(), BuildWindowId(client_id_1(), 1),
+ std::move(client2_ptr)));
+ client2.WaitForOnEmbed();
+ EXPECT_EQ("OnEmbed",
+ SingleChangeToDescription(*client2.tracker()->changes()));
+}
+
+TEST_F(WindowTreeClientTest, EmbedFailsFromOtherClient) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(true));
+
+ Id window_1_1 = BuildWindowId(client_id_1(), 1);
+ Id window_2_1 = wt_client2()->NewWindow(1);
+ ASSERT_TRUE(window_2_1);
+ ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_1));
+ ASSERT_NO_FATAL_FAILURE(EstablishThirdClient(wt2(), window_2_1));
+
+ Id window_3_3 = wt_client3()->NewWindow(3);
+ ASSERT_TRUE(window_3_3);
+ ASSERT_TRUE(wt_client3()->AddWindow(window_2_1, window_3_3));
+
+ // 2 should not be able to embed in window_3_3 as window_3_3 was not created
+ // by
+ // 2.
+ EXPECT_FALSE(EmbedUrl(connector(), wt2(), test_name(), window_3_3));
+}
+
+// Verifies Embed() from window manager on another clients window works.
+TEST_F(WindowTreeClientTest, EmbedFromOtherClient) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(true));
+
+ Id window_1_1 = BuildWindowId(client_id_1(), 1);
+ Id window_2_1 = wt_client2()->NewWindow(1);
+ ASSERT_TRUE(window_2_1);
+ ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_1));
+
+ changes2()->clear();
+
+ // Establish a third client in window_2_1.
+ ASSERT_NO_FATAL_FAILURE(EstablishThirdClient(wt1(), window_2_1));
+
+ ASSERT_TRUE(wt_client2()->WaitForAllMessages());
+ EXPECT_EQ(std::string(), SingleChangeToDescription(*changes2()));
+}
+
+TEST_F(WindowTreeClientTest, CantEmbedFromClientRoot) {
+ // Shouldn't be able to embed into the root.
+ ASSERT_FALSE(EmbedUrl(connector(), wt1(), test_name(), root_window_id()));
+
+ // Even though the call above failed a WindowTreeClient was obtained. We need
+ // to
+ // wait for it else we throw off the next connect.
+ WaitForWindowTreeClient();
+
+ // Don't allow a client to embed into its own root.
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(true));
+ EXPECT_FALSE(EmbedUrl(connector(), wt2(), test_name(),
+ BuildWindowId(client_id_1(), 1)));
+
+ // Need to wait for a WindowTreeClient for same reason as above.
+ WaitForWindowTreeClient();
+
+ Id window_1_2 = wt_client1()->NewWindow(2);
+ ASSERT_TRUE(window_1_2);
+ ASSERT_TRUE(
+ wt_client1()->AddWindow(BuildWindowId(client_id_1(), 1), window_1_2));
+ ASSERT_TRUE(wt_client3_.get() == nullptr);
+ wt_client3_ =
+ EstablishClientViaEmbedWithPolicyBitmask(wt1(), window_1_2, nullptr);
+ ASSERT_TRUE(wt_client3_.get() != nullptr);
+
+ // window_1_2 is ws3's root, so even though v3 is an embed root it should not
+ // be able to Embed into itself.
+ ASSERT_FALSE(EmbedUrl(connector(), wt3(), test_name(), window_1_2));
+}
+
+// Verifies that a transient window tracks its parent's lifetime.
+TEST_F(WindowTreeClientTest, TransientWindowTracksTransientParentLifetime) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(true));
+ Id window_1_1 = BuildWindowId(client_id_1(), 1);
+
+ Id window_2_1 = wt_client2()->NewWindow(1);
+ Id window_2_2 = wt_client2()->NewWindow(2);
+ Id window_2_3 = wt_client2()->NewWindow(3);
+ ASSERT_TRUE(window_2_1);
+
+ // root -> window_1_1 -> window_2_1
+ // root -> window_1_1 -> window_2_2
+ // root -> window_1_1 -> window_2_3
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
+ ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_1));
+ ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_2));
+ ASSERT_TRUE(wt_client2()->AddWindow(window_1_1, window_2_3));
+
+ // window_2_2 and window_2_3 now track the lifetime of window_2_1.
+ changes1()->clear();
+ wt2()->AddTransientWindow(10, window_2_1, window_2_2);
+ wt2()->AddTransientWindow(11, window_2_1, window_2_3);
+ wt_client1()->WaitForChangeCount(2);
+ EXPECT_EQ("AddTransientWindow parent = " + IdToString(window_2_1) +
+ " child = " + IdToString(window_2_2),
+ ChangesToDescription1(*changes1())[0]);
+ EXPECT_EQ("AddTransientWindow parent = " + IdToString(window_2_1) +
+ " child = " + IdToString(window_2_3),
+ ChangesToDescription1(*changes1())[1]);
+
+ changes1()->clear();
+ wt2()->RemoveTransientWindowFromParent(12, window_2_3);
+ wt_client1()->WaitForChangeCount(1);
+ EXPECT_EQ("RemoveTransientWindowFromParent parent = " +
+ IdToString(window_2_1) + " child = " + IdToString(window_2_3),
+ SingleChangeToDescription(*changes1()));
+
+ changes1()->clear();
+ ASSERT_TRUE(wt_client2()->DeleteWindow(window_2_1));
+ wt_client1()->WaitForChangeCount(2);
+ EXPECT_EQ("WindowDeleted window=" + IdToString(window_2_2),
+ ChangesToDescription1(*changes1())[0]);
+ EXPECT_EQ("WindowDeleted window=" + IdToString(window_2_1),
+ ChangesToDescription1(*changes1())[1]);
+}
+
+TEST_F(WindowTreeClientTest, Ids) {
+ const Id window_1_100 = wt_client1()->NewWindow(100);
+ ASSERT_TRUE(window_1_100);
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_100));
+
+ // Establish the second client at 1,100.
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondClientWithRoot(window_1_100));
+
+ // 1,100 is the id in the wt_client1's id space. The new client should see
+ // 2,1 (the server id).
+ const Id window_1_100_in_ws2 = BuildWindowId(client_id_1(), 1);
+ EXPECT_EQ(window_1_100_in_ws2, wt_client2()->root_window_id());
+
+ // The first window created in the second client gets a server id of 2,1
+ // regardless of the id the client uses.
+ const Id window_2_101 = wt_client2()->NewWindow(101);
+ ASSERT_TRUE(wt_client2()->AddWindow(window_1_100_in_ws2, window_2_101));
+ const Id window_2_101_in_ws1 = BuildWindowId(client_id_2(), 1);
+ wt_client1()->WaitForChangeCount(1);
+ EXPECT_EQ("HierarchyChanged window=" + IdToString(window_2_101_in_ws1) +
+ " old_parent=null new_parent=" + IdToString(window_1_100),
+ SingleChangeToDescription(*changes1()));
+ changes1()->clear();
+
+ // Change the bounds of window_2_101 and make sure server gets it.
+ wt2()->SetWindowBounds(11, window_2_101, gfx::Rect(1, 2, 3, 4));
+ ASSERT_TRUE(wt_client2()->WaitForChangeCompleted(11));
+ wt_client1()->WaitForChangeCount(1);
+ EXPECT_EQ("BoundsChanged window=" + IdToString(window_2_101_in_ws1) +
+ " old_bounds=0,0 0x0 new_bounds=1,2 3x4",
+ SingleChangeToDescription(*changes1()));
+ changes2()->clear();
+
+ // Remove 2_101 from wm, client1 should see the change.
+ wt1()->RemoveWindowFromParent(12, window_2_101_in_ws1);
+ ASSERT_TRUE(wt_client1()->WaitForChangeCompleted(12));
+ wt_client2()->WaitForChangeCount(1);
+ EXPECT_EQ("HierarchyChanged window=" + IdToString(window_2_101) +
+ " old_parent=" + IdToString(window_1_100_in_ws2) +
+ " new_parent=null",
+ SingleChangeToDescription(*changes2()));
+}
+
+// Tests that setting capture fails when no input event has occurred, and there
+// is no notification of lost capture.
+TEST_F(WindowTreeClientTest, ExplicitCaptureWithoutInput) {
+ Id window_1_1 = wt_client1()->NewWindow(1);
+
+ // Add the window to the root, so that they have a Display to handle input
+ // capture.
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
+ changes1()->clear();
+
+ // Since there has been no input, capture should not succeed. No lost capture
+ // message is expected.
+ wt1()->SetCapture(1, window_1_1);
+ wt_client1_->WaitForAllMessages();
+ EXPECT_TRUE(changes1()->empty());
+
+ // Since there is no window with capture, lost capture should not be notified.
+ wt1()->ReleaseCapture(3, window_1_1);
+ wt_client1_->WaitForAllMessages();
+ EXPECT_TRUE(changes1()->empty());
+}
+
+// TODO(jonross): Enable this once apptests can send input events to the server.
+// Enabling capture requires that the client be processing events.
+TEST_F(WindowTreeClientTest, DISABLED_ExplicitCapturePropagation) {
+ Id window_1_1 = wt_client1()->NewWindow(1);
+ Id window_1_2 = wt_client1()->NewWindow(2);
+
+ // Add the windows to the root, so that they have a Display to handle input
+ // capture.
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
+ ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_2));
+
+ changes1()->clear();
+ // Window 1 takes capture then Window 2 takes capture.
+ // Verify that window 1 has lost capture.
+ wt1()->SetCapture(1, window_1_1);
+ wt1()->SetCapture(2, window_1_2);
+ wt_client1_->WaitForChangeCount(1);
+
+ EXPECT_EQ("OnLostCapture window=" + IdToString(window_1_1),
+ SingleChangeToDescription(*changes1()));
+
+ changes1()->clear();
+ // Explicitly releasing capture should not notify of lost capture.
+ wt1()->ReleaseCapture(3, window_1_2);
+ wt_client1_->WaitForAllMessages();
+
+ EXPECT_TRUE(changes1()->empty());
+}
+
+// TODO(sky): need to better track changes to initial client. For example,
+// that SetBounsdWindows/AddWindow and the like don't result in messages to the
+// originating client.
+
+// TODO(sky): make sure coverage of what was
+// WindowManagerTest.SecondEmbedRoot_InitService and
+// WindowManagerTest.MultipleEmbedRootsBeforeWTHReady gets added to window
+// manager
+// tests.
+
+} // namespace test
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/window_tree_factory.cc b/chromium/components/mus/ws/window_tree_factory.cc
new file mode 100644
index 00000000000..c8befb724ab
--- /dev/null
+++ b/chromium/components/mus/ws/window_tree_factory.cc
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/window_tree_factory.h"
+
+#include "base/memory/ptr_util.h"
+#include "components/mus/ws/default_access_policy.h"
+#include "components/mus/ws/window_server.h"
+#include "components/mus/ws/window_tree.h"
+#include "components/mus/ws/window_tree_binding.h"
+
+namespace mus {
+namespace ws {
+
+WindowTreeFactory::WindowTreeFactory(WindowServer* window_server,
+ const UserId& user_id,
+ const std::string& client_name,
+ mojom::WindowTreeFactoryRequest request)
+ : window_server_(window_server),
+ user_id_(user_id),
+ client_name_(client_name),
+ binding_(this, std::move(request)) {}
+
+WindowTreeFactory::~WindowTreeFactory() {}
+
+void WindowTreeFactory::CreateWindowTree(
+ mojo::InterfaceRequest<mojom::WindowTree> tree_request,
+ mojom::WindowTreeClientPtr client) {
+ std::unique_ptr<ws::WindowTree> service(
+ new ws::WindowTree(window_server_, user_id_, nullptr,
+ base::WrapUnique(new DefaultAccessPolicy)));
+ std::unique_ptr<ws::DefaultWindowTreeBinding> binding(
+ new ws::DefaultWindowTreeBinding(service.get(), window_server_,
+ std::move(tree_request),
+ std::move(client)));
+ service->set_name(client_name_);
+ window_server_->AddTree(std::move(service), std::move(binding), nullptr);
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/window_tree_factory.h b/chromium/components/mus/ws/window_tree_factory.h
new file mode 100644
index 00000000000..7a21ea94a23
--- /dev/null
+++ b/chromium/components/mus/ws/window_tree_factory.h
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_WINDOW_TREE_FACTORY_H_
+#define COMPONENTS_MUS_WS_WINDOW_TREE_FACTORY_H_
+
+#include "base/macros.h"
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "components/mus/ws/user_id.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+namespace mus {
+namespace ws {
+
+class WindowServer;
+
+class WindowTreeFactory : public mus::mojom::WindowTreeFactory {
+ public:
+ WindowTreeFactory(WindowServer* window_server,
+ const UserId& user_id,
+ const std::string& client_name,
+ mojom::WindowTreeFactoryRequest request);
+ private:
+ ~WindowTreeFactory() override;
+
+ // mus::mojom::WindowTreeFactory:
+ void CreateWindowTree(mojo::InterfaceRequest<mojom::WindowTree> tree_request,
+ mojom::WindowTreeClientPtr client) override;
+
+ WindowServer* window_server_;
+ const UserId user_id_;
+ const std::string client_name_;
+ mojo::StrongBinding<mus::mojom::WindowTreeFactory> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowTreeFactory);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_WINDOW_TREE_FACTORY_H_
diff --git a/chromium/components/mus/ws/window_tree_host_factory.cc b/chromium/components/mus/ws/window_tree_host_factory.cc
new file mode 100644
index 00000000000..c5c6cbc208b
--- /dev/null
+++ b/chromium/components/mus/ws/window_tree_host_factory.cc
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/window_tree_host_factory.h"
+
+#include "components/mus/gles2/gpu_state.h"
+#include "components/mus/surfaces/surfaces_state.h"
+#include "components/mus/ws/display.h"
+#include "components/mus/ws/display_binding.h"
+#include "components/mus/ws/window_server.h"
+
+namespace mus {
+namespace ws {
+
+WindowTreeHostFactory::WindowTreeHostFactory(
+ WindowServer* window_server,
+ const UserId& user_id,
+ const PlatformDisplayInitParams& platform_display_init_params)
+ : window_server_(window_server),
+ user_id_(user_id),
+ platform_display_init_params_(platform_display_init_params) {}
+
+WindowTreeHostFactory::~WindowTreeHostFactory() {}
+
+void WindowTreeHostFactory::AddBinding(
+ mojom::WindowTreeHostFactoryRequest request) {
+ bindings_.AddBinding(this, std::move(request));
+}
+
+void WindowTreeHostFactory::CreateWindowTreeHost(
+ mojom::WindowTreeHostRequest host,
+ mojom::WindowTreeClientPtr tree_client) {
+ Display* display = new Display(window_server_, platform_display_init_params_);
+ std::unique_ptr<DisplayBindingImpl> display_binding(
+ new DisplayBindingImpl(std::move(host), display, user_id_,
+ std::move(tree_client), window_server_));
+ display->Init(std::move(display_binding));
+}
+
+} // namespace ws
+} // namespace mus
diff --git a/chromium/components/mus/ws/window_tree_host_factory.h b/chromium/components/mus/ws/window_tree_host_factory.h
new file mode 100644
index 00000000000..3df36ee7f85
--- /dev/null
+++ b/chromium/components/mus/ws/window_tree_host_factory.h
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MUS_WS_WINDOW_TREE_HOST_FACTORY_H_
+#define COMPONENTS_MUS_WS_WINDOW_TREE_HOST_FACTORY_H_
+
+#include <stdint.h>
+
+#include "components/mus/public/interfaces/window_tree_host.mojom.h"
+#include "components/mus/ws/platform_display_init_params.h"
+#include "components/mus/ws/user_id.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+
+namespace mus {
+namespace ws {
+
+class WindowServer;
+
+class WindowTreeHostFactory : public mojom::WindowTreeHostFactory {
+ public:
+ WindowTreeHostFactory(
+ WindowServer* window_server,
+ const UserId& user_id,
+ const PlatformDisplayInitParams& platform_display_init_params);
+ ~WindowTreeHostFactory() override;
+
+ void AddBinding(mojom::WindowTreeHostFactoryRequest request);
+
+ private:
+ // mojom::WindowTreeHostFactory implementation.
+ void CreateWindowTreeHost(mojom::WindowTreeHostRequest host,
+ mojom::WindowTreeClientPtr tree_client) override;
+
+ WindowServer* window_server_;
+ const UserId user_id_;
+ const PlatformDisplayInitParams platform_display_init_params_;
+ mojo::BindingSet<mojom::WindowTreeHostFactory> bindings_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowTreeHostFactory);
+};
+
+} // namespace ws
+} // namespace mus
+
+#endif // COMPONENTS_MUS_WS_WINDOW_TREE_HOST_FACTORY_H_
diff --git a/chromium/components/mus/ws/window_tree_unittest.cc b/chromium/components/mus/ws/window_tree_unittest.cc
new file mode 100644
index 00000000000..15356763085
--- /dev/null
+++ b/chromium/components/mus/ws/window_tree_unittest.cc
@@ -0,0 +1,1017 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/mus/ws/window_tree.h"
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/stringprintf.h"
+#include "components/mus/common/types.h"
+#include "components/mus/common/util.h"
+#include "components/mus/public/interfaces/window_tree.mojom.h"
+#include "components/mus/surfaces/surfaces_state.h"
+#include "components/mus/ws/default_access_policy.h"
+#include "components/mus/ws/display_binding.h"
+#include "components/mus/ws/ids.h"
+#include "components/mus/ws/platform_display.h"
+#include "components/mus/ws/platform_display_factory.h"
+#include "components/mus/ws/platform_display_init_params.h"
+#include "components/mus/ws/server_window.h"
+#include "components/mus/ws/server_window_surface_manager_test_api.h"
+#include "components/mus/ws/test_change_tracker.h"
+#include "components/mus/ws/test_server_window_delegate.h"
+#include "components/mus/ws/test_utils.h"
+#include "components/mus/ws/window_manager_access_policy.h"
+#include "components/mus/ws/window_manager_display_root.h"
+#include "components/mus/ws/window_server.h"
+#include "components/mus/ws/window_server_delegate.h"
+#include "components/mus/ws/window_tree_binding.h"
+#include "services/shell/public/interfaces/connector.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/event.h"
+#include "ui/events/event_utils.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace mus {
+namespace ws {
+namespace test {
+namespace {
+
+std::string WindowIdToString(const WindowId& id) {
+ return base::StringPrintf("%d,%d", id.client_id, id.window_id);
+}
+
+ClientWindowId BuildClientWindowId(WindowTree* tree,
+ ClientSpecificId window_id) {
+ return ClientWindowId(WindowIdToTransportId(WindowId(tree->id(), window_id)));
+}
+
+// -----------------------------------------------------------------------------
+
+ui::PointerEvent CreatePointerDownEvent(int x, int y) {
+ return ui::PointerEvent(ui::TouchEvent(ui::ET_TOUCH_PRESSED, gfx::Point(x, y),
+ 1, ui::EventTimeForNow()));
+}
+
+ui::PointerEvent CreatePointerUpEvent(int x, int y) {
+ return ui::PointerEvent(ui::TouchEvent(
+ ui::ET_TOUCH_RELEASED, gfx::Point(x, y), 1, ui::EventTimeForNow()));
+}
+
+ui::PointerEvent CreateMouseMoveEvent(int x, int y) {
+ return ui::PointerEvent(
+ ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(x, y), gfx::Point(x, y),
+ ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE));
+}
+
+ui::PointerEvent CreateMouseDownEvent(int x, int y) {
+ return ui::PointerEvent(
+ ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(x, y), gfx::Point(x, y),
+ ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+ ui::EF_LEFT_MOUSE_BUTTON));
+}
+
+ui::PointerEvent CreateMouseUpEvent(int x, int y) {
+ return ui::PointerEvent(
+ ui::MouseEvent(ui::ET_MOUSE_RELEASED, gfx::Point(x, y), gfx::Point(x, y),
+ ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+ ui::EF_LEFT_MOUSE_BUTTON));
+}
+
+ServerWindow* GetCaptureWindow(Display* display) {
+ return display->GetActiveWindowManagerDisplayRoot()
+ ->window_manager_state()
+ ->capture_window();
+}
+
+mojom::EventMatcherPtr CreateEventMatcher(ui::mojom::EventType type) {
+ mojom::EventMatcherPtr matcher = mojom::EventMatcher::New();
+ matcher->type_matcher = mojom::EventTypeMatcher::New();
+ matcher->type_matcher->type = type;
+ return matcher;
+}
+
+} // namespace
+
+// -----------------------------------------------------------------------------
+
+class WindowTreeTest : public testing::Test {
+ public:
+ WindowTreeTest() {}
+ ~WindowTreeTest() override {}
+
+ mus::mojom::Cursor cursor_id() {
+ return static_cast<mus::mojom::Cursor>(
+ window_event_targeting_helper_.cursor_id());
+ }
+ Display* display() { return window_event_targeting_helper_.display(); }
+ TestWindowTreeBinding* last_binding() {
+ return window_event_targeting_helper_.last_binding();
+ }
+ TestWindowTreeClient* last_window_tree_client() {
+ return window_event_targeting_helper_.last_window_tree_client();
+ }
+ TestWindowTreeClient* wm_client() {
+ return window_event_targeting_helper_.wm_client();
+ }
+ WindowServer* window_server() {
+ return window_event_targeting_helper_.window_server();
+ }
+ WindowTree* wm_tree() {
+ return window_event_targeting_helper_.window_server()->GetTreeWithId(1);
+ }
+
+ void DispatchEventWithoutAck(const ui::Event& event) {
+ DisplayTestApi(display()).OnEvent(event);
+ }
+
+ void set_window_manager_internal(WindowTree* tree,
+ mojom::WindowManager* wm_internal) {
+ WindowTreeTestApi(tree).set_window_manager_internal(wm_internal);
+ }
+
+ void AckPreviousEvent() {
+ WindowManagerStateTestApi test_api(
+ display()->GetActiveWindowManagerDisplayRoot()->window_manager_state());
+ while (test_api.tree_awaiting_input_ack()) {
+ test_api.tree_awaiting_input_ack()->OnWindowInputEventAck(
+ 0, mojom::EventResult::HANDLED);
+ }
+ }
+
+ void DispatchEventAndAckImmediately(const ui::Event& event) {
+ DispatchEventWithoutAck(event);
+ AckPreviousEvent();
+ }
+
+ // Creates a new window from wm_tree() and embeds a new client in it.
+ void SetupEventTargeting(TestWindowTreeClient** out_client,
+ WindowTree** window_tree,
+ ServerWindow** window);
+
+ // Creates a new tree as the specified user. This does what creation via
+ // a WindowTreeFactory does.
+ WindowTree* CreateNewTree(const UserId& user_id,
+ TestWindowTreeBinding** binding) {
+ WindowTree* tree =
+ new WindowTree(window_server(), user_id, nullptr,
+ base::WrapUnique(new DefaultAccessPolicy));
+ *binding = new TestWindowTreeBinding(tree);
+ window_server()->AddTree(base::WrapUnique(tree), base::WrapUnique(*binding),
+ nullptr);
+ return tree;
+ }
+
+ protected:
+ WindowEventTargetingHelper window_event_targeting_helper_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WindowTreeTest);
+};
+
+// Creates a new window in wm_tree(), adds it to the root, embeds a
+// new client in the window and creates a child of said window. |window| is
+// set to the child of |window_tree| that is created.
+void WindowTreeTest::SetupEventTargeting(TestWindowTreeClient** out_client,
+ WindowTree** window_tree,
+ ServerWindow** window) {
+ ServerWindow* embed_window = window_event_targeting_helper_.CreatePrimaryTree(
+ gfx::Rect(0, 0, 100, 100), gfx::Rect(0, 0, 50, 50));
+ window_event_targeting_helper_.CreateSecondaryTree(
+ embed_window, gfx::Rect(20, 20, 20, 20), out_client, window_tree, window);
+}
+
+// Verifies focus correctly changes on pointer events.
+TEST_F(WindowTreeTest, FocusOnPointer) {
+ const ClientWindowId embed_window_id = BuildClientWindowId(wm_tree(), 1);
+ EXPECT_TRUE(
+ wm_tree()->NewWindow(embed_window_id, ServerWindow::Properties()));
+ ServerWindow* embed_window = wm_tree()->GetWindowByClientId(embed_window_id);
+ ASSERT_TRUE(embed_window);
+ EXPECT_TRUE(wm_tree()->SetWindowVisibility(embed_window_id, true));
+ ASSERT_TRUE(FirstRoot(wm_tree()));
+ const ClientWindowId wm_root_id = FirstRootId(wm_tree());
+ EXPECT_TRUE(wm_tree()->AddWindow(wm_root_id, embed_window_id));
+ display()->root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+ mojom::WindowTreeClientPtr client;
+ mojom::WindowTreeClientRequest client_request = GetProxy(&client);
+ wm_client()->Bind(std::move(client_request));
+ const uint32_t embed_flags = 0;
+ wm_tree()->Embed(embed_window_id, std::move(client), embed_flags);
+ WindowTree* tree1 = window_server()->GetTreeWithRoot(embed_window);
+ ASSERT_TRUE(tree1 != nullptr);
+ ASSERT_NE(tree1, wm_tree());
+
+ embed_window->SetBounds(gfx::Rect(0, 0, 50, 50));
+
+ const ClientWindowId child1_id(BuildClientWindowId(tree1, 1));
+ EXPECT_TRUE(tree1->NewWindow(child1_id, ServerWindow::Properties()));
+ EXPECT_TRUE(tree1->AddWindow(ClientWindowIdForWindow(tree1, embed_window),
+ child1_id));
+ ServerWindow* child1 = tree1->GetWindowByClientId(child1_id);
+ ASSERT_TRUE(child1);
+ child1->SetVisible(true);
+ child1->SetBounds(gfx::Rect(20, 20, 20, 20));
+ EnableHitTest(child1);
+
+ TestWindowTreeClient* tree1_client = last_window_tree_client();
+ tree1_client->tracker()->changes()->clear();
+ wm_client()->tracker()->changes()->clear();
+
+ // Focus should not go to |child1| yet, since the parent still doesn't allow
+ // active children.
+ DispatchEventAndAckImmediately(CreatePointerDownEvent(21, 22));
+ Display* display1 = tree1->GetDisplay(embed_window);
+ EXPECT_EQ(nullptr, display1->GetFocusedWindow());
+ DispatchEventAndAckImmediately(CreatePointerUpEvent(21, 22));
+ tree1_client->tracker()->changes()->clear();
+ wm_client()->tracker()->changes()->clear();
+
+ display1->AddActivationParent(embed_window);
+
+ // Focus should go to child1. This result in notifying both the window
+ // manager and client client being notified.
+ DispatchEventAndAckImmediately(CreatePointerDownEvent(21, 22));
+ EXPECT_EQ(child1, display1->GetFocusedWindow());
+ ASSERT_GE(wm_client()->tracker()->changes()->size(), 1u);
+ EXPECT_EQ("Focused id=2,1",
+ ChangesToDescription1(*wm_client()->tracker()->changes())[0]);
+ ASSERT_GE(tree1_client->tracker()->changes()->size(), 1u);
+ EXPECT_EQ("Focused id=2,1",
+ ChangesToDescription1(*tree1_client->tracker()->changes())[0]);
+
+ DispatchEventAndAckImmediately(CreatePointerUpEvent(21, 22));
+ wm_client()->tracker()->changes()->clear();
+ tree1_client->tracker()->changes()->clear();
+
+ // Press outside of the embedded window. Note that root cannot be focused
+ // (because it cannot be activated). So the focus would not move in this case.
+ DispatchEventAndAckImmediately(CreatePointerDownEvent(61, 22));
+ EXPECT_EQ(child1, display()->GetFocusedWindow());
+
+ DispatchEventAndAckImmediately(CreatePointerUpEvent(21, 22));
+ wm_client()->tracker()->changes()->clear();
+ tree1_client->tracker()->changes()->clear();
+
+ // Press in the same location. Should not get a focus change event (only input
+ // event).
+ DispatchEventAndAckImmediately(CreatePointerDownEvent(61, 22));
+ EXPECT_EQ(child1, display()->GetFocusedWindow());
+ ASSERT_EQ(wm_client()->tracker()->changes()->size(), 1u)
+ << SingleChangeToDescription(*wm_client()->tracker()->changes());
+ EXPECT_EQ("InputEvent window=0,3 event_action=16",
+ ChangesToDescription1(*wm_client()->tracker()->changes())[0]);
+ EXPECT_TRUE(tree1_client->tracker()->changes()->empty());
+}
+
+TEST_F(WindowTreeTest, BasicInputEventTarget) {
+ TestWindowTreeClient* embed_client = nullptr;
+ WindowTree* tree = nullptr;
+ ServerWindow* window = nullptr;
+ EXPECT_NO_FATAL_FAILURE(
+ SetupEventTargeting(&embed_client, &tree, &window));
+
+ // Send an event to |v1|. |embed_client| should get the event, not
+ // |wm_client|, since |v1| lives inside an embedded window.
+ DispatchEventAndAckImmediately(CreatePointerDownEvent(21, 22));
+ ASSERT_EQ(1u, wm_client()->tracker()->changes()->size());
+ EXPECT_EQ("Focused id=2,1",
+ ChangesToDescription1(*wm_client()->tracker()->changes())[0]);
+ ASSERT_EQ(2u, embed_client->tracker()->changes()->size());
+ EXPECT_EQ("Focused id=2,1",
+ ChangesToDescription1(*embed_client->tracker()->changes())[0]);
+ EXPECT_EQ("InputEvent window=2,1 event_action=16",
+ ChangesToDescription1(*embed_client->tracker()->changes())[1]);
+}
+
+// Tests that a client can observe events outside its bounds.
+TEST_F(WindowTreeTest, SetEventObserver) {
+ // Create an embedded client.
+ TestWindowTreeClient* client = nullptr;
+ WindowTree* tree = nullptr;
+ ServerWindow* window = nullptr;
+ EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&client, &tree, &window));
+
+ // Create an event outside the bounds of the client.
+ ui::PointerEvent pointer_down = CreatePointerDownEvent(5, 5);
+
+ // Events are not observed before setting an observer.
+ DispatchEventAndAckImmediately(pointer_down);
+ ASSERT_EQ(0u, client->tracker()->changes()->size());
+
+ // Create an observer for pointer-down events.
+ WindowTreeTestApi(tree).SetEventObserver(
+ CreateEventMatcher(ui::mojom::EventType::POINTER_DOWN), 111u);
+
+ // Pointer-down events are sent to the client.
+ DispatchEventAndAckImmediately(pointer_down);
+ ASSERT_EQ(1u, client->tracker()->changes()->size());
+ EXPECT_EQ("EventObserved event_action=16 event_observer_id=111",
+ ChangesToDescription1(*client->tracker()->changes())[0]);
+ client->tracker()->changes()->clear();
+
+ // Clearing the observer stops sending events to the client.
+ WindowTreeTestApi(tree).SetEventObserver(nullptr, 0u);
+ DispatchEventAndAckImmediately(pointer_down);
+ ASSERT_EQ(0u, client->tracker()->changes()->size());
+}
+
+// Tests that a client using an event observer does not receive events that
+// don't match the EventMatcher spec.
+TEST_F(WindowTreeTest, SetEventObserverNonMatching) {
+ // Create an embedded client.
+ TestWindowTreeClient* client = nullptr;
+ WindowTree* tree = nullptr;
+ ServerWindow* window = nullptr;
+ EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&client, &tree, &window));
+
+ // Create an observer for pointer-down events.
+ WindowTreeTestApi(tree).SetEventObserver(
+ CreateEventMatcher(ui::mojom::EventType::POINTER_DOWN), 111u);
+
+ // Pointer-up events are not sent to the client, since they don't match.
+ DispatchEventAndAckImmediately(CreatePointerUpEvent(5, 5));
+ ASSERT_EQ(0u, client->tracker()->changes()->size());
+}
+
+// Tests that an event that both hits a client window and matches an event
+// observer is sent only once to the client.
+TEST_F(WindowTreeTest, SetEventObserverSendsOnce) {
+ // Create an embedded client.
+ TestWindowTreeClient* client = nullptr;
+ WindowTree* tree = nullptr;
+ ServerWindow* window = nullptr;
+ EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&client, &tree, &window));
+
+ // Create an observer for pointer-up events (which do not cause focus
+ // changes).
+ WindowTreeTestApi(tree).SetEventObserver(
+ CreateEventMatcher(ui::mojom::EventType::POINTER_UP), 111u);
+
+ // Create an event inside the bounds of the client.
+ ui::PointerEvent pointer_up = CreatePointerUpEvent(25, 25);
+
+ // The event is dispatched once, with a flag set that it matched the event
+ // observer.
+ DispatchEventAndAckImmediately(pointer_up);
+ ASSERT_EQ(1u, client->tracker()->changes()->size());
+ EXPECT_EQ("InputEvent window=2,1 event_action=18 event_observer_id=111",
+ SingleChangeToDescription(*client->tracker()->changes()));
+}
+
+// Tests that events generated by user A are not observed by event observers for
+// user B.
+TEST_F(WindowTreeTest, SetEventObserverWrongUser) {
+ // Embed a window tree belonging to a different user.
+ TestWindowTreeBinding* other_binding;
+ WindowTree* other_tree = CreateNewTree("other_user", &other_binding);
+ other_binding->client()->tracker()->changes()->clear();
+
+ // Set event observers on both the wm tree and the other user's tree.
+ WindowTreeTestApi(wm_tree()).SetEventObserver(
+ CreateEventMatcher(ui::mojom::EventType::POINTER_UP), 111u);
+ WindowTreeTestApi(other_tree)
+ .SetEventObserver(CreateEventMatcher(ui::mojom::EventType::POINTER_UP),
+ 222u);
+
+ // An event is observed by the wm tree, but not by the other user's tree.
+ DispatchEventAndAckImmediately(CreatePointerUpEvent(5, 5));
+ ASSERT_EQ(1u, wm_client()->tracker()->changes()->size());
+ EXPECT_EQ("InputEvent window=0,3 event_action=18 event_observer_id=111",
+ SingleChangeToDescription(*wm_client()->tracker()->changes()));
+ ASSERT_EQ(0u, other_binding->client()->tracker()->changes()->size());
+}
+
+// Tests that an event observer cannot observe keystrokes.
+TEST_F(WindowTreeTest, SetEventObserverKeyEventsDisallowed) {
+ WindowTreeTestApi(wm_tree()).SetEventObserver(
+ CreateEventMatcher(ui::mojom::EventType::KEY_PRESSED), 111u);
+ ui::KeyEvent key_pressed(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE);
+ DispatchEventAndAckImmediately(key_pressed);
+ EXPECT_EQ(0u, wm_client()->tracker()->changes()->size());
+
+ WindowTreeTestApi(wm_tree()).SetEventObserver(
+ CreateEventMatcher(ui::mojom::EventType::KEY_RELEASED), 222u);
+ ui::KeyEvent key_released(ui::ET_KEY_RELEASED, ui::VKEY_A, ui::EF_NONE);
+ DispatchEventAndAckImmediately(key_released);
+ EXPECT_EQ(0u, wm_client()->tracker()->changes()->size());
+}
+
+TEST_F(WindowTreeTest, CursorChangesWhenMouseOverWindowAndWindowSetsCursor) {
+ TestWindowTreeClient* embed_client = nullptr;
+ WindowTree* tree = nullptr;
+ ServerWindow* window = nullptr;
+ EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window));
+
+ // Like in BasicInputEventTarget, we send a pointer down event to be
+ // dispatched. This is only to place the mouse cursor over that window though.
+ DispatchEventAndAckImmediately(CreateMouseMoveEvent(21, 22));
+
+ window->SetPredefinedCursor(mojom::Cursor::IBEAM);
+
+ // Because the cursor is over the window when SetCursor was called, we should
+ // have immediately changed the cursor.
+ EXPECT_EQ(mojom::Cursor::IBEAM, cursor_id());
+}
+
+TEST_F(WindowTreeTest, CursorChangesWhenEnteringWindowWithDifferentCursor) {
+ TestWindowTreeClient* embed_client = nullptr;
+ WindowTree* tree = nullptr;
+ ServerWindow* window = nullptr;
+ EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window));
+
+ // Let's create a pointer event outside the window and then move the pointer
+ // inside.
+ DispatchEventAndAckImmediately(CreateMouseMoveEvent(5, 5));
+ window->SetPredefinedCursor(mojom::Cursor::IBEAM);
+ EXPECT_EQ(mojom::Cursor::CURSOR_NULL, cursor_id());
+
+ DispatchEventAndAckImmediately(CreateMouseMoveEvent(21, 22));
+ EXPECT_EQ(mojom::Cursor::IBEAM, cursor_id());
+}
+
+TEST_F(WindowTreeTest, TouchesDontChangeCursor) {
+ TestWindowTreeClient* embed_client = nullptr;
+ WindowTree* tree = nullptr;
+ ServerWindow* window = nullptr;
+ EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window));
+
+ // Let's create a pointer event outside the window and then move the pointer
+ // inside.
+ DispatchEventAndAckImmediately(CreateMouseMoveEvent(5, 5));
+ window->SetPredefinedCursor(mojom::Cursor::IBEAM);
+ EXPECT_EQ(mojom::Cursor::CURSOR_NULL, cursor_id());
+
+ // With a touch event, we shouldn't update the cursor.
+ DispatchEventAndAckImmediately(CreatePointerDownEvent(21, 22));
+ EXPECT_EQ(mojom::Cursor::CURSOR_NULL, cursor_id());
+}
+
+TEST_F(WindowTreeTest, DragOutsideWindow) {
+ TestWindowTreeClient* embed_client = nullptr;
+ WindowTree* tree = nullptr;
+ ServerWindow* window = nullptr;
+ EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window));
+
+ // Start with the cursor outside the window. Setting the cursor shouldn't
+ // change the cursor.
+ DispatchEventAndAckImmediately(CreateMouseMoveEvent(5, 5));
+ window->SetPredefinedCursor(mojom::Cursor::IBEAM);
+ EXPECT_EQ(mojom::Cursor::CURSOR_NULL, cursor_id());
+
+ // Move the pointer to the inside of the window
+ DispatchEventAndAckImmediately(CreateMouseMoveEvent(21, 22));
+ EXPECT_EQ(mojom::Cursor::IBEAM, cursor_id());
+
+ // Start the drag.
+ DispatchEventAndAckImmediately(CreateMouseDownEvent(21, 22));
+ EXPECT_EQ(mojom::Cursor::IBEAM, cursor_id());
+
+ // Move the cursor (mouse is still down) outside the window.
+ DispatchEventAndAckImmediately(CreateMouseMoveEvent(5, 5));
+ EXPECT_EQ(mojom::Cursor::IBEAM, cursor_id());
+
+ // Release the cursor. We should now adapt the cursor of the window
+ // underneath the pointer.
+ DispatchEventAndAckImmediately(CreateMouseUpEvent(5, 5));
+ EXPECT_EQ(mojom::Cursor::CURSOR_NULL, cursor_id());
+}
+
+TEST_F(WindowTreeTest, ChangingWindowBoundsChangesCursor) {
+ TestWindowTreeClient* embed_client = nullptr;
+ WindowTree* tree = nullptr;
+ ServerWindow* window = nullptr;
+ EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window));
+
+ // Put the cursor just outside the bounds of the window.
+ DispatchEventAndAckImmediately(CreateMouseMoveEvent(41, 41));
+ window->SetPredefinedCursor(mojom::Cursor::IBEAM);
+ EXPECT_EQ(mojom::Cursor::CURSOR_NULL, cursor_id());
+
+ // Expand the bounds of the window so they now include where the cursor now
+ // is.
+ window->SetBounds(gfx::Rect(20, 20, 25, 25));
+ EXPECT_EQ(mojom::Cursor::IBEAM, cursor_id());
+
+ // Contract the bounds again.
+ window->SetBounds(gfx::Rect(20, 20, 20, 20));
+ EXPECT_EQ(mojom::Cursor::CURSOR_NULL, cursor_id());
+}
+
+TEST_F(WindowTreeTest, WindowReorderingChangesCursor) {
+ TestWindowTreeClient* embed_client = nullptr;
+ WindowTree* tree = nullptr;
+ ServerWindow* window1 = nullptr;
+ EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window1));
+
+ // Create a second window right over the first.
+ const ClientWindowId embed_window_id(FirstRootId(tree));
+ const ClientWindowId child2_id(BuildClientWindowId(tree, 2));
+ EXPECT_TRUE(tree->NewWindow(child2_id, ServerWindow::Properties()));
+ ServerWindow* child2 = tree->GetWindowByClientId(child2_id);
+ ASSERT_TRUE(child2);
+ EXPECT_TRUE(tree->AddWindow(embed_window_id, child2_id));
+ child2->SetVisible(true);
+ child2->SetBounds(gfx::Rect(20, 20, 20, 20));
+ EnableHitTest(child2);
+
+ // Give each window a different cursor.
+ window1->SetPredefinedCursor(mojom::Cursor::IBEAM);
+ child2->SetPredefinedCursor(mojom::Cursor::HAND);
+
+ // We expect window2 to be over window1 now.
+ DispatchEventAndAckImmediately(CreateMouseMoveEvent(22, 22));
+ EXPECT_EQ(mojom::Cursor::HAND, cursor_id());
+
+ // But when we put window2 at the bottom, we should adapt window1's cursor.
+ child2->parent()->StackChildAtBottom(child2);
+ EXPECT_EQ(mojom::Cursor::IBEAM, cursor_id());
+}
+
+TEST_F(WindowTreeTest, EventAck) {
+ const ClientWindowId embed_window_id = BuildClientWindowId(wm_tree(), 1);
+ EXPECT_TRUE(
+ wm_tree()->NewWindow(embed_window_id, ServerWindow::Properties()));
+ EXPECT_TRUE(wm_tree()->SetWindowVisibility(embed_window_id, true));
+ ASSERT_TRUE(FirstRoot(wm_tree()));
+ EXPECT_TRUE(wm_tree()->AddWindow(FirstRootId(wm_tree()), embed_window_id));
+ display()->root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+
+ wm_client()->tracker()->changes()->clear();
+ DispatchEventWithoutAck(CreateMouseMoveEvent(21, 22));
+ ASSERT_EQ(1u, wm_client()->tracker()->changes()->size());
+ EXPECT_EQ("InputEvent window=0,3 event_action=17",
+ ChangesToDescription1(*wm_client()->tracker()->changes())[0]);
+ wm_client()->tracker()->changes()->clear();
+
+ // Send another event. This event shouldn't reach the client.
+ DispatchEventWithoutAck(CreateMouseMoveEvent(21, 22));
+ ASSERT_EQ(0u, wm_client()->tracker()->changes()->size());
+
+ // Ack the first event. That should trigger the dispatch of the second event.
+ AckPreviousEvent();
+ ASSERT_EQ(1u, wm_client()->tracker()->changes()->size());
+ EXPECT_EQ("InputEvent window=0,3 event_action=17",
+ ChangesToDescription1(*wm_client()->tracker()->changes())[0]);
+}
+
+// Establish client, call NewTopLevelWindow(), make sure get id, and make
+// sure client paused.
+TEST_F(WindowTreeTest, NewTopLevelWindow) {
+ TestWindowManager wm_internal;
+ set_window_manager_internal(wm_tree(), &wm_internal);
+
+ TestWindowTreeBinding* child_binding;
+ WindowTree* child_tree = CreateNewTree(wm_tree()->user_id(), &child_binding);
+ child_binding->client()->tracker()->changes()->clear();
+ child_binding->client()->set_record_on_change_completed(true);
+
+ // Create a new top level window.
+ mojo::Map<mojo::String, mojo::Array<uint8_t>> properties;
+ const uint32_t initial_change_id = 17;
+ // Explicitly use an id that does not contain the client id.
+ const ClientWindowId embed_window_id2_in_child(45 << 16 | 27);
+ static_cast<mojom::WindowTree*>(child_tree)
+ ->NewTopLevelWindow(initial_change_id, embed_window_id2_in_child.id,
+ std::move(properties));
+
+ // The binding should be paused until the wm acks the change.
+ uint32_t wm_change_id = 0u;
+ ASSERT_TRUE(wm_internal.did_call_create_top_level_window(&wm_change_id));
+ EXPECT_TRUE(child_binding->is_paused());
+
+ // Create the window for |embed_window_id2_in_child|.
+ const ClientWindowId embed_window_id2 = BuildClientWindowId(wm_tree(), 2);
+ EXPECT_TRUE(
+ wm_tree()->NewWindow(embed_window_id2, ServerWindow::Properties()));
+ EXPECT_TRUE(wm_tree()->SetWindowVisibility(embed_window_id2, true));
+ EXPECT_TRUE(wm_tree()->AddWindow(FirstRootId(wm_tree()), embed_window_id2));
+
+ // Ack the change, which should resume the binding.
+ child_binding->client()->tracker()->changes()->clear();
+ static_cast<mojom::WindowManagerClient*>(wm_tree())
+ ->OnWmCreatedTopLevelWindow(wm_change_id, embed_window_id2.id);
+ EXPECT_FALSE(child_binding->is_paused());
+ EXPECT_EQ("TopLevelCreated id=17 window_id=" +
+ WindowIdToString(
+ WindowIdFromTransportId(embed_window_id2_in_child.id)) +
+ " drawn=true",
+ SingleChangeToDescription(
+ *child_binding->client()->tracker()->changes()));
+ child_binding->client()->tracker()->changes()->clear();
+
+ // Change the visibility of the window from the owner and make sure the
+ // client sees the right id.
+ ServerWindow* embed_window = wm_tree()->GetWindowByClientId(embed_window_id2);
+ ASSERT_TRUE(embed_window);
+ EXPECT_TRUE(embed_window->visible());
+ ASSERT_TRUE(wm_tree()->SetWindowVisibility(
+ ClientWindowIdForWindow(wm_tree(), embed_window), false));
+ EXPECT_FALSE(embed_window->visible());
+ EXPECT_EQ("VisibilityChanged window=" +
+ WindowIdToString(
+ WindowIdFromTransportId(embed_window_id2_in_child.id)) +
+ " visible=false",
+ SingleChangeToDescription(
+ *child_binding->client()->tracker()->changes()));
+
+ // Set the visibility from the child using the client assigned id.
+ ASSERT_TRUE(child_tree->SetWindowVisibility(embed_window_id2_in_child, true));
+ EXPECT_TRUE(embed_window->visible());
+}
+
+// Tests that only the capture window can release capture.
+TEST_F(WindowTreeTest, ExplicitSetCapture) {
+ TestWindowTreeClient* embed_client = nullptr;
+ WindowTree* tree = nullptr;
+ ServerWindow* window = nullptr;
+ EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window));
+ const ServerWindow* root_window = *tree->roots().begin();
+ tree->AddWindow(FirstRootId(tree), ClientWindowIdForWindow(tree, window));
+ window->SetBounds(gfx::Rect(0, 0, 100, 100));
+ ASSERT_TRUE(tree->GetDisplay(window));
+
+ // Set capture.
+ mojom::WindowTree* mojom_window_tree = static_cast<mojom::WindowTree*>(tree);
+ uint32_t change_id = 42;
+ mojom_window_tree->SetCapture(change_id, WindowIdToTransportId(window->id()));
+ Display* display = tree->GetDisplay(window);
+ EXPECT_EQ(window, GetCaptureWindow(display));
+
+ // Only the capture window should be able to release capture
+ mojom_window_tree->ReleaseCapture(++change_id,
+ WindowIdToTransportId(root_window->id()));
+ EXPECT_EQ(window, GetCaptureWindow(display));
+ mojom_window_tree->ReleaseCapture(++change_id,
+ WindowIdToTransportId(window->id()));
+ EXPECT_EQ(nullptr, GetCaptureWindow(display));
+}
+
+// Tests that while a client is interacting with input, that capture is not
+// allowed for invisible windows.
+TEST_F(WindowTreeTest, CaptureWindowMustBeVisible) {
+ TestWindowTreeClient* embed_client = nullptr;
+ WindowTree* tree = nullptr;
+ ServerWindow* window = nullptr;
+ EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window));
+ tree->AddWindow(FirstRootId(tree), ClientWindowIdForWindow(tree, window));
+ window->SetBounds(gfx::Rect(0, 0, 100, 100));
+ ASSERT_TRUE(tree->GetDisplay(window));
+
+ DispatchEventWithoutAck(CreatePointerDownEvent(10, 10));
+ window->SetVisible(false);
+ EXPECT_FALSE(tree->SetCapture(ClientWindowIdForWindow(tree, window)));
+ EXPECT_NE(window, GetCaptureWindow(tree->GetDisplay(window)));
+}
+
+// Tests that showing a modal window releases the capture if the capture is on a
+// descendant of the modal parent.
+TEST_F(WindowTreeTest, ShowModalWindowWithDescendantCapture) {
+ TestWindowTreeClient* embed_client = nullptr;
+ WindowTree* tree = nullptr;
+ ServerWindow* w1 = nullptr;
+ EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &w1));
+
+ w1->SetBounds(gfx::Rect(10, 10, 30, 30));
+ const ServerWindow* root_window = *tree->roots().begin();
+ ClientWindowId root_window_id = ClientWindowIdForWindow(tree, root_window);
+ ClientWindowId w1_id = ClientWindowIdForWindow(tree, w1);
+ Display* display = tree->GetDisplay(w1);
+
+ // Create |w11| as a child of |w1| and make it visible.
+ ClientWindowId w11_id = BuildClientWindowId(tree, 11);
+ ASSERT_TRUE(tree->NewWindow(w11_id, ServerWindow::Properties()));
+ ServerWindow* w11 = tree->GetWindowByClientId(w11_id);
+ w11->SetBounds(gfx::Rect(10, 10, 10, 10));
+ ASSERT_TRUE(tree->AddWindow(w1_id, w11_id));
+ ASSERT_TRUE(tree->SetWindowVisibility(w11_id, true));
+
+ // Create |w2| as a child of |root_window| and modal to |w1| and leave it
+ // hidden.
+ ClientWindowId w2_id = BuildClientWindowId(tree, 2);
+ ASSERT_TRUE(tree->NewWindow(w2_id, ServerWindow::Properties()));
+ ServerWindow* w2 = tree->GetWindowByClientId(w2_id);
+ w2->SetBounds(gfx::Rect(50, 10, 10, 10));
+ ASSERT_TRUE(tree->AddWindow(root_window_id, w2_id));
+ ASSERT_TRUE(tree->AddTransientWindow(w1_id, w2_id));
+ ASSERT_TRUE(tree->SetModal(w2_id));
+
+ // Set capture to |w11|.
+ DispatchEventWithoutAck(CreatePointerDownEvent(25, 25));
+ ASSERT_TRUE(tree->SetCapture(w11_id));
+ EXPECT_EQ(w11, GetCaptureWindow(display));
+ AckPreviousEvent();
+
+ // Make |w2| visible. This should release capture as capture is set to a
+ // descendant of the modal parent.
+ ASSERT_TRUE(tree->SetWindowVisibility(w2_id, true));
+ EXPECT_EQ(nullptr, GetCaptureWindow(display));
+}
+
+// Tests that setting a visible window as modal releases the capture if the
+// capture is on a descendant of the modal parent.
+TEST_F(WindowTreeTest, VisibleWindowToModalWithDescendantCapture) {
+ TestWindowTreeClient* embed_client = nullptr;
+ WindowTree* tree = nullptr;
+ ServerWindow* w1 = nullptr;
+ EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &w1));
+
+ w1->SetBounds(gfx::Rect(10, 10, 30, 30));
+ const ServerWindow* root_window = *tree->roots().begin();
+ ClientWindowId root_window_id = ClientWindowIdForWindow(tree, root_window);
+ ClientWindowId w1_id = ClientWindowIdForWindow(tree, w1);
+ Display* display = tree->GetDisplay(w1);
+
+ // Create |w11| as a child of |w1| and make it visible.
+ ClientWindowId w11_id = BuildClientWindowId(tree, 11);
+ ASSERT_TRUE(tree->NewWindow(w11_id, ServerWindow::Properties()));
+ ServerWindow* w11 = tree->GetWindowByClientId(w11_id);
+ w11->SetBounds(gfx::Rect(10, 10, 10, 10));
+ ASSERT_TRUE(tree->AddWindow(w1_id, w11_id));
+ ASSERT_TRUE(tree->SetWindowVisibility(w11_id, true));
+
+ // Create |w2| as a child of |root_window| and make it visible.
+ ClientWindowId w2_id = BuildClientWindowId(tree, 2);
+ ASSERT_TRUE(tree->NewWindow(w2_id, ServerWindow::Properties()));
+ ServerWindow* w2 = tree->GetWindowByClientId(w2_id);
+ w2->SetBounds(gfx::Rect(50, 10, 10, 10));
+ ASSERT_TRUE(tree->AddWindow(root_window_id, w2_id));
+ ASSERT_TRUE(tree->SetWindowVisibility(w2_id, true));
+
+ // Set capture to |w11|.
+ DispatchEventWithoutAck(CreatePointerDownEvent(25, 25));
+ ASSERT_TRUE(tree->SetCapture(w11_id));
+ EXPECT_EQ(w11, GetCaptureWindow(display));
+ AckPreviousEvent();
+
+ // Set |w2| modal to |w1|. This should release the capture as the capture is
+ // set to a descendant of the modal parent.
+ ASSERT_TRUE(tree->AddTransientWindow(w1_id, w2_id));
+ ASSERT_TRUE(tree->SetModal(w2_id));
+ EXPECT_EQ(nullptr, GetCaptureWindow(display));
+}
+
+// Tests that showing a modal window does not change capture if the capture is
+// not on a descendant of the modal parent.
+TEST_F(WindowTreeTest, ShowModalWindowWithNonDescendantCapture) {
+ TestWindowTreeClient* embed_client = nullptr;
+ WindowTree* tree = nullptr;
+ ServerWindow* w1 = nullptr;
+ EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &w1));
+
+ w1->SetBounds(gfx::Rect(10, 10, 30, 30));
+ const ServerWindow* root_window = *tree->roots().begin();
+ ClientWindowId root_window_id = ClientWindowIdForWindow(tree, root_window);
+ ClientWindowId w1_id = ClientWindowIdForWindow(tree, w1);
+ Display* display = tree->GetDisplay(w1);
+
+ // Create |w2| as a child of |root_window| and modal to |w1| and leave it
+ // hidden..
+ ClientWindowId w2_id = BuildClientWindowId(tree, 2);
+ ASSERT_TRUE(tree->NewWindow(w2_id, ServerWindow::Properties()));
+ ServerWindow* w2 = tree->GetWindowByClientId(w2_id);
+ w2->SetBounds(gfx::Rect(50, 10, 10, 10));
+ ASSERT_TRUE(tree->AddWindow(root_window_id, w2_id));
+ ASSERT_TRUE(tree->AddTransientWindow(w1_id, w2_id));
+ ASSERT_TRUE(tree->SetModal(w2_id));
+
+ // Create |w3| as a child of |root_window| and make it visible.
+ ClientWindowId w3_id = BuildClientWindowId(tree, 3);
+ ASSERT_TRUE(tree->NewWindow(w3_id, ServerWindow::Properties()));
+ ServerWindow* w3 = tree->GetWindowByClientId(w3_id);
+ w3->SetBounds(gfx::Rect(70, 10, 10, 10));
+ ASSERT_TRUE(tree->AddWindow(root_window_id, w3_id));
+ ASSERT_TRUE(tree->SetWindowVisibility(w3_id, true));
+
+ // Set capture to |w3|.
+ DispatchEventWithoutAck(CreatePointerDownEvent(25, 25));
+ ASSERT_TRUE(tree->SetCapture(w3_id));
+ EXPECT_EQ(w3, GetCaptureWindow(display));
+ AckPreviousEvent();
+
+ // Make |w2| visible. This should not change the capture as the capture is not
+ // set to a descendant of the modal parent.
+ ASSERT_TRUE(tree->SetWindowVisibility(w2_id, true));
+ EXPECT_EQ(w3, GetCaptureWindow(display));
+}
+
+// Tests that setting a visible window as modal does not change the capture if
+// the capture is not set to a descendant of the modal parent.
+TEST_F(WindowTreeTest, VisibleWindowToModalWithNonDescendantCapture) {
+ TestWindowTreeClient* embed_client = nullptr;
+ WindowTree* tree = nullptr;
+ ServerWindow* w1 = nullptr;
+ EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &w1));
+
+ w1->SetBounds(gfx::Rect(10, 10, 30, 30));
+ const ServerWindow* root_window = *tree->roots().begin();
+ ClientWindowId root_window_id = ClientWindowIdForWindow(tree, root_window);
+ ClientWindowId w1_id = ClientWindowIdForWindow(tree, w1);
+ Display* display = tree->GetDisplay(w1);
+
+ // Create |w2| and |w3| as children of |root_window| and make them visible.
+ ClientWindowId w2_id = BuildClientWindowId(tree, 2);
+ ASSERT_TRUE(tree->NewWindow(w2_id, ServerWindow::Properties()));
+ ServerWindow* w2 = tree->GetWindowByClientId(w2_id);
+ w2->SetBounds(gfx::Rect(50, 10, 10, 10));
+ ASSERT_TRUE(tree->AddWindow(root_window_id, w2_id));
+ ASSERT_TRUE(tree->SetWindowVisibility(w2_id, true));
+
+ ClientWindowId w3_id = BuildClientWindowId(tree, 3);
+ ASSERT_TRUE(tree->NewWindow(w3_id, ServerWindow::Properties()));
+ ServerWindow* w3 = tree->GetWindowByClientId(w3_id);
+ w3->SetBounds(gfx::Rect(70, 10, 10, 10));
+ ASSERT_TRUE(tree->AddWindow(root_window_id, w3_id));
+ ASSERT_TRUE(tree->SetWindowVisibility(w3_id, true));
+
+ // Set capture to |w3|.
+ DispatchEventWithoutAck(CreatePointerDownEvent(25, 25));
+ ASSERT_TRUE(tree->SetCapture(w3_id));
+ EXPECT_EQ(w3, GetCaptureWindow(display));
+ AckPreviousEvent();
+
+ // Set |w2| modal to |w1|. This should not release the capture as the capture
+ // is not set to a descendant of the modal parent.
+ ASSERT_TRUE(tree->AddTransientWindow(w1_id, w2_id));
+ ASSERT_TRUE(tree->SetModal(w2_id));
+ EXPECT_EQ(w3, GetCaptureWindow(display));
+}
+
+// Tests that showing a system modal window releases the capture.
+TEST_F(WindowTreeTest, ShowSystemModalWindowWithCapture) {
+ TestWindowTreeClient* embed_client = nullptr;
+ WindowTree* tree = nullptr;
+ ServerWindow* w1 = nullptr;
+ EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &w1));
+
+ w1->SetBounds(gfx::Rect(10, 10, 10, 10));
+ const ServerWindow* root_window = *tree->roots().begin();
+ ClientWindowId root_window_id = ClientWindowIdForWindow(tree, root_window);
+ ClientWindowId w1_id = ClientWindowIdForWindow(tree, w1);
+ Display* display = tree->GetDisplay(w1);
+
+ // Create a system modal window |w2| as a child of |root_window| and leave it
+ // hidden.
+ ClientWindowId w2_id = BuildClientWindowId(tree, 2);
+ ASSERT_TRUE(tree->NewWindow(w2_id, ServerWindow::Properties()));
+ ServerWindow* w2 = tree->GetWindowByClientId(w2_id);
+ w2->SetBounds(gfx::Rect(30, 10, 10, 10));
+ ASSERT_TRUE(tree->AddWindow(root_window_id, w2_id));
+ ASSERT_TRUE(tree->SetModal(w2_id));
+
+ // Set capture to |w1|.
+ DispatchEventWithoutAck(CreatePointerDownEvent(15, 15));
+ ASSERT_TRUE(tree->SetCapture(w1_id));
+ EXPECT_EQ(w1, GetCaptureWindow(display));
+ AckPreviousEvent();
+
+ // Make |w2| visible. This should release capture as it is system modal
+ // window.
+ ASSERT_TRUE(tree->SetWindowVisibility(w2_id, true));
+ EXPECT_EQ(nullptr, GetCaptureWindow(display));
+}
+
+// Tests that setting a visible window as modal to system releases the capture.
+TEST_F(WindowTreeTest, VisibleWindowToSystemModalWithCapture) {
+ TestWindowTreeClient* embed_client = nullptr;
+ WindowTree* tree = nullptr;
+ ServerWindow* w1 = nullptr;
+ EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &w1));
+
+ w1->SetBounds(gfx::Rect(10, 10, 10, 10));
+ const ServerWindow* root_window = *tree->roots().begin();
+ ClientWindowId root_window_id = ClientWindowIdForWindow(tree, root_window);
+ ClientWindowId w1_id = ClientWindowIdForWindow(tree, w1);
+ Display* display = tree->GetDisplay(w1);
+
+ // Create |w2| as a child of |root_window| and make it visible.
+ ClientWindowId w2_id = BuildClientWindowId(tree, 2);
+ ASSERT_TRUE(tree->NewWindow(w2_id, ServerWindow::Properties()));
+ ServerWindow* w2 = tree->GetWindowByClientId(w2_id);
+ w2->SetBounds(gfx::Rect(30, 10, 10, 10));
+ ASSERT_TRUE(tree->AddWindow(root_window_id, w2_id));
+ ASSERT_TRUE(tree->SetWindowVisibility(w2_id, true));
+
+ // Set capture to |w1|.
+ DispatchEventWithoutAck(CreatePointerDownEvent(15, 15));
+ ASSERT_TRUE(tree->SetCapture(w1_id));
+ EXPECT_EQ(w1, GetCaptureWindow(display));
+ AckPreviousEvent();
+
+ // Make |w2| modal to system. This should release capture.
+ ASSERT_TRUE(tree->SetModal(w2_id));
+ EXPECT_EQ(nullptr, GetCaptureWindow(display));
+}
+
+// Tests that moving the capture window to a modal parent releases the capture
+// as capture cannot be blocked by a modal window.
+TEST_F(WindowTreeTest, MoveCaptureWindowToModalParent) {
+ TestWindowTreeClient* embed_client = nullptr;
+ WindowTree* tree = nullptr;
+ ServerWindow* w1 = nullptr;
+ EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &w1));
+
+ w1->SetBounds(gfx::Rect(10, 10, 30, 30));
+ const ServerWindow* root_window = *tree->roots().begin();
+ ClientWindowId root_window_id = ClientWindowIdForWindow(tree, root_window);
+ ClientWindowId w1_id = ClientWindowIdForWindow(tree, w1);
+ Display* display = tree->GetDisplay(w1);
+
+ // Create |w2| and |w3| as children of |root_window| and make them visible.
+ ClientWindowId w2_id = BuildClientWindowId(tree, 2);
+ ASSERT_TRUE(tree->NewWindow(w2_id, ServerWindow::Properties()));
+ ServerWindow* w2 = tree->GetWindowByClientId(w2_id);
+ w2->SetBounds(gfx::Rect(50, 10, 10, 10));
+ ASSERT_TRUE(tree->AddWindow(root_window_id, w2_id));
+ ASSERT_TRUE(tree->SetWindowVisibility(w2_id, true));
+
+ ClientWindowId w3_id = BuildClientWindowId(tree, 3);
+ ASSERT_TRUE(tree->NewWindow(w3_id, ServerWindow::Properties()));
+ ServerWindow* w3 = tree->GetWindowByClientId(w3_id);
+ w3->SetBounds(gfx::Rect(70, 10, 10, 10));
+ ASSERT_TRUE(tree->AddWindow(root_window_id, w3_id));
+ ASSERT_TRUE(tree->SetWindowVisibility(w3_id, true));
+
+ // Set |w2| modal to |w1|.
+ ASSERT_TRUE(tree->AddTransientWindow(w1_id, w2_id));
+ ASSERT_TRUE(tree->SetModal(w2_id));
+
+ // Set capture to |w3|.
+ DispatchEventWithoutAck(CreatePointerDownEvent(25, 25));
+ ASSERT_TRUE(tree->SetCapture(w3_id));
+ EXPECT_EQ(w3, GetCaptureWindow(display));
+ AckPreviousEvent();
+
+ // Make |w3| child of |w1|. This should release capture as |w3| is now blocked
+ // by a modal window.
+ ASSERT_TRUE(tree->AddWindow(w1_id, w3_id));
+ EXPECT_EQ(nullptr, GetCaptureWindow(display));
+}
+
+// Tests that opacity can be set on a known window.
+TEST_F(WindowTreeTest, SetOpacity) {
+ TestWindowTreeClient* embed_client = nullptr;
+ WindowTree* tree = nullptr;
+ ServerWindow* window = nullptr;
+ EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window));
+
+ const float new_opacity = 0.5f;
+ EXPECT_NE(new_opacity, window->opacity());
+ ASSERT_TRUE(tree->SetWindowOpacity(ClientWindowIdForWindow(tree, window),
+ new_opacity));
+ EXPECT_EQ(new_opacity, window->opacity());
+
+ // Re-applying the same opacity will succeed.
+ EXPECT_TRUE(tree->SetWindowOpacity(ClientWindowIdForWindow(tree, window),
+ new_opacity));
+}
+
+// Tests that opacity requests for unknown windows are rejected.
+TEST_F(WindowTreeTest, SetOpacityFailsOnUnknownWindow) {
+ TestWindowTreeClient* embed_client = nullptr;
+ WindowTree* tree = nullptr;
+ ServerWindow* window = nullptr;
+ EXPECT_NO_FATAL_FAILURE(SetupEventTargeting(&embed_client, &tree, &window));
+
+ TestServerWindowDelegate delegate;
+ WindowId window_id(42, 1337);
+ ServerWindow unknown_window(&delegate, window_id);
+ const float new_opacity = 0.5f;
+ ASSERT_NE(new_opacity, unknown_window.opacity());
+
+ EXPECT_FALSE(tree->SetWindowOpacity(
+ ClientWindowId(WindowIdToTransportId(window_id)), new_opacity));
+ EXPECT_NE(new_opacity, unknown_window.opacity());
+}
+
+TEST_F(WindowTreeTest, SetCaptureTargetsRightConnection) {
+ ServerWindow* window = window_event_targeting_helper_.CreatePrimaryTree(
+ gfx::Rect(0, 0, 100, 100), gfx::Rect(0, 0, 50, 50));
+ WindowTree* owning_tree =
+ window_server()->GetTreeWithId(window->id().client_id);
+ WindowTree* embed_tree = window_server()->GetTreeWithRoot(window);
+ ASSERT_NE(owning_tree, embed_tree);
+ ASSERT_TRUE(
+ owning_tree->SetCapture(ClientWindowIdForWindow(owning_tree, window)));
+ DispatchEventWithoutAck(CreateMouseMoveEvent(21, 22));
+ WindowManagerStateTestApi wm_state_test_api(
+ display()->GetActiveWindowManagerDisplayRoot()->window_manager_state());
+ EXPECT_EQ(owning_tree, wm_state_test_api.tree_awaiting_input_ack());
+ AckPreviousEvent();
+
+ // Set capture from the embedded client and make sure it gets the event.
+ ASSERT_TRUE(
+ embed_tree->SetCapture(ClientWindowIdForWindow(embed_tree, window)));
+ DispatchEventWithoutAck(CreateMouseMoveEvent(22, 23));
+ EXPECT_EQ(embed_tree, wm_state_test_api.tree_awaiting_input_ack());
+}
+
+} // namespace test
+} // namespace ws
+} // namespace mus