summaryrefslogtreecommitdiff
path: root/chromium/ui/aura
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
committerZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
commit679147eead574d186ebf3069647b4c23e8ccace6 (patch)
treefc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/ui/aura
downloadqtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz
Initial import.
Diffstat (limited to 'chromium/ui/aura')
-rw-r--r--chromium/ui/aura/DEPS7
-rw-r--r--chromium/ui/aura/OWNERS6
-rw-r--r--chromium/ui/aura/aura.gyp300
-rw-r--r--chromium/ui/aura/aura_export.h32
-rw-r--r--chromium/ui/aura/bench/DEPS5
-rw-r--r--chromium/ui/aura/bench/bench_main.cc367
-rw-r--r--chromium/ui/aura/client/activation_change_observer.cc29
-rw-r--r--chromium/ui/aura/client/activation_change_observer.h45
-rw-r--r--chromium/ui/aura/client/activation_client.cc38
-rw-r--r--chromium/ui/aura/client/activation_client.h78
-rw-r--r--chromium/ui/aura/client/activation_delegate.cc27
-rw-r--r--chromium/ui/aura/client/activation_delegate.h37
-rw-r--r--chromium/ui/aura/client/animation_host.cc32
-rw-r--r--chromium/ui/aura/client/animation_host.h42
-rw-r--r--chromium/ui/aura/client/aura_constants.cc38
-rw-r--r--chromium/ui/aura/client/aura_constants.h66
-rw-r--r--chromium/ui/aura/client/capture_client.cc34
-rw-r--r--chromium/ui/aura/client/capture_client.h44
-rw-r--r--chromium/ui/aura/client/capture_delegate.h34
-rw-r--r--chromium/ui/aura/client/cursor_client.cc28
-rw-r--r--chromium/ui/aura/client/cursor_client.h79
-rw-r--r--chromium/ui/aura/client/cursor_client_observer.cc7
-rw-r--r--chromium/ui/aura/client/cursor_client_observer.h24
-rw-r--r--chromium/ui/aura/client/default_capture_client.cc51
-rw-r--r--chromium/ui/aura/client/default_capture_client.h36
-rw-r--r--chromium/ui/aura/client/dispatcher_client.cc26
-rw-r--r--chromium/ui/aura/client/dispatcher_client.h31
-rw-r--r--chromium/ui/aura/client/drag_drop_client.cc28
-rw-r--r--chromium/ui/aura/client/drag_drop_client.h63
-rw-r--r--chromium/ui/aura/client/drag_drop_delegate.cc27
-rw-r--r--chromium/ui/aura/client/drag_drop_delegate.h51
-rw-r--r--chromium/ui/aura/client/event_client.cc27
-rw-r--r--chromium/ui/aura/client/event_client.h38
-rw-r--r--chromium/ui/aura/client/focus_change_observer.cc28
-rw-r--r--chromium/ui/aura/client/focus_change_observer.h33
-rw-r--r--chromium/ui/aura/client/focus_client.cc33
-rw-r--r--chromium/ui/aura/client/focus_client.h57
-rw-r--r--chromium/ui/aura/client/screen_position_client.cc29
-rw-r--r--chromium/ui/aura/client/screen_position_client.h58
-rw-r--r--chromium/ui/aura/client/stacking_client.cc38
-rw-r--r--chromium/ui/aura/client/stacking_client.h50
-rw-r--r--chromium/ui/aura/client/tooltip_client.cc39
-rw-r--r--chromium/ui/aura/client/tooltip_client.h40
-rw-r--r--chromium/ui/aura/client/user_action_client.cc27
-rw-r--r--chromium/ui/aura/client/user_action_client.h37
-rw-r--r--chromium/ui/aura/client/visibility_client.cc35
-rw-r--r--chromium/ui/aura/client/visibility_client.h37
-rw-r--r--chromium/ui/aura/client/window_move_client.cc28
-rw-r--r--chromium/ui/aura/client/window_move_client.h56
-rw-r--r--chromium/ui/aura/client/window_types.h36
-rw-r--r--chromium/ui/aura/demo/demo_main.cc167
-rw-r--r--chromium/ui/aura/device_list_updater_aurax11.cc32
-rw-r--r--chromium/ui/aura/device_list_updater_aurax11.h32
-rw-r--r--chromium/ui/aura/dispatcher_win.cc33
-rw-r--r--chromium/ui/aura/env.cc117
-rw-r--r--chromium/ui/aura/env.h106
-rw-r--r--chromium/ui/aura/env_observer.h35
-rw-r--r--chromium/ui/aura/focus_manager.cc105
-rw-r--r--chromium/ui/aura/focus_manager.h49
-rw-r--r--chromium/ui/aura/gestures/OWNERS2
-rw-r--r--chromium/ui/aura/gestures/gesture_recognizer_unittest.cc3422
-rw-r--r--chromium/ui/aura/layout_manager.cc22
-rw-r--r--chromium/ui/aura/layout_manager.h58
-rw-r--r--chromium/ui/aura/remote_root_window_host_win.cc571
-rw-r--r--chromium/ui/aura/remote_root_window_host_win.h227
-rw-r--r--chromium/ui/aura/root_window.cc1203
-rw-r--r--chromium/ui/aura/root_window.h453
-rw-r--r--chromium/ui/aura/root_window_host.h114
-rw-r--r--chromium/ui/aura/root_window_host_delegate.h63
-rw-r--r--chromium/ui/aura/root_window_host_mac.h29
-rw-r--r--chromium/ui/aura/root_window_host_mac.mm215
-rw-r--r--chromium/ui/aura/root_window_host_ozone.cc132
-rw-r--r--chromium/ui/aura/root_window_host_ozone.h67
-rw-r--r--chromium/ui/aura/root_window_host_win.cc318
-rw-r--r--chromium/ui/aura/root_window_host_win.h100
-rw-r--r--chromium/ui/aura/root_window_host_x11.cc1096
-rw-r--r--chromium/ui/aura/root_window_host_x11.h153
-rw-r--r--chromium/ui/aura/root_window_mac.h37
-rw-r--r--chromium/ui/aura/root_window_mac.mm35
-rw-r--r--chromium/ui/aura/root_window_observer.h41
-rw-r--r--chromium/ui/aura/root_window_transformer.h45
-rw-r--r--chromium/ui/aura/root_window_unittest.cc885
-rw-r--r--chromium/ui/aura/root_window_view_mac.h21
-rw-r--r--chromium/ui/aura/root_window_view_mac.mm16
-rw-r--r--chromium/ui/aura/window.cc1137
-rw-r--r--chromium/ui/aura/window.h529
-rw-r--r--chromium/ui/aura/window_delegate.h108
-rw-r--r--chromium/ui/aura/window_observer.h112
-rw-r--r--chromium/ui/aura/window_property.h144
-rw-r--r--chromium/ui/aura/window_tracker.cc43
-rw-r--r--chromium/ui/aura/window_tracker.h50
-rw-r--r--chromium/ui/aura/window_unittest.cc3054
92 files changed, 17616 insertions, 0 deletions
diff --git a/chromium/ui/aura/DEPS b/chromium/ui/aura/DEPS
new file mode 100644
index 00000000000..381faed57f5
--- /dev/null
+++ b/chromium/ui/aura/DEPS
@@ -0,0 +1,7 @@
+include_rules = [
+ "+grit/ui_resources.h",
+ "+grit/ui_strings.h",
+ "+skia/ext",
+ "+ui/base",
+ "+ui/gfx",
+]
diff --git a/chromium/ui/aura/OWNERS b/chromium/ui/aura/OWNERS
new file mode 100644
index 00000000000..447142eb555
--- /dev/null
+++ b/chromium/ui/aura/OWNERS
@@ -0,0 +1,6 @@
+ben@chromium.org
+sky@chromium.org
+sadrul@chromium.org
+
+per-file *x11.cc=erg@chromium.org
+per-file *x11.h=erg@chromium.org
diff --git a/chromium/ui/aura/aura.gyp b/chromium/ui/aura/aura.gyp
new file mode 100644
index 00000000000..a4fe8612de6
--- /dev/null
+++ b/chromium/ui/aura/aura.gyp
@@ -0,0 +1,300 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'targets': [
+ {
+ 'target_name': 'aura',
+ 'type': '<(component)',
+ 'dependencies': [
+ '../../base/base.gyp:base',
+ '../../base/base.gyp:base_i18n',
+ '../../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ '../../cc/cc.gyp:cc',
+ '../../gpu/gpu.gyp:gpu',
+ '../../skia/skia.gyp:skia',
+ '../compositor/compositor.gyp:compositor',
+ '../ui.gyp:ui',
+ '../ui.gyp:ui_resources',
+ ],
+ 'defines': [
+ 'AURA_IMPLEMENTATION',
+ ],
+ 'sources': [
+ 'client/activation_change_observer.h',
+ 'client/activation_change_observer.cc',
+ 'client/activation_client.cc',
+ 'client/activation_client.h',
+ 'client/activation_delegate.cc',
+ 'client/activation_delegate.h',
+ 'client/animation_host.cc',
+ 'client/animation_host.h',
+ 'client/aura_constants.cc',
+ 'client/aura_constants.h',
+ 'client/capture_client.cc',
+ 'client/capture_client.h',
+ 'client/capture_delegate.h',
+ 'client/cursor_client.cc',
+ 'client/cursor_client.h',
+ 'client/cursor_client_observer.h',
+ 'client/cursor_client_observer.cc',
+ 'client/default_capture_client.cc',
+ 'client/default_capture_client.h',
+ 'client/dispatcher_client.cc',
+ 'client/dispatcher_client.h',
+ 'client/drag_drop_client.cc',
+ 'client/drag_drop_client.h',
+ 'client/drag_drop_delegate.cc',
+ 'client/drag_drop_delegate.h',
+ 'client/event_client.cc',
+ 'client/event_client.h',
+ 'client/focus_change_observer.cc',
+ 'client/focus_change_observer.h',
+ 'client/focus_client.cc',
+ 'client/focus_client.h',
+ 'client/screen_position_client.cc',
+ 'client/screen_position_client.h',
+ 'client/stacking_client.cc',
+ 'client/stacking_client.h',
+ 'client/tooltip_client.cc',
+ 'client/tooltip_client.h',
+ 'client/user_action_client.cc',
+ 'client/user_action_client.h',
+ 'client/visibility_client.cc',
+ 'client/visibility_client.h',
+ 'client/window_move_client.cc',
+ 'client/window_move_client.h',
+ 'client/window_types.h',
+ 'device_list_updater_aurax11.cc',
+ 'device_list_updater_aurax11.h',
+ 'dispatcher_win.cc',
+ 'env.cc',
+ 'env.h',
+ 'env_observer.h',
+ 'focus_manager.cc',
+ 'focus_manager.h',
+ 'layout_manager.cc',
+ 'layout_manager.h',
+ 'remote_root_window_host_win.cc',
+ 'remote_root_window_host_win.h',
+ 'root_window_host.h',
+ 'root_window_host_delegate.h',
+ 'root_window_host_mac.h',
+ 'root_window_host_mac.mm',
+ 'root_window_host_ozone.cc',
+ 'root_window_host_ozone.h',
+ 'root_window_host_win.cc',
+ 'root_window_host_win.h',
+ 'root_window_host_x11.cc',
+ 'root_window_host_x11.h',
+ 'root_window_mac.h',
+ 'root_window_mac.mm',
+ 'root_window_transformer.h',
+ 'root_window_view_mac.h',
+ 'root_window_view_mac.mm',
+ 'root_window.cc',
+ 'root_window.h',
+ 'window.cc',
+ 'window.h',
+ 'window_delegate.h',
+ 'window_observer.h',
+ 'window_tracker.cc',
+ 'window_tracker.h',
+ ],
+ 'conditions': [
+ ['OS=="mac"', {
+ 'sources/': [
+ ['exclude', 'client/dispatcher_client.cc'],
+ ['exclude', 'client/dispatcher_client.h'],
+ ],
+ }],
+ ['use_x11==1', {
+ 'link_settings': {
+ 'libraries': [
+ '-lX11',
+ '-lXi',
+ '-lXfixes',
+ '-lXrandr',
+ ],
+ },
+ }],
+ ['OS=="win"', {
+ 'dependencies': [
+ '../metro_viewer/metro_viewer.gyp:metro_viewer_messages',
+ '../../ipc/ipc.gyp:ipc',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'aura_test_support',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../../skia/skia.gyp:skia',
+ '../../testing/gtest.gyp:gtest',
+ '../ui.gyp:ui',
+ '../ui.gyp:ui_test_support',
+ 'aura',
+ 'aura_test_support_pak',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'test/aura_test_base.cc',
+ 'test/aura_test_base.h',
+ 'test/aura_test_helper.cc',
+ 'test/aura_test_helper.h',
+ 'test/event_generator.cc',
+ 'test/event_generator.h',
+ 'test/test_activation_client.cc',
+ 'test/test_activation_client.h',
+ 'test/test_aura_initializer.cc',
+ 'test/test_aura_initializer.h',
+ 'test/test_cursor_client.cc',
+ 'test/test_cursor_client.h',
+ 'test/test_event_handler.cc',
+ 'test/test_event_handler.h',
+ 'test/test_screen.cc',
+ 'test/test_screen.h',
+ 'test/test_stacking_client.cc',
+ 'test/test_stacking_client.h',
+ 'test/test_windows.cc',
+ 'test/test_windows.h',
+ 'test/test_window_delegate.cc',
+ 'test/test_window_delegate.h',
+ 'test/ui_controls_factory_aura.h',
+ 'test/ui_controls_factory_aurawin.cc',
+ 'test/ui_controls_factory_aurax11.cc',
+ 'test/window_test_api.cc',
+ 'test/window_test_api.h',
+ ],
+ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ 'msvs_disabled_warnings': [ 4267, ],
+ },
+ {
+ # We build a minimal set of resources required for aura_test_support.
+ 'target_name': 'aura_test_support_pak',
+ 'type': 'none',
+ 'dependencies': [
+ '<(DEPTH)/ui/ui.gyp:ui_resources',
+ ],
+ 'variables': {
+ 'repack_path': '<(DEPTH)/tools/grit/grit/format/repack.py',
+ },
+ 'actions': [
+ {
+ 'action_name': 'repack_aura_test_support_pack',
+ 'variables': {
+ 'pak_inputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/ui/ui_resources/ui_resources_100_percent.pak',
+ ],
+ },
+ 'inputs': [
+ '<(repack_path)',
+ '<@(pak_inputs)',
+ ],
+ 'outputs': [
+ '<(PRODUCT_DIR)/aura_test_support_resources.pak',
+ ],
+ 'action': ['python', '<(repack_path)', '<@(_outputs)',
+ '<@(pak_inputs)'],
+ },
+ ],
+ },
+ {
+ 'target_name': 'aura_demo',
+ 'type': 'executable',
+ 'dependencies': [
+ '../../base/base.gyp:base',
+ '../../base/base.gyp:base_i18n',
+ '../../skia/skia.gyp:skia',
+ '../../third_party/icu/icu.gyp:icui18n',
+ '../../third_party/icu/icu.gyp:icuuc',
+ '../compositor/compositor.gyp:compositor',
+ '../compositor/compositor.gyp:compositor_test_support',
+ '../ui.gyp:ui',
+ '../ui.gyp:ui_resources',
+ '../../ipc/ipc.gyp:ipc',
+ 'aura',
+ 'aura_test_support',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'demo/demo_main.cc',
+ ],
+ },
+ {
+ 'target_name': 'aura_bench',
+ 'type': 'executable',
+ 'dependencies': [
+ '../../base/base.gyp:base',
+ '../../base/base.gyp:base_i18n',
+ '../../skia/skia.gyp:skia',
+ '../../third_party/icu/icu.gyp:icui18n',
+ '../../third_party/icu/icu.gyp:icuuc',
+ '../compositor/compositor.gyp:compositor',
+ '../compositor/compositor.gyp:compositor_test_support',
+ '../ui.gyp:ui',
+ '../ui.gyp:ui_resources',
+ 'aura',
+ 'aura_test_support',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'bench/bench_main.cc',
+ ],
+ },
+ {
+ 'target_name': 'aura_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../../base/base.gyp:test_support_base',
+ '../../chrome/chrome_resources.gyp:packed_resources',
+ '../../skia/skia.gyp:skia',
+ '../../testing/gtest.gyp:gtest',
+ '../compositor/compositor.gyp:compositor_test_support',
+ '../compositor/compositor.gyp:compositor',
+ '../gl/gl.gyp:gl',
+ '../ui.gyp:ui',
+ '../ui.gyp:ui_resources',
+ '../ui.gyp:ui_test_support',
+ 'aura_test_support',
+ 'aura',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'gestures/gesture_recognizer_unittest.cc',
+ 'test/run_all_unittests.cc',
+ 'test/test_suite.cc',
+ 'test/test_suite.h',
+ 'root_window_unittest.cc',
+ 'window_unittest.cc',
+ ],
+ 'conditions': [
+ # osmesa GL implementation is used on linux.
+ ['OS=="linux"', {
+ 'dependencies': [
+ '<(DEPTH)/third_party/mesa/mesa.gyp:osmesa',
+ ],
+ }],
+ ['OS=="linux" and linux_use_tcmalloc==1', {
+ 'dependencies': [
+ # See http://crbug.com/162998#c4 for why this is needed.
+ '../../base/allocator/allocator.gyp:allocator',
+ ],
+ }],
+ ],
+ },
+ ],
+}
diff --git a/chromium/ui/aura/aura_export.h b/chromium/ui/aura/aura_export.h
new file mode 100644
index 00000000000..9db662bbf7e
--- /dev/null
+++ b/chromium/ui/aura/aura_export.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_AURA_EXPORT_H
+#define UI_AURA_AURA_EXPORT_H
+
+// Defines AURA_EXPORT so that functionality implemented by the aura module
+// can be exported to consumers.
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(AURA_IMPLEMENTATION)
+#define AURA_EXPORT __declspec(dllexport)
+#else
+#define AURA_EXPORT __declspec(dllimport)
+#endif // defined(AURA_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(AURA_IMPLEMENTATION)
+#define AURA_EXPORT __attribute__((visibility("default")))
+#else
+#define AURA_EXPORT
+#endif
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define AURA_EXPORT
+#endif
+
+#endif // UI_AURA_AURA_EXPORT_H
diff --git a/chromium/ui/aura/bench/DEPS b/chromium/ui/aura/bench/DEPS
new file mode 100644
index 00000000000..1fda55436bc
--- /dev/null
+++ b/chromium/ui/aura/bench/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+cc",
+ "+third_party/khronos",
+ "+third_party/WebKit/public/platform",
+]
diff --git a/chromium/ui/aura/bench/bench_main.cc b/chromium/ui/aura/bench/bench_main.cc
new file mode 100644
index 00000000000..17ed068e60b
--- /dev/null
+++ b/chromium/ui/aura/bench/bench_main.cc
@@ -0,0 +1,367 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/i18n/icu_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_split.h"
+#include "base/time/time.h"
+#include "cc/output/context_provider.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/skia/include/core/SkXfermode.h"
+#include "ui/aura/client/default_capture_client.h"
+#include "ui/aura/env.h"
+#include "ui/aura/focus_manager.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/test/test_screen.h"
+#include "ui/aura/window.h"
+#include "ui/base/hit_test.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/ui_base_paths.h"
+#include "ui/compositor/compositor.h"
+#include "ui/compositor/compositor_observer.h"
+#include "ui/compositor/debug_utils.h"
+#include "ui/compositor/layer.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/skia_util.h"
+#ifndef GL_GLEXT_PROTOTYPES
+#define GL_GLEXT_PROTOTYPES 1
+#endif
+#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+
+#if defined(USE_X11)
+#include "base/message_loop/message_pump_aurax11.h"
+#endif
+
+using base::TimeTicks;
+using ui::Compositor;
+using ui::Layer;
+using ui::LayerDelegate;
+using WebKit::WebGraphicsContext3D;
+
+namespace {
+
+class ColoredLayer : public Layer, public LayerDelegate {
+ public:
+ explicit ColoredLayer(SkColor color)
+ : Layer(ui::LAYER_TEXTURED),
+ color_(color),
+ draw_(true) {
+ set_delegate(this);
+ }
+
+ virtual ~ColoredLayer() {}
+
+ // Overridden from LayerDelegate:
+ virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE {
+ if (draw_) {
+ canvas->DrawColor(color_);
+ }
+ }
+
+ virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {
+ }
+
+ virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE {
+ return base::Closure();
+ }
+
+ void set_color(SkColor color) { color_ = color; }
+ void set_draw(bool draw) { draw_ = draw; }
+
+ private:
+ SkColor color_;
+ bool draw_;
+
+ DISALLOW_COPY_AND_ASSIGN(ColoredLayer);
+};
+
+const int kFrames = 100;
+
+// Benchmark base class, hooks up drawing callback and displaying FPS.
+class BenchCompositorObserver : public ui::CompositorObserver {
+ public:
+ explicit BenchCompositorObserver(int max_frames)
+ : start_time_(),
+ frames_(0),
+ max_frames_(max_frames) {
+ }
+
+ virtual void OnCompositingDidCommit(ui::Compositor* compositor) OVERRIDE {}
+
+ virtual void OnCompositingStarted(Compositor* compositor,
+ base::TimeTicks start_time) OVERRIDE {}
+
+ virtual void OnCompositingEnded(Compositor* compositor) OVERRIDE {
+ if (start_time_.is_null()) {
+ start_time_ = TimeTicks::Now();
+ } else {
+ ++frames_;
+ if (frames_ % kFrames == 0) {
+ TimeTicks now = TimeTicks::Now();
+ double ms = (now - start_time_).InMillisecondsF() / kFrames;
+ LOG(INFO) << "FPS: " << 1000.f / ms << " (" << ms << " ms)";
+ start_time_ = now;
+ }
+ }
+ if (max_frames_ && frames_ == max_frames_) {
+ base::MessageLoop::current()->Quit();
+ } else {
+ Draw();
+ }
+ }
+
+ virtual void OnCompositingAborted(Compositor* compositor) OVERRIDE {}
+
+ virtual void OnCompositingLockStateChanged(
+ Compositor* compositor) OVERRIDE {}
+
+ virtual void OnUpdateVSyncParameters(ui::Compositor* compositor,
+ base::TimeTicks timebase,
+ base::TimeDelta interval) OVERRIDE {
+ }
+
+ virtual void Draw() {}
+
+ int frames() const { return frames_; }
+
+ private:
+ TimeTicks start_time_;
+ int frames_;
+ int max_frames_;
+
+ DISALLOW_COPY_AND_ASSIGN(BenchCompositorObserver);
+};
+
+class WebGLTexture : public ui::Texture {
+ public:
+ WebGLTexture(WebGraphicsContext3D* context, const gfx::Size& size)
+ : ui::Texture(false, size, 1.0f),
+ context_(context),
+ texture_id_(context_->createTexture()) {
+ context_->bindTexture(GL_TEXTURE_2D, texture_id_);
+ context_->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ context_->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ context_->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ context_->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ context_->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
+ size.width(), size.height(), 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ }
+
+ virtual unsigned int PrepareTexture() OVERRIDE {
+ return texture_id_;
+ }
+
+ virtual WebGraphicsContext3D* HostContext3D() OVERRIDE {
+ return context_;
+ }
+
+ private:
+ virtual ~WebGLTexture() {
+ context_->deleteTexture(texture_id_);
+ }
+
+ WebGraphicsContext3D* context_;
+ unsigned texture_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebGLTexture);
+};
+
+// A benchmark that adds a texture layer that is updated every frame.
+class WebGLBench : public BenchCompositorObserver {
+ public:
+ WebGLBench(Layer* parent, Compositor* compositor, int max_frames)
+ : BenchCompositorObserver(max_frames),
+ parent_(parent),
+ webgl_(ui::LAYER_TEXTURED),
+ compositor_(compositor),
+ context_provider_(),
+ texture_(),
+ fbo_(0),
+ do_draw_(true) {
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ do_draw_ = !command_line->HasSwitch("disable-draw");
+
+ std::string webgl_size = command_line->GetSwitchValueASCII("webgl-size");
+ int width = 0;
+ int height = 0;
+ if (!webgl_size.empty()) {
+ std::vector<std::string> split_size;
+ base::SplitString(webgl_size, 'x', &split_size);
+ if (split_size.size() == 2) {
+ width = atoi(split_size[0].c_str());
+ height = atoi(split_size[1].c_str());
+ }
+ }
+ if (!width || !height) {
+ width = 800;
+ height = 600;
+ }
+ gfx::Rect bounds(width, height);
+ webgl_.SetBounds(bounds);
+ parent_->Add(&webgl_);
+
+ context_provider_ = ui::ContextFactory::GetInstance()
+ ->OffscreenContextProviderForMainThread();
+ WebKit::WebGraphicsContext3D* context = context_provider_->Context3d();
+ context->makeContextCurrent();
+ texture_ = new WebGLTexture(context, bounds.size());
+ fbo_ = context->createFramebuffer();
+ compositor->AddObserver(this);
+ webgl_.SetExternalTexture(texture_.get());
+ context->bindFramebuffer(GL_FRAMEBUFFER, fbo_);
+ context->framebufferTexture2D(
+ GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, texture_->PrepareTexture(), 0);
+ context->clearColor(0.f, 1.f, 0.f, 1.f);
+ context->clear(GL_COLOR_BUFFER_BIT);
+ context->flush();
+ }
+
+ virtual ~WebGLBench() {
+ context_provider_->Context3d()->makeContextCurrent();
+ context_provider_->Context3d()->deleteFramebuffer(fbo_);
+ webgl_.SetExternalTexture(NULL);
+ texture_ = NULL;
+ compositor_->RemoveObserver(this);
+ }
+
+ virtual void Draw() OVERRIDE {
+ if (do_draw_) {
+ WebKit::WebGraphicsContext3D* context = context_provider_->Context3d();
+ context->makeContextCurrent();
+ context->clearColor((frames() % kFrames)*1.0/kFrames, 1.f, 0.f, 1.f);
+ context->clear(GL_COLOR_BUFFER_BIT);
+ context->flush();
+ }
+ webgl_.SetExternalTexture(texture_.get());
+ webgl_.SchedulePaint(gfx::Rect(webgl_.bounds().size()));
+ compositor_->ScheduleDraw();
+ }
+
+ private:
+ Layer* parent_;
+ Layer webgl_;
+ Compositor* compositor_;
+ scoped_refptr<cc::ContextProvider> context_provider_;
+ scoped_refptr<WebGLTexture> texture_;
+
+ // The FBO that is used to render to the texture.
+ unsigned int fbo_;
+
+ // Whether or not to draw to the texture every frame.
+ bool do_draw_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebGLBench);
+};
+
+// A benchmark that paints (in software) all tiles every frame.
+class SoftwareScrollBench : public BenchCompositorObserver {
+ public:
+ SoftwareScrollBench(ColoredLayer* layer,
+ Compositor* compositor,
+ int max_frames)
+ : BenchCompositorObserver(max_frames),
+ layer_(layer),
+ compositor_(compositor) {
+ compositor->AddObserver(this);
+ layer_->set_draw(
+ !CommandLine::ForCurrentProcess()->HasSwitch("disable-draw"));
+ }
+
+ virtual ~SoftwareScrollBench() {
+ compositor_->RemoveObserver(this);
+ }
+
+ virtual void Draw() OVERRIDE {
+ layer_->set_color(
+ SkColorSetARGBInline(255*(frames() % kFrames)/kFrames, 255, 0, 255));
+ layer_->SchedulePaint(gfx::Rect(layer_->bounds().size()));
+ }
+
+ private:
+ ColoredLayer* layer_;
+ Compositor* compositor_;
+
+ DISALLOW_COPY_AND_ASSIGN(SoftwareScrollBench);
+};
+
+} // namespace
+
+int main(int argc, char** argv) {
+ CommandLine::Init(argc, argv);
+
+ base::AtExitManager exit_manager;
+
+ ui::RegisterPathProvider();
+ icu_util::Initialize();
+ ResourceBundle::InitSharedInstanceWithLocale("en-US", NULL);
+
+ base::MessageLoop message_loop(base::MessageLoop::TYPE_UI);
+ aura::Env::GetInstance();
+ scoped_ptr<aura::TestScreen> test_screen(
+ aura::TestScreen::CreateFullscreen());
+ gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, test_screen.get());
+ scoped_ptr<aura::RootWindow> root_window(
+ test_screen->CreateRootWindowForPrimaryDisplay());
+ aura::client::SetCaptureClient(
+ root_window.get(),
+ new aura::client::DefaultCaptureClient(root_window.get()));
+
+ scoped_ptr<aura::client::FocusClient> focus_client(new aura::FocusManager);
+ aura::client::SetFocusClient(root_window.get(), focus_client.get());
+
+ // add layers
+ ColoredLayer background(SK_ColorRED);
+ background.SetBounds(root_window->bounds());
+ root_window->layer()->Add(&background);
+
+ ColoredLayer window(SK_ColorBLUE);
+ window.SetBounds(gfx::Rect(background.bounds().size()));
+ background.Add(&window);
+
+ Layer content_layer(ui::LAYER_NOT_DRAWN);
+
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ bool force = command_line->HasSwitch("force-render-surface");
+ content_layer.SetForceRenderSurface(force);
+ gfx::Rect bounds(window.bounds().size());
+ bounds.Inset(0, 30, 0, 0);
+ content_layer.SetBounds(bounds);
+ window.Add(&content_layer);
+
+ ColoredLayer page_background(SK_ColorWHITE);
+ page_background.SetBounds(gfx::Rect(content_layer.bounds().size()));
+ content_layer.Add(&page_background);
+
+ int frames = atoi(command_line->GetSwitchValueASCII("frames").c_str());
+ scoped_ptr<BenchCompositorObserver> bench;
+
+ if (command_line->HasSwitch("bench-software-scroll")) {
+ bench.reset(new SoftwareScrollBench(&page_background,
+ root_window->compositor(),
+ frames));
+ } else {
+ bench.reset(new WebGLBench(&page_background,
+ root_window->compositor(),
+ frames));
+ }
+
+#ifndef NDEBUG
+ ui::PrintLayerHierarchy(root_window->layer(), gfx::Point(100, 100));
+#endif
+
+ root_window->ShowRootWindow();
+ base::MessageLoopForUI::current()->Run();
+ focus_client.reset();
+ root_window.reset();
+
+ return 0;
+}
diff --git a/chromium/ui/aura/client/activation_change_observer.cc b/chromium/ui/aura/client/activation_change_observer.cc
new file mode 100644
index 00000000000..db9f332cfae
--- /dev/null
+++ b/chromium/ui/aura/client/activation_change_observer.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/client/activation_change_observer.h"
+
+#include "ui/aura/window.h"
+#include "ui/aura/window_property.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(aura::client::ActivationChangeObserver*)
+
+namespace aura {
+namespace client {
+
+DEFINE_LOCAL_WINDOW_PROPERTY_KEY(
+ ActivationChangeObserver*, kActivationChangeObserverKey, NULL);
+
+void SetActivationChangeObserver(
+ Window* window,
+ ActivationChangeObserver* observer) {
+ window->SetProperty(kActivationChangeObserverKey, observer);
+}
+
+ActivationChangeObserver* GetActivationChangeObserver(Window* window) {
+ return window ? window->GetProperty(kActivationChangeObserverKey) : NULL;
+}
+
+} // namespace client
+} // namespace aura
diff --git a/chromium/ui/aura/client/activation_change_observer.h b/chromium/ui/aura/client/activation_change_observer.h
new file mode 100644
index 00000000000..2f247db04b3
--- /dev/null
+++ b/chromium/ui/aura/client/activation_change_observer.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_CLIENT_ACTIVATION_CHANGE_OBSERVER_H_
+#define UI_AURA_CLIENT_ACTIVATION_CHANGE_OBSERVER_H_
+
+#include "ui/aura/aura_export.h"
+
+namespace aura {
+class Window;
+
+namespace client {
+
+class AURA_EXPORT ActivationChangeObserver {
+ public:
+ // Called when |active| gains focus, or there is no active window
+ // (|active| is NULL in this case.) |old_active| refers to the
+ // previous active window or NULL if there was no previously active
+ // window.
+ virtual void OnWindowActivated(Window* gained_active,
+ Window* lost_active) = 0;
+
+ // Called when during window activation the currently active window is
+ // selected for activation. This can happen when a window requested for
+ // activation cannot be activated because a system modal window is active.
+ virtual void OnAttemptToReactivateWindow(aura::Window* request_active,
+ aura::Window* actual_active) {}
+
+ protected:
+ virtual ~ActivationChangeObserver() {}
+};
+
+// Gets/Sets the ActivationChangeObserver for a specific window. This observer
+// is notified after the ActivationClient notifies all registered observers.
+AURA_EXPORT void SetActivationChangeObserver(
+ Window* window,
+ ActivationChangeObserver* observer);
+AURA_EXPORT ActivationChangeObserver* GetActivationChangeObserver(
+ Window* window);
+
+} // namespace client
+} // namespace aura
+
+#endif // UI_AURA_CLIENT_ACTIVATION_CHANGE_OBSERVER_H_
diff --git a/chromium/ui/aura/client/activation_client.cc b/chromium/ui/aura/client/activation_client.cc
new file mode 100644
index 00000000000..b55702381ea
--- /dev/null
+++ b/chromium/ui/aura/client/activation_client.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/client/activation_client.h"
+
+#include "ui/aura/root_window.h"
+#include "ui/aura/window_property.h"
+
+DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(AURA_EXPORT, aura::Window*)
+DECLARE_WINDOW_PROPERTY_TYPE(aura::client::ActivationClient*)
+
+namespace aura {
+namespace client {
+
+DEFINE_WINDOW_PROPERTY_KEY(
+ ActivationClient*, kRootWindowActivationClientKey, NULL);
+DEFINE_WINDOW_PROPERTY_KEY(bool, kHideOnDeactivate, false);
+
+void SetActivationClient(RootWindow* root_window, ActivationClient* client) {
+ root_window->SetProperty(kRootWindowActivationClientKey, client);
+}
+
+ActivationClient* GetActivationClient(RootWindow* root_window) {
+ return root_window ?
+ root_window->GetProperty(kRootWindowActivationClientKey) : NULL;
+}
+
+void SetHideOnDeactivate(Window* window, bool hide_on_deactivate) {
+ window->SetProperty(kHideOnDeactivate, hide_on_deactivate);
+}
+
+bool GetHideOnDeactivate(Window* window) {
+ return window->GetProperty(kHideOnDeactivate);
+}
+
+} // namespace client
+} // namespace aura
diff --git a/chromium/ui/aura/client/activation_client.h b/chromium/ui/aura/client/activation_client.h
new file mode 100644
index 00000000000..db4cd1b15b6
--- /dev/null
+++ b/chromium/ui/aura/client/activation_client.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_CLIENT_ACTIVATION_CLIENT_H_
+#define UI_AURA_CLIENT_ACTIVATION_CLIENT_H_
+
+#include "ui/aura/aura_export.h"
+#include "ui/aura/window.h"
+
+namespace ui {
+class Event;
+}
+
+namespace aura {
+class RootWindow;
+
+namespace client {
+class ActivationChangeObserver;
+
+// An interface implemented by an object that manages window activation.
+class AURA_EXPORT ActivationClient {
+ public:
+ // Adds/Removes ActivationChangeObservers.
+ virtual void AddObserver(ActivationChangeObserver* observer) = 0;
+ virtual void RemoveObserver(ActivationChangeObserver* observer) = 0;
+
+ // Activates |window|. If |window| is NULL, nothing happens.
+ virtual void ActivateWindow(Window* window) = 0;
+
+ // Deactivates |window|. What (if anything) is activated next is up to the
+ // client. If |window| is NULL, nothing happens.
+ virtual void DeactivateWindow(Window* window) = 0;
+
+ // Retrieves the active window, or NULL if there is none.
+ virtual Window* GetActiveWindow() = 0;
+
+ // Retrieves the activatable window for |window|, or NULL if there is none.
+ // Note that this is often but not always the toplevel window (see
+ // GetToplevelWindow() below), as the toplevel window may not be activatable
+ // (for example it may be blocked by a modal transient, or some other
+ // condition).
+ virtual Window* GetActivatableWindow(Window* window) = 0;
+
+ // Retrieves the toplevel window for |window|, or NULL if there is none.
+ virtual Window* GetToplevelWindow(Window* window) = 0;
+
+ // Invoked prior to |window| getting focus as a result of the |event|. |event|
+ // may be NULL. Returning false blocks |window| from getting focus.
+ virtual bool OnWillFocusWindow(Window* window, const ui::Event* event) = 0;
+
+ // Returns true if |window| can be activated, false otherwise. If |window| has
+ // a modal child it can not be activated.
+ virtual bool CanActivateWindow(Window* window) const = 0;
+
+ protected:
+ virtual ~ActivationClient() {}
+};
+
+// Sets/Gets the activation client on the RootWindow.
+AURA_EXPORT void SetActivationClient(RootWindow* root_window,
+ ActivationClient* client);
+AURA_EXPORT ActivationClient* GetActivationClient(RootWindow* root_window);
+
+// Some types of transient window are only visible when active.
+// The transient parents of these windows may have visual appearance properties
+// that differ from transient parents that can be deactivated.
+// The presence of this property implies these traits.
+// TODO(beng): currently the UI framework (views) implements the actual
+// close-on-deactivate component of this feature but it should be
+// possible to implement in the aura client.
+AURA_EXPORT void SetHideOnDeactivate(Window* window, bool hide_on_deactivate);
+AURA_EXPORT bool GetHideOnDeactivate(Window* window);
+
+} // namespace clients
+} // namespace aura
+
+#endif // UI_AURA_CLIENT_ACTIVATION_CLIENT_H_
diff --git a/chromium/ui/aura/client/activation_delegate.cc b/chromium/ui/aura/client/activation_delegate.cc
new file mode 100644
index 00000000000..51b22b00a61
--- /dev/null
+++ b/chromium/ui/aura/client/activation_delegate.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/client/activation_delegate.h"
+
+#include "ui/aura/window.h"
+#include "ui/aura/window_property.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(aura::client::ActivationDelegate*)
+
+namespace aura {
+namespace client {
+
+DEFINE_LOCAL_WINDOW_PROPERTY_KEY(
+ ActivationDelegate*, kActivationDelegateKey, NULL);
+
+void SetActivationDelegate(Window* window, ActivationDelegate* delegate) {
+ window->SetProperty(kActivationDelegateKey, delegate);
+}
+
+ActivationDelegate* GetActivationDelegate(Window* window) {
+ return window->GetProperty(kActivationDelegateKey);
+}
+
+} // namespace client
+} // namespace aura
diff --git a/chromium/ui/aura/client/activation_delegate.h b/chromium/ui/aura/client/activation_delegate.h
new file mode 100644
index 00000000000..0ce86388f52
--- /dev/null
+++ b/chromium/ui/aura/client/activation_delegate.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_CLIENT_ACTIVATION_DELEGATE_H_
+#define UI_AURA_CLIENT_ACTIVATION_DELEGATE_H_
+
+#include "ui/aura/aura_export.h"
+
+namespace ui {
+class Event;
+}
+
+namespace aura {
+class Window;
+namespace client {
+
+// An interface implemented by an object that configures and responds to changes
+// to a window's activation state.
+class AURA_EXPORT ActivationDelegate {
+ public:
+ // Returns true if the window should be activated.
+ virtual bool ShouldActivate() const = 0;
+
+ protected:
+ virtual ~ActivationDelegate() {}
+};
+
+// Sets/Gets the ActivationDelegate on the Window. No ownership changes.
+AURA_EXPORT void SetActivationDelegate(Window* window,
+ ActivationDelegate* delegate);
+AURA_EXPORT ActivationDelegate* GetActivationDelegate(Window* window);
+
+} // namespace client
+} // namespace aura
+
+#endif // UI_AURA_CLIENT_ACTIVATION_DELEGATE_H_
diff --git a/chromium/ui/aura/client/animation_host.cc b/chromium/ui/aura/client/animation_host.cc
new file mode 100644
index 00000000000..5df22babc62
--- /dev/null
+++ b/chromium/ui/aura/client/animation_host.cc
@@ -0,0 +1,32 @@
+// 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 "ui/aura/client/animation_host.h"
+
+#include "base/compiler_specific.h"
+#include "ui/aura/aura_export.h"
+
+#include "ui/aura/env.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/window_property.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(aura::client::AnimationHost*)
+
+namespace aura {
+namespace client {
+
+DEFINE_WINDOW_PROPERTY_KEY(AnimationHost*, kRootWindowAnimationHostKey, NULL);
+
+void SetAnimationHost(Window* window, AnimationHost* animation_host) {
+ DCHECK(window);
+ window->SetProperty(kRootWindowAnimationHostKey, animation_host);
+}
+
+AnimationHost* GetAnimationHost(Window* window) {
+ DCHECK(window);
+ return window->GetProperty(kRootWindowAnimationHostKey);
+}
+
+} // namespace client
+} // namespace aura
diff --git a/chromium/ui/aura/client/animation_host.h b/chromium/ui/aura/client/animation_host.h
new file mode 100644
index 00000000000..549c3469a8b
--- /dev/null
+++ b/chromium/ui/aura/client/animation_host.h
@@ -0,0 +1,42 @@
+// 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 UI_AURA_CLIENT_ANIMATION_HOST_H_
+#define UI_AURA_CLIENT_ANIMATION_HOST_H_
+
+#include "base/compiler_specific.h"
+#include "ui/aura/aura_export.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace aura {
+class Window;
+namespace client {
+
+// Interface for top level window host of animation. Communicates additional
+// bounds required for animation as well as animation completion for deferring
+// window closes on hide.
+class AURA_EXPORT AnimationHost {
+ public:
+ // Ensure the host window is at least this large so that transitions have
+ // sufficient space.
+ virtual void SetHostTransitionBounds(const gfx::Rect& bounds) = 0;
+
+ // Called after the window has faded out on a hide.
+ virtual void OnWindowHidingAnimationCompleted() = 0;
+
+ protected:
+ virtual ~AnimationHost() {}
+};
+
+AURA_EXPORT void SetAnimationHost(Window* window,
+ AnimationHost* animation_host);
+AURA_EXPORT AnimationHost* GetAnimationHost(Window* window);
+
+} // namespace client
+} // namespace aura
+
+#endif // UI_AURA_CLIENT_ANIMATION_HOST_H_
diff --git a/chromium/ui/aura/client/aura_constants.cc b/chromium/ui/aura/client/aura_constants.cc
new file mode 100644
index 00000000000..7ba9f874d25
--- /dev/null
+++ b/chromium/ui/aura/client/aura_constants.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/client/aura_constants.h"
+
+#include "ui/aura/window_property.h"
+#include "ui/gfx/rect.h"
+
+DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(AURA_EXPORT, bool)
+DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(AURA_EXPORT, ui::ModalType)
+DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(AURA_EXPORT, gfx::Rect*)
+DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(AURA_EXPORT, ui::InputMethod*)
+DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(AURA_EXPORT, ui::WindowShowState)
+
+namespace aura {
+namespace client {
+
+// Alphabetical sort.
+
+DEFINE_WINDOW_PROPERTY_KEY(bool, kAlwaysOnTopKey, false);
+DEFINE_WINDOW_PROPERTY_KEY(bool, kAnimationsDisabledKey, false);
+DEFINE_WINDOW_PROPERTY_KEY(bool, kCanMaximizeKey, false);
+DEFINE_WINDOW_PROPERTY_KEY(bool, kCanResizeKey, true);
+DEFINE_WINDOW_PROPERTY_KEY(bool, kConstrainedWindowKey, false);
+DEFINE_WINDOW_PROPERTY_KEY(bool, kDrawAttentionKey, false);
+DEFINE_WINDOW_PROPERTY_KEY(ui::ModalType, kModalKey, ui::MODAL_TYPE_NONE);
+// gfx::Rect object for RestoreBoundsKey property is owned by the window
+// and will be freed automatically.
+DEFINE_OWNED_WINDOW_PROPERTY_KEY(gfx::Rect, kRestoreBoundsKey, NULL);
+DEFINE_WINDOW_PROPERTY_KEY(
+ ui::WindowShowState, kRestoreShowStateKey, ui::SHOW_STATE_DEFAULT);
+DEFINE_WINDOW_PROPERTY_KEY(ui::InputMethod*, kRootWindowInputMethodKey, NULL);
+DEFINE_WINDOW_PROPERTY_KEY(
+ ui::WindowShowState, kShowStateKey, ui::SHOW_STATE_DEFAULT);
+
+} // namespace client
+} // namespace aura
diff --git a/chromium/ui/aura/client/aura_constants.h b/chromium/ui/aura/client/aura_constants.h
new file mode 100644
index 00000000000..32c524fde95
--- /dev/null
+++ b/chromium/ui/aura/client/aura_constants.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_CLIENT_AURA_CONSTANTS_H_
+#define UI_AURA_CLIENT_AURA_CONSTANTS_H_
+
+#include "ui/aura/aura_export.h"
+#include "ui/aura/window.h"
+#include "ui/base/ui_base_types.h"
+
+namespace ui {
+class InputMethod;
+}
+
+namespace aura {
+namespace client {
+
+// Alphabetical sort.
+
+// A property key to store always-on-top flag.
+AURA_EXPORT extern const WindowProperty<bool>* const kAlwaysOnTopKey;
+
+// A property key to store whether animations are disabled for the window. Type
+// of value is an int.
+AURA_EXPORT extern const WindowProperty<bool>* const kAnimationsDisabledKey;
+
+// A property key to store the can-maximize flag.
+AURA_EXPORT extern const WindowProperty<bool>* const kCanMaximizeKey;
+
+// A property key to store the can-resize flag.
+AURA_EXPORT extern const WindowProperty<bool>* const kCanResizeKey;
+
+// A property key to store if a window is a constrained window or not.
+AURA_EXPORT extern const WindowProperty<bool>* const kConstrainedWindowKey;
+
+// A property key to indicate that a window should show that it deserves
+// attention.
+AURA_EXPORT extern const aura::WindowProperty<bool>* const kDrawAttentionKey;
+
+// A property key to store the window modality.
+AURA_EXPORT extern const WindowProperty<ui::ModalType>* const kModalKey;
+
+// A property key to store the restore bounds for a window.
+AURA_EXPORT extern const WindowProperty<gfx::Rect*>* const kRestoreBoundsKey;
+
+// A property key to store ui::WindowShowState for restoring a window.
+// Used in Ash to remember the show state before the window was minimized.
+AURA_EXPORT extern const WindowProperty<ui::WindowShowState>* const
+ kRestoreShowStateKey;
+
+// A property key to store an input method object that handles a key event.
+AURA_EXPORT extern const WindowProperty<ui::InputMethod*>* const
+ kRootWindowInputMethodKey;
+
+// A property key to store ui::WindowShowState for a window.
+// See ui/base/ui_base_types.h for its definition.
+AURA_EXPORT extern const WindowProperty<ui::WindowShowState>* const
+ kShowStateKey;
+
+// Alphabetical sort.
+
+} // namespace client
+} // namespace aura
+
+#endif // UI_AURA_CLIENT_AURA_CONSTANTS_H_
diff --git a/chromium/ui/aura/client/capture_client.cc b/chromium/ui/aura/client/capture_client.cc
new file mode 100644
index 00000000000..d74940a67e5
--- /dev/null
+++ b/chromium/ui/aura/client/capture_client.cc
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/client/capture_client.h"
+
+#include "ui/aura/root_window.h"
+#include "ui/aura/window_property.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(aura::client::CaptureClient*)
+
+namespace aura {
+namespace client {
+
+DEFINE_WINDOW_PROPERTY_KEY(
+ CaptureClient*, kRootWindowCaptureClientKey, NULL);
+
+void SetCaptureClient(RootWindow* root_window, CaptureClient* client) {
+ root_window->SetProperty(kRootWindowCaptureClientKey, client);
+}
+
+CaptureClient* GetCaptureClient(RootWindow* root_window) {
+ return root_window ?
+ root_window->GetProperty(kRootWindowCaptureClientKey) : NULL;
+}
+
+Window* GetCaptureWindow(Window* window) {
+ RootWindow* root_window = window->GetRootWindow();
+ return root_window ?
+ GetCaptureClient(root_window)->GetCaptureWindow() : NULL;
+}
+
+} // namespace client
+} // namespace aura
diff --git a/chromium/ui/aura/client/capture_client.h b/chromium/ui/aura/client/capture_client.h
new file mode 100644
index 00000000000..b244c0caef1
--- /dev/null
+++ b/chromium/ui/aura/client/capture_client.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_CLIENT_CAPTURE_CLIENT_H_
+#define UI_AURA_CLIENT_CAPTURE_CLIENT_H_
+
+#include "ui/aura/aura_export.h"
+
+namespace aura {
+class RootWindow;
+class Window;
+
+namespace client {
+
+// An interface implemented by an object that manages input capture.
+class AURA_EXPORT CaptureClient {
+ public:
+ // Does a capture on the |window|.
+ virtual void SetCapture(Window* window) = 0;
+
+ // Releases a capture from the |window|.
+ virtual void ReleaseCapture(Window* window) = 0;
+
+ // Returns the current capture window.
+ virtual Window* GetCaptureWindow() = 0;
+
+ protected:
+ virtual ~CaptureClient() {}
+};
+
+// Sets/Gets the capture client on the RootWindow.
+AURA_EXPORT void SetCaptureClient(RootWindow* root_window,
+ CaptureClient* client);
+AURA_EXPORT CaptureClient* GetCaptureClient(RootWindow* root_window);
+
+// A utility function to get the current capture window. Returns NULL
+// if the window doesn't have a root window, or there is no capture window.
+AURA_EXPORT Window* GetCaptureWindow(Window* window);
+
+} // namespace clients
+} // namespace aura
+
+#endif // UI_AURA_CLIENT_CAPTURE_CLIENT_H_
diff --git a/chromium/ui/aura/client/capture_delegate.h b/chromium/ui/aura/client/capture_delegate.h
new file mode 100644
index 00000000000..c727b0a0697
--- /dev/null
+++ b/chromium/ui/aura/client/capture_delegate.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_CLIENT_CAPTURE_DELEGATE_H_
+#define UI_AURA_CLIENT_CAPTURE_DELEGATE_H_
+
+#include "ui/aura/aura_export.h"
+
+namespace aura {
+class Window;
+namespace client {
+
+// This interface provides API to change the RootWindow's capture state
+// without exposing them as RootWindow API.
+class AURA_EXPORT CaptureDelegate {
+ public:
+ // Called when a capture is set on the |new_capture| which is owned by
+ // this root window, and/or a capture is released on the |old_capture|
+ // which is owned by this root window.
+ virtual void UpdateCapture(aura::Window* old_capture,
+ aura::Window* new_capture) = 0;
+ // Sets/Release a native capture on host windows.
+ virtual void SetNativeCapture() = 0;
+ virtual void ReleaseNativeCapture() = 0;
+
+ protected:
+ virtual ~CaptureDelegate() {}
+};
+
+} // namespace client
+} // namespace aura
+
+#endif // UI_AURA_CLIENT_CAPTURE_DELEGATE_H_
diff --git a/chromium/ui/aura/client/cursor_client.cc b/chromium/ui/aura/client/cursor_client.cc
new file mode 100644
index 00000000000..ca105ed7556
--- /dev/null
+++ b/chromium/ui/aura/client/cursor_client.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/client/cursor_client.h"
+
+#include "ui/aura/window.h"
+#include "ui/aura/window_property.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(aura::client::CursorClient*)
+
+namespace aura {
+namespace client {
+
+// A property key to store a client that handles window moves.
+DEFINE_LOCAL_WINDOW_PROPERTY_KEY(
+ CursorClient*, kCursorClientKey, NULL);
+
+void SetCursorClient(Window* window, CursorClient* client) {
+ window->SetProperty(kCursorClientKey, client);
+}
+
+CursorClient* GetCursorClient(Window* window) {
+ return window->GetProperty(kCursorClientKey);
+}
+
+} // namespace client
+} // namespace aura
diff --git a/chromium/ui/aura/client/cursor_client.h b/chromium/ui/aura/client/cursor_client.h
new file mode 100644
index 00000000000..acdf255b850
--- /dev/null
+++ b/chromium/ui/aura/client/cursor_client.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_CLIENT_CURSOR_CLIENT_H_
+#define UI_AURA_CLIENT_CURSOR_CLIENT_H_
+
+#include "base/strings/string16.h"
+#include "ui/aura/aura_export.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace gfx {
+class Display;
+}
+
+namespace aura {
+class Window;
+namespace client {
+class CursorClientObserver;
+
+// An interface that receives cursor change events.
+class AURA_EXPORT CursorClient {
+ public:
+ // Notes that |window| has requested the change to |cursor|.
+ virtual void SetCursor(gfx::NativeCursor cursor) = 0;
+
+ // Shows the cursor. This does not take effect When mouse events are disabled.
+ virtual void ShowCursor() = 0;
+
+ // Hides the cursor. Mouse events keep being sent even when the cursor is
+ // invisible.
+ virtual void HideCursor() = 0;
+
+ // Sets the scale of the mouse cursor icon.
+ virtual void SetScale(float scale) = 0;
+
+ // Gets whether the cursor is visible.
+ virtual bool IsCursorVisible() const = 0;
+
+ // Makes mouse events start being sent and shows the cursor if it was hidden
+ // with DisableMouseEvents.
+ virtual void EnableMouseEvents() = 0;
+
+ // Makes mouse events stop being sent and hides the cursor if it is visible.
+ virtual void DisableMouseEvents() = 0;
+
+ // Returns true if mouse events are enabled.
+ virtual bool IsMouseEventsEnabled() const = 0;
+
+ // Sets the display for the cursor.
+ virtual void SetDisplay(const gfx::Display& display) = 0;
+
+ // Locks the cursor change. The cursor type, cursor visibility, and mouse
+ // events enable state never change as long as lock is held by anyone.
+ virtual void LockCursor() = 0;
+
+ // Unlocks the cursor change. If all the locks are released, the cursor type,
+ // cursor visibility, and mouse events enable state are restored to the ones
+ // set by the lastest call of SetCursor, ShowCursor/HideCursor, and
+ // EnableMouseEvents/DisableMouseEvents.
+ virtual void UnlockCursor() = 0;
+
+ // Used to add or remove a CursorClientObserver.
+ virtual void AddObserver(CursorClientObserver* observer) = 0;
+ virtual void RemoveObserver(CursorClientObserver* observer) = 0;
+
+ protected:
+ virtual ~CursorClient() {}
+};
+
+// Sets/Gets the activation client for the specified window.
+AURA_EXPORT void SetCursorClient(Window* window,
+ CursorClient* client);
+AURA_EXPORT CursorClient* GetCursorClient(Window* window);
+
+} // namespace client
+} // namespace aura
+
+#endif // UI_AURA_CLIENT_CURSOR_CLIENT_H_
diff --git a/chromium/ui/aura/client/cursor_client_observer.cc b/chromium/ui/aura/client/cursor_client_observer.cc
new file mode 100644
index 00000000000..6e40e60c9d0
--- /dev/null
+++ b/chromium/ui/aura/client/cursor_client_observer.cc
@@ -0,0 +1,7 @@
+// Copyright (c) 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.
+
+// Added to make sure that something is built in aura with this
+// include so that the symbol CursorClientObserver is exported.
+#include "ui/aura/client/cursor_client_observer.h"
diff --git a/chromium/ui/aura/client/cursor_client_observer.h b/chromium/ui/aura/client/cursor_client_observer.h
new file mode 100644
index 00000000000..1a6d1a83048
--- /dev/null
+++ b/chromium/ui/aura/client/cursor_client_observer.h
@@ -0,0 +1,24 @@
+// Copyright (c) 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 UI_AURA_CLIENT_CURSOR_CLIENT_OBSERVER_H_
+#define UI_AURA_CLIENT_CURSOR_CLIENT_OBSERVER_H_
+
+#include "ui/aura/aura_export.h"
+
+namespace aura {
+namespace client {
+
+class AURA_EXPORT CursorClientObserver {
+ public:
+ virtual void OnCursorVisibilityChanged(bool is_visible) = 0;
+
+ protected:
+ virtual ~CursorClientObserver() {}
+};
+
+} // namespace client
+} // namespace aura
+
+#endif // UI_AURA_CLIENT_CURSOR_CLIENT_OBSERVER_H_
diff --git a/chromium/ui/aura/client/default_capture_client.cc b/chromium/ui/aura/client/default_capture_client.cc
new file mode 100644
index 00000000000..94f5f810532
--- /dev/null
+++ b/chromium/ui/aura/client/default_capture_client.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/client/default_capture_client.h"
+
+#include "ui/aura/root_window.h"
+
+namespace aura {
+namespace client {
+
+DefaultCaptureClient::DefaultCaptureClient(RootWindow* root_window)
+ : root_window_(root_window),
+ capture_window_(NULL) {
+ client::SetCaptureClient(root_window_, this);
+}
+
+DefaultCaptureClient::~DefaultCaptureClient() {
+ client::SetCaptureClient(root_window_, NULL);
+}
+
+void DefaultCaptureClient::SetCapture(Window* window) {
+ if (capture_window_ == window)
+ return;
+ if (window)
+ root_window_->gesture_recognizer()->
+ TransferEventsTo(capture_window_, window);
+
+ Window* old_capture_window = capture_window_;
+ capture_window_ = window;
+
+ if (capture_window_)
+ root_window_->SetNativeCapture();
+ else
+ root_window_->ReleaseNativeCapture();
+
+ root_window_->UpdateCapture(old_capture_window, capture_window_);
+}
+
+void DefaultCaptureClient::ReleaseCapture(Window* window) {
+ if (capture_window_ != window)
+ return;
+ SetCapture(NULL);
+}
+
+Window* DefaultCaptureClient::GetCaptureWindow() {
+ return capture_window_;
+}
+
+} // namespace client
+} // namespace aura
diff --git a/chromium/ui/aura/client/default_capture_client.h b/chromium/ui/aura/client/default_capture_client.h
new file mode 100644
index 00000000000..a628b2d96ef
--- /dev/null
+++ b/chromium/ui/aura/client/default_capture_client.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_CLIENT_DEFAULT_CAPTURE_CLIENT_H_
+#define UI_AURA_CLIENT_DEFAULT_CAPTURE_CLIENT_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "ui/aura/aura_export.h"
+#include "ui/aura/client/capture_client.h"
+
+namespace aura {
+namespace client {
+
+class AURA_EXPORT DefaultCaptureClient : public client::CaptureClient {
+ public:
+ explicit DefaultCaptureClient(RootWindow* root_window);
+ virtual ~DefaultCaptureClient();
+
+ private:
+ // Overridden from client::CaptureClient:
+ virtual void SetCapture(Window* window) OVERRIDE;
+ virtual void ReleaseCapture(Window* window) OVERRIDE;
+ virtual Window* GetCaptureWindow() OVERRIDE;
+
+ RootWindow* root_window_;
+ Window* capture_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(DefaultCaptureClient);
+};
+
+} // namespace client
+} // namespace aura
+
+#endif // UI_AURA_CLIENT_DEFAULT_CAPTURE_CLIENT_H_
diff --git a/chromium/ui/aura/client/dispatcher_client.cc b/chromium/ui/aura/client/dispatcher_client.cc
new file mode 100644
index 00000000000..8a2d1c36431
--- /dev/null
+++ b/chromium/ui/aura/client/dispatcher_client.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/client/dispatcher_client.h"
+
+#include "ui/aura/root_window.h"
+#include "ui/aura/window_property.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(aura::client::DispatcherClient*);
+
+namespace aura {
+namespace client {
+
+DEFINE_LOCAL_WINDOW_PROPERTY_KEY(DispatcherClient*, kDispatcherClientKey, NULL);
+
+void SetDispatcherClient(RootWindow* root_window, DispatcherClient* client) {
+ root_window->SetProperty(kDispatcherClientKey, client);
+}
+
+DispatcherClient* GetDispatcherClient(RootWindow* root_window) {
+ return root_window ? root_window->GetProperty(kDispatcherClientKey) : NULL;
+}
+
+} // namespace client
+} // namespace aura
diff --git a/chromium/ui/aura/client/dispatcher_client.h b/chromium/ui/aura/client/dispatcher_client.h
new file mode 100644
index 00000000000..c775611db71
--- /dev/null
+++ b/chromium/ui/aura/client/dispatcher_client.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_CLIENT_DISPATCHER_CLIENT_H_
+#define UI_AURA_CLIENT_DISPATCHER_CLIENT_H_
+
+#include "base/message_loop/message_loop.h"
+#include "ui/aura/aura_export.h"
+#include "ui/aura/window.h"
+
+namespace aura {
+class RootWindow;
+namespace client {
+
+// An interface implemented by an object which handles nested dispatchers.
+class AURA_EXPORT DispatcherClient {
+ public:
+ virtual void RunWithDispatcher(base::MessageLoop::Dispatcher* dispatcher,
+ aura::Window* associated_window,
+ bool nestable_tasks_allowed) = 0;
+};
+
+AURA_EXPORT void SetDispatcherClient(RootWindow* root_window,
+ DispatcherClient* client);
+AURA_EXPORT DispatcherClient* GetDispatcherClient(RootWindow* root_window);
+
+} // namespace client
+} // namespace aura
+
+#endif // UI_AURA_CLIENT_DISPATCHER_CLIENT_H_
diff --git a/chromium/ui/aura/client/drag_drop_client.cc b/chromium/ui/aura/client/drag_drop_client.cc
new file mode 100644
index 00000000000..1122c1bc2d6
--- /dev/null
+++ b/chromium/ui/aura/client/drag_drop_client.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/client/drag_drop_client.h"
+
+#include "ui/aura/root_window.h"
+#include "ui/aura/window_property.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(aura::client::DragDropClient*)
+
+namespace aura {
+namespace client {
+
+DEFINE_LOCAL_WINDOW_PROPERTY_KEY(
+ DragDropClient*, kRootWindowDragDropClientKey, NULL);
+
+void SetDragDropClient(RootWindow* root_window, DragDropClient* client) {
+ root_window->SetProperty(kRootWindowDragDropClientKey, client);
+}
+
+DragDropClient* GetDragDropClient(RootWindow* root_window) {
+ return root_window ?
+ root_window->GetProperty(kRootWindowDragDropClientKey) : NULL;
+}
+
+} // namespace client
+} // namespace aura
diff --git a/chromium/ui/aura/client/drag_drop_client.h b/chromium/ui/aura/client/drag_drop_client.h
new file mode 100644
index 00000000000..c7b145b41aa
--- /dev/null
+++ b/chromium/ui/aura/client/drag_drop_client.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_CLIENT_DRAG_DROP_CLIENT_H_
+#define UI_AURA_CLIENT_DRAG_DROP_CLIENT_H_
+
+#include "ui/aura/aura_export.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace gfx {
+class Point;
+}
+
+namespace ui {
+class LocatedEvent;
+class OSExchangeData;
+}
+
+namespace aura {
+class RootWindow;
+class Window;
+namespace client {
+
+// An interface implemented by an object that controls a drag and drop session.
+class AURA_EXPORT DragDropClient {
+ public:
+ virtual ~DragDropClient() {}
+
+ // Initiates a drag and drop session. Returns the drag operation that was
+ // applied at the end of the drag drop session. |root_location| is in the
+ // RootWindow's coordinate system.
+ virtual int StartDragAndDrop(const ui::OSExchangeData& data,
+ aura::RootWindow* root_window,
+ aura::Window* source_window,
+ const gfx::Point& root_location,
+ int operation,
+ ui::DragDropTypes::DragEventSource source) = 0;
+
+ // Called when mouse is dragged during a drag and drop.
+ virtual void DragUpdate(aura::Window* target,
+ const ui::LocatedEvent& event) = 0;
+
+ // Called when mouse is released during a drag and drop.
+ virtual void Drop(aura::Window* target,
+ const ui::LocatedEvent& event) = 0;
+
+ // Called when a drag and drop session is cancelled.
+ virtual void DragCancel() = 0;
+
+ // Returns true if a drag and drop session is in progress.
+ virtual bool IsDragDropInProgress() = 0;
+};
+
+AURA_EXPORT void SetDragDropClient(RootWindow* root_window,
+ DragDropClient* client);
+AURA_EXPORT DragDropClient* GetDragDropClient(RootWindow* root_window);
+
+} // namespace client
+} // namespace aura
+
+#endif // UI_AURA_CLIENT_DRAG_DROP_CLIENT_H_
diff --git a/chromium/ui/aura/client/drag_drop_delegate.cc b/chromium/ui/aura/client/drag_drop_delegate.cc
new file mode 100644
index 00000000000..92d986be8da
--- /dev/null
+++ b/chromium/ui/aura/client/drag_drop_delegate.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/client/drag_drop_delegate.h"
+
+#include "ui/aura/window.h"
+#include "ui/aura/window_property.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(aura::client::DragDropDelegate*)
+
+namespace aura {
+namespace client {
+
+DEFINE_LOCAL_WINDOW_PROPERTY_KEY(
+ DragDropDelegate*, kDragDropDelegateKey, NULL);
+
+void SetDragDropDelegate(Window* window, DragDropDelegate* delegate) {
+ window->SetProperty(kDragDropDelegateKey, delegate);
+}
+
+DragDropDelegate* GetDragDropDelegate(Window* window) {
+ return window->GetProperty(kDragDropDelegateKey);
+}
+
+} // namespace client
+} // namespace aura
diff --git a/chromium/ui/aura/client/drag_drop_delegate.h b/chromium/ui/aura/client/drag_drop_delegate.h
new file mode 100644
index 00000000000..dde7f32ec73
--- /dev/null
+++ b/chromium/ui/aura/client/drag_drop_delegate.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_CLIENT_DRAG_DROP_DELEGATE_H_
+#define UI_AURA_CLIENT_DRAG_DROP_DELEGATE_H_
+
+#include "ui/aura/aura_export.h"
+
+namespace ui {
+class DropTargetEvent;
+}
+
+namespace aura {
+class Window;
+namespace client {
+
+// Delegate interface for drag and drop actions on aura::Window.
+class AURA_EXPORT DragDropDelegate {
+ public:
+ // OnDragEntered is invoked when the mouse enters this window during a drag &
+ // drop session. This is immediately followed by an invocation of
+ // OnDragUpdated, and eventually one of OnDragExited or OnPerformDrop.
+ virtual void OnDragEntered(const ui::DropTargetEvent& event) = 0;
+
+ // Invoked during a drag and drop session while the mouse is over the window.
+ // This should return a bitmask of the DragDropTypes::DragOperation supported
+ // based on the location of the event. Return 0 to indicate the drop should
+ // not be accepted.
+ virtual int OnDragUpdated(const ui::DropTargetEvent& event) = 0;
+
+ // Invoked during a drag and drop session when the mouse exits the window, or
+ // when the drag session was canceled and the mouse was over the window.
+ virtual void OnDragExited() = 0;
+
+ // Invoked during a drag and drop session when OnDragUpdated returns a valid
+ // operation and the user release the mouse.
+ virtual int OnPerformDrop(const ui::DropTargetEvent& event) = 0;
+
+ protected:
+ virtual ~DragDropDelegate() {}
+};
+
+AURA_EXPORT void SetDragDropDelegate(Window* window,
+ DragDropDelegate* delegate);
+AURA_EXPORT DragDropDelegate* GetDragDropDelegate(Window* window);
+
+} // namespace client
+} // namespace aura
+
+#endif // UI_AURA_CLIENT_DRAG_DROP_DELEGATE_H_
diff --git a/chromium/ui/aura/client/event_client.cc b/chromium/ui/aura/client/event_client.cc
new file mode 100644
index 00000000000..dfb3410da47
--- /dev/null
+++ b/chromium/ui/aura/client/event_client.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/client/event_client.h"
+
+#include "ui/aura/root_window.h"
+#include "ui/aura/window_property.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(aura::client::EventClient*)
+
+namespace aura {
+namespace client {
+
+DEFINE_WINDOW_PROPERTY_KEY(EventClient*, kRootWindowEventClientKey, NULL);
+
+void SetEventClient(RootWindow* root_window, EventClient* client) {
+ root_window->SetProperty(kRootWindowEventClientKey, client);
+}
+
+EventClient* GetEventClient(const RootWindow* root_window) {
+ return root_window ?
+ root_window->GetProperty(kRootWindowEventClientKey) : NULL;
+}
+
+} // namespace client
+} // namespace aura
diff --git a/chromium/ui/aura/client/event_client.h b/chromium/ui/aura/client/event_client.h
new file mode 100644
index 00000000000..31565e2053b
--- /dev/null
+++ b/chromium/ui/aura/client/event_client.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_CLIENT_EVENT_CLIENT_H_
+#define UI_AURA_CLIENT_EVENT_CLIENT_H_
+
+#include "ui/aura/aura_export.h"
+#include "ui/aura/window.h"
+
+namespace aura {
+
+class Event;
+class RootWindow;
+
+namespace client {
+
+// An interface implemented by an object that alters event processing.
+class AURA_EXPORT EventClient {
+ public:
+ // Returns true if events can be processed by |window| or any of its children.
+ virtual bool CanProcessEventsWithinSubtree(const Window* window) const = 0;
+
+ // Returns the top level EventTarget for the current environment.
+ virtual ui::EventTarget* GetToplevelEventTarget() = 0;
+
+ protected:
+ virtual ~EventClient() {}
+};
+
+// Sets/Gets the event client on the RootWindow.
+AURA_EXPORT void SetEventClient(RootWindow* root_window, EventClient* client);
+AURA_EXPORT EventClient* GetEventClient(const RootWindow* root_window);
+
+} // namespace clients
+} // namespace aura
+
+#endif // UI_AURA_CLIENT_EVENT_CLIENT_H_
diff --git a/chromium/ui/aura/client/focus_change_observer.cc b/chromium/ui/aura/client/focus_change_observer.cc
new file mode 100644
index 00000000000..8db471dfd80
--- /dev/null
+++ b/chromium/ui/aura/client/focus_change_observer.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/client/focus_change_observer.h"
+
+#include "ui/aura/window.h"
+#include "ui/aura/window_property.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(aura::client::FocusChangeObserver*)
+
+namespace aura {
+namespace client {
+
+DEFINE_LOCAL_WINDOW_PROPERTY_KEY(
+ FocusChangeObserver*, kFocusChangeObserverKey, NULL);
+
+FocusChangeObserver* GetFocusChangeObserver(Window* window) {
+ return window ? window->GetProperty(kFocusChangeObserverKey) : NULL;
+}
+
+void SetFocusChangeObserver(Window* window,
+ FocusChangeObserver* focus_change_observer) {
+ window->SetProperty(kFocusChangeObserverKey, focus_change_observer);
+}
+
+} // namespace client
+} // namespace aura
diff --git a/chromium/ui/aura/client/focus_change_observer.h b/chromium/ui/aura/client/focus_change_observer.h
new file mode 100644
index 00000000000..46db114b20c
--- /dev/null
+++ b/chromium/ui/aura/client/focus_change_observer.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_CLIENT_FOCUS_CHANGE_OBSERVER_H_
+#define UI_AURA_CLIENT_FOCUS_CHANGE_OBSERVER_H_
+
+#include "ui/aura/aura_export.h"
+
+namespace aura {
+class Window;
+namespace client {
+
+// TODO(beng): this interface will be OBSOLETE by FocusChangeEvent.
+class AURA_EXPORT FocusChangeObserver {
+ public:
+ // Called when focus moves from |lost_focus| to |gained_focus|.
+ virtual void OnWindowFocused(Window* gained_focus, Window* lost_focus) = 0;
+
+ protected:
+ virtual ~FocusChangeObserver() {}
+};
+
+AURA_EXPORT FocusChangeObserver* GetFocusChangeObserver(Window* window);
+AURA_EXPORT void SetFocusChangeObserver(
+ Window* window,
+ FocusChangeObserver* focus_change_observer);
+
+
+} // namespace client
+} // namespace aura
+
+#endif // UI_AURA_CLIENT_FOCUS_CHANGE_OBSERVER_H_
diff --git a/chromium/ui/aura/client/focus_client.cc b/chromium/ui/aura/client/focus_client.cc
new file mode 100644
index 00000000000..83ad1a9e28a
--- /dev/null
+++ b/chromium/ui/aura/client/focus_client.cc
@@ -0,0 +1,33 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/client/focus_client.h"
+
+#include "ui/aura/root_window.h"
+#include "ui/aura/window_property.h"
+
+DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(AURA_EXPORT, aura::Window*)
+DECLARE_WINDOW_PROPERTY_TYPE(aura::client::FocusClient*)
+
+namespace aura {
+namespace client {
+
+DEFINE_WINDOW_PROPERTY_KEY(FocusClient*, kRootWindowFocusClientKey, NULL);
+
+void SetFocusClient(RootWindow* root_window, FocusClient* client) {
+ root_window->SetProperty(kRootWindowFocusClientKey, client);
+}
+
+FocusClient* GetFocusClient(Window* window) {
+ return GetFocusClient(static_cast<const Window*>(window));
+}
+
+FocusClient* GetFocusClient(const Window* window) {
+ const RootWindow* root_window = window->GetRootWindow();
+ return root_window ?
+ root_window->GetProperty(kRootWindowFocusClientKey) : NULL;
+}
+
+} // namespace client
+} // namespace aura
diff --git a/chromium/ui/aura/client/focus_client.h b/chromium/ui/aura/client/focus_client.h
new file mode 100644
index 00000000000..ff5f7f6b542
--- /dev/null
+++ b/chromium/ui/aura/client/focus_client.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_CLIENT_FOCUS_CLIENT_H_
+#define UI_AURA_CLIENT_FOCUS_CLIENT_H_
+
+#include "ui/aura/aura_export.h"
+
+namespace ui {
+class Event;
+}
+
+namespace aura {
+class RootWindow;
+class Window;
+
+namespace client {
+class FocusChangeObserver;
+
+// An interface implemented by an object that manages window focus.
+class AURA_EXPORT FocusClient {
+ public:
+ virtual ~FocusClient() {}
+
+ // TODO(beng): these methods will be OBSOLETE by FocusChangeEvent.
+ virtual void AddObserver(FocusChangeObserver* observer) = 0;
+ virtual void RemoveObserver(FocusChangeObserver* observer) = 0;
+
+ // Focuses |window|. Passing NULL clears focus.
+ virtual void FocusWindow(Window* window) = 0;
+
+ // Sets focus to |window| if it's within the active window. Not intended as a
+ // general purpose API, use FocusWindow() instead.
+ virtual void ResetFocusWithinActiveWindow(Window* window) = 0;
+
+ // Retrieves the focused window, or NULL if there is none.
+ virtual Window* GetFocusedWindow() = 0;
+
+ // TODO(beng): temporary compat until FocusController is on.
+ // Called when |window|'s disposition in |root_window| changes such that
+ // focus must be shifted away from it. |destroyed| is true if the disposition
+ // change is that |window| is being destroyed.
+ virtual void OnWindowHiddenInRootWindow(aura::Window* window,
+ aura::RootWindow* root_window,
+ bool destroyed) = 0;
+};
+
+// Sets/Gets the focus client on the RootWindow.
+AURA_EXPORT void SetFocusClient(RootWindow* root_window, FocusClient* client);
+AURA_EXPORT FocusClient* GetFocusClient(Window* window);
+AURA_EXPORT FocusClient* GetFocusClient(const Window* window);
+
+} // namespace clients
+} // namespace aura
+
+#endif // UI_AURA_CLIENT_FOCUS_CLIENT_H_
diff --git a/chromium/ui/aura/client/screen_position_client.cc b/chromium/ui/aura/client/screen_position_client.cc
new file mode 100644
index 00000000000..ac131561a79
--- /dev/null
+++ b/chromium/ui/aura/client/screen_position_client.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/client/screen_position_client.h"
+
+#include "ui/aura/root_window.h"
+#include "ui/aura/window_property.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(aura::client::ScreenPositionClient*)
+
+namespace aura {
+namespace client {
+
+DEFINE_LOCAL_WINDOW_PROPERTY_KEY(ScreenPositionClient*,
+ kScreenPositionClientKey,
+ NULL);
+
+void SetScreenPositionClient(RootWindow* window,
+ ScreenPositionClient* client) {
+ window->SetProperty(kScreenPositionClientKey, client);
+}
+
+ScreenPositionClient* GetScreenPositionClient(const RootWindow* window) {
+ return window ? window->GetProperty(kScreenPositionClientKey) : NULL;
+}
+
+} // namespace client
+} // namespace aura
diff --git a/chromium/ui/aura/client/screen_position_client.h b/chromium/ui/aura/client/screen_position_client.h
new file mode 100644
index 00000000000..e5505eb75ef
--- /dev/null
+++ b/chromium/ui/aura/client/screen_position_client.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_SCREEN_POSITION_CLIENT_H_
+#define UI_AURA_SCREEN_POSITION_CLIENT_H_
+
+#include "ui/aura/aura_export.h"
+#include "ui/aura/window.h"
+
+namespace gfx {
+class Display;
+class Rect;
+}
+
+namespace aura {
+
+class Event;
+class RootWindow;
+class Window;
+
+namespace client {
+
+// An interface implemented by an object that changes coordinates on a
+// RootWindow into system coordinates.
+class AURA_EXPORT ScreenPositionClient {
+ public:
+ // Converts the |screen_point| from a given |window|'s coordinate space
+ // into screen coordinate space.
+ virtual void ConvertPointToScreen(const Window* window,
+ gfx::Point* point) = 0;
+ virtual void ConvertPointFromScreen(const Window* window,
+ gfx::Point* point) = 0;
+ // Converts the |screen_point| from root window host's coordinate of
+ // into screen coordinate space.
+ // A typical example of using this function instead of ConvertPointToScreen is
+ // when X's native input is captured by a drag operation.
+ // See the comments for ash::GetRootWindowRelativeToWindow for details.
+ virtual void ConvertHostPointToScreen(aura::RootWindow* root_window,
+ gfx::Point* point) = 0;
+ // Sets the bounds of the window. The implementation is responsible
+ // for finding out and translating the right coordinates for the |window|.
+ virtual void SetBounds(Window* window,
+ const gfx::Rect& bounds,
+ const gfx::Display& display) = 0;
+ virtual ~ScreenPositionClient() {}
+};
+
+// Sets/Gets the activation client on the Window.
+AURA_EXPORT void SetScreenPositionClient(RootWindow* window,
+ ScreenPositionClient* client);
+AURA_EXPORT ScreenPositionClient* GetScreenPositionClient(
+ const RootWindow* window);
+
+} // namespace clients
+} // namespace aura
+
+#endif // UI_AURA_SCREEN_POSITION_CLIENT_H_
diff --git a/chromium/ui/aura/client/stacking_client.cc b/chromium/ui/aura/client/stacking_client.cc
new file mode 100644
index 00000000000..5d93d984840
--- /dev/null
+++ b/chromium/ui/aura/client/stacking_client.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/client/stacking_client.h"
+
+#include "ui/aura/env.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/window_property.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(aura::client::StackingClient*)
+
+namespace aura {
+namespace client {
+
+DEFINE_WINDOW_PROPERTY_KEY(
+ StackingClient*, kRootWindowStackingClientKey, NULL);
+
+void SetStackingClient(Window* window, StackingClient* stacking_client) {
+ DCHECK(window);
+
+ RootWindow* root_window = window->GetRootWindow();
+ DCHECK(root_window);
+ root_window->SetProperty(kRootWindowStackingClientKey, stacking_client);
+}
+
+StackingClient* GetStackingClient(Window* window) {
+ DCHECK(window);
+ RootWindow* root_window = window->GetRootWindow();
+ DCHECK(root_window);
+ StackingClient* root_window_stacking_client =
+ root_window->GetProperty(kRootWindowStackingClientKey);
+ DCHECK(root_window_stacking_client);
+ return root_window_stacking_client;
+}
+
+} // namespace client
+} // namespace aura
diff --git a/chromium/ui/aura/client/stacking_client.h b/chromium/ui/aura/client/stacking_client.h
new file mode 100644
index 00000000000..89f83eb4601
--- /dev/null
+++ b/chromium/ui/aura/client/stacking_client.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_CLIENT_STACKING_CLIENT_H_
+#define UI_AURA_CLIENT_STACKING_CLIENT_H_
+
+#include "ui/aura/aura_export.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace aura {
+class Window;
+namespace client {
+
+// An interface implemented by an object that stacks windows.
+class AURA_EXPORT StackingClient {
+ public:
+ virtual ~StackingClient() {}
+
+ // Called by the Window when it looks for a default parent. Returns the
+ // window that |window| should be added to instead. |context| provides a
+ // Window (generally a RootWindow) that can be used to determine which
+ // desktop type the default parent should be chosen from. NOTE: this may
+ // have side effects. It should only be used when |window| is going to be
+ // immediately added.
+ //
+ // TODO(erg): Remove |context|, and maybe after oshima's patch lands,
+ // |bounds|.
+ virtual Window* GetDefaultParent(
+ Window* context,
+ Window* window,
+ const gfx::Rect& bounds) = 0;
+};
+
+// Set/Get a stacking client for a specific window. Setting the stacking client
+// sets the stacking client on the window's RootWindow, not the window itself.
+// Likewise getting obtains it from the window's RootWindow. If |window| is
+// non-NULL it must be in a RootWindow. If |window| is NULL, the default
+// stacking client is used.
+AURA_EXPORT void SetStackingClient(Window* window,
+ StackingClient* stacking_client);
+StackingClient* GetStackingClient(Window* window);
+
+} // namespace client
+} // namespace aura
+
+#endif // UI_AURA_CLIENT_STACKING_CLIENT_H_
diff --git a/chromium/ui/aura/client/tooltip_client.cc b/chromium/ui/aura/client/tooltip_client.cc
new file mode 100644
index 00000000000..a966c28392c
--- /dev/null
+++ b/chromium/ui/aura/client/tooltip_client.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/client/tooltip_client.h"
+
+#include "ui/aura/root_window.h"
+#include "ui/aura/window_property.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(aura::client::TooltipClient*)
+DECLARE_WINDOW_PROPERTY_TYPE(base::string16*)
+
+namespace aura {
+namespace client {
+
+DEFINE_LOCAL_WINDOW_PROPERTY_KEY(
+ TooltipClient*, kRootWindowTooltipClientKey, NULL);
+DEFINE_LOCAL_WINDOW_PROPERTY_KEY(base::string16*, kTooltipTextKey, NULL);
+
+void SetTooltipClient(RootWindow* root_window, TooltipClient* client) {
+ root_window->SetProperty(kRootWindowTooltipClientKey, client);
+}
+
+TooltipClient* GetTooltipClient(RootWindow* root_window) {
+ return root_window ?
+ root_window->GetProperty(kRootWindowTooltipClientKey) : NULL;
+}
+
+void SetTooltipText(Window* window, base::string16* tooltip_text) {
+ window->SetProperty(kTooltipTextKey, tooltip_text);
+}
+
+const base::string16 GetTooltipText(Window* window) {
+ base::string16* string_ptr = window->GetProperty(kTooltipTextKey);
+ return string_ptr ? *string_ptr : base::string16();
+}
+
+} // namespace client
+} // namespace aura
diff --git a/chromium/ui/aura/client/tooltip_client.h b/chromium/ui/aura/client/tooltip_client.h
new file mode 100644
index 00000000000..d6d33ceb2fb
--- /dev/null
+++ b/chromium/ui/aura/client/tooltip_client.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_CLIENT_TOOLTIP_CLIENT_H_
+#define UI_AURA_CLIENT_TOOLTIP_CLIENT_H_
+
+#include "ui/aura/aura_export.h"
+#include "ui/gfx/font.h"
+
+namespace aura {
+class RootWindow;
+class Window;
+
+namespace client {
+
+class AURA_EXPORT TooltipClient {
+ public:
+ // Informs the shell tooltip manager of change in tooltip for window |target|.
+ virtual void UpdateTooltip(Window* target) = 0;
+
+ // Sets the time after which the tooltip is hidden for Window |target|. If
+ // |timeout_in_ms| is <= 0, the tooltip is shown indefinitely.
+ virtual void SetTooltipShownTimeout(Window* target, int timeout_in_ms) = 0;
+
+ // Enables/Disables tooltips.
+ virtual void SetTooltipsEnabled(bool enable) = 0;
+};
+
+AURA_EXPORT void SetTooltipClient(RootWindow* root_window,
+ TooltipClient* client);
+AURA_EXPORT TooltipClient* GetTooltipClient(RootWindow* root_window);
+
+AURA_EXPORT void SetTooltipText(Window* window, base::string16* tooltip_text);
+AURA_EXPORT const base::string16 GetTooltipText(Window* window);
+
+} // namespace client
+} // namespace aura
+
+#endif // UI_AURA_CLIENT_TOOLTIP_CLIENT_H_
diff --git a/chromium/ui/aura/client/user_action_client.cc b/chromium/ui/aura/client/user_action_client.cc
new file mode 100644
index 00000000000..cfb5fe1b42f
--- /dev/null
+++ b/chromium/ui/aura/client/user_action_client.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/client/user_action_client.h"
+
+#include "ui/aura/root_window.h"
+#include "ui/aura/window_property.h"
+
+namespace aura {
+namespace client {
+
+DEFINE_WINDOW_PROPERTY_KEY(UserActionClient*,
+ kRootWindowUserActionClientKey,
+ NULL);
+
+void SetUserActionClient(RootWindow* root_window, UserActionClient* client) {
+ root_window->SetProperty(kRootWindowUserActionClientKey, client);
+}
+
+UserActionClient* GetUserActionClient(RootWindow* root_window) {
+ return root_window ?
+ root_window->GetProperty(kRootWindowUserActionClientKey) : NULL;
+}
+
+} // namespace client
+} // namespace aura
diff --git a/chromium/ui/aura/client/user_action_client.h b/chromium/ui/aura/client/user_action_client.h
new file mode 100644
index 00000000000..16f0b9ff09d
--- /dev/null
+++ b/chromium/ui/aura/client/user_action_client.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_CLIENT_USER_ACTION_CLIENT_H_
+#define UI_AURA_CLIENT_USER_ACTION_CLIENT_H_
+
+#include "ui/aura/aura_export.h"
+
+namespace aura {
+class RootWindow;
+namespace client {
+
+// An interface for handling a user action that isn't handled by the standard
+// event path.
+class AURA_EXPORT UserActionClient {
+ public:
+ enum Command {
+ BACK = 0,
+ FORWARD,
+ };
+
+ // Returns true if the command was handled and false otherwise.
+ virtual bool OnUserAction(Command command) = 0;
+
+ virtual ~UserActionClient() {}
+};
+
+// Sets/gets the client for handling user action on the specified root window.
+AURA_EXPORT void SetUserActionClient(RootWindow* root_window,
+ UserActionClient* client);
+AURA_EXPORT UserActionClient* GetUserActionClient(RootWindow* root_window);
+
+} // namespace client
+} // namespace aura
+
+#endif // UI_AURA_CLIENT_USER_ACTION_CLIENT_H_
diff --git a/chromium/ui/aura/client/visibility_client.cc b/chromium/ui/aura/client/visibility_client.cc
new file mode 100644
index 00000000000..5fc7beed7c1
--- /dev/null
+++ b/chromium/ui/aura/client/visibility_client.cc
@@ -0,0 +1,35 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/client/visibility_client.h"
+
+#include "ui/aura/root_window.h"
+#include "ui/aura/window_property.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(aura::client::VisibilityClient*)
+
+namespace aura {
+namespace client {
+
+// A property key to store a client that handles window visibility changes.
+DEFINE_LOCAL_WINDOW_PROPERTY_KEY(
+ VisibilityClient*, kWindowVisibilityClientKey, NULL);
+
+
+void SetVisibilityClient(Window* window, VisibilityClient* client) {
+ window->SetProperty(kWindowVisibilityClientKey, client);
+}
+
+VisibilityClient* GetVisibilityClient(Window* window) {
+ VisibilityClient* visibility_client = NULL;
+ aura::Window* current = window;
+ do {
+ visibility_client = current->GetProperty(kWindowVisibilityClientKey);
+ current = current->parent();
+ } while (current && !visibility_client);
+ return visibility_client;
+}
+
+} // namespace client
+} // namespace aura
diff --git a/chromium/ui/aura/client/visibility_client.h b/chromium/ui/aura/client/visibility_client.h
new file mode 100644
index 00000000000..b36cddc41b8
--- /dev/null
+++ b/chromium/ui/aura/client/visibility_client.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_CLIENT_VISIBILITY_CLIENT_H_
+#define UI_AURA_CLIENT_VISIBILITY_CLIENT_H_
+
+#include "ui/aura/aura_export.h"
+
+namespace aura {
+class Window;
+namespace client {
+
+// An interface implemented by an object that manages the visibility of Windows'
+// layers as Window visibility changes.
+class AURA_EXPORT VisibilityClient {
+ public:
+ // Called when |window|'s visibility is changing to |visible|. The implementor
+ // can decide whether or not to pass on the visibility to the underlying
+ // layer.
+ virtual void UpdateLayerVisibility(Window* window, bool visible) = 0;
+
+ protected:
+ virtual ~VisibilityClient() {}
+};
+
+// Sets the VisibilityClient on the Window.
+AURA_EXPORT void SetVisibilityClient(Window* window, VisibilityClient* client);
+
+// Gets the VisibilityClient for the window. This will crawl up |window|'s
+// hierarchy until it finds one.
+AURA_EXPORT VisibilityClient* GetVisibilityClient(Window* window);
+
+} // namespace clients
+} // namespace aura
+
+#endif // UI_AURA_CLIENT_VISIBILITY_CLIENT_H_
diff --git a/chromium/ui/aura/client/window_move_client.cc b/chromium/ui/aura/client/window_move_client.cc
new file mode 100644
index 00000000000..c65452db607
--- /dev/null
+++ b/chromium/ui/aura/client/window_move_client.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/client/window_move_client.h"
+
+#include "ui/aura/window.h"
+#include "ui/aura/window_property.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(aura::client::WindowMoveClient*)
+
+namespace aura {
+namespace client {
+
+// A property key to store a client that handles window moves.
+DEFINE_LOCAL_WINDOW_PROPERTY_KEY(
+ WindowMoveClient*, kWindowMoveClientKey, NULL);
+
+void SetWindowMoveClient(Window* window, WindowMoveClient* client) {
+ window->SetProperty(kWindowMoveClientKey, client);
+}
+
+WindowMoveClient* GetWindowMoveClient(Window* window) {
+ return window->GetProperty(kWindowMoveClientKey);
+}
+
+} // namespace client
+} // namespace aura
diff --git a/chromium/ui/aura/client/window_move_client.h b/chromium/ui/aura/client/window_move_client.h
new file mode 100644
index 00000000000..15e4245757b
--- /dev/null
+++ b/chromium/ui/aura/client/window_move_client.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_CLIENT_WINDOW_MOVE_CLIENT_H_
+#define UI_AURA_CLIENT_WINDOW_MOVE_CLIENT_H_
+
+#include "ui/aura/aura_export.h"
+#include "ui/gfx/vector2d.h"
+
+namespace gfx {
+class Point;
+}
+
+namespace aura {
+class Window;
+namespace client {
+
+enum WindowMoveResult {
+ MOVE_SUCCESSFUL, // Moving window was successful.
+ MOVE_CANCELED // Moving window was canceled.
+};
+
+enum WindowMoveSource {
+ WINDOW_MOVE_SOURCE_MOUSE,
+ WINDOW_MOVE_SOURCE_TOUCH,
+};
+
+// An interface implemented by an object that manages programatically keyed
+// window moving.
+class AURA_EXPORT WindowMoveClient {
+ public:
+ // Starts a nested message loop for moving the window. |drag_offset| is the
+ // offset from the window origin to the cursor when the drag was started.
+ // Returns MOVE_SUCCESSFUL if the move has completed successfully, or
+ // MOVE_CANCELED otherwise.
+ virtual WindowMoveResult RunMoveLoop(Window* window,
+ const gfx::Vector2d& drag_offset,
+ WindowMoveSource source) = 0;
+
+ // Ends a previously started move loop.
+ virtual void EndMoveLoop() = 0;
+
+ protected:
+ virtual ~WindowMoveClient() {}
+};
+
+// Sets/Gets the activation client for the specified window.
+AURA_EXPORT void SetWindowMoveClient(Window* window,
+ WindowMoveClient* client);
+AURA_EXPORT WindowMoveClient* GetWindowMoveClient(Window* window);
+
+} // namespace client
+} // namespace aura
+
+#endif // UI_AURA_CLIENT_WINDOW_MOVE_CLIENT_H_
diff --git a/chromium/ui/aura/client/window_types.h b/chromium/ui/aura/client/window_types.h
new file mode 100644
index 00000000000..f0a7b777a8f
--- /dev/null
+++ b/chromium/ui/aura/client/window_types.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_WINDOW_TYPES_H_
+#define UI_AURA_WINDOW_TYPES_H_
+
+namespace aura {
+namespace client {
+
+// This isn't a property because it can't change after the window has been
+// initialized. It's in client because the Aura Client application derives
+// meaning from these values, not Aura itself.
+enum WindowType {
+ WINDOW_TYPE_UNKNOWN = 0,
+
+ // Regular windows that should be laid out by the client.
+ WINDOW_TYPE_NORMAL,
+
+ // Miscellaneous windows that should not be laid out by the shell.
+ WINDOW_TYPE_POPUP,
+
+ // A window intended as a control. Not laid out by the shell.
+ WINDOW_TYPE_CONTROL,
+
+ // Always on top windows aligned to bottom right of screen.
+ WINDOW_TYPE_PANEL,
+
+ WINDOW_TYPE_MENU,
+ WINDOW_TYPE_TOOLTIP,
+};
+
+} // namespace client
+} // namespace aura
+
+#endif // UI_AURA_WINDOW_TYPES_H_
diff --git a/chromium/ui/aura/demo/demo_main.cc b/chromium/ui/aura/demo/demo_main.cc
new file mode 100644
index 00000000000..06938d87419
--- /dev/null
+++ b/chromium/ui/aura/demo/demo_main.cc
@@ -0,0 +1,167 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/i18n/icu_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "third_party/skia/include/core/SkXfermode.h"
+#include "ui/aura/client/default_capture_client.h"
+#include "ui/aura/client/stacking_client.h"
+#include "ui/aura/env.h"
+#include "ui/aura/focus_manager.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/test/test_screen.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_delegate.h"
+#include "ui/base/events/event.h"
+#include "ui/base/hit_test.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/ui_base_paths.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/rect.h"
+
+#if defined(USE_X11)
+#include "base/message_loop/message_pump_aurax11.h"
+#endif
+
+namespace {
+
+// Trivial WindowDelegate implementation that draws a colored background.
+class DemoWindowDelegate : public aura::WindowDelegate {
+ public:
+ explicit DemoWindowDelegate(SkColor color) : color_(color) {}
+
+ // Overridden from WindowDelegate:
+ virtual gfx::Size GetMinimumSize() const OVERRIDE {
+ return gfx::Size();
+ }
+
+ virtual gfx::Size GetMaximumSize() const OVERRIDE {
+ return gfx::Size();
+ }
+
+ virtual void OnBoundsChanged(const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) OVERRIDE {}
+ virtual gfx::NativeCursor GetCursor(const gfx::Point& point) OVERRIDE {
+ return gfx::kNullCursor;
+ }
+ virtual int GetNonClientComponent(const gfx::Point& point) const OVERRIDE {
+ return HTCAPTION;
+ }
+ virtual bool ShouldDescendIntoChildForEventHandling(
+ aura::Window* child,
+ const gfx::Point& location) OVERRIDE {
+ return true;
+ }
+ virtual bool CanFocus() OVERRIDE { return true; }
+ virtual void OnCaptureLost() OVERRIDE {}
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
+ canvas->DrawColor(color_, SkXfermode::kSrc_Mode);
+ }
+ virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {}
+ virtual void OnWindowDestroying() OVERRIDE {}
+ virtual void OnWindowDestroyed() OVERRIDE {}
+ virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE {}
+ virtual bool HasHitTestMask() const OVERRIDE { return false; }
+ virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE {}
+ virtual scoped_refptr<ui::Texture> CopyTexture() OVERRIDE {
+ return scoped_refptr<ui::Texture>();
+ }
+
+ private:
+ SkColor color_;
+
+ DISALLOW_COPY_AND_ASSIGN(DemoWindowDelegate);
+};
+
+class DemoStackingClient : public aura::client::StackingClient {
+ public:
+ explicit DemoStackingClient(aura::RootWindow* root_window)
+ : root_window_(root_window) {
+ aura::client::SetStackingClient(root_window_, this);
+ }
+
+ virtual ~DemoStackingClient() {
+ aura::client::SetStackingClient(root_window_, NULL);
+ }
+
+ // Overridden from aura::client::StackingClient:
+ virtual aura::Window* GetDefaultParent(aura::Window* context,
+ aura::Window* window,
+ const gfx::Rect& bounds) OVERRIDE {
+ if (!capture_client_) {
+ capture_client_.reset(
+ new aura::client::DefaultCaptureClient(root_window_));
+ }
+ return root_window_;
+ }
+
+ private:
+ aura::RootWindow* root_window_;
+
+ scoped_ptr<aura::client::DefaultCaptureClient> capture_client_;
+
+ DISALLOW_COPY_AND_ASSIGN(DemoStackingClient);
+};
+
+int DemoMain() {
+ // Create the message-loop here before creating the root window.
+ base::MessageLoop message_loop(base::MessageLoop::TYPE_UI);
+ aura::Env::GetInstance();
+ scoped_ptr<aura::TestScreen> test_screen(aura::TestScreen::Create());
+ gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, test_screen.get());
+ scoped_ptr<aura::RootWindow> root_window(
+ test_screen->CreateRootWindowForPrimaryDisplay());
+ scoped_ptr<DemoStackingClient> stacking_client(new DemoStackingClient(
+ root_window.get()));
+ aura::FocusManager focus_manager;
+ aura::client::SetFocusClient(root_window.get(), &focus_manager);
+
+ // Create a hierarchy of test windows.
+ DemoWindowDelegate window_delegate1(SK_ColorBLUE);
+ aura::Window window1(&window_delegate1);
+ window1.set_id(1);
+ window1.Init(ui::LAYER_TEXTURED);
+ window1.SetBounds(gfx::Rect(100, 100, 400, 400));
+ window1.Show();
+ window1.SetDefaultParentByRootWindow(root_window.get(), gfx::Rect());
+
+ DemoWindowDelegate window_delegate2(SK_ColorRED);
+ aura::Window window2(&window_delegate2);
+ window2.set_id(2);
+ window2.Init(ui::LAYER_TEXTURED);
+ window2.SetBounds(gfx::Rect(200, 200, 350, 350));
+ window2.Show();
+ window2.SetDefaultParentByRootWindow(root_window.get(), gfx::Rect());
+
+ DemoWindowDelegate window_delegate3(SK_ColorGREEN);
+ aura::Window window3(&window_delegate3);
+ window3.set_id(3);
+ window3.Init(ui::LAYER_TEXTURED);
+ window3.SetBounds(gfx::Rect(10, 10, 50, 50));
+ window3.Show();
+ window2.AddChild(&window3);
+
+ root_window->ShowRootWindow();
+ base::MessageLoopForUI::current()->Run();
+
+ return 0;
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+ CommandLine::Init(argc, argv);
+
+ // The exit manager is in charge of calling the dtors of singleton objects.
+ base::AtExitManager exit_manager;
+
+ ui::RegisterPathProvider();
+ icu_util::Initialize();
+ ResourceBundle::InitSharedInstanceWithLocale("en-US", NULL);
+
+ return DemoMain();
+}
diff --git a/chromium/ui/aura/device_list_updater_aurax11.cc b/chromium/ui/aura/device_list_updater_aurax11.cc
new file mode 100644
index 00000000000..0671dfecbfd
--- /dev/null
+++ b/chromium/ui/aura/device_list_updater_aurax11.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/device_list_updater_aurax11.h"
+
+#include <X11/extensions/XInput2.h>
+
+#include "ui/base/events/event_utils.h"
+
+namespace aura {
+
+DeviceListUpdaterAuraX11::DeviceListUpdaterAuraX11() {}
+
+DeviceListUpdaterAuraX11::~DeviceListUpdaterAuraX11() {}
+
+base::EventStatus DeviceListUpdaterAuraX11::WillProcessEvent(
+ const base::NativeEvent& event) {
+ // XI_HierarchyChanged events are special. There is no window associated with
+ // these events. So process them directly from here.
+ if (event->type == GenericEvent &&
+ event->xgeneric.evtype == XI_HierarchyChanged) {
+ ui::UpdateDeviceList();
+ }
+
+ return base::EVENT_CONTINUE;
+}
+
+void DeviceListUpdaterAuraX11::DidProcessEvent(const base::NativeEvent& event) {
+}
+
+} // namespace aura
diff --git a/chromium/ui/aura/device_list_updater_aurax11.h b/chromium/ui/aura/device_list_updater_aurax11.h
new file mode 100644
index 00000000000..cb8c884fe85
--- /dev/null
+++ b/chromium/ui/aura/device_list_updater_aurax11.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_DEVICE_LIST_UPDATER_AURAX11_H_
+#define UI_AURA_DEVICE_LIST_UPDATER_AURAX11_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/message_loop/message_pump_observer.h"
+
+namespace aura {
+
+// Filters out global XInput notifications and updates the DeviceList.
+class DeviceListUpdaterAuraX11 : public base::MessagePumpObserver {
+ public:
+ DeviceListUpdaterAuraX11();
+ virtual ~DeviceListUpdaterAuraX11();
+
+ // Overridden from base::MessagePumpObserver:
+ virtual base::EventStatus WillProcessEvent(
+ const base::NativeEvent& event) OVERRIDE;
+ virtual void DidProcessEvent(
+ const base::NativeEvent& event) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DeviceListUpdaterAuraX11);
+};
+
+} // namespace aura
+
+#endif // UI_AURA_DEVICE_LIST_UPDATER_AURAX11_H_
diff --git a/chromium/ui/aura/dispatcher_win.cc b/chromium/ui/aura/dispatcher_win.cc
new file mode 100644
index 00000000000..7160dc7771d
--- /dev/null
+++ b/chromium/ui/aura/dispatcher_win.cc
@@ -0,0 +1,33 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/message_loop/message_loop.h"
+
+namespace aura {
+
+class DispatcherWin : public base::MessageLoop::Dispatcher {
+ public:
+ DispatcherWin() {}
+ virtual ~DispatcherWin() {}
+
+ // Overridden from MessageLoop::Dispatcher:
+ virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DispatcherWin);
+};
+
+bool DispatcherWin::Dispatch(const base::NativeEvent& msg) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ return true;
+}
+
+base::MessageLoop::Dispatcher* CreateDispatcher() {
+ return new DispatcherWin;
+}
+
+} // namespace aura
diff --git a/chromium/ui/aura/env.cc b/chromium/ui/aura/env.cc
new file mode 100644
index 00000000000..6b2b3e023a3
--- /dev/null
+++ b/chromium/ui/aura/env.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/env.h"
+
+#include "base/command_line.h"
+#include "ui/aura/env_observer.h"
+#include "ui/aura/root_window_host.h"
+#include "ui/aura/window.h"
+#include "ui/compositor/compositor.h"
+#include "ui/compositor/compositor_switches.h"
+
+#if defined(USE_X11)
+#include "base/message_loop/message_pump_aurax11.h"
+#endif
+
+namespace aura {
+
+// static
+Env* Env::instance_ = NULL;
+
+////////////////////////////////////////////////////////////////////////////////
+// Env, public:
+
+Env::Env()
+ : mouse_button_flags_(0),
+ is_touch_down_(false) {
+}
+
+Env::~Env() {
+#if defined(USE_X11)
+ base::MessagePumpAuraX11::Current()->RemoveObserver(
+ &device_list_updater_aurax11_);
+#endif
+
+ FOR_EACH_OBSERVER(EnvObserver, observers_, OnWillDestroyEnv());
+
+ ui::Compositor::Terminate();
+}
+
+// static
+Env* Env::GetInstance() {
+ if (!instance_) {
+ instance_ = new Env;
+ instance_->Init();
+ }
+ return instance_;
+}
+
+// static
+void Env::DeleteInstance() {
+ delete instance_;
+ instance_ = NULL;
+}
+
+void Env::AddObserver(EnvObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void Env::RemoveObserver(EnvObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+#if !defined(OS_MACOSX)
+base::MessageLoop::Dispatcher* Env::GetDispatcher() {
+#if defined(USE_X11)
+ return base::MessagePumpAuraX11::Current();
+#else
+ return dispatcher_.get();
+#endif
+}
+#endif
+
+void Env::RootWindowActivated(RootWindow* root_window) {
+ FOR_EACH_OBSERVER(EnvObserver, observers_,
+ OnRootWindowActivated(root_window));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Env, private:
+
+void Env::Init() {
+#if !defined(USE_X11) && !defined(USE_OZONE)
+ dispatcher_.reset(CreateDispatcher());
+#endif
+#if defined(USE_X11)
+ // We can't do this with a root window listener because XI_HierarchyChanged
+ // messages don't have a target window.
+ base::MessagePumpAuraX11::Current()->AddObserver(
+ &device_list_updater_aurax11_);
+#endif
+ ui::Compositor::Initialize();
+}
+
+void Env::NotifyWindowInitialized(Window* window) {
+ FOR_EACH_OBSERVER(EnvObserver, observers_, OnWindowInitialized(window));
+}
+
+void Env::NotifyRootWindowInitialized(RootWindow* root_window) {
+ FOR_EACH_OBSERVER(EnvObserver,
+ observers_,
+ OnRootWindowInitialized(root_window));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Env, ui::EventTarget implementation:
+
+bool Env::CanAcceptEvent(const ui::Event& event) {
+ return true;
+}
+
+ui::EventTarget* Env::GetParentTarget() {
+ return NULL;
+}
+
+} // namespace aura
diff --git a/chromium/ui/aura/env.h b/chromium/ui/aura/env.h
new file mode 100644
index 00000000000..ad31cd57ac5
--- /dev/null
+++ b/chromium/ui/aura/env.h
@@ -0,0 +1,106 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_ENV_H_
+#define UI_AURA_ENV_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/observer_list.h"
+#include "ui/aura/aura_export.h"
+#include "ui/base/events/event_handler.h"
+#include "ui/base/events/event_target.h"
+#include "ui/gfx/point.h"
+
+#if defined(USE_X11)
+#include "ui/aura/device_list_updater_aurax11.h"
+#endif
+
+namespace aura {
+class EnvObserver;
+class RootWindow;
+class Window;
+
+#if !defined(USE_X11)
+// Creates a platform-specific native event dispatcher.
+base::MessageLoop::Dispatcher* CreateDispatcher();
+#endif
+
+// A singleton object that tracks general state within Aura.
+// TODO(beng): manage RootWindows.
+class AURA_EXPORT Env : public ui::EventTarget {
+ public:
+ Env();
+ virtual ~Env();
+
+ static Env* GetInstance();
+ static void DeleteInstance();
+
+ void AddObserver(EnvObserver* observer);
+ void RemoveObserver(EnvObserver* observer);
+
+ bool is_mouse_button_down() const { return mouse_button_flags_ != 0; }
+ void set_mouse_button_flags(int mouse_button_flags) {
+ mouse_button_flags_ = mouse_button_flags;
+ }
+
+ // Gets/sets the last mouse location seen in a mouse event in the screen
+ // coordinates.
+ const gfx::Point& last_mouse_location() const { return last_mouse_location_; }
+ void set_last_mouse_location(const gfx::Point& last_mouse_location) {
+ last_mouse_location_ = last_mouse_location;
+ }
+
+ // Whether any touch device is currently down.
+ bool is_touch_down() const { return is_touch_down_; }
+ void set_touch_down(bool value) { is_touch_down_ = value; }
+
+
+ // Returns the native event dispatcher. The result should only be passed to
+ // base::RunLoop(dispatcher), or used to dispatch an event by
+ // |Dispatch(const NativeEvent&)| on it. It must never be stored.
+#if !defined(OS_MACOSX)
+ base::MessageLoop::Dispatcher* GetDispatcher();
+#endif
+
+ // Invoked by RootWindow when its host is activated.
+ void RootWindowActivated(RootWindow* root_window);
+
+ private:
+ friend class Window;
+ friend class RootWindow;
+
+ void Init();
+
+ // Called by the Window when it is initialized. Notifies observers.
+ void NotifyWindowInitialized(Window* window);
+
+ // Called by the RootWindow when it is initialized. Notifies observers.
+ void NotifyRootWindowInitialized(RootWindow* root_window);
+
+ // Overridden from ui::EventTarget:
+ virtual bool CanAcceptEvent(const ui::Event& event) OVERRIDE;
+ virtual ui::EventTarget* GetParentTarget() OVERRIDE;
+
+ ObserverList<EnvObserver> observers_;
+#if !defined(USE_X11)
+ scoped_ptr<base::MessageLoop::Dispatcher> dispatcher_;
+#endif
+
+ static Env* instance_;
+ int mouse_button_flags_;
+ // Location of last mouse event, in screen coordinates.
+ gfx::Point last_mouse_location_;
+ bool is_touch_down_;
+
+#if defined(USE_X11)
+ DeviceListUpdaterAuraX11 device_list_updater_aurax11_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(Env);
+};
+
+} // namespace aura
+
+#endif // UI_AURA_ENV_H_
diff --git a/chromium/ui/aura/env_observer.h b/chromium/ui/aura/env_observer.h
new file mode 100644
index 00000000000..b624f91b6ec
--- /dev/null
+++ b/chromium/ui/aura/env_observer.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_ENV_OBSERVER_H_
+#define UI_AURA_ENV_OBSERVER_H_
+
+#include "ui/aura/aura_export.h"
+
+namespace aura {
+
+class RootWindow;
+class Window;
+
+class AURA_EXPORT EnvObserver {
+ public:
+ // Called when |window| has been initialized.
+ virtual void OnWindowInitialized(Window* window) = 0;
+
+ // Called when |root_window| has been initialized.
+ virtual void OnRootWindowInitialized(RootWindow* root_window) {};
+
+ // Called when a RootWindow's host is activated.
+ virtual void OnRootWindowActivated(RootWindow* root_window) {}
+
+ // Called right before Env is destroyed.
+ virtual void OnWillDestroyEnv() {}
+
+ protected:
+ virtual ~EnvObserver() {}
+};
+
+} // namespace aura
+
+#endif // UI_AURA_ENV_OBSERVER_H_
diff --git a/chromium/ui/aura/focus_manager.cc b/chromium/ui/aura/focus_manager.cc
new file mode 100644
index 00000000000..921406b4c76
--- /dev/null
+++ b/chromium/ui/aura/focus_manager.cc
@@ -0,0 +1,105 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/focus_manager.h"
+
+#include "ui/aura/client/activation_client.h"
+#include "ui/aura/client/focus_change_observer.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/window_delegate.h"
+
+namespace aura {
+
+////////////////////////////////////////////////////////////////////////////////
+// FocusManager, public:
+
+FocusManager::FocusManager() : focused_window_(NULL) {
+}
+
+FocusManager::~FocusManager() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FocusManager, client::FocusClient implementation:
+
+void FocusManager::AddObserver(client::FocusChangeObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void FocusManager::RemoveObserver(client::FocusChangeObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void FocusManager::FocusWindow(Window* focused_window) {
+ if (focused_window == focused_window_)
+ return;
+ if (focused_window && !focused_window->CanFocus())
+ return;
+ // The NULL-check of |focused_window| is essential here before asking the
+ // activation client, since it is valid to clear the focus by calling
+ // SetFocusedWindow() to NULL.
+
+ if (focused_window) {
+ RootWindow* root = focused_window->GetRootWindow();
+ DCHECK(root);
+ if (client::GetActivationClient(root) &&
+ !client::GetActivationClient(root)->OnWillFocusWindow(
+ focused_window, NULL)) {
+ return;
+ }
+ }
+
+ Window* old_focused_window = focused_window_;
+ focused_window_ = focused_window;
+
+ FOR_EACH_OBSERVER(client::FocusChangeObserver, observers_,
+ OnWindowFocused(focused_window, old_focused_window));
+ client::FocusChangeObserver* observer =
+ client::GetFocusChangeObserver(old_focused_window);
+ if (observer)
+ observer->OnWindowFocused(focused_window_, old_focused_window);
+ observer = client::GetFocusChangeObserver(focused_window_);
+ if (observer)
+ observer->OnWindowFocused(focused_window_, old_focused_window);
+}
+
+void FocusManager::ResetFocusWithinActiveWindow(Window* window) {
+ FocusWindow(window);
+}
+
+Window* FocusManager::GetFocusedWindow() {
+ return focused_window_;
+}
+
+void FocusManager::OnWindowHiddenInRootWindow(
+ aura::Window* window,
+ aura::RootWindow* root_window,
+ bool destroyed) {
+ Window* focused_window =
+ client::GetFocusClient(root_window)->GetFocusedWindow();
+ if (window->Contains(focused_window)) {
+ Window* focus_to = window->transient_parent();
+ if (focus_to) {
+ // Has to be removed from the transient parent before focusing,
+ // otherwise |window| will be focused again.
+ if (destroyed)
+ focus_to->RemoveTransientChild(window);
+ } else {
+ // If the invisible view has no visible transient window, focus to the
+ // topmost visible parent window.
+ focus_to = window->parent();
+ }
+ if (focus_to &&
+ (!focus_to->IsVisible() ||
+ !focus_to->CanFocus() ||
+ (client::GetActivationClient(root_window) &&
+ !client::GetActivationClient(root_window)->OnWillFocusWindow(
+ focus_to, NULL)))) {
+ focus_to = NULL;
+ }
+ client::GetFocusClient(root_window)->FocusWindow(focus_to);
+ }
+}
+
+} // namespace aura
diff --git a/chromium/ui/aura/focus_manager.h b/chromium/ui/aura/focus_manager.h
new file mode 100644
index 00000000000..b3a13313edc
--- /dev/null
+++ b/chromium/ui/aura/focus_manager.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_FOCUS_MANAGER_H_
+#define UI_AURA_FOCUS_MANAGER_H_
+
+#include "base/basictypes.h"
+#include "base/observer_list.h"
+#include "ui/aura/aura_export.h"
+#include "ui/aura/client/focus_client.h"
+
+namespace ui {
+class Event;
+}
+
+namespace aura {
+
+class Window;
+
+// An interface implemented by the Desktop to expose the focused window and
+// allow for it to be changed.
+class AURA_EXPORT FocusManager : public client::FocusClient {
+ public:
+ FocusManager();
+ virtual ~FocusManager();
+
+ private:
+ // Overridden from client::FocusClient:
+ virtual void AddObserver(client::FocusChangeObserver* observer) OVERRIDE;
+ virtual void RemoveObserver(client::FocusChangeObserver* observer) OVERRIDE;
+ virtual void FocusWindow(Window* window) OVERRIDE;
+ virtual void ResetFocusWithinActiveWindow(Window* window) OVERRIDE;
+ virtual Window* GetFocusedWindow() OVERRIDE;
+ virtual void OnWindowHiddenInRootWindow(aura::Window* window,
+ aura::RootWindow* root_window,
+ bool destroyed) OVERRIDE;
+
+ protected:
+ aura::Window* focused_window_;
+
+ ObserverList<client::FocusChangeObserver> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(FocusManager);
+};
+
+} // namespace aura
+
+#endif // UI_AURA_FOCUS_MANAGER_H_
diff --git a/chromium/ui/aura/gestures/OWNERS b/chromium/ui/aura/gestures/OWNERS
new file mode 100644
index 00000000000..6a1dd965022
--- /dev/null
+++ b/chromium/ui/aura/gestures/OWNERS
@@ -0,0 +1,2 @@
+rjkroege@chromium.org
+sadrul@chromium.org
diff --git a/chromium/ui/aura/gestures/gesture_recognizer_unittest.cc b/chromium/ui/aura/gestures/gesture_recognizer_unittest.cc
new file mode 100644
index 00000000000..d377ef458a3
--- /dev/null
+++ b/chromium/ui/aura/gestures/gesture_recognizer_unittest.cc
@@ -0,0 +1,3422 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "base/memory/scoped_vector.h"
+#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/timer/timer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/aura/env.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/test/aura_test_base.h"
+#include "ui/aura/test/event_generator.h"
+#include "ui/aura/test/test_window_delegate.h"
+#include "ui/aura/test/test_windows.h"
+#include "ui/base/events/event.h"
+#include "ui/base/events/event_utils.h"
+#include "ui/base/gestures/gesture_configuration.h"
+#include "ui/base/gestures/gesture_recognizer_impl.h"
+#include "ui/base/gestures/gesture_sequence.h"
+#include "ui/base/gestures/gesture_types.h"
+#include "ui/base/hit_test.h"
+#include "ui/base/ui_base_switches.h"
+#include "ui/gfx/point.h"
+#include "ui/gfx/rect.h"
+
+#include <queue>
+
+namespace aura {
+namespace test {
+
+namespace {
+
+std::string WindowIDAsString(ui::GestureConsumer* consumer) {
+ return consumer && !consumer->ignores_events() ?
+ base::IntToString(static_cast<Window*>(consumer)->id()) : "?";
+}
+
+#define EXPECT_0_EVENTS(events) \
+ EXPECT_EQ(0u, events.size())
+
+#define EXPECT_1_EVENT(events, e0) \
+ EXPECT_EQ(1u, events.size()); \
+ EXPECT_EQ(e0, events[0])
+
+#define EXPECT_2_EVENTS(events, e0, e1) \
+ EXPECT_EQ(2u, events.size()); \
+ EXPECT_EQ(e0, events[0]); \
+ EXPECT_EQ(e1, events[1])
+
+#define EXPECT_3_EVENTS(events, e0, e1, e2) \
+ EXPECT_EQ(3u, events.size()); \
+ EXPECT_EQ(e0, events[0]); \
+ EXPECT_EQ(e1, events[1]); \
+ EXPECT_EQ(e2, events[2])
+
+#define EXPECT_4_EVENTS(events, e0, e1, e2, e3) \
+ EXPECT_EQ(4u, events.size()); \
+ EXPECT_EQ(e0, events[0]); \
+ EXPECT_EQ(e1, events[1]); \
+ EXPECT_EQ(e2, events[2]); \
+ EXPECT_EQ(e3, events[3])
+
+// A delegate that keeps track of gesture events.
+class GestureEventConsumeDelegate : public TestWindowDelegate {
+ public:
+ GestureEventConsumeDelegate()
+ : tap_(false),
+ tap_down_(false),
+ tap_cancel_(false),
+ begin_(false),
+ end_(false),
+ scroll_begin_(false),
+ scroll_update_(false),
+ scroll_end_(false),
+ pinch_begin_(false),
+ pinch_update_(false),
+ pinch_end_(false),
+ long_press_(false),
+ fling_(false),
+ two_finger_tap_(false),
+ swipe_left_(false),
+ swipe_right_(false),
+ swipe_up_(false),
+ swipe_down_(false),
+ scroll_x_(0),
+ scroll_y_(0),
+ scroll_velocity_x_(0),
+ scroll_velocity_y_(0),
+ velocity_x_(0),
+ velocity_y_(0),
+ scroll_x_ordinal_(0),
+ scroll_y_ordinal_(0),
+ scroll_velocity_x_ordinal_(0),
+ scroll_velocity_y_ordinal_(0),
+ velocity_x_ordinal_(0),
+ velocity_y_ordinal_(0),
+ tap_count_(0),
+ wait_until_event_(ui::ET_UNKNOWN) {
+ }
+
+ virtual ~GestureEventConsumeDelegate() {}
+
+ void Reset() {
+ events_.clear();
+ tap_ = false;
+ tap_down_ = false;
+ tap_cancel_ = false;
+ begin_ = false;
+ end_ = false;
+ scroll_begin_ = false;
+ scroll_update_ = false;
+ scroll_end_ = false;
+ pinch_begin_ = false;
+ pinch_update_ = false;
+ pinch_end_ = false;
+ long_press_ = false;
+ fling_ = false;
+ two_finger_tap_ = false;
+ swipe_left_ = false;
+ swipe_right_ = false;
+ swipe_up_ = false;
+ swipe_down_ = false;
+
+ scroll_begin_position_.SetPoint(0, 0);
+ tap_location_.SetPoint(0, 0);
+
+ scroll_x_ = 0;
+ scroll_y_ = 0;
+ scroll_velocity_x_ = 0;
+ scroll_velocity_y_ = 0;
+ velocity_x_ = 0;
+ velocity_y_ = 0;
+ scroll_x_ordinal_ = 0;
+ scroll_y_ordinal_ = 0;
+ scroll_velocity_x_ordinal_ = 0;
+ scroll_velocity_y_ordinal_ = 0;
+ velocity_x_ordinal_ = 0;
+ velocity_y_ordinal_ = 0;
+ tap_count_ = 0;
+ }
+
+ const std::vector<ui::EventType>& events() const { return events_; };
+
+ bool tap() const { return tap_; }
+ bool tap_down() const { return tap_down_; }
+ bool tap_cancel() const { return tap_cancel_; }
+ bool begin() const { return begin_; }
+ bool end() const { return end_; }
+ bool scroll_begin() const { return scroll_begin_; }
+ bool scroll_update() const { return scroll_update_; }
+ bool scroll_end() const { return scroll_end_; }
+ bool pinch_begin() const { return pinch_begin_; }
+ bool pinch_update() const { return pinch_update_; }
+ bool pinch_end() const { return pinch_end_; }
+ bool long_press() const { return long_press_; }
+ bool long_tap() const { return long_tap_; }
+ bool fling() const { return fling_; }
+ bool two_finger_tap() const { return two_finger_tap_; }
+ bool swipe_left() const { return swipe_left_; }
+ bool swipe_right() const { return swipe_right_; }
+ bool swipe_up() const { return swipe_up_; }
+ bool swipe_down() const { return swipe_down_; }
+
+ const gfx::Point scroll_begin_position() const {
+ return scroll_begin_position_;
+ }
+
+ const gfx::Point tap_location() const {
+ return tap_location_;
+ }
+
+ float scroll_x() const { return scroll_x_; }
+ float scroll_y() const { return scroll_y_; }
+ float scroll_velocity_x() const { return scroll_velocity_x_; }
+ float scroll_velocity_y() const { return scroll_velocity_y_; }
+ float velocity_x() const { return velocity_x_; }
+ float velocity_y() const { return velocity_y_; }
+ float scroll_x_ordinal() const { return scroll_x_ordinal_; }
+ float scroll_y_ordinal() const { return scroll_y_ordinal_; }
+ float scroll_velocity_x_ordinal() const { return scroll_velocity_x_ordinal_; }
+ float scroll_velocity_y_ordinal() const { return scroll_velocity_y_ordinal_; }
+ float velocity_x_ordinal() const { return velocity_x_ordinal_; }
+ float velocity_y_ordinal() const { return velocity_y_ordinal_; }
+ int touch_id() const { return touch_id_; }
+ const gfx::Rect& bounding_box() const { return bounding_box_; }
+ int tap_count() const { return tap_count_; }
+
+ void WaitUntilReceivedGesture(ui::EventType type) {
+ wait_until_event_ = type;
+ run_loop_.reset(new base::RunLoop(
+ Env::GetInstance()->GetDispatcher()));
+ run_loop_->Run();
+ }
+
+ virtual void OnGestureEvent(ui::GestureEvent* gesture) OVERRIDE {
+ events_.push_back(gesture->type());
+ bounding_box_ = gesture->details().bounding_box();
+ switch (gesture->type()) {
+ case ui::ET_GESTURE_TAP:
+ tap_location_ = gesture->location();
+ tap_count_ = gesture->details().tap_count();
+ tap_ = true;
+ break;
+ case ui::ET_GESTURE_TAP_DOWN:
+ tap_down_ = true;
+ break;
+ case ui::ET_GESTURE_TAP_CANCEL:
+ tap_cancel_ = true;
+ break;
+ case ui::ET_GESTURE_BEGIN:
+ begin_ = true;
+ break;
+ case ui::ET_GESTURE_END:
+ end_ = true;
+ break;
+ case ui::ET_GESTURE_SCROLL_BEGIN:
+ scroll_begin_ = true;
+ scroll_begin_position_ = gesture->location();
+ break;
+ case ui::ET_GESTURE_SCROLL_UPDATE:
+ scroll_update_ = true;
+ scroll_x_ += gesture->details().scroll_x();
+ scroll_y_ += gesture->details().scroll_y();
+ scroll_velocity_x_ = gesture->details().velocity_x();
+ scroll_velocity_y_ = gesture->details().velocity_y();
+ scroll_x_ordinal_ += gesture->details().scroll_x_ordinal();
+ scroll_y_ordinal_ += gesture->details().scroll_y_ordinal();
+ scroll_velocity_x_ordinal_ = gesture->details().velocity_x_ordinal();
+ scroll_velocity_y_ordinal_ = gesture->details().velocity_y_ordinal();
+ break;
+ case ui::ET_GESTURE_SCROLL_END:
+ EXPECT_TRUE(velocity_x_ == 0 && velocity_y_ == 0);
+ scroll_end_ = true;
+ break;
+ case ui::ET_GESTURE_PINCH_BEGIN:
+ pinch_begin_ = true;
+ break;
+ case ui::ET_GESTURE_PINCH_UPDATE:
+ pinch_update_ = true;
+ break;
+ case ui::ET_GESTURE_PINCH_END:
+ pinch_end_ = true;
+ break;
+ case ui::ET_GESTURE_LONG_PRESS:
+ long_press_ = true;
+ touch_id_ = gesture->details().touch_id();
+ break;
+ case ui::ET_GESTURE_LONG_TAP:
+ long_tap_ = true;
+ break;
+ case ui::ET_SCROLL_FLING_START:
+ EXPECT_TRUE(gesture->details().velocity_x() != 0 ||
+ gesture->details().velocity_y() != 0);
+ EXPECT_FALSE(scroll_end_);
+ fling_ = true;
+ velocity_x_ = gesture->details().velocity_x();
+ velocity_y_ = gesture->details().velocity_y();
+ velocity_x_ordinal_ = gesture->details().velocity_x_ordinal();
+ velocity_y_ordinal_ = gesture->details().velocity_y_ordinal();
+ break;
+ case ui::ET_GESTURE_TWO_FINGER_TAP:
+ two_finger_tap_ = true;
+ break;
+ case ui::ET_GESTURE_MULTIFINGER_SWIPE:
+ swipe_left_ = gesture->details().swipe_left();
+ swipe_right_ = gesture->details().swipe_right();
+ swipe_up_ = gesture->details().swipe_up();
+ swipe_down_ = gesture->details().swipe_down();
+ break;
+ default:
+ NOTREACHED();
+ }
+ if (wait_until_event_ == gesture->type() && run_loop_) {
+ run_loop_->Quit();
+ wait_until_event_ = ui::ET_UNKNOWN;
+ }
+ gesture->StopPropagation();
+ }
+
+ private:
+ scoped_ptr<base::RunLoop> run_loop_;
+ std::vector<ui::EventType> events_;
+
+ bool tap_;
+ bool tap_down_;
+ bool tap_cancel_;
+ bool begin_;
+ bool end_;
+ bool scroll_begin_;
+ bool scroll_update_;
+ bool scroll_end_;
+ bool pinch_begin_;
+ bool pinch_update_;
+ bool pinch_end_;
+ bool long_press_;
+ bool long_tap_;
+ bool fling_;
+ bool two_finger_tap_;
+ bool swipe_left_;
+ bool swipe_right_;
+ bool swipe_up_;
+ bool swipe_down_;
+
+ gfx::Point scroll_begin_position_;
+ gfx::Point tap_location_;
+
+ float scroll_x_;
+ float scroll_y_;
+ float scroll_velocity_x_;
+ float scroll_velocity_y_;
+ float velocity_x_;
+ float velocity_y_;
+ float scroll_x_ordinal_;
+ float scroll_y_ordinal_;
+ float scroll_velocity_x_ordinal_;
+ float scroll_velocity_y_ordinal_;
+ float velocity_x_ordinal_;
+ float velocity_y_ordinal_;
+ int touch_id_;
+ gfx::Rect bounding_box_;
+ int tap_count_;
+
+ ui::EventType wait_until_event_;
+
+ DISALLOW_COPY_AND_ASSIGN(GestureEventConsumeDelegate);
+};
+
+class QueueTouchEventDelegate : public GestureEventConsumeDelegate {
+ public:
+ explicit QueueTouchEventDelegate(RootWindow* root_window)
+ : window_(NULL),
+ root_window_(root_window),
+ queue_events_(true) {
+ }
+ virtual ~QueueTouchEventDelegate() {}
+
+ virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE {
+ if (queue_events_) {
+ queue_.push(new ui::TouchEvent(*event, window_, window_));
+ event->StopPropagation();
+ }
+ }
+
+ void ReceivedAck() {
+ ReceivedAckImpl(false);
+ }
+
+ void ReceivedAckPreventDefaulted() {
+ ReceivedAckImpl(true);
+ }
+
+ void set_window(Window* w) { window_ = w; }
+ void set_queue_events(bool queue) { queue_events_ = queue; }
+
+ private:
+ void ReceivedAckImpl(bool prevent_defaulted) {
+ scoped_ptr<ui::TouchEvent> event(queue_.front());
+ root_window_->ProcessedTouchEvent(event.get(), window_,
+ prevent_defaulted ? ui::ER_HANDLED : ui::ER_UNHANDLED);
+ queue_.pop();
+ }
+
+ std::queue<ui::TouchEvent*> queue_;
+ Window* window_;
+ RootWindow* root_window_;
+ bool queue_events_;
+
+ DISALLOW_COPY_AND_ASSIGN(QueueTouchEventDelegate);
+};
+
+// A delegate that ignores gesture events but keeps track of [synthetic] mouse
+// events.
+class GestureEventSynthDelegate : public TestWindowDelegate {
+ public:
+ GestureEventSynthDelegate()
+ : mouse_enter_(false),
+ mouse_exit_(false),
+ mouse_press_(false),
+ mouse_release_(false),
+ mouse_move_(false),
+ double_click_(false) {
+ }
+
+ void Reset() {
+ mouse_enter_ = false;
+ mouse_exit_ = false;
+ mouse_press_ = false;
+ mouse_release_ = false;
+ mouse_move_ = false;
+ double_click_ = false;
+ }
+
+ bool mouse_enter() const { return mouse_enter_; }
+ bool mouse_exit() const { return mouse_exit_; }
+ bool mouse_press() const { return mouse_press_; }
+ bool mouse_move() const { return mouse_move_; }
+ bool mouse_release() const { return mouse_release_; }
+ bool double_click() const { return double_click_; }
+
+ virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
+ switch (event->type()) {
+ case ui::ET_MOUSE_PRESSED:
+ double_click_ = event->flags() & ui::EF_IS_DOUBLE_CLICK;
+ mouse_press_ = true;
+ break;
+ case ui::ET_MOUSE_RELEASED:
+ mouse_release_ = true;
+ break;
+ case ui::ET_MOUSE_MOVED:
+ mouse_move_ = true;
+ break;
+ case ui::ET_MOUSE_ENTERED:
+ mouse_enter_ = true;
+ break;
+ case ui::ET_MOUSE_EXITED:
+ mouse_exit_ = true;
+ break;
+ default:
+ NOTREACHED();
+ }
+ event->SetHandled();
+ }
+
+ private:
+ bool mouse_enter_;
+ bool mouse_exit_;
+ bool mouse_press_;
+ bool mouse_release_;
+ bool mouse_move_;
+ bool double_click_;
+
+ DISALLOW_COPY_AND_ASSIGN(GestureEventSynthDelegate);
+};
+
+class TestOneShotGestureSequenceTimer
+ : public base::OneShotTimer<ui::GestureSequence> {
+ public:
+ TestOneShotGestureSequenceTimer() {}
+
+ void ForceTimeout() {
+ if (IsRunning()) {
+ user_task().Run();
+ Stop();
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestOneShotGestureSequenceTimer);
+};
+
+class TimerTestGestureSequence : public ui::GestureSequence {
+ public:
+ explicit TimerTestGestureSequence(ui::GestureEventHelper* helper)
+ : ui::GestureSequence(helper) {
+ }
+
+ void ForceTimeout() {
+ static_cast<TestOneShotGestureSequenceTimer*>(
+ GetLongPressTimer())->ForceTimeout();
+ }
+
+ bool IsTimerRunning() {
+ return GetLongPressTimer()->IsRunning();
+ }
+
+ virtual base::OneShotTimer<ui::GestureSequence>* CreateTimer() OVERRIDE {
+ return new TestOneShotGestureSequenceTimer();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TimerTestGestureSequence);
+};
+
+class TestGestureRecognizer : public ui::GestureRecognizerImpl {
+ public:
+ explicit TestGestureRecognizer(RootWindow* root_window)
+ : GestureRecognizerImpl(root_window) {
+ }
+
+ ui::GestureSequence* GetGestureSequenceForTesting(Window* window) {
+ return GetGestureSequenceForConsumer(window);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestGestureRecognizer);
+};
+
+class TimerTestGestureRecognizer : public TestGestureRecognizer {
+ public:
+ explicit TimerTestGestureRecognizer(RootWindow* root_window)
+ : TestGestureRecognizer(root_window) {
+ }
+
+ virtual ui::GestureSequence* CreateSequence(
+ ui::GestureEventHelper* helper) OVERRIDE {
+ return new TimerTestGestureSequence(helper);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TimerTestGestureRecognizer);
+};
+
+base::TimeDelta GetTime() {
+ return ui::EventTimeForNow();
+}
+
+class TimedEvents {
+ private:
+ int simulated_now_;
+
+ public:
+ TimedEvents() : simulated_now_(0) {
+ }
+
+ base::TimeDelta Now() {
+ base::TimeDelta t = base::TimeDelta::FromMilliseconds(simulated_now_);
+ simulated_now_++;
+ return t;
+ }
+
+ base::TimeDelta LeapForward(int time_in_millis) {
+ simulated_now_ += time_in_millis;
+ return base::TimeDelta::FromMilliseconds(simulated_now_);
+ }
+
+ base::TimeDelta InFuture(int time_in_millis) {
+ return base::TimeDelta::FromMilliseconds(simulated_now_ + time_in_millis);
+ }
+
+ void SendScrollEvents(RootWindow* root_window,
+ int x_start,
+ int y_start,
+ int dx,
+ int dy,
+ int touch_id,
+ int time_step,
+ int num_steps,
+ GestureEventConsumeDelegate* delegate) {
+ int x = x_start;
+ int y = y_start;
+
+ for (int i = 0; i < num_steps; i++) {
+ x += dx;
+ y += dy;
+ ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(x, y),
+ touch_id,
+ base::TimeDelta::FromMilliseconds(simulated_now_));
+ root_window->AsRootWindowHostDelegate()->OnHostTouchEvent(&move);
+ simulated_now_ += time_step;
+ }
+ }
+
+ void SendScrollEvent(RootWindow* root_window,
+ int x,
+ int y,
+ int touch_id,
+ GestureEventConsumeDelegate* delegate) {
+ delegate->Reset();
+ ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(x, y),
+ touch_id,
+ base::TimeDelta::FromMilliseconds(simulated_now_));
+ root_window->AsRootWindowHostDelegate()->OnHostTouchEvent(&move);
+ simulated_now_++;
+ }
+};
+
+// An event handler to keep track of events.
+class TestEventHandler : public ui::EventHandler {
+ public:
+ TestEventHandler() : touch_released_count_(0), touch_pressed_count_(0),
+ touch_moved_count_(0), touch_stationary_count_(0),
+ touch_cancelled_count_(0) {
+ }
+
+ virtual ~TestEventHandler() {}
+
+ virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE {
+ switch (event->type()) {
+ case ui::ET_TOUCH_RELEASED:
+ touch_released_count_++;
+ break;
+ case ui::ET_TOUCH_PRESSED:
+ touch_pressed_count_++;
+ break;
+ case ui::ET_TOUCH_MOVED:
+ touch_moved_count_++;
+ break;
+ case ui::ET_TOUCH_STATIONARY:
+ touch_stationary_count_++;
+ break;
+ case ui::ET_TOUCH_CANCELLED:
+ touch_cancelled_count_++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ void Reset() {
+ touch_released_count_ = 0;
+ touch_pressed_count_ = 0;
+ touch_moved_count_ = 0;
+ touch_stationary_count_ = 0;
+ touch_cancelled_count_ = 0;
+ }
+
+ int touch_released_count() const { return touch_released_count_; }
+ int touch_pressed_count() const { return touch_pressed_count_; }
+ int touch_moved_count() const { return touch_moved_count_; }
+ int touch_stationary_count() const { return touch_stationary_count_; }
+ int touch_cancelled_count() const { return touch_cancelled_count_; }
+
+ private:
+ int touch_released_count_;
+ int touch_pressed_count_;
+ int touch_moved_count_;
+ int touch_stationary_count_;
+ int touch_cancelled_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestEventHandler);
+};
+
+} // namespace
+
+class GestureRecognizerTest : public AuraTestBase {
+ public:
+ GestureRecognizerTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableScrollPrediction);
+ AuraTestBase::SetUp();
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(GestureRecognizerTest);
+};
+
+// Check that appropriate touch events generate tap gesture events.
+TEST_F(GestureRecognizerTest, GestureEventTap) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ TimedEvents tes;
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+ const int kTouchId = 2;
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ delegate->Reset();
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_TRUE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_TRUE(delegate->begin());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_FALSE(delegate->long_press());
+
+ // Make sure there is enough delay before the touch is released so that it is
+ // recognized as a tap.
+ delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId, tes.LeapForward(50));
+
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ EXPECT_TRUE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->begin());
+ EXPECT_TRUE(delegate->end());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+
+ EXPECT_EQ(1, delegate->tap_count());
+}
+
+// Check that appropriate touch events generate tap gesture events
+// when information about the touch radii are provided.
+TEST_F(GestureRecognizerTest, GestureEventTapRegion) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ TimedEvents tes;
+ const int kWindowWidth = 800;
+ const int kWindowHeight = 600;
+ const int kTouchId = 2;
+ gfx::Rect bounds(0, 0, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ // Test with no ET_TOUCH_MOVED events.
+ {
+ delegate->Reset();
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId, tes.Now());
+ press.set_radius_x(5);
+ press.set_radius_y(12);
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_TRUE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_TRUE(delegate->begin());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_FALSE(delegate->long_press());
+
+ // Make sure there is enough delay before the touch is released so that it
+ // is recognized as a tap.
+ delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId, tes.LeapForward(50));
+ release.set_radius_x(5);
+ release.set_radius_y(12);
+
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ EXPECT_TRUE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->begin());
+ EXPECT_TRUE(delegate->end());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+
+ EXPECT_EQ(1, delegate->tap_count());
+ gfx::Point actual_point(delegate->tap_location());
+ EXPECT_EQ(24, delegate->bounding_box().width());
+ EXPECT_EQ(24, delegate->bounding_box().height());
+ EXPECT_EQ(101, actual_point.x());
+ EXPECT_EQ(201, actual_point.y());
+ }
+
+ // Test with no ET_TOUCH_MOVED events but different touch points and radii.
+ {
+ delegate->Reset();
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(365, 290),
+ kTouchId, tes.Now());
+ press.set_radius_x(8);
+ press.set_radius_y(14);
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_TRUE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_TRUE(delegate->begin());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_FALSE(delegate->long_press());
+
+ delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(367, 291),
+ kTouchId, tes.LeapForward(50));
+ release.set_radius_x(20);
+ release.set_radius_y(13);
+
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ EXPECT_TRUE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->begin());
+ EXPECT_TRUE(delegate->end());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+
+ EXPECT_EQ(1, delegate->tap_count());
+ gfx::Point actual_point(delegate->tap_location());
+ EXPECT_EQ(40, delegate->bounding_box().width());
+ EXPECT_EQ(40, delegate->bounding_box().height());
+ EXPECT_EQ(367, actual_point.x());
+ EXPECT_EQ(291, actual_point.y());
+ }
+
+ // Test with a single ET_TOUCH_MOVED event.
+ {
+ delegate->Reset();
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(46, 205),
+ kTouchId, tes.Now());
+ press.set_radius_x(6);
+ press.set_radius_y(10);
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_TRUE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_TRUE(delegate->begin());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_FALSE(delegate->long_press());
+
+ delegate->Reset();
+ ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(49, 204),
+ kTouchId, tes.LeapForward(50));
+ move.set_radius_x(8);
+ move.set_radius_y(12);
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->begin());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_FALSE(delegate->long_press());
+
+ delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(49, 204),
+ kTouchId, tes.LeapForward(50));
+ release.set_radius_x(4);
+ release.set_radius_y(8);
+
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ EXPECT_TRUE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->begin());
+ EXPECT_TRUE(delegate->end());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+
+ EXPECT_EQ(1, delegate->tap_count());
+ gfx::Point actual_point(delegate->tap_location());
+ EXPECT_EQ(25, delegate->bounding_box().width());
+ EXPECT_EQ(24, delegate->bounding_box().height());
+ EXPECT_EQ(48, actual_point.x());
+ EXPECT_EQ(204, actual_point.y());
+ }
+
+ // Test with a few ET_TOUCH_MOVED events.
+ {
+ delegate->Reset();
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(400, 150),
+ kTouchId, tes.Now());
+ press.set_radius_x(7);
+ press.set_radius_y(10);
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_TRUE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_TRUE(delegate->begin());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_FALSE(delegate->long_press());
+
+ delegate->Reset();
+ ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(397, 151),
+ kTouchId, tes.LeapForward(50));
+ move.set_radius_x(13);
+ move.set_radius_y(12);
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->begin());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_FALSE(delegate->long_press());
+
+ delegate->Reset();
+ ui::TouchEvent move1(ui::ET_TOUCH_MOVED, gfx::Point(397, 149),
+ kTouchId, tes.LeapForward(50));
+ move1.set_radius_x(16);
+ move1.set_radius_y(16);
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move1);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->begin());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_FALSE(delegate->long_press());
+
+ delegate->Reset();
+ ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(400, 150),
+ kTouchId, tes.LeapForward(50));
+ move2.set_radius_x(14);
+ move2.set_radius_y(10);
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move2);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->begin());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_FALSE(delegate->long_press());
+
+ delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(401, 149),
+ kTouchId, tes.LeapForward(50));
+ release.set_radius_x(8);
+ release.set_radius_y(9);
+
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ EXPECT_TRUE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->begin());
+ EXPECT_TRUE(delegate->end());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+
+ EXPECT_EQ(1, delegate->tap_count());
+ gfx::Point actual_point(delegate->tap_location());
+ EXPECT_EQ(33, delegate->bounding_box().width());
+ EXPECT_EQ(32, delegate->bounding_box().height());
+ EXPECT_EQ(397, actual_point.x());
+ EXPECT_EQ(149, actual_point.y());
+ }
+}
+
+// Check that appropriate touch events generate scroll gesture events.
+TEST_F(GestureRecognizerTest, GestureEventScroll) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ TimedEvents tes;
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+ const int kTouchId = 5;
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ delegate->Reset();
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ EXPECT_2_EVENTS(delegate->events(),
+ ui::ET_GESTURE_BEGIN,
+ ui::ET_GESTURE_TAP_DOWN);
+
+ // Move the touch-point enough so that it is considered as a scroll. This
+ // should generate both SCROLL_BEGIN and SCROLL_UPDATE gestures.
+ // The first movement is diagonal, to ensure that we have a free scroll,
+ // and not a rail scroll.
+ tes.SendScrollEvent(root_window(), 130, 230, kTouchId, delegate.get());
+ EXPECT_3_EVENTS(delegate->events(),
+ ui::ET_GESTURE_TAP_CANCEL,
+ ui::ET_GESTURE_SCROLL_BEGIN,
+ ui::ET_GESTURE_SCROLL_UPDATE);
+ EXPECT_EQ(29, delegate->scroll_x());
+ EXPECT_EQ(29, delegate->scroll_y());
+ EXPECT_EQ(gfx::Point(1, 1).ToString(),
+ delegate->scroll_begin_position().ToString());
+
+ // When scrolling with a single finger, the bounding box of the gesture should
+ // be empty, since it's a single point and the radius for testing is zero.
+ EXPECT_TRUE(delegate->bounding_box().IsEmpty());
+
+ // Move some more to generate a few more scroll updates.
+ tes.SendScrollEvent(root_window(), 110, 211, kTouchId, delegate.get());
+ EXPECT_1_EVENT(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE);
+ EXPECT_EQ(-20, delegate->scroll_x());
+ EXPECT_EQ(-19, delegate->scroll_y());
+ EXPECT_TRUE(delegate->bounding_box().IsEmpty());
+
+ tes.SendScrollEvent(root_window(), 140, 215, kTouchId, delegate.get());
+ EXPECT_1_EVENT(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE);
+ EXPECT_EQ(30, delegate->scroll_x());
+ EXPECT_EQ(4, delegate->scroll_y());
+ EXPECT_TRUE(delegate->bounding_box().IsEmpty());
+
+ // Release the touch. This should end the scroll.
+ delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId,
+ tes.LeapForward(50));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ EXPECT_2_EVENTS(delegate->events(),
+ ui::ET_SCROLL_FLING_START,
+ ui::ET_GESTURE_END);
+ EXPECT_TRUE(delegate->bounding_box().IsEmpty());
+}
+
+// Check that predicted scroll update positions are correct.
+TEST_F(GestureRecognizerTest, GestureEventScrollPrediction) {
+ const double prediction_interval = 0.03;
+ ui::GestureConfiguration::set_scroll_prediction_seconds(prediction_interval);
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ TimedEvents tes;
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+ const int kTouchId = 5;
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ delegate->Reset();
+ // Tracks the total scroll since we want to verify that the correct position
+ // will be scrolled to throughout the prediction.
+ gfx::Vector2dF total_scroll;
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ EXPECT_2_EVENTS(delegate->events(),
+ ui::ET_GESTURE_BEGIN,
+ ui::ET_GESTURE_TAP_DOWN);
+
+ // Move the touch-point enough so that it is considered as a scroll. This
+ // should generate both SCROLL_BEGIN and SCROLL_UPDATE gestures.
+ // The first movement is diagonal, to ensure that we have a free scroll,
+ // and not a rail scroll.
+ tes.LeapForward(30);
+ tes.SendScrollEvent(root_window(), 130, 230, kTouchId, delegate.get());
+ EXPECT_3_EVENTS(delegate->events(),
+ ui::ET_GESTURE_TAP_CANCEL,
+ ui::ET_GESTURE_SCROLL_BEGIN,
+ ui::ET_GESTURE_SCROLL_UPDATE);
+ EXPECT_GT(delegate->scroll_velocity_x(), 0);
+ EXPECT_GT(delegate->scroll_velocity_y(), 0);
+ total_scroll.set_x(total_scroll.x() + delegate->scroll_x());
+ total_scroll.set_y(total_scroll.y() + delegate->scroll_y());
+ EXPECT_EQ((int)(29 + delegate->scroll_velocity_x() * prediction_interval),
+ (int)(total_scroll.x()));
+ EXPECT_EQ((int)(29 + delegate->scroll_velocity_y() * prediction_interval),
+ (int)(total_scroll.y()));
+
+ // Move some more to generate a few more scroll updates.
+ tes.LeapForward(30);
+ tes.SendScrollEvent(root_window(), 110, 211, kTouchId, delegate.get());
+ EXPECT_1_EVENT(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE);
+ total_scroll.set_x(total_scroll.x() + delegate->scroll_x());
+ total_scroll.set_y(total_scroll.y() + delegate->scroll_y());
+ EXPECT_EQ((int)(9 + delegate->scroll_velocity_x() * prediction_interval),
+ (int)(total_scroll.x()));
+ EXPECT_EQ((int)(10 + delegate->scroll_velocity_y() * prediction_interval),
+ (int)(total_scroll.y()));
+
+ tes.LeapForward(30);
+ tes.SendScrollEvent(root_window(), 140, 215, kTouchId, delegate.get());
+ EXPECT_1_EVENT(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE);
+ total_scroll.set_x(total_scroll.x() + delegate->scroll_x());
+ total_scroll.set_y(total_scroll.y() + delegate->scroll_y());
+ EXPECT_EQ((int)(39 + delegate->scroll_velocity_x() * prediction_interval),
+ (int)(total_scroll.x()));
+ EXPECT_EQ((int)(14 + delegate->scroll_velocity_y() * prediction_interval),
+ (int)(total_scroll.y()));
+
+ // Release the touch. This should end the scroll.
+ delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId,
+ tes.LeapForward(50));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+}
+
+// Check that the bounding box during a scroll event is correct.
+TEST_F(GestureRecognizerTest, GestureEventScrollBoundingBox) {
+ TimedEvents tes;
+ for (int radius = 1; radius <= 10; ++radius) {
+ ui::GestureConfiguration::set_default_radius(radius);
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+ const int kTouchId = 5;
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ const int kPositionX = 101;
+ const int kPositionY = 201;
+ delegate->Reset();
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED,
+ gfx::Point(kPositionX, kPositionY),
+ kTouchId,
+ tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ EXPECT_EQ(gfx::Rect(kPositionX - radius,
+ kPositionY - radius,
+ radius * 2,
+ radius * 2).ToString(),
+ delegate->bounding_box().ToString());
+
+ const int kScrollAmount = 50;
+ tes.SendScrollEvents(root_window(), kPositionX, kPositionY,
+ 1, 1, kTouchId, 1, kScrollAmount, delegate.get());
+ EXPECT_EQ(gfx::Point(1, 1).ToString(),
+ delegate->scroll_begin_position().ToString());
+ EXPECT_EQ(gfx::Rect(kPositionX + kScrollAmount - radius,
+ kPositionY + kScrollAmount - radius,
+ radius * 2,
+ radius * 2).ToString(),
+ delegate->bounding_box().ToString());
+
+ // Release the touch. This should end the scroll.
+ delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED,
+ gfx::Point(kPositionX + kScrollAmount,
+ kPositionY + kScrollAmount),
+ kTouchId, press.time_stamp() +
+ base::TimeDelta::FromMilliseconds(50));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ EXPECT_EQ(gfx::Rect(kPositionX + kScrollAmount - radius,
+ kPositionY + kScrollAmount - radius,
+ radius * 2,
+ radius * 2).ToString(),
+ delegate->bounding_box().ToString());
+ }
+ ui::GestureConfiguration::set_default_radius(0);
+}
+
+// Check Scroll End Events report correct velocities
+// if the user was on a horizontal rail
+TEST_F(GestureRecognizerTest, GestureEventHorizontalRailFling) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ TimedEvents tes;
+ const int kTouchId = 7;
+ gfx::Rect bounds(0, 0, 1000, 1000);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+
+ // Move the touch-point horizontally enough that it is considered a
+ // horizontal scroll.
+ tes.SendScrollEvent(root_window(), 20, 1, kTouchId, delegate.get());
+ EXPECT_EQ(0, delegate->scroll_y());
+ EXPECT_EQ(1, delegate->scroll_y_ordinal());
+ EXPECT_EQ(20, delegate->scroll_x());
+ EXPECT_EQ(20, delegate->scroll_x_ordinal());
+
+ // Get a high x velocity, while still staying on the rail
+ tes.SendScrollEvents(root_window(), 1, 1,
+ 100, 10, kTouchId, 1,
+ ui::GestureConfiguration::points_buffered_for_velocity(),
+ delegate.get());
+ // The y-velocity during the scroll should be 0 since this is in a horizontal
+ // rail scroll.
+ EXPECT_GT(delegate->scroll_velocity_x(), 0);
+ EXPECT_EQ(0, delegate->scroll_velocity_y());
+
+ delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+
+ EXPECT_TRUE(delegate->fling());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_GT(delegate->velocity_x(), 0);
+ EXPECT_EQ(0, delegate->velocity_y());
+}
+
+// Check Scroll End Events report correct velocities
+// if the user was on a vertical rail
+TEST_F(GestureRecognizerTest, GestureEventVerticalRailFling) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ TimedEvents tes;
+ const int kTouchId = 7;
+ gfx::Rect bounds(0, 0, 1000, 1000);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+
+ // Move the touch-point vertically enough that it is considered a
+ // vertical scroll.
+ tes.SendScrollEvent(root_window(), 1, 20, kTouchId, delegate.get());
+ EXPECT_EQ(20, delegate->scroll_y());
+ EXPECT_EQ(20, delegate->scroll_y_ordinal());
+ EXPECT_EQ(0, delegate->scroll_x());
+ EXPECT_EQ(1, delegate->scroll_x_ordinal());
+ EXPECT_EQ(0, delegate->scroll_velocity_x());
+ EXPECT_GT(delegate->scroll_velocity_x_ordinal(), 0);
+
+ // Get a high y velocity, while still staying on the rail
+ tes.SendScrollEvents(root_window(), 1, 1,
+ 10, 100, kTouchId, 1,
+ ui::GestureConfiguration::points_buffered_for_velocity(),
+ delegate.get());
+ EXPECT_EQ(0, delegate->scroll_velocity_x());
+ EXPECT_GT(delegate->scroll_velocity_y(), 0);
+
+ delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+
+ EXPECT_TRUE(delegate->fling());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_EQ(0, delegate->velocity_x());
+ EXPECT_GT(delegate->velocity_y(), 0);
+}
+
+// Check Scroll End Events reports zero velocities
+// if the user is not on a rail
+TEST_F(GestureRecognizerTest, GestureEventNonRailFling) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ TimedEvents tes;
+ const int kTouchId = 7;
+ gfx::Rect bounds(0, 0, 1000, 1000);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+
+ // Move the touch-point such that a non-rail scroll begins
+ tes.SendScrollEvent(root_window(), 20, 20, kTouchId, delegate.get());
+ EXPECT_EQ(20, delegate->scroll_y());
+ EXPECT_EQ(20, delegate->scroll_x());
+
+ tes.SendScrollEvents(root_window(), 1, 1,
+ 10, 100, kTouchId, 1,
+ ui::GestureConfiguration::points_buffered_for_velocity(),
+ delegate.get());
+
+ delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+
+ EXPECT_TRUE(delegate->fling());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_GT(delegate->velocity_x(), 0);
+ EXPECT_GT(delegate->velocity_y(), 0);
+}
+
+// Check that appropriate touch events generate long press events
+TEST_F(GestureRecognizerTest, GestureEventLongPress) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ TimedEvents tes;
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+ const int kTouchId = 2;
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ delegate->Reset();
+
+ TimerTestGestureRecognizer* gesture_recognizer =
+ new TimerTestGestureRecognizer(root_window());
+
+ root_window()->SetGestureRecognizerForTesting(gesture_recognizer);
+
+ ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press1);
+ EXPECT_TRUE(delegate->tap_down());
+ EXPECT_TRUE(delegate->begin());
+ EXPECT_FALSE(delegate->tap_cancel());
+
+ // We haven't pressed long enough for a long press to occur
+ EXPECT_FALSE(delegate->long_press());
+
+ // Wait until the timer runs out
+ delegate->WaitUntilReceivedGesture(ui::ET_GESTURE_LONG_PRESS);
+ EXPECT_TRUE(delegate->long_press());
+ EXPECT_EQ(0, delegate->touch_id());
+ EXPECT_FALSE(delegate->tap_cancel());
+
+ delegate->Reset();
+ ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release1);
+ EXPECT_FALSE(delegate->long_press());
+
+ // Note the tap down isn't cancelled until the release
+ EXPECT_TRUE(delegate->tap_cancel());
+}
+
+// Check that scrolling cancels a long press
+TEST_F(GestureRecognizerTest, GestureEventLongPressCancelledByScroll) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ TimedEvents tes;
+ ui::GestureConfiguration::set_long_press_time_in_seconds(.01);
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+ const int kTouchId = 6;
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ delegate->Reset();
+
+ TimerTestGestureRecognizer* gesture_recognizer =
+ new TimerTestGestureRecognizer(root_window());
+ TimerTestGestureSequence* gesture_sequence =
+ static_cast<TimerTestGestureSequence*>(
+ gesture_recognizer->GetGestureSequenceForTesting(window.get()));
+
+ root_window()->SetGestureRecognizerForTesting(gesture_recognizer);
+
+ ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press1);
+ EXPECT_TRUE(delegate->tap_down());
+
+ // We haven't pressed long enough for a long press to occur
+ EXPECT_FALSE(delegate->long_press());
+ EXPECT_FALSE(delegate->tap_cancel());
+
+ // Scroll around, to cancel the long press
+ tes.SendScrollEvent(root_window(), 130, 230, kTouchId, delegate.get());
+ // Wait until the timer runs out
+ gesture_sequence->ForceTimeout();
+ EXPECT_FALSE(delegate->long_press());
+ EXPECT_TRUE(delegate->tap_cancel());
+
+ delegate->Reset();
+ ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId, tes.LeapForward(10));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release1);
+ EXPECT_FALSE(delegate->long_press());
+ EXPECT_FALSE(delegate->tap_cancel());
+}
+
+// Check that appropriate touch events generate long tap events
+TEST_F(GestureRecognizerTest, GestureEventLongTap) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ TimedEvents tes;
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+ const int kTouchId = 2;
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ delegate->Reset();
+
+ TimerTestGestureRecognizer* gesture_recognizer =
+ new TimerTestGestureRecognizer(root_window());
+
+ root_window()->SetGestureRecognizerForTesting(gesture_recognizer);
+
+ ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press1);
+ EXPECT_TRUE(delegate->tap_down());
+ EXPECT_TRUE(delegate->begin());
+ EXPECT_FALSE(delegate->tap_cancel());
+
+ // We haven't pressed long enough for a long press to occur
+ EXPECT_FALSE(delegate->long_press());
+
+ // Wait until the timer runs out
+ delegate->WaitUntilReceivedGesture(ui::ET_GESTURE_LONG_PRESS);
+ EXPECT_TRUE(delegate->long_press());
+ EXPECT_EQ(0, delegate->touch_id());
+ EXPECT_FALSE(delegate->tap_cancel());
+
+ delegate->Reset();
+ ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release1);
+ EXPECT_FALSE(delegate->long_press());
+ EXPECT_TRUE(delegate->long_tap());
+
+ // Note the tap down isn't cancelled until the release
+ EXPECT_TRUE(delegate->tap_cancel());
+}
+
+// Check that second tap cancels a long press
+TEST_F(GestureRecognizerTest, GestureEventLongPressCancelledBySecondTap) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ TimedEvents tes;
+ ui::GestureConfiguration::set_long_press_time_in_seconds(.01);
+ const int kWindowWidth = 300;
+ const int kWindowHeight = 400;
+ const int kTouchId1 = 8;
+ const int kTouchId2 = 2;
+ gfx::Rect bounds(5, 5, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ TimerTestGestureRecognizer* gesture_recognizer =
+ new TimerTestGestureRecognizer(root_window());
+ TimerTestGestureSequence* gesture_sequence =
+ static_cast<TimerTestGestureSequence*>(
+ gesture_recognizer->GetGestureSequenceForTesting(window.get()));
+
+ root_window()->SetGestureRecognizerForTesting(gesture_recognizer);
+
+ delegate->Reset();
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId1, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ EXPECT_TRUE(delegate->tap_down());
+ EXPECT_TRUE(delegate->begin());
+
+ // We haven't pressed long enough for a long press to occur
+ EXPECT_FALSE(delegate->long_press());
+
+ // Second tap, to cancel the long press
+ delegate->Reset();
+ ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10),
+ kTouchId2, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+ EXPECT_FALSE(delegate->tap_down()); // no touch down for second tap.
+ EXPECT_TRUE(delegate->tap_cancel());
+ EXPECT_TRUE(delegate->begin());
+
+ // Wait until the timer runs out
+ gesture_sequence->ForceTimeout();
+
+ // No long press occurred
+ EXPECT_FALSE(delegate->long_press());
+
+ delegate->Reset();
+ ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId1, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release1);
+ EXPECT_FALSE(delegate->long_press());
+ EXPECT_TRUE(delegate->two_finger_tap());
+ EXPECT_FALSE(delegate->tap_cancel());
+}
+
+// Check that horizontal scroll gestures cause scrolls on horizontal rails.
+// Also tests that horizontal rails can be broken.
+TEST_F(GestureRecognizerTest, GestureEventHorizontalRailScroll) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ TimedEvents tes;
+ const int kTouchId = 7;
+ gfx::Rect bounds(0, 0, 1000, 1000);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+
+ // Move the touch-point horizontally enough that it is considered a
+ // horizontal scroll.
+ tes.SendScrollEvent(root_window(), 20, 1, kTouchId, delegate.get());
+ EXPECT_EQ(0, delegate->scroll_y());
+ EXPECT_EQ(20, delegate->scroll_x());
+
+ tes.SendScrollEvent(root_window(), 25, 6, kTouchId, delegate.get());
+ EXPECT_TRUE(delegate->scroll_update());
+ EXPECT_EQ(5, delegate->scroll_x());
+ // y shouldn't change, as we're on a horizontal rail.
+ EXPECT_EQ(0, delegate->scroll_y());
+
+ // Send enough information that a velocity can be calculated for the gesture,
+ // and we can break the rail
+ tes.SendScrollEvents(root_window(), 1, 1,
+ 1, 100, kTouchId, 1,
+ ui::GestureConfiguration::points_buffered_for_velocity(),
+ delegate.get());
+ // Since the scroll is not longer railing, the velocity should be set for both
+ // axis.
+ EXPECT_GT(delegate->scroll_velocity_x(), 0);
+ EXPECT_GT(delegate->scroll_velocity_y(), 0);
+
+ tes.SendScrollEvent(root_window(), 0, 0, kTouchId, delegate.get());
+ tes.SendScrollEvent(root_window(), 5, 5, kTouchId, delegate.get());
+
+ // The rail should be broken
+ EXPECT_TRUE(delegate->scroll_update());
+ EXPECT_EQ(5, delegate->scroll_x());
+ EXPECT_EQ(5, delegate->scroll_y());
+}
+
+// Check that vertical scroll gestures cause scrolls on vertical rails.
+// Also tests that vertical rails can be broken.
+TEST_F(GestureRecognizerTest, GestureEventVerticalRailScroll) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ TimedEvents tes;
+ const int kTouchId = 7;
+ gfx::Rect bounds(0, 0, 1000, 1000);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+
+ // Move the touch-point vertically enough that it is considered a
+ // vertical scroll.
+ tes.SendScrollEvent(root_window(), 1, 20, kTouchId, delegate.get());
+ EXPECT_EQ(0, delegate->scroll_x());
+ EXPECT_EQ(20, delegate->scroll_y());
+
+ tes.SendScrollEvent(root_window(), 6, 25, kTouchId, delegate.get());
+ EXPECT_TRUE(delegate->scroll_update());
+ EXPECT_EQ(5, delegate->scroll_y());
+ // x shouldn't change, as we're on a vertical rail.
+ EXPECT_EQ(0, delegate->scroll_x());
+ EXPECT_EQ(0, delegate->scroll_velocity_x());
+
+ // Send enough information that a velocity can be calculated for the gesture,
+ // and we can break the rail
+ tes.SendScrollEvents(root_window(), 1, 1,
+ 100, 1, kTouchId, 1,
+ ui::GestureConfiguration::points_buffered_for_velocity(),
+ delegate.get());
+ EXPECT_GT(delegate->scroll_velocity_x(), 0);
+ EXPECT_GT(delegate->scroll_velocity_y(), 0);
+
+ tes.SendScrollEvent(root_window(), 0, 0, kTouchId, delegate.get());
+ tes.SendScrollEvent(root_window(), 5, 5, kTouchId, delegate.get());
+
+ // The rail should be broken
+ EXPECT_TRUE(delegate->scroll_update());
+ EXPECT_EQ(5, delegate->scroll_x());
+ EXPECT_EQ(5, delegate->scroll_y());
+}
+
+TEST_F(GestureRecognizerTest, GestureTapFollowedByScroll) {
+ // First, tap. Then, do a scroll using the same touch-id.
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ TimedEvents tes;
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+ const int kTouchId = 3;
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ delegate->Reset();
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_TRUE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+
+ // Make sure there is enough delay before the touch is released so that it is
+ // recognized as a tap.
+ delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId, tes.LeapForward(50));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ EXPECT_TRUE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+
+ // Now, do a scroll gesture. Delay it sufficiently so that it doesn't trigger
+ // a double-tap.
+ delegate->Reset();
+ ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId, tes.LeapForward(1000));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press1);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_TRUE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+
+ // Move the touch-point enough so that it is considered as a scroll. This
+ // should generate both SCROLL_BEGIN and SCROLL_UPDATE gestures.
+ // The first movement is diagonal, to ensure that we have a free scroll,
+ // and not a rail scroll.
+ delegate->Reset();
+ ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(130, 230),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_TRUE(delegate->tap_cancel());
+ EXPECT_TRUE(delegate->scroll_begin());
+ EXPECT_TRUE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_EQ(29, delegate->scroll_x());
+ EXPECT_EQ(29, delegate->scroll_y());
+
+ // Move some more to generate a few more scroll updates.
+ delegate->Reset();
+ ui::TouchEvent move1(ui::ET_TOUCH_MOVED, gfx::Point(110, 211),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move1);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_TRUE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_EQ(-20, delegate->scroll_x());
+ EXPECT_EQ(-19, delegate->scroll_y());
+
+ delegate->Reset();
+ ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(140, 215),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move2);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_TRUE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_EQ(30, delegate->scroll_x());
+ EXPECT_EQ(4, delegate->scroll_y());
+
+ // Release the touch. This should end the scroll.
+ delegate->Reset();
+ ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release1);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_TRUE(delegate->fling());
+}
+
+TEST_F(GestureRecognizerTest, AsynchronousGestureRecognition) {
+ scoped_ptr<QueueTouchEventDelegate> queued_delegate(
+ new QueueTouchEventDelegate(root_window()));
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+ const int kTouchId1 = 6;
+ const int kTouchId2 = 4;
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> queue(CreateTestWindowWithDelegate(
+ queued_delegate.get(), -1234, bounds, root_window()));
+
+ queued_delegate->set_window(queue.get());
+
+ // Touch down on the window. This should not generate any gesture event.
+ queued_delegate->Reset();
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId1, GetTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ EXPECT_FALSE(queued_delegate->tap());
+ EXPECT_FALSE(queued_delegate->tap_down());
+ EXPECT_FALSE(queued_delegate->tap_cancel());
+ EXPECT_FALSE(queued_delegate->begin());
+ EXPECT_FALSE(queued_delegate->scroll_begin());
+ EXPECT_FALSE(queued_delegate->scroll_update());
+ EXPECT_FALSE(queued_delegate->scroll_end());
+
+ // Introduce some delay before the touch is released so that it is recognized
+ // as a tap. However, this still should not create any gesture events.
+ queued_delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId1, press.time_stamp() +
+ base::TimeDelta::FromMilliseconds(50));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ EXPECT_FALSE(queued_delegate->tap());
+ EXPECT_FALSE(queued_delegate->tap_down());
+ EXPECT_FALSE(queued_delegate->tap_cancel());
+ EXPECT_FALSE(queued_delegate->begin());
+ EXPECT_FALSE(queued_delegate->end());
+ EXPECT_FALSE(queued_delegate->scroll_begin());
+ EXPECT_FALSE(queued_delegate->scroll_update());
+ EXPECT_FALSE(queued_delegate->scroll_end());
+
+ // Create another window, and place a touch-down on it. This should create a
+ // tap-down gesture.
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -2345, gfx::Rect(0, 0, 50, 50), root_window()));
+ delegate->Reset();
+ ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(10, 20),
+ kTouchId2, GetTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_TRUE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(queued_delegate->begin());
+ EXPECT_FALSE(queued_delegate->end());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+
+ ui::TouchEvent release2(ui::ET_TOUCH_RELEASED, gfx::Point(10, 20),
+ kTouchId2, GetTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release2);
+
+ // Process the first queued event.
+ queued_delegate->Reset();
+ queued_delegate->ReceivedAck();
+ EXPECT_FALSE(queued_delegate->tap());
+ EXPECT_TRUE(queued_delegate->tap_down());
+ EXPECT_TRUE(queued_delegate->begin());
+ EXPECT_FALSE(queued_delegate->tap_cancel());
+ EXPECT_FALSE(queued_delegate->end());
+ EXPECT_FALSE(queued_delegate->scroll_begin());
+ EXPECT_FALSE(queued_delegate->scroll_update());
+ EXPECT_FALSE(queued_delegate->scroll_end());
+
+ // Now, process the second queued event.
+ queued_delegate->Reset();
+ queued_delegate->ReceivedAck();
+ EXPECT_TRUE(queued_delegate->tap());
+ EXPECT_FALSE(queued_delegate->tap_down());
+ EXPECT_FALSE(queued_delegate->tap_cancel());
+ EXPECT_FALSE(queued_delegate->begin());
+ EXPECT_TRUE(queued_delegate->end());
+ EXPECT_FALSE(queued_delegate->scroll_begin());
+ EXPECT_FALSE(queued_delegate->scroll_update());
+ EXPECT_FALSE(queued_delegate->scroll_end());
+
+ // Start all over. Press on the first window, then press again on the second
+ // window. The second press should still go to the first window.
+ queued_delegate->Reset();
+ ui::TouchEvent press3(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId1, GetTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press3);
+ EXPECT_FALSE(queued_delegate->tap());
+ EXPECT_FALSE(queued_delegate->tap_down());
+ EXPECT_FALSE(queued_delegate->tap_cancel());
+ EXPECT_FALSE(queued_delegate->begin());
+ EXPECT_FALSE(queued_delegate->end());
+ EXPECT_FALSE(queued_delegate->begin());
+ EXPECT_FALSE(queued_delegate->end());
+ EXPECT_FALSE(queued_delegate->scroll_begin());
+ EXPECT_FALSE(queued_delegate->scroll_update());
+ EXPECT_FALSE(queued_delegate->scroll_end());
+
+ queued_delegate->Reset();
+ delegate->Reset();
+ ui::TouchEvent press4(ui::ET_TOUCH_PRESSED, gfx::Point(103, 203),
+ kTouchId2, GetTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press4);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->begin());
+ EXPECT_FALSE(delegate->end());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_FALSE(queued_delegate->tap());
+ EXPECT_FALSE(queued_delegate->tap_down());
+ EXPECT_FALSE(queued_delegate->tap_cancel());
+ EXPECT_FALSE(queued_delegate->begin());
+ EXPECT_FALSE(queued_delegate->end());
+ EXPECT_FALSE(queued_delegate->scroll_begin());
+ EXPECT_FALSE(queued_delegate->scroll_update());
+ EXPECT_FALSE(queued_delegate->scroll_end());
+
+ // Move the second touch-point enough so that it is considered a pinch. This
+ // should generate both SCROLL_BEGIN and PINCH_BEGIN gestures.
+ queued_delegate->Reset();
+ delegate->Reset();
+ int x_move = ui::GestureConfiguration::max_touch_move_in_pixels_for_click();
+ ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(103 + x_move, 203),
+ kTouchId2, GetTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->begin());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_FALSE(queued_delegate->tap());
+ EXPECT_FALSE(queued_delegate->tap_down());
+ EXPECT_FALSE(queued_delegate->tap_cancel());
+ EXPECT_FALSE(queued_delegate->begin());
+ EXPECT_FALSE(queued_delegate->scroll_begin());
+ EXPECT_FALSE(queued_delegate->scroll_update());
+ EXPECT_FALSE(queued_delegate->scroll_end());
+
+ queued_delegate->Reset();
+ queued_delegate->ReceivedAck();
+ EXPECT_FALSE(queued_delegate->tap());
+ EXPECT_TRUE(queued_delegate->tap_down());
+ EXPECT_TRUE(queued_delegate->begin());
+ EXPECT_FALSE(queued_delegate->tap_cancel());
+ EXPECT_FALSE(queued_delegate->end());
+ EXPECT_FALSE(queued_delegate->scroll_begin());
+ EXPECT_FALSE(queued_delegate->scroll_update());
+ EXPECT_FALSE(queued_delegate->scroll_end());
+
+ queued_delegate->Reset();
+ queued_delegate->ReceivedAck();
+ EXPECT_FALSE(queued_delegate->tap());
+ EXPECT_FALSE(queued_delegate->tap_down()); // no touch down for second tap.
+ EXPECT_TRUE(queued_delegate->tap_cancel());
+ EXPECT_TRUE(queued_delegate->begin());
+ EXPECT_FALSE(queued_delegate->end());
+ EXPECT_FALSE(queued_delegate->scroll_begin());
+ EXPECT_FALSE(queued_delegate->scroll_update());
+ EXPECT_FALSE(queued_delegate->scroll_end());
+ EXPECT_FALSE(queued_delegate->pinch_begin());
+ EXPECT_FALSE(queued_delegate->pinch_update());
+ EXPECT_FALSE(queued_delegate->pinch_end());
+
+ queued_delegate->Reset();
+ queued_delegate->ReceivedAck();
+ EXPECT_FALSE(queued_delegate->tap());
+ EXPECT_FALSE(queued_delegate->tap_down());
+ EXPECT_FALSE(queued_delegate->tap_cancel());
+ EXPECT_FALSE(queued_delegate->begin());
+ EXPECT_FALSE(queued_delegate->end());
+ EXPECT_TRUE(queued_delegate->scroll_begin());
+ EXPECT_FALSE(queued_delegate->scroll_update());
+ EXPECT_FALSE(queued_delegate->scroll_end());
+ EXPECT_TRUE(queued_delegate->pinch_begin());
+ EXPECT_FALSE(queued_delegate->pinch_update());
+ EXPECT_FALSE(queued_delegate->pinch_end());
+}
+
+// Check that appropriate touch events generate pinch gesture events.
+TEST_F(GestureRecognizerTest, GestureEventPinchFromScroll) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ TimedEvents tes;
+ const int kWindowWidth = 300;
+ const int kWindowHeight = 400;
+ const int kTouchId1 = 5;
+ const int kTouchId2 = 3;
+ gfx::Rect bounds(5, 5, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ aura::RootWindow* root = root_window();
+
+ delegate->Reset();
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId1, tes.Now());
+ root->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ EXPECT_2_EVENTS(delegate->events(),
+ ui::ET_GESTURE_BEGIN,
+ ui::ET_GESTURE_TAP_DOWN);
+
+ // Move the touch-point enough so that it is considered as a scroll. This
+ // should generate both SCROLL_BEGIN and SCROLL_UPDATE gestures.
+ delegate->Reset();
+ ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(130, 301),
+ kTouchId1, tes.Now());
+ root->AsRootWindowHostDelegate()->OnHostTouchEvent(&move);
+ EXPECT_3_EVENTS(delegate->events(),
+ ui::ET_GESTURE_TAP_CANCEL,
+ ui::ET_GESTURE_SCROLL_BEGIN,
+ ui::ET_GESTURE_SCROLL_UPDATE);
+
+ // Press the second finger. It should cause pinch-begin. Note that we will not
+ // transition to two finger tap here because the touch points are far enough.
+ delegate->Reset();
+ ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10),
+ kTouchId2, tes.Now());
+ root->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+ EXPECT_2_EVENTS(delegate->events(),
+ ui::ET_GESTURE_BEGIN,
+ ui::ET_GESTURE_PINCH_BEGIN);
+ EXPECT_EQ(gfx::Rect(10, 10, 120, 291).ToString(),
+ delegate->bounding_box().ToString());
+
+ // Move the first finger.
+ delegate->Reset();
+ ui::TouchEvent move3(ui::ET_TOUCH_MOVED, gfx::Point(95, 201),
+ kTouchId1, tes.Now());
+ root->AsRootWindowHostDelegate()->OnHostTouchEvent(&move3);
+ EXPECT_2_EVENTS(delegate->events(),
+ ui::ET_GESTURE_PINCH_UPDATE,
+ ui::ET_GESTURE_SCROLL_UPDATE);
+ EXPECT_EQ(gfx::Rect(10, 10, 85, 191).ToString(),
+ delegate->bounding_box().ToString());
+
+ // Now move the second finger.
+ delegate->Reset();
+ ui::TouchEvent move4(ui::ET_TOUCH_MOVED, gfx::Point(55, 15),
+ kTouchId2, tes.Now());
+ root->AsRootWindowHostDelegate()->OnHostTouchEvent(&move4);
+ EXPECT_2_EVENTS(delegate->events(),
+ ui::ET_GESTURE_PINCH_UPDATE,
+ ui::ET_GESTURE_SCROLL_UPDATE);
+ EXPECT_EQ(gfx::Rect(55, 15, 40, 186).ToString(),
+ delegate->bounding_box().ToString());
+
+ // Release the first finger. This should end pinch.
+ delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId1, tes.Now());
+ root->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ EXPECT_2_EVENTS(delegate->events(),
+ ui::ET_GESTURE_PINCH_END,
+ ui::ET_GESTURE_END);
+ EXPECT_EQ(gfx::Rect(55, 15, 46, 186).ToString(),
+ delegate->bounding_box().ToString());
+
+ // Move the second finger. This should still generate a scroll.
+ delegate->Reset();
+ ui::TouchEvent move5(ui::ET_TOUCH_MOVED, gfx::Point(25, 10),
+ kTouchId2, tes.Now());
+ root->AsRootWindowHostDelegate()->OnHostTouchEvent(&move5);
+ EXPECT_1_EVENT(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE);
+ EXPECT_TRUE(delegate->bounding_box().IsEmpty());
+}
+
+TEST_F(GestureRecognizerTest, GestureEventPinchFromScrollFromPinch) {
+scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ TimedEvents tes;
+ const int kWindowWidth = 300;
+ const int kWindowHeight = 400;
+ const int kTouchId1 = 5;
+ const int kTouchId2 = 3;
+ gfx::Rect bounds(5, 5, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 301),
+ kTouchId1, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ delegate->Reset();
+ ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10),
+ kTouchId2, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+ // Since the touch points are far enough we will go to pinch rather than two
+ // finger tap.
+ EXPECT_TRUE(delegate->pinch_begin());
+
+ tes.SendScrollEvent(root_window(), 130, 230, kTouchId1, delegate.get());
+ EXPECT_TRUE(delegate->pinch_update());
+
+ // Pinch has started, now release the second finger
+ delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId1, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ EXPECT_TRUE(delegate->pinch_end());
+
+ tes.SendScrollEvent(root_window(), 130, 230, kTouchId2, delegate.get());
+ EXPECT_TRUE(delegate->scroll_update());
+
+ // Pinch again
+ delegate->Reset();
+ ui::TouchEvent press3(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10),
+ kTouchId1, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press3);
+ // Now the touch points are close. So we will go into two finger tap.
+ // Move the touch-point enough to break two-finger-tap and enter pinch.
+ ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(101, 202),
+ kTouchId1, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move2);
+ EXPECT_TRUE(delegate->pinch_begin());
+
+ tes.SendScrollEvent(root_window(), 130, 230, kTouchId1, delegate.get());
+ EXPECT_TRUE(delegate->pinch_update());
+}
+
+TEST_F(GestureRecognizerTest, GestureEventPinchFromTap) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ TimedEvents tes;
+ const int kWindowWidth = 300;
+ const int kWindowHeight = 400;
+ const int kTouchId1 = 3;
+ const int kTouchId2 = 5;
+ gfx::Rect bounds(5, 5, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ aura::RootWindow* root = root_window();
+
+ delegate->Reset();
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 301),
+ kTouchId1, tes.Now());
+ root->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ EXPECT_2_EVENTS(delegate->events(),
+ ui::ET_GESTURE_BEGIN,
+ ui::ET_GESTURE_TAP_DOWN);
+ EXPECT_TRUE(delegate->bounding_box().IsEmpty());
+
+ // Press the second finger far enough to break two finger tap. It should
+ // instead cause a scroll-begin and pinch-begin.
+ delegate->Reset();
+ ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10),
+ kTouchId2, tes.Now());
+ root->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+ EXPECT_4_EVENTS(delegate->events(),
+ ui::ET_GESTURE_TAP_CANCEL,
+ ui::ET_GESTURE_BEGIN,
+ ui::ET_GESTURE_PINCH_BEGIN,
+ ui::ET_GESTURE_SCROLL_BEGIN);
+ EXPECT_EQ(gfx::Rect(10, 10, 91, 291).ToString(),
+ delegate->bounding_box().ToString());
+
+ // Move the first finger.
+ delegate->Reset();
+ ui::TouchEvent move3(ui::ET_TOUCH_MOVED, gfx::Point(65, 201),
+ kTouchId1, tes.Now());
+ root->AsRootWindowHostDelegate()->OnHostTouchEvent(&move3);
+ EXPECT_2_EVENTS(delegate->events(),
+ ui::ET_GESTURE_PINCH_UPDATE,
+ ui::ET_GESTURE_SCROLL_UPDATE);
+ EXPECT_EQ(gfx::Rect(10, 10, 55, 191).ToString(),
+ delegate->bounding_box().ToString());
+
+ // Now move the second finger.
+ delegate->Reset();
+ ui::TouchEvent move4(ui::ET_TOUCH_MOVED, gfx::Point(55, 15),
+ kTouchId2, tes.Now());
+ root->AsRootWindowHostDelegate()->OnHostTouchEvent(&move4);
+ EXPECT_2_EVENTS(delegate->events(),
+ ui::ET_GESTURE_PINCH_UPDATE,
+ ui::ET_GESTURE_SCROLL_UPDATE);
+ EXPECT_EQ(gfx::Rect(55, 15, 10, 186).ToString(),
+ delegate->bounding_box().ToString());
+
+ // Release the first finger. This should end pinch.
+ delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId1, tes.LeapForward(10));
+ root->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ EXPECT_2_EVENTS(delegate->events(),
+ ui::ET_GESTURE_PINCH_END,
+ ui::ET_GESTURE_END);
+ EXPECT_EQ(gfx::Rect(55, 15, 46, 186).ToString(),
+ delegate->bounding_box().ToString());
+
+ // Move the second finger. This should still generate a scroll.
+ delegate->Reset();
+ ui::TouchEvent move5(ui::ET_TOUCH_MOVED, gfx::Point(25, 10),
+ kTouchId2, tes.Now());
+ root->AsRootWindowHostDelegate()->OnHostTouchEvent(&move5);
+ EXPECT_1_EVENT(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE);
+ EXPECT_TRUE(delegate->bounding_box().IsEmpty());
+}
+
+TEST_F(GestureRecognizerTest, GestureEventIgnoresDisconnectedEvents) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ TimedEvents tes;
+
+ ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ 6, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release1);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+}
+
+// Check that a touch is locked to the window of the closest current touch
+// within max_separation_for_gesture_touches_in_pixels
+TEST_F(GestureRecognizerTest, GestureEventTouchLockSelectsCorrectWindow) {
+ ui::GestureRecognizer* gesture_recognizer =
+ new ui::GestureRecognizerImpl(root_window());
+ TimedEvents tes;
+ root_window()->SetGestureRecognizerForTesting(gesture_recognizer);
+
+ ui::GestureConsumer* target;
+ const int kNumWindows = 4;
+
+ scoped_ptr<GestureEventConsumeDelegate*[]> delegates(
+ new GestureEventConsumeDelegate*[kNumWindows]);
+
+ ui::GestureConfiguration::
+ set_max_separation_for_gesture_touches_in_pixels(499);
+
+ scoped_ptr<gfx::Rect[]> window_bounds(new gfx::Rect[kNumWindows]);
+ window_bounds[0] = gfx::Rect(0, 0, 1, 1);
+ window_bounds[1] = gfx::Rect(500, 0, 1, 1);
+ window_bounds[2] = gfx::Rect(0, 500, 1, 1);
+ window_bounds[3] = gfx::Rect(500, 500, 1, 1);
+
+ scoped_ptr<aura::Window*[]> windows(new aura::Window*[kNumWindows]);
+
+ // Instantiate windows with |window_bounds| and touch each window at
+ // its origin.
+ for (int i = 0; i < kNumWindows; ++i) {
+ delegates[i] = new GestureEventConsumeDelegate();
+ windows[i] = CreateTestWindowWithDelegate(
+ delegates[i], i, window_bounds[i], root_window());
+ windows[i]->set_id(i);
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, window_bounds[i].origin(),
+ i, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ }
+
+ // Touches should now be associated with the closest touch within
+ // ui::GestureConfiguration::max_separation_for_gesture_touches_in_pixels
+ target = gesture_recognizer->GetTargetForLocation(gfx::Point(11, 11));
+ EXPECT_EQ("0", WindowIDAsString(target));
+ target = gesture_recognizer->GetTargetForLocation(gfx::Point(511, 11));
+ EXPECT_EQ("1", WindowIDAsString(target));
+ target = gesture_recognizer->GetTargetForLocation(gfx::Point(11, 511));
+ EXPECT_EQ("2", WindowIDAsString(target));
+ target = gesture_recognizer->GetTargetForLocation(gfx::Point(511, 511));
+ EXPECT_EQ("3", WindowIDAsString(target));
+
+ // Add a touch in the middle associated with windows[2]
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(0, 500),
+ kNumWindows, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(250, 250),
+ kNumWindows, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move);
+
+ target = gesture_recognizer->GetTargetForLocation(gfx::Point(250, 250));
+ EXPECT_EQ("2", WindowIDAsString(target));
+
+ // Make sure that ties are broken by distance to a current touch
+ // Closer to the point in the bottom right.
+ target = gesture_recognizer->GetTargetForLocation(gfx::Point(380, 380));
+ EXPECT_EQ("3", WindowIDAsString(target));
+
+ // This touch is closer to the point in the middle
+ target = gesture_recognizer->GetTargetForLocation(gfx::Point(300, 300));
+ EXPECT_EQ("2", WindowIDAsString(target));
+
+ // A touch too far from other touches won't be locked to anything
+ target = gesture_recognizer->GetTargetForLocation(gfx::Point(1000, 1000));
+ EXPECT_TRUE(target == NULL);
+
+ // Move a touch associated with windows[2] to 1000, 1000
+ ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(1000, 1000),
+ kNumWindows, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move2);
+
+ target = gesture_recognizer->GetTargetForLocation(gfx::Point(1000, 1000));
+ EXPECT_EQ("2", WindowIDAsString(target));
+
+ for (int i = 0; i < kNumWindows; ++i) {
+ // Delete windows before deleting delegates.
+ delete windows[i];
+ delete delegates[i];
+ }
+}
+
+// Check that touch events outside the root window are still handled
+// by the root window's gesture sequence.
+TEST_F(GestureRecognizerTest, GestureEventOutsideRootWindowTap) {
+ TestGestureRecognizer* gesture_recognizer =
+ new TestGestureRecognizer(root_window());
+ TimedEvents tes;
+ root_window()->SetGestureRecognizerForTesting(gesture_recognizer);
+
+ scoped_ptr<aura::Window> window(CreateTestWindowWithBounds(
+ gfx::Rect(-100, -100, 2000, 2000), root_window()));
+
+ ui::GestureSequence* window_gesture_sequence =
+ gesture_recognizer->GetGestureSequenceForTesting(window.get());
+
+ ui::GestureSequence* root_window_gesture_sequence =
+ gesture_recognizer->GetGestureSequenceForTesting(root_window());
+
+ gfx::Point pos1(-10, -10);
+ ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, pos1, 0, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press1);
+
+ gfx::Point pos2(1000, 1000);
+ ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, pos2, 1, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+
+ // As these presses were outside the root window, they should be
+ // associated with the root window.
+ EXPECT_EQ(0, window_gesture_sequence->point_count());
+ EXPECT_EQ(2, root_window_gesture_sequence->point_count());
+}
+
+TEST_F(GestureRecognizerTest, NoTapWithPreventDefaultedRelease) {
+ scoped_ptr<QueueTouchEventDelegate> delegate(
+ new QueueTouchEventDelegate(root_window()));
+ TimedEvents tes;
+ const int kTouchId = 2;
+ gfx::Rect bounds(100, 200, 100, 100);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+ delegate->set_window(window.get());
+
+ delegate->Reset();
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId, tes.LeapForward(50));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+
+ delegate->Reset();
+ delegate->ReceivedAck();
+ EXPECT_TRUE(delegate->tap_down());
+ delegate->Reset();
+ delegate->ReceivedAckPreventDefaulted();
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_TRUE(delegate->tap_cancel());
+}
+
+TEST_F(GestureRecognizerTest, PinchScrollWithPreventDefaultedRelease) {
+ scoped_ptr<QueueTouchEventDelegate> delegate(
+ new QueueTouchEventDelegate(root_window()));
+ TimedEvents tes;
+ const int kTouchId1 = 7;
+ const int kTouchId2 = 5;
+ gfx::Rect bounds(10, 20, 100, 100);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+ delegate->set_window(window.get());
+
+ {
+ delegate->Reset();
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(15, 25), kTouchId1,
+ tes.Now());
+ ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(20, 95), kTouchId1,
+ tes.LeapForward(200));
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(15, 25), kTouchId1,
+ tes.LeapForward(50));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move);
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ delegate->Reset();
+
+ // Ack the press event.
+ delegate->ReceivedAck();
+ EXPECT_TRUE(delegate->tap_down());
+ delegate->Reset();
+
+ // Ack the move event.
+ delegate->ReceivedAck();
+ EXPECT_TRUE(delegate->tap_cancel());
+ EXPECT_TRUE(delegate->scroll_begin());
+ delegate->Reset();
+
+ // Ack the release event. Although the release event has been processed, it
+ // should still generate a scroll-end event.
+ delegate->ReceivedAckPreventDefaulted();
+ EXPECT_TRUE(delegate->scroll_end());
+ }
+
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(15, 25), kTouchId1,
+ tes.Now());
+ ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(20, 95), kTouchId1,
+ tes.LeapForward(200));
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(15, 25), kTouchId1,
+ tes.LeapForward(50));
+ ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(55, 25), kTouchId2,
+ tes.Now());
+ ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(45, 85), kTouchId2,
+ tes.LeapForward(1000));
+ ui::TouchEvent release2(ui::ET_TOUCH_RELEASED, gfx::Point(45, 85), kTouchId2,
+ tes.LeapForward(14));
+
+ // Do a pinch.
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move);
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move2);
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release2);
+
+ // Ack the press and move events.
+ delegate->Reset();
+ delegate->ReceivedAck();
+ EXPECT_TRUE(delegate->begin());
+ EXPECT_TRUE(delegate->tap_down());
+
+ delegate->Reset();
+ delegate->ReceivedAck();
+ EXPECT_TRUE(delegate->scroll_begin());
+
+ delegate->Reset();
+ delegate->ReceivedAck();
+ EXPECT_TRUE(delegate->begin());
+ EXPECT_TRUE(delegate->pinch_begin());
+
+ delegate->Reset();
+ delegate->ReceivedAck();
+ EXPECT_TRUE(delegate->pinch_update());
+
+ // Ack the first release. Although the release is processed, it should still
+ // generate a pinch-end event.
+ delegate->Reset();
+ delegate->ReceivedAckPreventDefaulted();
+ EXPECT_TRUE(delegate->pinch_end());
+ EXPECT_TRUE(delegate->end());
+
+ delegate->Reset();
+ delegate->ReceivedAckPreventDefaulted();
+ EXPECT_TRUE(delegate->scroll_end());
+ EXPECT_TRUE(delegate->end());
+}
+
+TEST_F(GestureRecognizerTest, CaptureSendsGestureEnd) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ TestGestureRecognizer* gesture_recognizer =
+ new TestGestureRecognizer(root_window());
+ root_window()->SetGestureRecognizerForTesting(gesture_recognizer);
+
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, gfx::Rect(10, 10, 300, 300), root_window()));
+ EventGenerator generator(root_window());
+
+ generator.MoveMouseRelativeTo(window.get(), gfx::Point(10, 10));
+ generator.PressTouch();
+ RunAllPendingInMessageLoop();
+
+ EXPECT_TRUE(delegate->tap_down());
+
+ scoped_ptr<aura::Window> capture(CreateTestWindowWithBounds(
+ gfx::Rect(10, 10, 200, 200), root_window()));
+ capture->SetCapture();
+ RunAllPendingInMessageLoop();
+
+ EXPECT_TRUE(delegate->end());
+ EXPECT_TRUE(delegate->tap_cancel());
+}
+
+// Check that previous touch actions that are completely finished (either
+// released or cancelled), do not receive extra synthetic cancels upon change of
+// capture.
+TEST_F(GestureRecognizerTest, CaptureDoesNotCancelFinishedTouches) {
+ scoped_ptr<TestEventHandler> handler(new TestEventHandler);
+ root_window()->AddPreTargetHandler(handler.get());
+
+ // Create a window and set it as the capture window.
+ scoped_ptr<aura::Window> window1(CreateTestWindowWithBounds(
+ gfx::Rect(10, 10, 300, 300), root_window()));
+ window1->SetCapture();
+
+ EventGenerator generator(root_window());
+ TimedEvents tes;
+
+ // Generate two touch-press events on the window.
+ scoped_ptr<ui::TouchEvent> touch0(new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
+ gfx::Point(20, 20), 0,
+ tes.Now()));
+ scoped_ptr<ui::TouchEvent> touch1(new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
+ gfx::Point(30, 30), 1,
+ tes.Now()));
+ generator.Dispatch(touch0.get());
+ generator.Dispatch(touch1.get());
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ(2, handler->touch_pressed_count());
+
+ // Advance time.
+ tes.LeapForward(1000);
+
+ // End the two touches, one by a touch-release and one by a touch-cancel; to
+ // cover both cases.
+ touch0.reset(new ui::TouchEvent(ui::ET_TOUCH_RELEASED, gfx::Point(20, 20), 0,
+ tes.Now()));
+ touch1.reset(new ui::TouchEvent(ui::ET_TOUCH_CANCELLED, gfx::Point(30, 30), 1,
+ tes.Now()));
+ generator.Dispatch(touch0.get());
+ generator.Dispatch(touch1.get());
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ(1, handler->touch_released_count());
+ EXPECT_EQ(1, handler->touch_cancelled_count());
+
+ // Create a new window and set it as the new capture window.
+ scoped_ptr<aura::Window> window2(CreateTestWindowWithBounds(
+ gfx::Rect(100, 100, 300, 300), root_window()));
+ window2->SetCapture();
+ RunAllPendingInMessageLoop();
+ // Check that setting capture does not generate any synthetic touch-cancels
+ // for the two previously finished touch actions.
+ EXPECT_EQ(1, handler->touch_cancelled_count());
+
+ root_window()->RemovePreTargetHandler(handler.get());
+}
+
+TEST_F(GestureRecognizerTest, PressDoesNotCrash) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ TestGestureRecognizer* gesture_recognizer =
+ new TestGestureRecognizer(root_window());
+ root_window()->SetGestureRecognizerForTesting(gesture_recognizer);
+ TimedEvents tes;
+
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, gfx::Rect(10, 10, 300, 300), root_window()));
+
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(45, 45), 7, tes.Now());
+ press.set_radius_x(40);
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ EXPECT_TRUE(delegate->tap_down());
+ EXPECT_EQ(gfx::Rect(5, 5, 80, 80).ToString(),
+ delegate->bounding_box().ToString());
+ delegate->Reset();
+
+ ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(55, 45), 7, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+
+ // This new press should not generate a tap-down.
+ EXPECT_FALSE(delegate->begin());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->scroll_begin());
+}
+
+TEST_F(GestureRecognizerTest, TwoFingerTap) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+ const int kTouchId1 = 2;
+ const int kTouchId2 = 3;
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+ TimedEvents tes;
+
+ delegate->Reset();
+ ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId1, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press1);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_TRUE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_FALSE(delegate->long_press());
+ EXPECT_FALSE(delegate->two_finger_tap());
+
+ delegate->Reset();
+ ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(130, 201),
+ kTouchId2, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down()); // no touch down for second tap.
+ EXPECT_TRUE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_FALSE(delegate->long_press());
+ EXPECT_FALSE(delegate->two_finger_tap());
+
+ // Little bit of touch move should not affect our state.
+ delegate->Reset();
+ ui::TouchEvent move1(ui::ET_TOUCH_MOVED, gfx::Point(102, 202),
+ kTouchId1, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move1);
+ ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(131, 202),
+ kTouchId2, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move2);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_FALSE(delegate->long_press());
+ EXPECT_FALSE(delegate->two_finger_tap());
+
+ // Make sure there is enough delay before the touch is released so that it is
+ // recognized as a tap.
+ delegate->Reset();
+ ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId1, tes.LeapForward(50));
+
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release1);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_TRUE(delegate->two_finger_tap());
+
+ // Lift second finger.
+ // Make sure there is enough delay before the touch is released so that it is
+ // recognized as a tap.
+ delegate->Reset();
+ ui::TouchEvent release2(ui::ET_TOUCH_RELEASED, gfx::Point(130, 201),
+ kTouchId2, tes.LeapForward(50));
+
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release2);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_TRUE(delegate->fling());
+ EXPECT_FALSE(delegate->two_finger_tap());
+}
+
+TEST_F(GestureRecognizerTest, TwoFingerTapExpired) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+ const int kTouchId1 = 2;
+ const int kTouchId2 = 3;
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+ TimedEvents tes;
+
+ delegate->Reset();
+ ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId1, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press1);
+
+ delegate->Reset();
+ ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(130, 201),
+ kTouchId2, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+
+ // Send release event after sufficient delay so that two finger time expires.
+ delegate->Reset();
+ ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId1, tes.LeapForward(1000));
+
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release1);
+ EXPECT_FALSE(delegate->two_finger_tap());
+
+ // Lift second finger.
+ // Make sure there is enough delay before the touch is released so that it is
+ // recognized as a tap.
+ delegate->Reset();
+ ui::TouchEvent release2(ui::ET_TOUCH_RELEASED, gfx::Point(130, 201),
+ kTouchId2, tes.LeapForward(50));
+
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release2);
+ EXPECT_FALSE(delegate->two_finger_tap());
+}
+
+TEST_F(GestureRecognizerTest, TwoFingerTapChangesToPinch) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+ const int kTouchId1 = 2;
+ const int kTouchId2 = 3;
+ TimedEvents tes;
+
+ // Test moving first finger
+ {
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ delegate->Reset();
+ ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId1, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press1);
+
+ delegate->Reset();
+ ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(130, 201),
+ kTouchId2, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+
+ tes.SendScrollEvent(root_window(), 130, 230, kTouchId1, delegate.get());
+ EXPECT_FALSE(delegate->two_finger_tap());
+ EXPECT_TRUE(delegate->pinch_begin());
+
+ // Make sure there is enough delay before the touch is released so that it
+ // is recognized as a tap.
+ delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId2, tes.LeapForward(50));
+
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ EXPECT_FALSE(delegate->two_finger_tap());
+ EXPECT_TRUE(delegate->pinch_end());
+ }
+
+ // Test moving second finger
+ {
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ delegate->Reset();
+ ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId1, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press1);
+
+ delegate->Reset();
+ ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(130, 201),
+ kTouchId2, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+
+ tes.SendScrollEvent(root_window(), 101, 230, kTouchId2, delegate.get());
+ EXPECT_FALSE(delegate->two_finger_tap());
+ EXPECT_TRUE(delegate->pinch_begin());
+
+ // Make sure there is enough delay before the touch is released so that it
+ // is recognized as a tap.
+ delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId1, tes.LeapForward(50));
+
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ EXPECT_FALSE(delegate->two_finger_tap());
+ EXPECT_TRUE(delegate->pinch_end());
+ }
+}
+
+TEST_F(GestureRecognizerTest, NoTwoFingerTapWhenFirstFingerHasScrolled) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+ const int kTouchId1 = 2;
+ const int kTouchId2 = 3;
+ TimedEvents tes;
+
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ delegate->Reset();
+ ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId1, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press1);
+ tes.SendScrollEvent(root_window(), 130, 230, kTouchId1, delegate.get());
+
+ delegate->Reset();
+ ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(130, 201),
+ kTouchId2, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+
+ EXPECT_TRUE(delegate->pinch_begin());
+
+ // Make sure there is enough delay before the touch is released so that it
+ // is recognized as a tap.
+ delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId2, tes.LeapForward(50));
+
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ EXPECT_FALSE(delegate->two_finger_tap());
+ EXPECT_TRUE(delegate->pinch_end());
+}
+
+TEST_F(GestureRecognizerTest, MultiFingerSwipe) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+
+ gfx::Rect bounds(5, 10, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ const int kSteps = 15;
+ const int kTouchPoints = 4;
+ gfx::Point points[kTouchPoints] = {
+ gfx::Point(10, 30),
+ gfx::Point(30, 20),
+ gfx::Point(50, 30),
+ gfx::Point(80, 50)
+ };
+
+ aura::test::EventGenerator generator(root_window(), window.get());
+
+ for (int count = 2; count <= kTouchPoints; ++count) {
+ generator.GestureMultiFingerScroll(count, points, 15, kSteps, 0, -150);
+ EXPECT_TRUE(delegate->swipe_up());
+ delegate->Reset();
+
+ generator.GestureMultiFingerScroll(count, points, 15, kSteps, 0, 150);
+ EXPECT_TRUE(delegate->swipe_down());
+ delegate->Reset();
+
+ generator.GestureMultiFingerScroll(count, points, 15, kSteps, -150, 0);
+ EXPECT_TRUE(delegate->swipe_left());
+ delegate->Reset();
+
+ generator.GestureMultiFingerScroll(count, points, 15, kSteps, 150, 0);
+ EXPECT_TRUE(delegate->swipe_right());
+ delegate->Reset();
+ }
+}
+
+TEST_F(GestureRecognizerTest, TwoFingerTapCancelled) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+ const int kTouchId1 = 2;
+ const int kTouchId2 = 3;
+ TimedEvents tes;
+
+ // Test canceling first finger.
+ {
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ delegate->Reset();
+ ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId1, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press1);
+
+ delegate->Reset();
+ ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(130, 201),
+ kTouchId2, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+
+ delegate->Reset();
+ ui::TouchEvent cancel(ui::ET_TOUCH_CANCELLED, gfx::Point(130, 201),
+ kTouchId1, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&cancel);
+ EXPECT_FALSE(delegate->two_finger_tap());
+
+ // Make sure there is enough delay before the touch is released so that it
+ // is recognized as a tap.
+ delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId2, tes.LeapForward(50));
+
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ EXPECT_FALSE(delegate->two_finger_tap());
+ }
+
+ // Test canceling second finger
+ {
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ delegate->Reset();
+ ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId1, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press1);
+
+ delegate->Reset();
+ ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(130, 201),
+ kTouchId2, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+
+ delegate->Reset();
+ ui::TouchEvent cancel(ui::ET_TOUCH_CANCELLED, gfx::Point(130, 201),
+ kTouchId2, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&cancel);
+ EXPECT_FALSE(delegate->two_finger_tap());
+
+ // Make sure there is enough delay before the touch is released so that it
+ // is recognized as a tap.
+ delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId1, tes.LeapForward(50));
+
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ EXPECT_FALSE(delegate->two_finger_tap());
+ }
+}
+
+TEST_F(GestureRecognizerTest, VeryWideTwoFingerTouchDownShouldBeAPinch) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ const int kWindowWidth = 523;
+ const int kWindowHeight = 45;
+ const int kTouchId1 = 2;
+ const int kTouchId2 = 3;
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+ TimedEvents tes;
+
+ delegate->Reset();
+ ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId1, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press1);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_TRUE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_FALSE(delegate->long_press());
+ EXPECT_FALSE(delegate->two_finger_tap());
+
+ delegate->Reset();
+ ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(430, 201),
+ kTouchId2, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down()); // no touch down for second tap.
+ EXPECT_TRUE(delegate->tap_cancel());
+ EXPECT_TRUE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_FALSE(delegate->long_press());
+ EXPECT_FALSE(delegate->two_finger_tap());
+ EXPECT_TRUE(delegate->pinch_begin());
+}
+
+// Verifies if a window is the target of multiple touch-ids and we hide the
+// window everything is cleaned up correctly.
+TEST_F(GestureRecognizerTest, FlushAllOnHide) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ gfx::Rect bounds(0, 0, 200, 200);
+ scoped_ptr<aura::Window> window(
+ CreateTestWindowWithDelegate(delegate.get(), 0, bounds, root_window()));
+ const int kTouchId1 = 8;
+ const int kTouchId2 = 2;
+ TimedEvents tes;
+
+ ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10),
+ kTouchId1, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press1);
+ ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(20, 20),
+ kTouchId2, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+ window->Hide();
+ EXPECT_EQ(NULL,
+ root_window()->gesture_recognizer()->GetTouchLockedTarget(&press1));
+ EXPECT_EQ(NULL,
+ root_window()->gesture_recognizer()->GetTouchLockedTarget(&press2));
+}
+
+TEST_F(GestureRecognizerTest, LongPressTimerStopsOnPreventDefaultedTouchMoves) {
+ scoped_ptr<QueueTouchEventDelegate> delegate(
+ new QueueTouchEventDelegate(root_window()));
+ const int kTouchId = 2;
+ gfx::Rect bounds(100, 200, 100, 100);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+ delegate->set_window(window.get());
+ TimedEvents tes;
+
+ TimerTestGestureRecognizer* gesture_recognizer =
+ new TimerTestGestureRecognizer(root_window());
+ TimerTestGestureSequence* gesture_sequence =
+ static_cast<TimerTestGestureSequence*>(
+ gesture_recognizer->GetGestureSequenceForTesting(window.get()));
+
+ root_window()->SetGestureRecognizerForTesting(gesture_recognizer);
+
+ delegate->Reset();
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ // Scroll around, to cancel the long press
+ tes.SendScrollEvent(root_window(), 130, 230, kTouchId, delegate.get());
+
+ delegate->Reset();
+ delegate->ReceivedAck();
+ EXPECT_TRUE(delegate->tap_down());
+ EXPECT_TRUE(gesture_sequence->IsTimerRunning());
+
+ delegate->Reset();
+ delegate->ReceivedAckPreventDefaulted();
+ EXPECT_FALSE(gesture_sequence->IsTimerRunning());
+ gesture_sequence->ForceTimeout();
+ EXPECT_FALSE(delegate->long_press());
+}
+
+// Same as GestureEventConsumeDelegate, but consumes all the touch-move events.
+class ConsumesTouchMovesDelegate : public GestureEventConsumeDelegate {
+ public:
+ ConsumesTouchMovesDelegate() : consume_touch_move_(true) {}
+ virtual ~ConsumesTouchMovesDelegate() {}
+
+ void set_consume_touch_move(bool consume) { consume_touch_move_ = consume; }
+
+ private:
+ virtual void OnTouchEvent(ui::TouchEvent* touch) OVERRIDE {
+ if (consume_touch_move_ && touch->type() == ui::ET_TOUCH_MOVED)
+ touch->SetHandled();
+ else
+ GestureEventConsumeDelegate::OnTouchEvent(touch);
+ }
+
+ bool consume_touch_move_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConsumesTouchMovesDelegate);
+};
+
+// Same as GestureEventScroll, but tests that the behavior is the same
+// even if all the touch-move events are consumed.
+TEST_F(GestureRecognizerTest, GestureEventScrollTouchMoveConsumed) {
+ scoped_ptr<ConsumesTouchMovesDelegate> delegate(
+ new ConsumesTouchMovesDelegate());
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+ const int kTouchId = 5;
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+ TimedEvents tes;
+
+ delegate->Reset();
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_TRUE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_TRUE(delegate->begin());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+
+ // Move the touch-point enough so that it would normally be considered a
+ // scroll. But since the touch-moves will be consumed, the scroll should not
+ // start.
+ tes.SendScrollEvent(root_window(), 130, 230, kTouchId, delegate.get());
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ // TODO(rbyers): Really we should get the TapCancel here instead of below,
+ // but this is a symptom of a larger issue: crbug.com/146397.
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->begin());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+
+ // Release the touch back at the start point. This should end without causing
+ // a tap.
+ delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(130, 230),
+ kTouchId, tes.LeapForward(50));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_TRUE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->begin());
+ EXPECT_TRUE(delegate->end());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+}
+
+// Tests the behavior of 2F scroll when all the touch-move events are consumed.
+TEST_F(GestureRecognizerTest, GestureEventScrollTwoFingerTouchMoveConsumed) {
+ scoped_ptr<ConsumesTouchMovesDelegate> delegate(
+ new ConsumesTouchMovesDelegate());
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 100;
+ const int kTouchId1 = 2;
+ const int kTouchId2 = 3;
+ TimedEvents tes;
+
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ delegate->Reset();
+ ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId1, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press1);
+ tes.SendScrollEvent(root_window(), 131, 231, kTouchId1, delegate.get());
+
+ // First finger touches down and moves.
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+
+ delegate->Reset();
+ // Second finger touches down and moves.
+ ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(130, 201),
+ kTouchId2, tes.LeapForward(50));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+ tes.SendScrollEvent(root_window(), 161, 231, kTouchId2, delegate.get());
+
+ // PinchBegin & ScrollBegin were sent even though the touch-move events
+ // were consumed. This seems reasonable, as long as we don't send PinchUpdate
+ // ScrollUpdate when touch-move are consumed.
+ EXPECT_TRUE(delegate->pinch_begin());
+ EXPECT_TRUE(delegate->scroll_begin());
+
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->two_finger_tap());
+
+ // Should be no PinchUpdate & ScrollUpdate.
+ EXPECT_FALSE(delegate->pinch_update());
+ EXPECT_FALSE(delegate->pinch_end());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+
+ delegate->Reset();
+ // Moves First finger again, no PinchUpdate & ScrollUpdate.
+ tes.SendScrollEvent(root_window(), 161, 261, kTouchId1, delegate.get());
+
+ EXPECT_FALSE(delegate->pinch_update());
+ EXPECT_FALSE(delegate->pinch_end());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+
+ // Stops consuming touch-move.
+ delegate->set_consume_touch_move(false);
+
+ delegate->Reset();
+ // Making a pinch gesture.
+ tes.SendScrollEvent(root_window(), 161, 251, kTouchId1, delegate.get());
+ tes.SendScrollEvent(root_window(), 161, 241, kTouchId2, delegate.get());
+
+ // Now we see PinchUpdate & ScrollUpdate.
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_TRUE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+
+ EXPECT_FALSE(delegate->pinch_begin());
+ EXPECT_TRUE(delegate->pinch_update());
+ EXPECT_FALSE(delegate->pinch_end());
+
+ delegate->Reset();
+ ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId1, tes.Now());
+ ui::TouchEvent release2(ui::ET_TOUCH_RELEASED, gfx::Point(130, 201),
+ kTouchId2, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release1);
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release2);
+
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->two_finger_tap());
+
+ // Should see PinchEnd & ScrollEnd.
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_TRUE(delegate->scroll_end());
+
+ EXPECT_FALSE(delegate->pinch_begin());
+ EXPECT_FALSE(delegate->pinch_update());
+ EXPECT_TRUE(delegate->pinch_end());
+}
+
+// Like as GestureEventTouchMoveConsumed but tests the different behavior
+// depending on whether the events were consumed before or after the scroll
+// started.
+TEST_F(GestureRecognizerTest, GestureEventScrollTouchMovePartialConsumed) {
+ scoped_ptr<ConsumesTouchMovesDelegate> delegate(
+ new ConsumesTouchMovesDelegate());
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+ const int kTouchId = 5;
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+ TimedEvents tes;
+
+ delegate->Reset();
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_TRUE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_TRUE(delegate->begin());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+
+ // Move the touch-point enough so that it would normally be considered a
+ // scroll. But since the touch-moves will be consumed, the scroll should not
+ // start.
+ tes.SendScrollEvent(root_window(), 130, 230, kTouchId, delegate.get());
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ // TODO(rbyers): Really we should get the TapCancel here instead of below,
+ // but this is a symptom of a larger issue: crbug.com/146397.
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->begin());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+
+ // Now, stop consuming touch-move events, and move the touch-point again.
+ delegate->set_consume_touch_move(false);
+ tes.SendScrollEvent(root_window(), 159, 259, kTouchId, delegate.get());
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_TRUE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->begin());
+ EXPECT_TRUE(delegate->scroll_begin());
+ EXPECT_TRUE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ // Consuming move events doesn't effect what the ultimate scroll position
+ // will be if scrolling is later allowed to happen.
+ EXPECT_EQ(58, delegate->scroll_x());
+ EXPECT_EQ(58, delegate->scroll_y());
+ EXPECT_EQ(gfx::Point(1, 1).ToString(),
+ delegate->scroll_begin_position().ToString());
+
+ // Start consuming touch-move events again. However, since gesture-scroll has
+ // already started, the touch-move events should still result in scroll-update
+ // gestures.
+ delegate->set_consume_touch_move(true);
+
+ // Move some more to generate a few more scroll updates.
+ tes.SendScrollEvent(root_window(), 110, 211, kTouchId, delegate.get());
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->begin());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_TRUE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_EQ(-49, delegate->scroll_x());
+ EXPECT_EQ(-48, delegate->scroll_y());
+
+ tes.SendScrollEvent(root_window(), 140, 215, kTouchId, delegate.get());
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->begin());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_TRUE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ EXPECT_EQ(30, delegate->scroll_x());
+ EXPECT_EQ(4, delegate->scroll_y());
+
+ // Release the touch. This should end the scroll.
+ delegate->Reset();
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId, tes.LeapForward(50));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->begin());
+ EXPECT_TRUE(delegate->end());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+ // Moves arrive without delays and hence have high velocity.
+ EXPECT_TRUE(delegate->fling());
+}
+
+// Check that appropriate touch events generate double tap gesture events.
+TEST_F(GestureRecognizerTest, GestureEventDoubleTap) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+ const int kTouchId = 2;
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+ TimedEvents tes;
+
+ // First tap (tested in GestureEventTap)
+ ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(104, 201),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press1);
+ ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(104, 201),
+ kTouchId, tes.LeapForward(50));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release1);
+ delegate->Reset();
+
+ // Second tap
+ ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(101, 203),
+ kTouchId, tes.LeapForward(200));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+ ui::TouchEvent release2(ui::ET_TOUCH_RELEASED, gfx::Point(102, 206),
+ kTouchId, tes.LeapForward(50));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release2);
+
+ EXPECT_TRUE(delegate->tap());
+ EXPECT_TRUE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_TRUE(delegate->begin());
+ EXPECT_TRUE(delegate->end());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+
+ EXPECT_EQ(2, delegate->tap_count());
+}
+
+// Check that appropriate touch events generate triple tap gesture events.
+TEST_F(GestureRecognizerTest, GestureEventTripleTap) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+ const int kTouchId = 2;
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+ TimedEvents tes;
+
+ // First tap (tested in GestureEventTap)
+ ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(104, 201),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press1);
+ ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(104, 201),
+ kTouchId, tes.LeapForward(50));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release1);
+
+ EXPECT_EQ(1, delegate->tap_count());
+ delegate->Reset();
+
+ // Second tap (tested in GestureEventDoubleTap)
+ ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(101, 203),
+ kTouchId, tes.LeapForward(200));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+ ui::TouchEvent release2(ui::ET_TOUCH_RELEASED, gfx::Point(102, 206),
+ kTouchId, tes.LeapForward(50));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release2);
+
+ EXPECT_EQ(2, delegate->tap_count());
+ delegate->Reset();
+
+ // Third tap
+ ui::TouchEvent press3(ui::ET_TOUCH_PRESSED, gfx::Point(102, 206),
+ kTouchId, tes.LeapForward(200));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press3);
+ ui::TouchEvent release3(ui::ET_TOUCH_RELEASED, gfx::Point(102, 206),
+ kTouchId, tes.LeapForward(50));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release3);
+
+
+ EXPECT_TRUE(delegate->tap());
+ EXPECT_TRUE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_TRUE(delegate->begin());
+ EXPECT_TRUE(delegate->end());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+
+ EXPECT_EQ(3, delegate->tap_count());
+}
+
+// Check that we don't get a double tap when the two taps are far apart.
+TEST_F(GestureRecognizerTest, TwoTapsFarApart) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+ const int kTouchId = 2;
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+ TimedEvents tes;
+
+ // First tap (tested in GestureEventTap)
+ ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press1);
+ ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId, tes.LeapForward(50));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release1);
+ delegate->Reset();
+
+ // Second tap, close in time but far in distance
+ ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(201, 201),
+ kTouchId, tes.LeapForward(200));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+ ui::TouchEvent release2(ui::ET_TOUCH_RELEASED, gfx::Point(201, 201),
+ kTouchId, tes.LeapForward(50));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release2);
+
+ EXPECT_TRUE(delegate->tap());
+ EXPECT_TRUE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_TRUE(delegate->begin());
+ EXPECT_TRUE(delegate->end());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+
+ EXPECT_EQ(1, delegate->tap_count());
+}
+
+// Check that we don't get a double tap when the two taps have a long enough
+// delay in between.
+TEST_F(GestureRecognizerTest, TwoTapsWithDelayBetween) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+ const int kTouchId = 2;
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+ TimedEvents tes;
+
+ // First tap (tested in GestureEventTap)
+ ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press1);
+ ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId, tes.LeapForward(50));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release1);
+ delegate->Reset();
+
+ // Second tap, close in distance but after some delay
+ ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
+ kTouchId, tes.LeapForward(2000));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+ ui::TouchEvent release2(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201),
+ kTouchId, tes.LeapForward(50));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release2);
+
+ EXPECT_TRUE(delegate->tap());
+ EXPECT_TRUE(delegate->tap_down());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_TRUE(delegate->begin());
+ EXPECT_TRUE(delegate->end());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+
+ EXPECT_EQ(1, delegate->tap_count());
+}
+
+// Checks that if the bounding-box of a gesture changes because of change in
+// radius of a touch-point, and not because of change in position, then there
+// are not gesture events from that.
+TEST_F(GestureRecognizerTest, BoundingBoxRadiusChange) {
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ const int kWindowWidth = 234;
+ const int kWindowHeight = 345;
+ const int kTouchId = 5, kTouchId2 = 7;
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+ TimedEvents tes;
+
+ ui::TouchEvent press1(
+ ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press1);
+ EXPECT_TRUE(delegate->bounding_box().IsEmpty());
+
+ delegate->Reset();
+
+ ui::TouchEvent press2(
+ ui::ET_TOUCH_PRESSED, gfx::Point(201, 201), kTouchId2,
+ tes.LeapForward(400));
+ press2.set_radius_x(5);
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+ EXPECT_FALSE(delegate->pinch_begin());
+ EXPECT_EQ(gfx::Rect(101, 201, 100, 0).ToString(),
+ delegate->bounding_box().ToString());
+
+ delegate->Reset();
+
+ ui::TouchEvent move1(ui::ET_TOUCH_MOVED, gfx::Point(141, 201), kTouchId,
+ tes.LeapForward(40));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move1);
+ EXPECT_TRUE(delegate->pinch_begin());
+ EXPECT_EQ(gfx::Rect(141, 201, 60, 0).ToString(),
+ delegate->bounding_box().ToString());
+
+ delegate->Reset();
+
+ // The position doesn't move, but the radius changes.
+ ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(101, 201), kTouchId,
+ tes.LeapForward(40));
+ move2.set_radius_x(50);
+ move2.set_radius_y(60);
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move2);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_FALSE(delegate->tap_cancel());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->pinch_update());
+
+ delegate->Reset();
+}
+
+// Checks that slow scrolls deliver the correct deltas.
+// In particular, fix for http;//crbug.com/150573.
+TEST_F(GestureRecognizerTest, NoDriftInScroll) {
+ ui::GestureConfiguration::set_max_touch_move_in_pixels_for_click(3);
+ ui::GestureConfiguration::set_min_scroll_delta_squared(9);
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ const int kWindowWidth = 234;
+ const int kWindowHeight = 345;
+ const int kTouchId = 5;
+ TimedEvents tes;
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -1234, bounds, root_window()));
+
+ ui::TouchEvent press1(
+ ui::ET_TOUCH_PRESSED, gfx::Point(101, 208), kTouchId, tes.Now());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press1);
+ EXPECT_TRUE(delegate->begin());
+
+ delegate->Reset();
+
+ ui::TouchEvent move1(ui::ET_TOUCH_MOVED, gfx::Point(101, 206), kTouchId,
+ tes.LeapForward(40));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move1);
+ EXPECT_FALSE(delegate->scroll_begin());
+
+ delegate->Reset();
+
+ ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(101, 204), kTouchId,
+ tes.LeapForward(40));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move2);
+ EXPECT_TRUE(delegate->tap_cancel());
+ EXPECT_TRUE(delegate->scroll_begin());
+ EXPECT_TRUE(delegate->scroll_update());
+ EXPECT_EQ(-4, delegate->scroll_y());
+
+ delegate->Reset();
+
+ ui::TouchEvent move3(ui::ET_TOUCH_MOVED, gfx::Point(101, 204), kTouchId,
+ tes.LeapForward(40));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move3);
+ EXPECT_FALSE(delegate->scroll_update());
+
+ delegate->Reset();
+
+ ui::TouchEvent move4(ui::ET_TOUCH_MOVED, gfx::Point(101, 203), kTouchId,
+ tes.LeapForward(40));
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move4);
+ EXPECT_TRUE(delegate->scroll_update());
+ EXPECT_EQ(-1, delegate->scroll_y());
+
+ delegate->Reset();
+}
+
+} // namespace test
+} // namespace aura
diff --git a/chromium/ui/aura/layout_manager.cc b/chromium/ui/aura/layout_manager.cc
new file mode 100644
index 00000000000..1621995d5a7
--- /dev/null
+++ b/chromium/ui/aura/layout_manager.cc
@@ -0,0 +1,22 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/layout_manager.h"
+
+#include "ui/aura/window.h"
+
+namespace aura {
+
+LayoutManager::LayoutManager() {
+}
+
+LayoutManager::~LayoutManager() {
+}
+
+void LayoutManager::SetChildBoundsDirect(aura::Window* child,
+ const gfx::Rect& bounds) {
+ child->SetBoundsInternal(bounds);
+}
+
+} // namespace aura
diff --git a/chromium/ui/aura/layout_manager.h b/chromium/ui/aura/layout_manager.h
new file mode 100644
index 00000000000..2ae460676bc
--- /dev/null
+++ b/chromium/ui/aura/layout_manager.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_LAYOUT_MANAGER_H_
+#define UI_AURA_LAYOUT_MANAGER_H_
+
+#include "base/basictypes.h"
+#include "ui/aura/aura_export.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace aura {
+class Window;
+
+// An interface implemented by an object that places child windows.
+class AURA_EXPORT LayoutManager {
+ public:
+ LayoutManager();
+ virtual ~LayoutManager();
+
+ // Invoked when the window is resized.
+ virtual void OnWindowResized() = 0;
+
+ // Invoked when the window |child| has been added.
+ virtual void OnWindowAddedToLayout(Window* child) = 0;
+
+ // Invoked prior to removing |window|.
+ virtual void OnWillRemoveWindowFromLayout(Window* child) = 0;
+
+ // Invoked after removing |window|.
+ virtual void OnWindowRemovedFromLayout(Window* child) = 0;
+
+ // Invoked when the |SetVisible()| is invoked on the window |child|.
+ // |visible| is the value supplied to |SetVisible()|. If |visible| is true,
+ // window->IsVisible() may still return false. See description in
+ // Window::IsVisible() for details.
+ virtual void OnChildWindowVisibilityChanged(Window* child, bool visible) = 0;
+
+ // Invoked when |Window::SetBounds| is called on |child|.
+ // Implementation must call |SetChildBoundsDirect| to change the
+ // |child|'s bounds. LayoutManager may modify |requested_bounds|
+ // before applying, or ignore the request.
+ virtual void SetChildBounds(Window* child,
+ const gfx::Rect& requested_bounds) = 0;
+
+ protected:
+ // Sets the child's bounds forcibly. LayoutManager is responsible
+ // for checking the state and make sure the bounds are correctly
+ // adjusted.
+ void SetChildBoundsDirect(aura::Window* child, const gfx::Rect& bounds);
+};
+
+} // namespace aura
+
+#endif // UI_AURA_LAYOUT_MANAGER_H_
diff --git a/chromium/ui/aura/remote_root_window_host_win.cc b/chromium/ui/aura/remote_root_window_host_win.cc
new file mode 100644
index 00000000000..d4285389578
--- /dev/null
+++ b/chromium/ui/aura/remote_root_window_host_win.cc
@@ -0,0 +1,571 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/remote_root_window_host_win.h"
+
+#include <windows.h>
+
+#include <algorithm>
+
+#include "base/message_loop/message_loop.h"
+#include "ipc/ipc_message.h"
+#include "ipc/ipc_sender.h"
+#include "ui/aura/client/capture_client.h"
+#include "ui/aura/client/cursor_client.h"
+#include "ui/aura/root_window.h"
+#include "ui/base/cursor/cursor_loader_win.h"
+#include "ui/base/events/event_utils.h"
+#include "ui/base/keycodes/keyboard_code_conversion_win.h"
+#include "ui/base/view_prop.h"
+#include "ui/gfx/insets.h"
+#include "ui/metro_viewer/metro_viewer_messages.h"
+
+namespace aura {
+
+namespace {
+
+const char* kRootWindowHostWinKey = "__AURA_REMOTE_ROOT_WINDOW_HOST_WIN__";
+
+// Sets the keystate for the virtual key passed in to down or up.
+void SetKeyState(uint8* key_states, bool key_down, uint32 virtual_key_code) {
+ DCHECK(key_states);
+
+ if (key_down)
+ key_states[virtual_key_code] |= 0x80;
+ else
+ key_states[virtual_key_code] &= 0x7F;
+}
+
+// Sets the keyboard states for the Shift/Control/Alt/Caps lock keys.
+void SetVirtualKeyStates(uint32 flags) {
+ uint8 keyboard_state[256] = {0};
+ ::GetKeyboardState(keyboard_state);
+
+ SetKeyState(keyboard_state, !!(flags & ui::EF_SHIFT_DOWN), VK_SHIFT);
+ SetKeyState(keyboard_state, !!(flags & ui::EF_CONTROL_DOWN), VK_CONTROL);
+ SetKeyState(keyboard_state, !!(flags & ui::EF_ALT_DOWN), VK_MENU);
+ SetKeyState(keyboard_state, !!(flags & ui::EF_CAPS_LOCK_DOWN), VK_CAPITAL);
+
+ ::SetKeyboardState(keyboard_state);
+}
+
+} // namespace
+
+void HandleOpenFile(const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ const OpenFileCompletion& on_success,
+ const FileSelectionCanceled& on_failure) {
+ DCHECK(aura::RemoteRootWindowHostWin::Instance());
+ aura::RemoteRootWindowHostWin::Instance()->HandleOpenFile(title,
+ default_path,
+ filter,
+ on_success,
+ on_failure);
+}
+
+void HandleOpenMultipleFiles(const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ const OpenMultipleFilesCompletion& on_success,
+ const FileSelectionCanceled& on_failure) {
+ DCHECK(aura::RemoteRootWindowHostWin::Instance());
+ aura::RemoteRootWindowHostWin::Instance()->HandleOpenMultipleFiles(
+ title,
+ default_path,
+ filter,
+ on_success,
+ on_failure);
+}
+
+void HandleSaveFile(const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ int filter_index,
+ const base::string16& default_extension,
+ const SaveFileCompletion& on_success,
+ const FileSelectionCanceled& on_failure) {
+ DCHECK(aura::RemoteRootWindowHostWin::Instance());
+ aura::RemoteRootWindowHostWin::Instance()->HandleSaveFile(title,
+ default_path,
+ filter,
+ filter_index,
+ default_extension,
+ on_success,
+ on_failure);
+}
+
+void HandleSelectFolder(const base::string16& title,
+ const SelectFolderCompletion& on_success,
+ const FileSelectionCanceled& on_failure) {
+ DCHECK(aura::RemoteRootWindowHostWin::Instance());
+ aura::RemoteRootWindowHostWin::Instance()->HandleSelectFolder(title,
+ on_success,
+ on_failure);
+}
+
+RemoteRootWindowHostWin* g_instance = NULL;
+
+RemoteRootWindowHostWin* RemoteRootWindowHostWin::Instance() {
+ return g_instance;
+}
+
+RemoteRootWindowHostWin* RemoteRootWindowHostWin::Create(
+ const gfx::Rect& bounds) {
+ g_instance = new RemoteRootWindowHostWin(bounds);
+ return g_instance;
+}
+
+RemoteRootWindowHostWin::RemoteRootWindowHostWin(const gfx::Rect& bounds)
+ : delegate_(NULL),
+ host_(NULL),
+ ignore_mouse_moves_until_set_cursor_ack_(false) {
+ prop_.reset(new ui::ViewProp(NULL, kRootWindowHostWinKey, this));
+}
+
+RemoteRootWindowHostWin::~RemoteRootWindowHostWin() {
+}
+
+void RemoteRootWindowHostWin::Connected(IPC::Sender* host) {
+ CHECK(host_ == NULL);
+ host_ = host;
+}
+
+void RemoteRootWindowHostWin::Disconnected() {
+ CHECK(host_ != NULL);
+ host_ = NULL;
+}
+
+bool RemoteRootWindowHostWin::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(RemoteRootWindowHostWin, message)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_MouseMoved, OnMouseMoved)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_MouseButton, OnMouseButton)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_KeyDown, OnKeyDown)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_KeyUp, OnKeyUp)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_Character, OnChar)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_VisibilityChanged,
+ OnVisibilityChanged)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_TouchDown,
+ OnTouchDown)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_TouchUp,
+ OnTouchUp)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_TouchMoved,
+ OnTouchMoved)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_FileSaveAsDone,
+ OnFileSaveAsDone)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_FileOpenDone,
+ OnFileOpenDone)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_MultiFileOpenDone,
+ OnMultiFileOpenDone)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SelectFolderDone,
+ OnSelectFolderDone)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_WindowActivated,
+ OnWindowActivated)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SetCursorPosAck,
+ OnSetCursorPosAck)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_WindowSizeChanged,
+ OnWindowSizeChanged)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void RemoteRootWindowHostWin::HandleOpenFile(
+ const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ const OpenFileCompletion& on_success,
+ const FileSelectionCanceled& on_failure) {
+ if (!host_)
+ return;
+
+ // Can only have one of these operations in flight.
+ DCHECK(file_open_completion_callback_.is_null());
+ DCHECK(failure_callback_.is_null());
+
+ file_open_completion_callback_ = on_success;
+ failure_callback_ = on_failure;
+
+ host_->Send(new MetroViewerHostMsg_DisplayFileOpen(title,
+ filter,
+ default_path,
+ false));
+}
+
+void RemoteRootWindowHostWin::HandleOpenMultipleFiles(
+ const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ const OpenMultipleFilesCompletion& on_success,
+ const FileSelectionCanceled& on_failure) {
+ if (!host_)
+ return;
+
+ // Can only have one of these operations in flight.
+ DCHECK(multi_file_open_completion_callback_.is_null());
+ DCHECK(failure_callback_.is_null());
+ multi_file_open_completion_callback_ = on_success;
+ failure_callback_ = on_failure;
+
+ host_->Send(new MetroViewerHostMsg_DisplayFileOpen(title,
+ filter,
+ default_path,
+ true));
+}
+
+void RemoteRootWindowHostWin::HandleSaveFile(
+ const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ int filter_index,
+ const base::string16& default_extension,
+ const SaveFileCompletion& on_success,
+ const FileSelectionCanceled& on_failure) {
+ if (!host_)
+ return;
+
+ MetroViewerHostMsg_SaveAsDialogParams params;
+ params.title = title;
+ params.default_extension = default_extension;
+ params.filter = filter;
+ params.filter_index = filter_index;
+ params.suggested_name = default_path;
+
+ // Can only have one of these operations in flight.
+ DCHECK(file_saveas_completion_callback_.is_null());
+ DCHECK(failure_callback_.is_null());
+ file_saveas_completion_callback_ = on_success;
+ failure_callback_ = on_failure;
+
+ host_->Send(new MetroViewerHostMsg_DisplayFileSaveAs(params));
+}
+
+void RemoteRootWindowHostWin::HandleSelectFolder(
+ const base::string16& title,
+ const SelectFolderCompletion& on_success,
+ const FileSelectionCanceled& on_failure) {
+ if (!host_)
+ return;
+
+ // Can only have one of these operations in flight.
+ DCHECK(select_folder_completion_callback_.is_null());
+ DCHECK(failure_callback_.is_null());
+ select_folder_completion_callback_ = on_success;
+ failure_callback_ = on_failure;
+
+ host_->Send(new MetroViewerHostMsg_DisplaySelectFolder(title));
+}
+
+Window* RemoteRootWindowHostWin::GetAshWindow() {
+ return GetRootWindow();
+}
+
+void RemoteRootWindowHostWin::SetDelegate(RootWindowHostDelegate* delegate) {
+ delegate_ = delegate;
+}
+
+RootWindow* RemoteRootWindowHostWin::GetRootWindow() {
+ return delegate_->AsRootWindow();
+}
+
+gfx::AcceleratedWidget RemoteRootWindowHostWin::GetAcceleratedWidget() {
+ // TODO(cpu): This is bad. Chrome's compositor needs a valid window
+ // initially and then later on we swap it. Since the compositor never
+ // uses this initial window we tell ourselves this hack is ok to get
+ // thing off the ground.
+ return ::GetDesktopWindow();
+}
+
+void RemoteRootWindowHostWin::Show() {
+}
+
+void RemoteRootWindowHostWin::Hide() {
+ NOTIMPLEMENTED();
+}
+
+void RemoteRootWindowHostWin::ToggleFullScreen() {
+}
+
+gfx::Rect RemoteRootWindowHostWin::GetBounds() const {
+ gfx::Rect r(gfx::Point(0, 0), aura::RootWindowHost::GetNativeScreenSize());
+ return r;
+}
+
+void RemoteRootWindowHostWin::SetBounds(const gfx::Rect& bounds) {
+ delegate_->OnHostResized(bounds.size());
+}
+
+gfx::Insets RemoteRootWindowHostWin::GetInsets() const {
+ return gfx::Insets();
+}
+
+void RemoteRootWindowHostWin::SetInsets(const gfx::Insets& insets) {
+}
+
+gfx::Point RemoteRootWindowHostWin::GetLocationOnNativeScreen() const {
+ return gfx::Point(0, 0);
+}
+
+void RemoteRootWindowHostWin::SetCursor(gfx::NativeCursor native_cursor) {
+ if (!host_)
+ return;
+ host_->Send(
+ new MetroViewerHostMsg_SetCursor(uint64(native_cursor.platform())));
+}
+
+void RemoteRootWindowHostWin::SetCapture() {
+}
+
+void RemoteRootWindowHostWin::ReleaseCapture() {
+}
+
+bool RemoteRootWindowHostWin::QueryMouseLocation(gfx::Point* location_return) {
+ aura::client::CursorClient* cursor_client =
+ aura::client::GetCursorClient(GetRootWindow());
+ if (cursor_client && !cursor_client->IsMouseEventsEnabled()) {
+ *location_return = gfx::Point(0, 0);
+ return false;
+ }
+ POINT pt;
+ GetCursorPos(&pt);
+ *location_return =
+ gfx::Point(static_cast<int>(pt.x), static_cast<int>(pt.y));
+ return true;
+}
+
+bool RemoteRootWindowHostWin::ConfineCursorToRootWindow() {
+ return true;
+}
+
+void RemoteRootWindowHostWin::UnConfineCursor() {
+}
+
+void RemoteRootWindowHostWin::OnCursorVisibilityChanged(bool show) {
+ NOTIMPLEMENTED();
+}
+
+void RemoteRootWindowHostWin::MoveCursorTo(const gfx::Point& location) {
+ VLOG(1) << "In MoveCursorTo: " << location.x() << ", " << location.y();
+ if (!host_)
+ return;
+
+ // This function can be called in cases like when the mouse cursor is
+ // restricted within a viewport (For e.g. LockCursor) which assumes that
+ // subsequent mouse moves would be received starting with the new cursor
+ // coordinates. This is a challenge for Windows ASH for the reasons
+ // outlined below.
+ // Other cases which don't expect this behavior should continue to work
+ // without issues.
+
+ // The mouse events are received by the viewer process and sent to the
+ // browser. If we invoke the SetCursor API here we continue to receive
+ // mouse messages from the viewer which were posted before the SetCursor
+ // API executes which messes up the state in the browser. To workaround
+ // this we invoke the SetCursor API in the viewer process and ignore
+ // mouse messages until we received an ACK from the viewer indicating that
+ // the SetCursor operation completed.
+ ignore_mouse_moves_until_set_cursor_ack_ = true;
+ VLOG(1) << "In MoveCursorTo. Sending IPC";
+ host_->Send(new MetroViewerHostMsg_SetCursorPos(location.x(), location.y()));
+}
+
+void RemoteRootWindowHostWin::SetFocusWhenShown(bool focus_when_shown) {
+ NOTIMPLEMENTED();
+}
+
+void RemoteRootWindowHostWin::PostNativeEvent(
+ const base::NativeEvent& native_event) {
+}
+
+void RemoteRootWindowHostWin::OnDeviceScaleFactorChanged(
+ float device_scale_factor) {
+ NOTIMPLEMENTED();
+}
+
+void RemoteRootWindowHostWin::PrepareForShutdown() {
+}
+
+void RemoteRootWindowHostWin::OnMouseMoved(int32 x, int32 y, int32 flags) {
+ if (ignore_mouse_moves_until_set_cursor_ack_)
+ return;
+
+ gfx::Point location(x, y);
+ ui::MouseEvent event(ui::ET_MOUSE_MOVED, location, location, flags);
+ delegate_->OnHostMouseEvent(&event);
+}
+
+void RemoteRootWindowHostWin::OnMouseButton(
+ int32 x, int32 y, int32 extra, ui::EventType type, ui::EventFlags flags) {
+ gfx::Point location(x, y);
+ ui::MouseEvent mouse_event(type, location, location, flags);
+
+ if (type == ui::ET_MOUSEWHEEL) {
+ ui::MouseWheelEvent wheel_event(mouse_event, 0, extra);
+ delegate_->OnHostMouseEvent(&wheel_event);
+ } else if (type == ui::ET_MOUSE_PRESSED) {
+ // TODO(shrikant): Ideally modify code in event.cc by adding automatic
+ // tracking of double clicks in synthetic MouseEvent constructor code.
+ // Non-synthetic MouseEvent constructor code does automatically track
+ // this. Need to use some caution while modifying synthetic constructor
+ // as many tests and other code paths depend on it and apparently
+ // specifically depend on non implicit tracking of previous mouse event.
+ if (last_mouse_click_event_ &&
+ ui::MouseEvent::IsRepeatedClickEvent(mouse_event,
+ *last_mouse_click_event_)) {
+ mouse_event.SetClickCount(2);
+ } else {
+ mouse_event.SetClickCount(1);
+ }
+ last_mouse_click_event_ .reset(new ui::MouseEvent(mouse_event));
+ delegate_->OnHostMouseEvent(&mouse_event);
+ } else {
+ delegate_->OnHostMouseEvent(&mouse_event);
+ }
+}
+
+void RemoteRootWindowHostWin::OnKeyDown(uint32 vkey,
+ uint32 repeat_count,
+ uint32 scan_code,
+ uint32 flags) {
+ DispatchKeyboardMessage(ui::ET_KEY_PRESSED, vkey, repeat_count, scan_code,
+ flags, false);
+}
+
+void RemoteRootWindowHostWin::OnKeyUp(uint32 vkey,
+ uint32 repeat_count,
+ uint32 scan_code,
+ uint32 flags) {
+ DispatchKeyboardMessage(ui::ET_KEY_RELEASED, vkey, repeat_count, scan_code,
+ flags, false);
+}
+
+void RemoteRootWindowHostWin::OnChar(uint32 key_code,
+ uint32 repeat_count,
+ uint32 scan_code,
+ uint32 flags) {
+ DispatchKeyboardMessage(ui::ET_KEY_PRESSED, key_code, repeat_count,
+ scan_code, flags, true);
+}
+
+void RemoteRootWindowHostWin::OnVisibilityChanged(bool visible) {
+ if (visible)
+ delegate_->OnHostActivated();
+}
+
+void RemoteRootWindowHostWin::OnTouchDown(int32 x,
+ int32 y,
+ uint64 timestamp,
+ uint32 pointer_id) {
+ ui::TouchEvent event(ui::ET_TOUCH_PRESSED,
+ gfx::Point(x, y),
+ pointer_id,
+ base::TimeDelta::FromMicroseconds(timestamp));
+ delegate_->OnHostTouchEvent(&event);
+}
+
+void RemoteRootWindowHostWin::OnTouchUp(int32 x,
+ int32 y,
+ uint64 timestamp,
+ uint32 pointer_id) {
+ ui::TouchEvent event(ui::ET_TOUCH_RELEASED,
+ gfx::Point(x, y),
+ pointer_id,
+ base::TimeDelta::FromMicroseconds(timestamp));
+ delegate_->OnHostTouchEvent(&event);
+}
+
+void RemoteRootWindowHostWin::OnTouchMoved(int32 x,
+ int32 y,
+ uint64 timestamp,
+ uint32 pointer_id) {
+ ui::TouchEvent event(ui::ET_TOUCH_MOVED,
+ gfx::Point(x, y),
+ pointer_id,
+ base::TimeDelta::FromMicroseconds(timestamp));
+ delegate_->OnHostTouchEvent(&event);
+}
+
+void RemoteRootWindowHostWin::OnFileSaveAsDone(bool success,
+ const base::FilePath& filename,
+ int filter_index) {
+ if (success)
+ file_saveas_completion_callback_.Run(filename, filter_index, NULL);
+ else
+ failure_callback_.Run(NULL);
+ file_saveas_completion_callback_.Reset();
+ failure_callback_.Reset();
+}
+
+
+void RemoteRootWindowHostWin::OnFileOpenDone(bool success,
+ const base::FilePath& filename) {
+ if (success)
+ file_open_completion_callback_.Run(base::FilePath(filename), 0, NULL);
+ else
+ failure_callback_.Run(NULL);
+ file_open_completion_callback_.Reset();
+ failure_callback_.Reset();
+}
+
+void RemoteRootWindowHostWin::OnMultiFileOpenDone(
+ bool success,
+ const std::vector<base::FilePath>& files) {
+ if (success)
+ multi_file_open_completion_callback_.Run(files, NULL);
+ else
+ failure_callback_.Run(NULL);
+ multi_file_open_completion_callback_.Reset();
+ failure_callback_.Reset();
+}
+
+void RemoteRootWindowHostWin::OnSelectFolderDone(
+ bool success,
+ const base::FilePath& folder) {
+ if (success)
+ select_folder_completion_callback_.Run(base::FilePath(folder), 0, NULL);
+ else
+ failure_callback_.Run(NULL);
+ select_folder_completion_callback_.Reset();
+ failure_callback_.Reset();
+}
+
+void RemoteRootWindowHostWin::OnWindowActivated(bool active) {
+ active ? GetRootWindow()->Focus() : GetRootWindow()->Blur();
+}
+
+void RemoteRootWindowHostWin::OnSetCursorPosAck() {
+ DCHECK(ignore_mouse_moves_until_set_cursor_ack_);
+ ignore_mouse_moves_until_set_cursor_ack_ = false;
+}
+
+void RemoteRootWindowHostWin::OnWindowSizeChanged(uint32 width, uint32 height) {
+ SetBounds(gfx::Rect(0, 0, width, height));
+}
+
+void RemoteRootWindowHostWin::DispatchKeyboardMessage(ui::EventType type,
+ uint32 vkey,
+ uint32 repeat_count,
+ uint32 scan_code,
+ uint32 flags,
+ bool is_character) {
+ if (base::MessageLoop::current()->IsNested()) {
+ SetVirtualKeyStates(flags);
+
+ uint32 message = is_character ? WM_CHAR :
+ (type == ui::ET_KEY_PRESSED ? WM_KEYDOWN : WM_KEYUP);
+ ::PostThreadMessage(::GetCurrentThreadId(),
+ message,
+ vkey,
+ repeat_count | scan_code >> 15);
+ } else {
+ ui::KeyEvent event(type,
+ ui::KeyboardCodeForWindowsKeyCode(vkey),
+ flags,
+ is_character);
+ delegate_->OnHostKeyEvent(&event);
+ }
+}
+
+} // namespace aura
diff --git a/chromium/ui/aura/remote_root_window_host_win.h b/chromium/ui/aura/remote_root_window_host_win.h
new file mode 100644
index 00000000000..e2e6d1ca286
--- /dev/null
+++ b/chromium/ui/aura/remote_root_window_host_win.h
@@ -0,0 +1,227 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_REMOTE_ROOT_WINDOW_HOST_WIN_H_
+#define UI_AURA_REMOTE_ROOT_WINDOW_HOST_WIN_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/strings/string16.h"
+#include "ui/aura/root_window_host.h"
+#include "ui/base/events/event.h"
+#include "ui/base/events/event_constants.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace ui {
+class ViewProp;
+}
+
+namespace IPC {
+class Message;
+class Sender;
+}
+
+namespace aura {
+
+typedef base::Callback<void(const base::FilePath&, int, void*)>
+ OpenFileCompletion;
+
+typedef base::Callback<void(const std::vector<base::FilePath>&, void*)>
+ OpenMultipleFilesCompletion;
+
+typedef base::Callback<void(const base::FilePath&, int, void*)>
+ SaveFileCompletion;
+
+typedef base::Callback<void(const base::FilePath&, int, void*)>
+ SelectFolderCompletion;
+
+typedef base::Callback<void(void*)> FileSelectionCanceled;
+
+// Handles the open file operation for Metro Chrome Ash. The on_success
+// callback passed in is invoked when we receive the opened file name from
+// the metro viewer. The on failure callback is invoked on failure.
+AURA_EXPORT void HandleOpenFile(const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ const OpenFileCompletion& on_success,
+ const FileSelectionCanceled& on_failure);
+
+// Handles the open multiple file operation for Metro Chrome Ash. The
+// on_success callback passed in is invoked when we receive the opened file
+// names from the metro viewer. The on failure callback is invoked on failure.
+AURA_EXPORT void HandleOpenMultipleFiles(
+ const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ const OpenMultipleFilesCompletion& on_success,
+ const FileSelectionCanceled& on_failure);
+
+// Handles the save file operation for Metro Chrome Ash. The on_success
+// callback passed in is invoked when we receive the saved file name from
+// the metro viewer. The on failure callback is invoked on failure.
+AURA_EXPORT void HandleSaveFile(const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ int filter_index,
+ const base::string16& default_extension,
+ const SaveFileCompletion& on_success,
+ const FileSelectionCanceled& on_failure);
+
+// Handles the select folder for Metro Chrome Ash. The on_success
+// callback passed in is invoked when we receive the folder name from the
+// metro viewer. The on failure callback is invoked on failure.
+AURA_EXPORT void HandleSelectFolder(const base::string16& title,
+ const SelectFolderCompletion& on_success,
+ const FileSelectionCanceled& on_failure);
+
+// RootWindowHost implementaton that receives events from a different
+// process. In the case of Windows this is the Windows 8 (aka Metro)
+// frontend process, which forwards input events to this class.
+class AURA_EXPORT RemoteRootWindowHostWin : public RootWindowHost {
+ public:
+ static RemoteRootWindowHostWin* Instance();
+ static RemoteRootWindowHostWin* Create(const gfx::Rect& bounds);
+
+ // Called when the remote process has established its IPC connection.
+ // The |host| can be used when we need to send a message to it.
+ void Connected(IPC::Sender* host);
+ // Called when the remote process has closed its IPC connection.
+ void Disconnected();
+
+ // Called when we have a message from the remote process.
+ bool OnMessageReceived(const IPC::Message& message);
+
+ void HandleOpenFile(const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ const OpenFileCompletion& on_success,
+ const FileSelectionCanceled& on_failure);
+
+ void HandleOpenMultipleFiles(const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ const OpenMultipleFilesCompletion& on_success,
+ const FileSelectionCanceled& on_failure);
+
+ void HandleSaveFile(const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ int filter_index,
+ const base::string16& default_extension,
+ const SaveFileCompletion& on_success,
+ const FileSelectionCanceled& on_failure);
+
+ void HandleSelectFolder(const base::string16& title,
+ const SelectFolderCompletion& on_success,
+ const FileSelectionCanceled& on_failure);
+
+ // Returns the active ASH root window.
+ Window* GetAshWindow();
+
+ private:
+ explicit RemoteRootWindowHostWin(const gfx::Rect& bounds);
+ virtual ~RemoteRootWindowHostWin();
+
+ // IPC message handing methods:
+ void OnMouseMoved(int32 x, int32 y, int32 flags);
+ void OnMouseButton(int32 x,
+ int32 y,
+ int32 extra,
+ ui::EventType type,
+ ui::EventFlags flags);
+ void OnKeyDown(uint32 vkey,
+ uint32 repeat_count,
+ uint32 scan_code,
+ uint32 flags);
+ void OnKeyUp(uint32 vkey,
+ uint32 repeat_count,
+ uint32 scan_code,
+ uint32 flags);
+ void OnChar(uint32 key_code,
+ uint32 repeat_count,
+ uint32 scan_code,
+ uint32 flags);
+ void OnVisibilityChanged(bool visible);
+ void OnTouchDown(int32 x, int32 y, uint64 timestamp, uint32 pointer_id);
+ void OnTouchUp(int32 x, int32 y, uint64 timestamp, uint32 pointer_id);
+ void OnTouchMoved(int32 x, int32 y, uint64 timestamp, uint32 pointer_id);
+ void OnFileSaveAsDone(bool success,
+ const base::FilePath& filename,
+ int filter_index);
+ void OnFileOpenDone(bool success, const base::FilePath& filename);
+ void OnMultiFileOpenDone(bool success,
+ const std::vector<base::FilePath>& files);
+ void OnSelectFolderDone(bool success, const base::FilePath& folder);
+ void OnWindowActivated(bool active);
+ void OnSetCursorPosAck();
+ void OnWindowSizeChanged(uint32 width, uint32 height);
+
+ // RootWindowHost overrides:
+ virtual void SetDelegate(RootWindowHostDelegate* delegate) OVERRIDE;
+ virtual RootWindow* GetRootWindow() OVERRIDE;
+ virtual gfx::AcceleratedWidget GetAcceleratedWidget() OVERRIDE;
+ virtual void Show() OVERRIDE;
+ virtual void Hide() OVERRIDE;
+ virtual void ToggleFullScreen() OVERRIDE;
+ virtual gfx::Rect GetBounds() const OVERRIDE;
+ virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE;
+ virtual gfx::Insets GetInsets() const OVERRIDE;
+ virtual void SetInsets(const gfx::Insets& insets) OVERRIDE;
+ virtual gfx::Point GetLocationOnNativeScreen() const OVERRIDE;
+ virtual void SetCapture() OVERRIDE;
+ virtual void ReleaseCapture() OVERRIDE;
+ virtual void SetCursor(gfx::NativeCursor cursor) OVERRIDE;
+ virtual bool QueryMouseLocation(gfx::Point* location_return) OVERRIDE;
+ virtual bool ConfineCursorToRootWindow() OVERRIDE;
+ virtual void UnConfineCursor() OVERRIDE;
+ virtual void OnCursorVisibilityChanged(bool show) OVERRIDE;
+ virtual void MoveCursorTo(const gfx::Point& location) OVERRIDE;
+ virtual void SetFocusWhenShown(bool focus_when_shown) OVERRIDE;
+ virtual void PostNativeEvent(const base::NativeEvent& native_event) OVERRIDE;
+ virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE;
+ virtual void PrepareForShutdown() OVERRIDE;
+
+ // Helper function to dispatch a keyboard message to the desired target.
+ // The default target is the RootWindowHostDelegate. For nested message loop
+ // invocations we post a synthetic keyboard message directly into the message
+ // loop. The dispatcher for the nested loop would then decide how this
+ // message is routed.
+ void DispatchKeyboardMessage(ui::EventType type,
+ uint32 vkey,
+ uint32 repeat_count,
+ uint32 scan_code,
+ uint32 flags,
+ bool is_character);
+
+ RootWindowHostDelegate* delegate_;
+ IPC::Sender* host_;
+ scoped_ptr<ui::ViewProp> prop_;
+
+ // Saved callbacks which inform the caller about the result of the open file/
+ // save file/select operations.
+ OpenFileCompletion file_open_completion_callback_;
+ OpenMultipleFilesCompletion multi_file_open_completion_callback_;
+ SaveFileCompletion file_saveas_completion_callback_;
+ SelectFolderCompletion select_folder_completion_callback_;
+ FileSelectionCanceled failure_callback_;
+
+ // Set to true if we need to ignore mouse messages until the SetCursorPos
+ // operation is acked by the viewer.
+ bool ignore_mouse_moves_until_set_cursor_ack_;
+
+ // Tracking last click event for synthetically generated mouse events.
+ scoped_ptr<ui::MouseEvent> last_mouse_click_event_;
+
+ DISALLOW_COPY_AND_ASSIGN(RemoteRootWindowHostWin);
+};
+
+} // namespace aura
+
+#endif // UI_AURA_REMOTE_ROOT_WINDOW_HOST_WIN_H_
diff --git a/chromium/ui/aura/root_window.cc b/chromium/ui/aura/root_window.cc
new file mode 100644
index 00000000000..a638dc1bbe6
--- /dev/null
+++ b/chromium/ui/aura/root_window.cc
@@ -0,0 +1,1203 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/root_window.h"
+
+#include <vector>
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "ui/aura/client/activation_client.h"
+#include "ui/aura/client/capture_client.h"
+#include "ui/aura/client/cursor_client.h"
+#include "ui/aura/client/event_client.h"
+#include "ui/aura/client/focus_client.h"
+#include "ui/aura/client/screen_position_client.h"
+#include "ui/aura/env.h"
+#include "ui/aura/root_window_host.h"
+#include "ui/aura/root_window_observer.h"
+#include "ui/aura/root_window_transformer.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_delegate.h"
+#include "ui/aura/window_tracker.h"
+#include "ui/base/events/event.h"
+#include "ui/base/gestures/gesture_recognizer.h"
+#include "ui/base/gestures/gesture_types.h"
+#include "ui/base/hit_test.h"
+#include "ui/base/view_prop.h"
+#include "ui/compositor/compositor.h"
+#include "ui/compositor/dip_util.h"
+#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_animator.h"
+#include "ui/gfx/display.h"
+#include "ui/gfx/point3_f.h"
+#include "ui/gfx/point_conversions.h"
+#include "ui/gfx/screen.h"
+#include "ui/gfx/size_conversions.h"
+
+using std::vector;
+
+namespace aura {
+
+namespace {
+
+const char kRootWindowForAcceleratedWidget[] =
+ "__AURA_ROOT_WINDOW_ACCELERATED_WIDGET__";
+
+// Returns true if |target| has a non-client (frame) component at |location|,
+// in window coordinates.
+bool IsNonClientLocation(Window* target, const gfx::Point& location) {
+ if (!target->delegate())
+ return false;
+ int hit_test_code = target->delegate()->GetNonClientComponent(location);
+ return hit_test_code != HTCLIENT && hit_test_code != HTNOWHERE;
+}
+
+float GetDeviceScaleFactorFromDisplay(Window* window) {
+ return gfx::Screen::GetScreenFor(window)->
+ GetDisplayNearestWindow(window).device_scale_factor();
+}
+
+Window* ConsumerToWindow(ui::GestureConsumer* consumer) {
+ return consumer && !consumer->ignores_events() ?
+ static_cast<Window*>(consumer) : NULL;
+}
+
+void SetLastMouseLocation(const RootWindow* root_window,
+ const gfx::Point& location_in_root) {
+ client::ScreenPositionClient* client =
+ client::GetScreenPositionClient(root_window);
+ if (client) {
+ gfx::Point location_in_screen = location_in_root;
+ client->ConvertPointToScreen(root_window, &location_in_screen);
+ Env::GetInstance()->set_last_mouse_location(location_in_screen);
+ } else {
+ Env::GetInstance()->set_last_mouse_location(location_in_root);
+ }
+}
+
+RootWindowHost* CreateHost(RootWindow* root_window,
+ const RootWindow::CreateParams& params) {
+ RootWindowHost* host = params.host ?
+ params.host : RootWindowHost::Create(params.initial_bounds);
+ host->SetDelegate(root_window);
+ return host;
+}
+
+class SimpleRootWindowTransformer : public RootWindowTransformer {
+ public:
+ SimpleRootWindowTransformer(const RootWindow* root_window,
+ const gfx::Transform& transform)
+ : root_window_(root_window),
+ transform_(transform) {
+ }
+
+ // RootWindowTransformer overrides:
+ virtual gfx::Transform GetTransform() const OVERRIDE {
+ return transform_;
+ }
+
+ virtual gfx::Transform GetInverseTransform() const OVERRIDE {
+ gfx::Transform invert;
+ if (!transform_.GetInverse(&invert))
+ return transform_;
+ return invert;
+ }
+
+ virtual gfx::Rect GetRootWindowBounds(
+ const gfx::Size& host_size) const OVERRIDE {
+ gfx::Rect bounds(host_size);
+ gfx::RectF new_bounds(ui::ConvertRectToDIP(root_window_->layer(), bounds));
+ transform_.TransformRect(&new_bounds);
+ return gfx::Rect(gfx::ToFlooredSize(new_bounds.size()));
+ }
+
+ virtual gfx::Insets GetHostInsets() const OVERRIDE {
+ return gfx::Insets();
+ }
+
+ private:
+ virtual ~SimpleRootWindowTransformer() {}
+
+ const RootWindow* root_window_;
+ const gfx::Transform transform_;
+
+ DISALLOW_COPY_AND_ASSIGN(SimpleRootWindowTransformer);
+};
+
+} // namespace
+
+RootWindow::CreateParams::CreateParams(const gfx::Rect& a_initial_bounds)
+ : initial_bounds(a_initial_bounds),
+ host(NULL) {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RootWindow, public:
+
+RootWindow::RootWindow(const CreateParams& params)
+ : Window(NULL),
+ host_(CreateHost(this, params)),
+ schedule_paint_factory_(this),
+ event_factory_(this),
+ touch_ids_down_(0),
+ last_cursor_(ui::kCursorNull),
+ mouse_pressed_handler_(NULL),
+ mouse_moved_handler_(NULL),
+ mouse_event_dispatch_target_(NULL),
+ event_dispatch_target_(NULL),
+ gesture_recognizer_(ui::GestureRecognizer::Create(this)),
+ synthesize_mouse_move_(false),
+ waiting_on_compositing_end_(false),
+ draw_on_compositing_end_(false),
+ defer_draw_scheduling_(false),
+ move_hold_count_(0),
+ held_event_factory_(this),
+ repostable_event_factory_(this) {
+ SetName("RootWindow");
+
+ compositor_.reset(new ui::Compositor(this, host_->GetAcceleratedWidget()));
+ DCHECK(compositor_.get());
+ compositor_->AddObserver(this);
+
+ prop_.reset(new ui::ViewProp(host_->GetAcceleratedWidget(),
+ kRootWindowForAcceleratedWidget,
+ this));
+}
+
+RootWindow::~RootWindow() {
+ compositor_->RemoveObserver(this);
+ // Make sure to destroy the compositor before terminating so that state is
+ // cleared and we don't hit asserts.
+ compositor_.reset();
+
+ // Tear down in reverse. Frees any references held by the host.
+ host_.reset(NULL);
+
+ // An observer may have been added by an animation on the RootWindow.
+ layer()->GetAnimator()->RemoveObserver(this);
+}
+
+// static
+RootWindow* RootWindow::GetForAcceleratedWidget(
+ gfx::AcceleratedWidget widget) {
+ return reinterpret_cast<RootWindow*>(
+ ui::ViewProp::GetValue(widget, kRootWindowForAcceleratedWidget));
+}
+
+void RootWindow::Init() {
+ compositor()->SetScaleAndSize(GetDeviceScaleFactorFromDisplay(this),
+ host_->GetBounds().size());
+ Window::Init(ui::LAYER_NOT_DRAWN);
+ compositor()->SetRootLayer(layer());
+ transformer_.reset(new SimpleRootWindowTransformer(this, gfx::Transform()));
+ UpdateRootWindowSize(GetHostSize());
+ Env::GetInstance()->NotifyRootWindowInitialized(this);
+ Show();
+}
+
+void RootWindow::ShowRootWindow() {
+ host_->Show();
+}
+
+void RootWindow::HideRootWindow() {
+ host_->Hide();
+}
+
+void RootWindow::PrepareForShutdown() {
+ host_->PrepareForShutdown();
+ // discard synthesize event request as well.
+ synthesize_mouse_move_ = false;
+}
+
+void RootWindow::RepostEvent(const ui::LocatedEvent& event) {
+ // We allow for only one outstanding repostable event. This is used
+ // in exiting context menus. A dropped repost request is allowed.
+ if (event.type() == ui::ET_MOUSE_PRESSED) {
+ held_repostable_event_.reset(
+ new ui::MouseEvent(
+ static_cast<const ui::MouseEvent&>(event),
+ static_cast<aura::Window*>(event.target()),
+ static_cast<aura::Window*>(this)));
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&RootWindow::DispatchHeldEvents,
+ repostable_event_factory_.GetWeakPtr()));
+ } else {
+ DCHECK(event.type() == ui::ET_GESTURE_TAP_DOWN);
+ held_repostable_event_.reset();
+ // TODO(sschmitz): add similar code for gesture events.
+ }
+}
+
+RootWindowHostDelegate* RootWindow::AsRootWindowHostDelegate() {
+ return this;
+}
+
+void RootWindow::SetHostSize(const gfx::Size& size_in_pixel) {
+ DispatchHeldEvents();
+ gfx::Rect bounds = host_->GetBounds();
+ bounds.set_size(size_in_pixel);
+ host_->SetBounds(bounds);
+
+ // Requery the location to constrain it within the new root window size.
+ gfx::Point point;
+ if (host_->QueryMouseLocation(&point))
+ SetLastMouseLocation(this, ui::ConvertPointToDIP(layer(), point));
+
+ synthesize_mouse_move_ = false;
+}
+
+gfx::Size RootWindow::GetHostSize() const {
+ return host_->GetBounds().size();
+}
+
+void RootWindow::SetHostBounds(const gfx::Rect& bounds_in_pixel) {
+ DCHECK(!bounds_in_pixel.IsEmpty());
+ DispatchHeldEvents();
+ host_->SetBounds(bounds_in_pixel);
+ synthesize_mouse_move_ = false;
+}
+
+gfx::Point RootWindow::GetHostOrigin() const {
+ return host_->GetBounds().origin();
+}
+
+void RootWindow::SetCursor(gfx::NativeCursor cursor) {
+ last_cursor_ = cursor;
+ // A lot of code seems to depend on NULL cursors actually showing an arrow,
+ // so just pass everything along to the host.
+ host_->SetCursor(cursor);
+}
+
+void RootWindow::OnCursorVisibilityChanged(bool show) {
+ host_->OnCursorVisibilityChanged(show);
+}
+
+void RootWindow::OnMouseEventsEnableStateChanged(bool enabled) {
+ // Send entered / exited so that visual state can be updated to match
+ // mouse events state.
+ PostMouseMoveEventAfterWindowChange();
+ // TODO(mazda): Add code to disable mouse events when |enabled| == false.
+}
+
+void RootWindow::MoveCursorTo(const gfx::Point& location_in_dip) {
+ gfx::Point host_location(location_in_dip);
+ ConvertPointToHost(&host_location);
+ MoveCursorToInternal(location_in_dip, host_location);
+}
+
+void RootWindow::MoveCursorToHostLocation(const gfx::Point& host_location) {
+ gfx::Point root_location(host_location);
+ ConvertPointFromHost(&root_location);
+ MoveCursorToInternal(root_location, host_location);
+}
+
+bool RootWindow::ConfineCursorToWindow() {
+ // We would like to be able to confine the cursor to that window. However,
+ // currently, we do not have such functionality in X. So we just confine
+ // to the root window. This is ok because this option is currently only
+ // being used in fullscreen mode, so root_window bounds = window bounds.
+ return host_->ConfineCursorToRootWindow();
+}
+
+void RootWindow::Draw() {
+ defer_draw_scheduling_ = false;
+ if (waiting_on_compositing_end_) {
+ draw_on_compositing_end_ = true;
+ return;
+ }
+ waiting_on_compositing_end_ = true;
+
+ TRACE_EVENT_ASYNC_BEGIN0("ui", "RootWindow::Draw",
+ compositor_->last_started_frame() + 1);
+
+ compositor_->Draw();
+}
+
+void RootWindow::ScheduleFullRedraw() {
+ compositor_->ScheduleFullRedraw();
+}
+
+void RootWindow::ScheduleRedrawRect(const gfx::Rect& damage_rect) {
+ compositor_->ScheduleRedrawRect(damage_rect);
+}
+
+Window* RootWindow::GetGestureTarget(ui::GestureEvent* event) {
+ Window* target = client::GetCaptureWindow(this);
+ if (!target) {
+ target = ConsumerToWindow(
+ gesture_recognizer_->GetTargetForGestureEvent(event));
+ }
+
+ return target;
+}
+
+bool RootWindow::DispatchGestureEvent(ui::GestureEvent* event) {
+ DispatchHeldEvents();
+
+ Window* target = GetGestureTarget(event);
+ if (target) {
+ event->ConvertLocationToTarget(static_cast<Window*>(this), target);
+ ProcessEvent(target, event);
+ return event->handled();
+ }
+
+ return false;
+}
+
+void RootWindow::OnWindowDestroying(Window* window) {
+ DispatchMouseExitToHidingWindow(window);
+ OnWindowHidden(window, WINDOW_DESTROYED);
+
+ if (window->IsVisible() &&
+ window->ContainsPointInRoot(GetLastMouseLocationInRoot())) {
+ PostMouseMoveEventAfterWindowChange();
+ }
+}
+
+void RootWindow::OnWindowBoundsChanged(Window* window,
+ bool contained_mouse_point) {
+ if (contained_mouse_point ||
+ (window->IsVisible() &&
+ window->ContainsPointInRoot(GetLastMouseLocationInRoot()))) {
+ PostMouseMoveEventAfterWindowChange();
+ }
+}
+
+void RootWindow::DispatchMouseExitToHidingWindow(Window* window) {
+ // The mouse capture is intentionally ignored. Think that a mouse enters
+ // to a window, the window sets the capture, the mouse exits the window,
+ // and then it releases the capture. In that case OnMouseExited won't
+ // be called. So it is natural not to emit OnMouseExited even though
+ // |window| is the capture window.
+ gfx::Point last_mouse_location = GetLastMouseLocationInRoot();
+ if (window->Contains(mouse_moved_handler_) &&
+ window->ContainsPointInRoot(last_mouse_location)) {
+ ui::MouseEvent event(ui::ET_MOUSE_EXITED,
+ last_mouse_location,
+ last_mouse_location,
+ ui::EF_NONE);
+ DispatchMouseEnterOrExit(event, ui::ET_MOUSE_EXITED);
+ }
+}
+
+void RootWindow::OnWindowVisibilityChanged(Window* window, bool is_visible) {
+ if (!is_visible)
+ OnWindowHidden(window, WINDOW_HIDDEN);
+
+ if (window->ContainsPointInRoot(GetLastMouseLocationInRoot()))
+ PostMouseMoveEventAfterWindowChange();
+}
+
+void RootWindow::OnWindowTransformed(Window* window, bool contained_mouse) {
+ if (contained_mouse ||
+ (window->IsVisible() &&
+ window->ContainsPointInRoot(GetLastMouseLocationInRoot()))) {
+ PostMouseMoveEventAfterWindowChange();
+ }
+}
+
+void RootWindow::OnKeyboardMappingChanged() {
+ FOR_EACH_OBSERVER(RootWindowObserver, observers_,
+ OnKeyboardMappingChanged(this));
+}
+
+void RootWindow::OnRootWindowHostCloseRequested() {
+ FOR_EACH_OBSERVER(RootWindowObserver, observers_,
+ OnRootWindowHostCloseRequested(this));
+}
+
+void RootWindow::AddRootWindowObserver(RootWindowObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void RootWindow::RemoveRootWindowObserver(RootWindowObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void RootWindow::PostNativeEvent(const base::NativeEvent& native_event) {
+#if !defined(OS_MACOSX)
+ host_->PostNativeEvent(native_event);
+#endif
+}
+
+void RootWindow::ConvertPointToNativeScreen(gfx::Point* point) const {
+ ConvertPointToHost(point);
+ gfx::Point location = host_->GetLocationOnNativeScreen();
+ point->Offset(location.x(), location.y());
+}
+
+void RootWindow::ConvertPointFromNativeScreen(gfx::Point* point) const {
+ gfx::Point location = host_->GetLocationOnNativeScreen();
+ point->Offset(-location.x(), -location.y());
+ ConvertPointFromHost(point);
+}
+
+void RootWindow::ConvertPointToHost(gfx::Point* point) const {
+ gfx::Point3F point_3f(*point);
+ GetRootTransform().TransformPoint(point_3f);
+ *point = gfx::ToFlooredPoint(point_3f.AsPointF());
+}
+
+void RootWindow::ConvertPointFromHost(gfx::Point* point) const {
+ gfx::Point3F point_3f(*point);
+ GetInverseRootTransform().TransformPoint(point_3f);
+ *point = gfx::ToFlooredPoint(point_3f.AsPointF());
+}
+
+void RootWindow::ProcessedTouchEvent(ui::TouchEvent* event,
+ Window* window,
+ ui::EventResult result) {
+ scoped_ptr<ui::GestureRecognizer::Gestures> gestures;
+ gestures.reset(gesture_recognizer_->ProcessTouchEventForGesture(
+ *event, result, window));
+ ProcessGestures(gestures.get());
+}
+
+void RootWindow::SetGestureRecognizerForTesting(ui::GestureRecognizer* gr) {
+ gesture_recognizer_.reset(gr);
+}
+
+gfx::AcceleratedWidget RootWindow::GetAcceleratedWidget() {
+ return host_->GetAcceleratedWidget();
+}
+
+void RootWindow::ToggleFullScreen() {
+ host_->ToggleFullScreen();
+}
+
+void RootWindow::HoldPointerMoves() {
+ if (!move_hold_count_)
+ held_event_factory_.InvalidateWeakPtrs();
+ ++move_hold_count_;
+ TRACE_EVENT_ASYNC_BEGIN0("ui", "RootWindow::HoldPointerMoves", this);
+}
+
+void RootWindow::ReleasePointerMoves() {
+ --move_hold_count_;
+ DCHECK_GE(move_hold_count_, 0);
+ if (!move_hold_count_ && held_move_event_) {
+ // We don't want to call DispatchHeldEvents directly, because this might be
+ // called from a deep stack while another event, in which case dispatching
+ // another one may not be safe/expected. Instead we post a task, that we
+ // may cancel if HoldPointerMoves is called again before it executes.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&RootWindow::DispatchHeldEvents,
+ held_event_factory_.GetWeakPtr()));
+ }
+ TRACE_EVENT_ASYNC_END0("ui", "RootWindow::HoldPointerMoves", this);
+}
+
+void RootWindow::SetFocusWhenShown(bool focused) {
+ host_->SetFocusWhenShown(focused);
+}
+
+gfx::Point RootWindow::GetLastMouseLocationInRoot() const {
+ gfx::Point location = Env::GetInstance()->last_mouse_location();
+ client::ScreenPositionClient* client = client::GetScreenPositionClient(this);
+ if (client)
+ client->ConvertPointFromScreen(this, &location);
+ return location;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RootWindow, Window overrides:
+
+RootWindow* RootWindow::GetRootWindow() {
+ return this;
+}
+
+const RootWindow* RootWindow::GetRootWindow() const {
+ return this;
+}
+
+void RootWindow::SetTransform(const gfx::Transform& transform) {
+ scoped_ptr<RootWindowTransformer> transformer(
+ new SimpleRootWindowTransformer(this, transform));
+ SetRootWindowTransformer(transformer.Pass());
+}
+
+void RootWindow::SetRootWindowTransformer(
+ scoped_ptr<RootWindowTransformer> transformer) {
+ transformer_ = transformer.Pass();
+ host_->SetInsets(transformer_->GetHostInsets());
+ Window::SetTransform(transformer_->GetTransform());
+ // If the layer is not animating, then we need to update the root window
+ // size immediately.
+ if (!layer()->GetAnimator()->is_animating())
+ UpdateRootWindowSize(GetHostSize());
+}
+
+gfx::Transform RootWindow::GetRootTransform() const {
+ float scale = ui::GetDeviceScaleFactor(layer());
+ gfx::Transform transform;
+ transform.Scale(scale, scale);
+ transform *= transformer_->GetTransform();
+ return transform;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RootWindow, ui::EventTarget implementation:
+
+ui::EventTarget* RootWindow::GetParentTarget() {
+ return client::GetEventClient(this) ?
+ client::GetEventClient(this)->GetToplevelEventTarget() :
+ Env::GetInstance();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RootWindow, ui::CompositorDelegate implementation:
+
+void RootWindow::ScheduleDraw() {
+ DCHECK(!ui::Compositor::WasInitializedWithThread());
+ if (!defer_draw_scheduling_) {
+ defer_draw_scheduling_ = true;
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&RootWindow::Draw, schedule_paint_factory_.GetWeakPtr()));
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RootWindow, ui::CompositorObserver implementation:
+
+void RootWindow::OnCompositingDidCommit(ui::Compositor*) {
+}
+
+void RootWindow::OnCompositingStarted(ui::Compositor*,
+ base::TimeTicks start_time) {
+}
+
+void RootWindow::OnCompositingEnded(ui::Compositor*) {
+ TRACE_EVENT_ASYNC_END0("ui", "RootWindow::Draw",
+ compositor_->last_ended_frame());
+ waiting_on_compositing_end_ = false;
+ if (draw_on_compositing_end_) {
+ draw_on_compositing_end_ = false;
+
+ // Call ScheduleDraw() instead of Draw() in order to allow other
+ // ui::CompositorObservers to be notified before starting another
+ // draw cycle.
+ ScheduleDraw();
+ }
+}
+
+void RootWindow::OnCompositingAborted(ui::Compositor*) {
+}
+
+void RootWindow::OnCompositingLockStateChanged(ui::Compositor*) {
+}
+
+void RootWindow::OnUpdateVSyncParameters(ui::Compositor* compositor,
+ base::TimeTicks timebase,
+ base::TimeDelta interval) {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RootWindow, ui::LayerDelegate implementation:
+
+void RootWindow::OnDeviceScaleFactorChanged(
+ float device_scale_factor) {
+ const bool cursor_is_in_bounds =
+ GetBoundsInScreen().Contains(Env::GetInstance()->last_mouse_location());
+ bool cursor_visible = false;
+ client::CursorClient* cursor_client = client::GetCursorClient(this);
+ if (cursor_is_in_bounds && cursor_client) {
+ cursor_visible = cursor_client->IsCursorVisible();
+ if (cursor_visible)
+ cursor_client->HideCursor();
+ }
+ host_->OnDeviceScaleFactorChanged(device_scale_factor);
+ Window::OnDeviceScaleFactorChanged(device_scale_factor);
+ // Update the device scale factor of the cursor client only when the last
+ // mouse location is on this root window.
+ if (cursor_is_in_bounds) {
+ if (cursor_client) {
+ const gfx::Display& display =
+ gfx::Screen::GetScreenFor(this)->GetDisplayNearestWindow(this);
+ cursor_client->SetDisplay(display);
+ }
+ }
+ if (cursor_is_in_bounds && cursor_client && cursor_visible)
+ cursor_client->ShowCursor();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RootWindow, overridden from aura::Window:
+
+bool RootWindow::CanFocus() const {
+ return IsVisible();
+}
+
+bool RootWindow::CanReceiveEvents() const {
+ return IsVisible();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RootWindow, overridden from aura::client::CaptureDelegate:
+
+void RootWindow::UpdateCapture(Window* old_capture,
+ Window* new_capture) {
+ if (old_capture && old_capture->GetRootWindow() == this &&
+ old_capture->delegate()) {
+ // Send a capture changed event with bogus location data.
+ ui::MouseEvent event(ui::ET_MOUSE_CAPTURE_CHANGED, gfx::Point(),
+ gfx::Point(), 0);
+
+ ProcessEvent(old_capture, &event);
+
+ old_capture->delegate()->OnCaptureLost();
+ }
+
+ // Reset the mouse_moved_handler_ if the mouse_moved_handler_ belongs
+ // to another root window when losing the capture.
+ if (mouse_moved_handler_ && old_capture &&
+ old_capture->Contains(mouse_moved_handler_) &&
+ old_capture->GetRootWindow() != this) {
+ mouse_moved_handler_ = NULL;
+ }
+
+ if (new_capture) {
+ // Make all subsequent mouse events and touch go to the capture window. We
+ // shouldn't need to send an event here as OnCaptureLost should take care of
+ // that.
+ if (mouse_moved_handler_ || Env::GetInstance()->is_mouse_button_down())
+ mouse_moved_handler_ = new_capture;
+ } else {
+ // Make sure mouse_moved_handler gets updated.
+ SynthesizeMouseMoveEvent();
+ }
+ mouse_pressed_handler_ = NULL;
+}
+
+void RootWindow::SetNativeCapture() {
+ host_->SetCapture();
+}
+
+void RootWindow::ReleaseNativeCapture() {
+ host_->ReleaseCapture();
+}
+
+bool RootWindow::QueryMouseLocationForTest(gfx::Point* point) const {
+ return host_->QueryMouseLocation(point);
+}
+
+void RootWindow::ClearMouseHandlers() {
+ mouse_pressed_handler_ = NULL;
+ mouse_moved_handler_ = NULL;
+ mouse_event_dispatch_target_ = NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RootWindow, private:
+
+void RootWindow::TransformEventForDeviceScaleFactor(ui::LocatedEvent* event) {
+ event->UpdateForRootTransform(GetInverseRootTransform());
+}
+
+void RootWindow::MoveCursorToInternal(const gfx::Point& root_location,
+ const gfx::Point& host_location) {
+ host_->MoveCursorTo(host_location);
+ SetLastMouseLocation(this, root_location);
+ client::CursorClient* cursor_client = client::GetCursorClient(this);
+ if (cursor_client) {
+ const gfx::Display& display =
+ gfx::Screen::GetScreenFor(this)->GetDisplayNearestWindow(this);
+ cursor_client->SetDisplay(display);
+ }
+ synthesize_mouse_move_ = false;
+}
+
+void RootWindow::HandleMouseMoved(const ui::MouseEvent& event, Window* target) {
+ if (target == mouse_moved_handler_)
+ return;
+
+ DispatchMouseEnterOrExit(event, ui::ET_MOUSE_EXITED);
+
+ if (mouse_event_dispatch_target_ != target) {
+ mouse_moved_handler_ = NULL;
+ return;
+ }
+
+ mouse_moved_handler_ = target;
+
+ DispatchMouseEnterOrExit(event, ui::ET_MOUSE_ENTERED);
+}
+
+void RootWindow::DispatchMouseEnterOrExit(const ui::MouseEvent& event,
+ ui::EventType type) {
+ if (!mouse_moved_handler_ || !mouse_moved_handler_->delegate())
+ return;
+
+ ui::MouseEvent translated_event(event,
+ static_cast<Window*>(this),
+ mouse_moved_handler_,
+ type,
+ event.flags() | ui::EF_IS_SYNTHESIZED);
+ ProcessEvent(mouse_moved_handler_, &translated_event);
+}
+
+void RootWindow::ProcessEvent(Window* target, ui::Event* event) {
+ Window* old_target = event_dispatch_target_;
+ event_dispatch_target_ = target;
+ if (DispatchEvent(target, event))
+ event_dispatch_target_ = old_target;
+}
+
+bool RootWindow::ProcessGestures(ui::GestureRecognizer::Gestures* gestures) {
+ if (!gestures || gestures->empty())
+ return false;
+
+ Window* target = GetGestureTarget(gestures->get().at(0));
+ Window* old_target = event_dispatch_target_;
+ event_dispatch_target_ = target;
+
+ bool handled = false;
+ for (size_t i = 0; i < gestures->size(); ++i) {
+ ui::GestureEvent* event = gestures->get().at(i);
+ event->ConvertLocationToTarget(static_cast<Window*>(this), target);
+ if (!DispatchEvent(target, event))
+ return false; // |this| has been destroyed.
+ if (event->handled())
+ handled = true;
+ if (event_dispatch_target_ != target) // |target| has been destroyed.
+ break;
+ }
+ event_dispatch_target_ = old_target;
+ return handled;
+}
+
+void RootWindow::OnWindowRemovedFromRootWindow(Window* detached,
+ RootWindow* new_root) {
+ DCHECK(aura::client::GetCaptureWindow(this) != this);
+
+ DispatchMouseExitToHidingWindow(detached);
+ OnWindowHidden(detached, new_root ? WINDOW_MOVING : WINDOW_HIDDEN);
+
+ if (detached->IsVisible() &&
+ detached->ContainsPointInRoot(GetLastMouseLocationInRoot())) {
+ PostMouseMoveEventAfterWindowChange();
+ }
+}
+
+void RootWindow::OnWindowHidden(Window* invisible, WindowHiddenReason reason) {
+ // TODO(beng): This should be removed once FocusController is turned on.
+ if (client::GetFocusClient(this)) {
+ client::GetFocusClient(this)->OnWindowHiddenInRootWindow(
+ invisible, this, reason == WINDOW_DESTROYED);
+ }
+
+ // Do not clear the capture, and the |event_dispatch_target_| if the
+ // window is moving across root windows, because the target itself
+ // is actually still visible and clearing them stops further event
+ // processing, which can cause unexpected behaviors. See
+ // crbug.com/157583
+ if (reason != WINDOW_MOVING) {
+ Window* capture_window = aura::client::GetCaptureWindow(this);
+ // If the ancestor of the capture window is hidden,
+ // release the capture.
+ if (invisible->Contains(capture_window) && invisible != this)
+ capture_window->ReleaseCapture();
+
+ if (invisible->Contains(event_dispatch_target_))
+ event_dispatch_target_ = NULL;
+ }
+
+ // If the ancestor of any event handler windows are invisible, release the
+ // pointer to those windows.
+ if (invisible->Contains(mouse_pressed_handler_))
+ mouse_pressed_handler_ = NULL;
+ if (invisible->Contains(mouse_moved_handler_))
+ mouse_moved_handler_ = NULL;
+ if (invisible->Contains(mouse_event_dispatch_target_))
+ mouse_event_dispatch_target_ = NULL;
+
+ CleanupGestureRecognizerState(invisible);
+}
+
+void RootWindow::CleanupGestureRecognizerState(Window* window) {
+ gesture_recognizer_->CleanupStateForConsumer(window);
+ Windows windows = window->children();
+ for (Windows::const_iterator iter = windows.begin();
+ iter != windows.end();
+ ++iter) {
+ CleanupGestureRecognizerState(*iter);
+ }
+}
+
+void RootWindow::UpdateRootWindowSize(const gfx::Size& host_size) {
+ SetBounds(transformer_->GetRootWindowBounds(host_size));
+}
+
+void RootWindow::OnWindowAddedToRootWindow(Window* attached) {
+ if (attached->IsVisible() &&
+ attached->ContainsPointInRoot(GetLastMouseLocationInRoot()))
+ PostMouseMoveEventAfterWindowChange();
+}
+
+bool RootWindow::CanDispatchToTarget(ui::EventTarget* target) {
+ return event_dispatch_target_ == target;
+}
+
+bool RootWindow::DispatchLongPressGestureEvent(ui::GestureEvent* event) {
+ return DispatchGestureEvent(event);
+}
+
+bool RootWindow::DispatchCancelTouchEvent(ui::TouchEvent* event) {
+ return OnHostTouchEvent(event);
+}
+
+void RootWindow::OnLayerAnimationEnded(
+ ui::LayerAnimationSequence* animation) {
+ UpdateRootWindowSize(GetHostSize());
+}
+
+void RootWindow::OnLayerAnimationScheduled(
+ ui::LayerAnimationSequence* animation) {
+}
+
+void RootWindow::OnLayerAnimationAborted(
+ ui::LayerAnimationSequence* animation) {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RootWindow, RootWindowHostDelegate implementation:
+
+bool RootWindow::OnHostKeyEvent(ui::KeyEvent* event) {
+ DispatchHeldEvents();
+ if (event->key_code() == ui::VKEY_UNKNOWN)
+ return false;
+ client::EventClient* client = client::GetEventClient(GetRootWindow());
+ Window* focused_window = client::GetFocusClient(this)->GetFocusedWindow();
+ if (client && !client->CanProcessEventsWithinSubtree(focused_window)) {
+ client::GetFocusClient(this)->FocusWindow(NULL);
+ return false;
+ }
+ ProcessEvent(focused_window ? focused_window : this, event);
+ return event->handled();
+}
+
+bool RootWindow::OnHostMouseEvent(ui::MouseEvent* event) {
+ if (event->type() == ui::ET_MOUSE_DRAGGED ||
+ (event->flags() & ui::EF_IS_SYNTHESIZED)) {
+ if (move_hold_count_) {
+ Window* null_window = static_cast<Window*>(NULL);
+ held_move_event_.reset(
+ new ui::MouseEvent(*event, null_window, null_window));
+ return true;
+ } else {
+ // We may have a held event for a period between the time move_hold_count_
+ // fell to 0 and the DispatchHeldEvents executes. Since we're going to
+ // dispatch the new event directly below, we can reset the old one.
+ held_move_event_.reset();
+ }
+ }
+ DispatchHeldEvents();
+ return DispatchMouseEventImpl(event);
+}
+
+bool RootWindow::OnHostScrollEvent(ui::ScrollEvent* event) {
+ DispatchHeldEvents();
+
+ TransformEventForDeviceScaleFactor(event);
+ SetLastMouseLocation(this, event->location());
+ synthesize_mouse_move_ = false;
+
+ Window* target = mouse_pressed_handler_ ?
+ mouse_pressed_handler_ : client::GetCaptureWindow(this);
+
+ if (!target)
+ target = GetEventHandlerForPoint(event->location());
+
+ if (!target)
+ target = this;
+
+ event->ConvertLocationToTarget(static_cast<Window*>(this), target);
+ int flags = event->flags();
+ if (IsNonClientLocation(target, event->location()))
+ flags |= ui::EF_IS_NON_CLIENT;
+ event->set_flags(flags);
+
+ ProcessEvent(target, event);
+ return event->handled();
+}
+
+bool RootWindow::OnHostTouchEvent(ui::TouchEvent* event) {
+ if ((event->type() == ui::ET_TOUCH_MOVED)) {
+ if (move_hold_count_) {
+ Window* null_window = static_cast<Window*>(NULL);
+ held_move_event_.reset(
+ new ui::TouchEvent(*event, null_window, null_window));
+ return true;
+ } else {
+ // We may have a held event for a period between the time move_hold_count_
+ // fell to 0 and the DispatchHeldEvents executes. Since we're going to
+ // dispatch the new event directly below, we can reset the old one.
+ held_move_event_.reset();
+ }
+ }
+ DispatchHeldEvents();
+ return DispatchTouchEventImpl(event);
+}
+
+void RootWindow::OnHostCancelMode() {
+ ui::CancelModeEvent event;
+ Window* focused_window = client::GetFocusClient(this)->GetFocusedWindow();
+ ProcessEvent(focused_window ? focused_window : this, &event);
+}
+
+void RootWindow::OnHostActivated() {
+ Env::GetInstance()->RootWindowActivated(this);
+}
+
+void RootWindow::OnHostLostWindowCapture() {
+ Window* capture_window = client::GetCaptureWindow(this);
+ if (capture_window && capture_window->GetRootWindow() == this)
+ capture_window->ReleaseCapture();
+}
+
+void RootWindow::OnHostLostMouseGrab() {
+ ClearMouseHandlers();
+}
+
+void RootWindow::OnHostPaint(const gfx::Rect& damage_rect) {
+ compositor_->ScheduleRedrawRect(damage_rect);
+}
+
+void RootWindow::OnHostMoved(const gfx::Point& origin) {
+ FOR_EACH_OBSERVER(RootWindowObserver, observers_,
+ OnRootWindowHostMoved(this, origin));
+}
+
+void RootWindow::OnHostResized(const gfx::Size& size) {
+ DispatchHeldEvents();
+ // The compositor should have the same size as the native root window host.
+ // Get the latest scale from display because it might have been changed.
+ compositor_->SetScaleAndSize(GetDeviceScaleFactorFromDisplay(this), size);
+
+ // The layer, and the observers should be notified of the
+ // transformed size of the root window.
+ UpdateRootWindowSize(size);
+ FOR_EACH_OBSERVER(RootWindowObserver, observers_,
+ OnRootWindowHostResized(this));
+}
+
+float RootWindow::GetDeviceScaleFactor() {
+ return compositor()->device_scale_factor();
+}
+
+RootWindow* RootWindow::AsRootWindow() {
+ return this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RootWindow, private:
+
+bool RootWindow::DispatchMouseEventImpl(ui::MouseEvent* event) {
+ TransformEventForDeviceScaleFactor(event);
+ Window* target = mouse_pressed_handler_ ?
+ mouse_pressed_handler_ : client::GetCaptureWindow(this);
+ if (!target)
+ target = GetEventHandlerForPoint(event->location());
+ return DispatchMouseEventToTarget(event, target);
+}
+
+bool RootWindow::DispatchMouseEventRepost(ui::MouseEvent* event) {
+ if (event->type() != ui::ET_MOUSE_PRESSED)
+ return false;
+ mouse_pressed_handler_ = NULL;
+ Window* target = GetEventHandlerForPoint(event->location());
+ return DispatchMouseEventToTarget(event, target);
+}
+
+bool RootWindow::DispatchMouseEventToTarget(ui::MouseEvent* event,
+ Window* target) {
+ client::CursorClient* cursor_client = client::GetCursorClient(this);
+ if (cursor_client &&
+ !cursor_client->IsMouseEventsEnabled() &&
+ (event->flags() & ui::EF_IS_SYNTHESIZED))
+ return false;
+
+ static const int kMouseButtonFlagMask =
+ ui::EF_LEFT_MOUSE_BUTTON |
+ ui::EF_MIDDLE_MOUSE_BUTTON |
+ ui::EF_RIGHT_MOUSE_BUTTON;
+ base::AutoReset<Window*> reset(&mouse_event_dispatch_target_, target);
+ SetLastMouseLocation(this, event->location());
+ synthesize_mouse_move_ = false;
+ switch (event->type()) {
+ case ui::ET_MOUSE_EXITED:
+ if (!target) {
+ DispatchMouseEnterOrExit(*event, ui::ET_MOUSE_EXITED);
+ mouse_moved_handler_ = NULL;
+ }
+ break;
+ case ui::ET_MOUSE_MOVED:
+ mouse_event_dispatch_target_ = target;
+ HandleMouseMoved(*event, target);
+ if (mouse_event_dispatch_target_ != target)
+ return false;
+ break;
+ case ui::ET_MOUSE_PRESSED:
+ // Don't set the mouse pressed handler for non client mouse down events.
+ // These are only sent by Windows and are not always followed with non
+ // client mouse up events which causes subsequent mouse events to be
+ // sent to the wrong target.
+ if (!(event->flags() & ui::EF_IS_NON_CLIENT) && !mouse_pressed_handler_)
+ mouse_pressed_handler_ = target;
+ Env::GetInstance()->set_mouse_button_flags(
+ event->flags() & kMouseButtonFlagMask);
+ break;
+ case ui::ET_MOUSE_RELEASED:
+ mouse_pressed_handler_ = NULL;
+ Env::GetInstance()->set_mouse_button_flags(event->flags() &
+ kMouseButtonFlagMask & ~event->changed_button_flags());
+ break;
+ default:
+ break;
+ }
+ if (target) {
+ event->ConvertLocationToTarget(static_cast<Window*>(this), target);
+ if (IsNonClientLocation(target, event->location()))
+ event->set_flags(event->flags() | ui::EF_IS_NON_CLIENT);
+ ProcessEvent(target, event);
+ return event->handled();
+ }
+ return false;
+}
+
+bool RootWindow::DispatchTouchEventImpl(ui::TouchEvent* event) {
+ switch (event->type()) {
+ case ui::ET_TOUCH_PRESSED:
+ touch_ids_down_ |= (1 << event->touch_id());
+ Env::GetInstance()->set_touch_down(touch_ids_down_ != 0);
+ break;
+
+ // Handle ET_TOUCH_CANCELLED only if it has a native event.
+ case ui::ET_TOUCH_CANCELLED:
+ if (!event->HasNativeEvent())
+ break;
+ // fallthrough
+ case ui::ET_TOUCH_RELEASED:
+ touch_ids_down_ = (touch_ids_down_ | (1 << event->touch_id())) ^
+ (1 << event->touch_id());
+ Env::GetInstance()->set_touch_down(touch_ids_down_ != 0);
+ break;
+
+ default:
+ break;
+ }
+ TransformEventForDeviceScaleFactor(event);
+ bool handled = false;
+ Window* target = client::GetCaptureWindow(this);
+ if (!target) {
+ target = ConsumerToWindow(
+ gesture_recognizer_->GetTouchLockedTarget(event));
+ if (!target) {
+ target = ConsumerToWindow(
+ gesture_recognizer_->GetTargetForLocation(event->location()));
+ }
+ }
+
+ // The gesture recognizer processes touch events in the system coordinates. So
+ // keep a copy of the touch event here before possibly converting the event to
+ // a window's local coordinate system.
+ ui::TouchEvent event_for_gr(*event);
+
+ ui::EventResult result = ui::ER_UNHANDLED;
+ if (!target && !bounds().Contains(event->location())) {
+ // If the initial touch is outside the root window, target the root.
+ target = this;
+ ProcessEvent(target ? target : NULL, event);
+ result = event->result();
+ } else {
+ // We only come here when the first contact was within the root window.
+ if (!target) {
+ target = GetEventHandlerForPoint(event->location());
+ if (!target)
+ return false;
+ }
+
+ event->ConvertLocationToTarget(static_cast<Window*>(this), target);
+ ProcessEvent(target, event);
+ handled = event->handled();
+ result = event->result();
+ }
+
+ // Get the list of GestureEvents from GestureRecognizer.
+ scoped_ptr<ui::GestureRecognizer::Gestures> gestures;
+ gestures.reset(gesture_recognizer_->ProcessTouchEventForGesture(
+ event_for_gr, result, target));
+
+ return ProcessGestures(gestures.get()) ? true : handled;
+}
+
+void RootWindow::DispatchHeldEvents() {
+ if (held_repostable_event_) {
+ if (held_repostable_event_->type() == ui::ET_MOUSE_PRESSED) {
+ ui::MouseEvent mouse_event(
+ static_cast<const ui::MouseEvent&>(*held_repostable_event_.get()));
+ held_repostable_event_.reset(); // must be reset before dispatch
+ DispatchMouseEventRepost(&mouse_event);
+ } else {
+ DCHECK(held_repostable_event_->type() == ui::ET_GESTURE_TAP_DOWN);
+ // TODO(sschmitz): add similar code for gesture events
+ }
+ held_repostable_event_.reset();
+ }
+ if (held_move_event_ && held_move_event_->IsMouseEvent()) {
+ // If a mouse move has been synthesized, the target location is suspect,
+ // so drop the held event.
+ if (!synthesize_mouse_move_)
+ DispatchMouseEventImpl(
+ static_cast<ui::MouseEvent*>(held_move_event_.get()));
+ held_move_event_.reset();
+ } else if (held_move_event_ && held_move_event_->IsTouchEvent()) {
+ DispatchTouchEventImpl(
+ static_cast<ui::TouchEvent*>(held_move_event_.get()));
+ held_move_event_.reset();
+ }
+}
+
+void RootWindow::PostMouseMoveEventAfterWindowChange() {
+ if (synthesize_mouse_move_)
+ return;
+ synthesize_mouse_move_ = true;
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&RootWindow::SynthesizeMouseMoveEvent,
+ event_factory_.GetWeakPtr()));
+}
+
+void RootWindow::SynthesizeMouseMoveEvent() {
+ if (!synthesize_mouse_move_)
+ return;
+ synthesize_mouse_move_ = false;
+ gfx::Point root_mouse_location = GetLastMouseLocationInRoot();
+ if (!bounds().Contains(root_mouse_location))
+ return;
+ gfx::Point host_mouse_location = root_mouse_location;
+ ConvertPointToHost(&host_mouse_location);
+
+ ui::MouseEvent event(ui::ET_MOUSE_MOVED,
+ host_mouse_location,
+ host_mouse_location,
+ ui::EF_IS_SYNTHESIZED);
+ OnHostMouseEvent(&event);
+}
+
+gfx::Transform RootWindow::GetInverseRootTransform() const {
+ float scale = ui::GetDeviceScaleFactor(layer());
+ gfx::Transform transform;
+ transform.Scale(1.0f / scale, 1.0f / scale);
+ return transformer_->GetInverseTransform() * transform;
+}
+
+} // namespace aura
diff --git a/chromium/ui/aura/root_window.h b/chromium/ui/aura/root_window.h
new file mode 100644
index 00000000000..d9055175163
--- /dev/null
+++ b/chromium/ui/aura/root_window.h
@@ -0,0 +1,453 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_ROOT_WINDOW_H_
+#define UI_AURA_ROOT_WINDOW_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "ui/aura/aura_export.h"
+#include "ui/aura/client/capture_delegate.h"
+#include "ui/aura/root_window_host_delegate.h"
+#include "ui/aura/window.h"
+#include "ui/base/cursor/cursor.h"
+#include "ui/base/events/event_constants.h"
+#include "ui/base/events/event_dispatcher.h"
+#include "ui/base/gestures/gesture_recognizer.h"
+#include "ui/base/gestures/gesture_types.h"
+#include "ui/compositor/compositor.h"
+#include "ui/compositor/compositor_observer.h"
+#include "ui/compositor/layer_animation_observer.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/point.h"
+#include "ui/gfx/transform.h"
+
+namespace gfx {
+class Size;
+class Transform;
+}
+
+namespace ui {
+class GestureEvent;
+class GestureRecognizer;
+class KeyEvent;
+class LayerAnimationSequence;
+class MouseEvent;
+class ScrollEvent;
+class TouchEvent;
+class ViewProp;
+}
+
+namespace aura {
+class TestScreen;
+class RootWindow;
+class RootWindowHost;
+class RootWindowObserver;
+class RootWindowTransformer;
+
+// RootWindow is responsible for hosting a set of windows.
+class AURA_EXPORT RootWindow : public ui::CompositorDelegate,
+ public ui::CompositorObserver,
+ public Window,
+ public ui::EventDispatcherDelegate,
+ public ui::GestureEventHelper,
+ public ui::LayerAnimationObserver,
+ public aura::client::CaptureDelegate,
+ public aura::RootWindowHostDelegate {
+ public:
+ struct AURA_EXPORT CreateParams {
+ // CreateParams with initial_bounds and default host in pixel.
+ explicit CreateParams(const gfx::Rect& initial_bounds);
+ ~CreateParams() {}
+
+ gfx::Rect initial_bounds;
+
+ // A host to use in place of the default one that RootWindow will create.
+ // NULL by default.
+ RootWindowHost* host;
+ };
+
+ explicit RootWindow(const CreateParams& params);
+ virtual ~RootWindow();
+
+ // Returns the RootWindowHost for the specified accelerated widget, or NULL
+ // if there is none associated.
+ static RootWindow* GetForAcceleratedWidget(gfx::AcceleratedWidget widget);
+
+ ui::Compositor* compositor() { return compositor_.get(); }
+ gfx::NativeCursor last_cursor() const { return last_cursor_; }
+ Window* mouse_pressed_handler() { return mouse_pressed_handler_; }
+
+ // Initializes the root window.
+ void Init();
+
+ // Shows the root window host.
+ void ShowRootWindow();
+
+ // Hides the root window host.
+ void HideRootWindow();
+
+ // Stop listening events in preparation for shutdown.
+ void PrepareForShutdown();
+
+ // Repost event for re-processing. Used when exiting context menus.
+ void RepostEvent(const ui::LocatedEvent& event);
+
+ RootWindowHostDelegate* AsRootWindowHostDelegate();
+
+ // Gets/sets the size of the host window.
+ void SetHostSize(const gfx::Size& size_in_pixel);
+ gfx::Size GetHostSize() const;
+
+ // Sets the bounds of the host window.
+ void SetHostBounds(const gfx::Rect& size_in_pizel);
+
+ // Returns where the RootWindow is on screen.
+ gfx::Point GetHostOrigin() const;
+
+ // Sets the currently-displayed cursor. If the cursor was previously hidden
+ // via ShowCursor(false), it will remain hidden until ShowCursor(true) is
+ // called, at which point the cursor that was last set via SetCursor() will be
+ // used.
+ void SetCursor(gfx::NativeCursor cursor);
+
+ // Invoked when the cursor's visibility has changed.
+ void OnCursorVisibilityChanged(bool visible);
+
+ // Invoked when the mouse events get enabled or disabled.
+ void OnMouseEventsEnableStateChanged(bool enabled);
+
+ // Moves the cursor to the specified location relative to the root window.
+ virtual void MoveCursorTo(const gfx::Point& location) OVERRIDE;
+
+ // Moves the cursor to the |host_location| given in host coordinates.
+ void MoveCursorToHostLocation(const gfx::Point& host_location);
+
+ // Clips the cursor movement to the root_window.
+ bool ConfineCursorToWindow();
+
+ // Draws the necessary set of windows.
+ void Draw();
+
+ // Draw the whole screen.
+ void ScheduleFullRedraw();
+
+ // Draw the damage_rect.
+ void ScheduleRedrawRect(const gfx::Rect& damage_rect);
+
+ // Returns a target window for the given gesture event.
+ Window* GetGestureTarget(ui::GestureEvent* event);
+
+ // Handles a gesture event. Returns true if handled. Unlike the other
+ // event-dispatching function (e.g. for touch/mouse/keyboard events), gesture
+ // events are dispatched from GestureRecognizer instead of RootWindowHost.
+ bool DispatchGestureEvent(ui::GestureEvent* event);
+
+ // Invoked when |window| is being destroyed.
+ void OnWindowDestroying(Window* window);
+
+ // Invoked when |window|'s bounds have changed. |contained_mouse| indicates if
+ // the bounds before change contained the |last_moust_location()|.
+ void OnWindowBoundsChanged(Window* window, bool contained_mouse);
+
+ // Dispatches OnMouseExited to the |window| which is hiding if nessessary.
+ void DispatchMouseExitToHidingWindow(Window* window);
+
+ // Invoked when |window|'s visibility has changed.
+ void OnWindowVisibilityChanged(Window* window, bool is_visible);
+
+ // Invoked when |window|'s tranfrom has changed. |contained_mouse|
+ // indicates if the bounds before change contained the
+ // |last_moust_location()|.
+ void OnWindowTransformed(Window* window, bool contained_mouse);
+
+ // Invoked when the keyboard mapping (in X11 terms: the mapping between
+ // keycodes and keysyms) has changed.
+ void OnKeyboardMappingChanged();
+
+ // The system windowing system has sent a request that we close our window.
+ void OnRootWindowHostCloseRequested();
+
+ // Add/remove observer. There is no need to remove the observer if
+ // the root window is being deleted. In particular, you SHOULD NOT remove
+ // in |WindowObserver::OnWindowDestroying| of the observer observing
+ // the root window because it is too late to remove it.
+ void AddRootWindowObserver(RootWindowObserver* observer);
+ void RemoveRootWindowObserver(RootWindowObserver* observer);
+
+ // Posts |native_event| to the platform's event queue.
+ void PostNativeEvent(const base::NativeEvent& native_event);
+
+ // Converts |point| from the root window's coordinate system to native
+ // screen's.
+ void ConvertPointToNativeScreen(gfx::Point* point) const;
+
+ // Converts |point| from native screen coordinate system to the root window's.
+ void ConvertPointFromNativeScreen(gfx::Point* point) const;
+
+ // Converts |point| from the root window's coordinate system to the
+ // host window's.
+ void ConvertPointToHost(gfx::Point* point) const;
+
+ // Converts |point| from the host window's coordinate system to the
+ // root window's.
+ void ConvertPointFromHost(gfx::Point* point) const;
+
+ // Gesture Recognition -------------------------------------------------------
+
+ // When a touch event is dispatched to a Window, it may want to process the
+ // touch event asynchronously. In such cases, the window should consume the
+ // event during the event dispatch. Once the event is properly processed, the
+ // window should let the RootWindow know about the result of the event
+ // processing, so that gesture events can be properly created and dispatched.
+ void ProcessedTouchEvent(ui::TouchEvent* event,
+ Window* window,
+ ui::EventResult result);
+
+ ui::GestureRecognizer* gesture_recognizer() const {
+ return gesture_recognizer_.get();
+ }
+
+ // Provided only for testing:
+ void SetGestureRecognizerForTesting(ui::GestureRecognizer* gr);
+
+ // Returns the accelerated widget from the RootWindowHost.
+ gfx::AcceleratedWidget GetAcceleratedWidget();
+
+ // Toggles the host's full screen state.
+ void ToggleFullScreen();
+
+ // These methods are used to defer the processing of mouse/touch events
+ // related to resize. A client (typically a RenderWidgetHostViewAura) can call
+ // HoldPointerMoves when an resize is initiated and then ReleasePointerMoves
+ // once the resize is completed.
+ //
+ // More than one hold can be invoked and each hold must be cancelled by a
+ // release before we resume normal operation.
+ void HoldPointerMoves();
+ void ReleasePointerMoves();
+
+ // Sets if the window should be focused when shown.
+ void SetFocusWhenShown(bool focus_when_shown);
+
+ // Gets the last location seen in a mouse event in this root window's
+ // coordinates. This may return a point outside the root window's bounds.
+ gfx::Point GetLastMouseLocationInRoot() const;
+
+ // Overridden from Window:
+ virtual RootWindow* GetRootWindow() OVERRIDE;
+ virtual const RootWindow* GetRootWindow() const OVERRIDE;
+ virtual void SetTransform(const gfx::Transform& transform) OVERRIDE;
+
+ // Overridden from ui::EventTarget:
+ virtual ui::EventTarget* GetParentTarget() OVERRIDE;
+
+ // Overridden from ui::CompositorDelegate:
+ virtual void ScheduleDraw() OVERRIDE;
+
+ // Overridden from ui::CompositorObserver:
+ virtual void OnCompositingDidCommit(ui::Compositor*) OVERRIDE;
+ virtual void OnCompositingStarted(ui::Compositor*,
+ base::TimeTicks start_time) OVERRIDE;
+ virtual void OnCompositingEnded(ui::Compositor*) OVERRIDE;
+ virtual void OnCompositingAborted(ui::Compositor*) OVERRIDE;
+ virtual void OnCompositingLockStateChanged(ui::Compositor*) OVERRIDE;
+ virtual void OnUpdateVSyncParameters(ui::Compositor* compositor,
+ base::TimeTicks timebase,
+ base::TimeDelta interval) OVERRIDE;
+
+ // Overridden from ui::LayerDelegate:
+ virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE;
+
+ // Overridden from Window:
+ virtual bool CanFocus() const OVERRIDE;
+ virtual bool CanReceiveEvents() const OVERRIDE;
+
+ // Overridden from aura::client::CaptureDelegate:
+ virtual void UpdateCapture(Window* old_capture, Window* new_capture) OVERRIDE;
+ virtual void SetNativeCapture() OVERRIDE;
+ virtual void ReleaseNativeCapture() OVERRIDE;
+
+ // Exposes RootWindowHost::QueryMouseLocation() for test purposes.
+ bool QueryMouseLocationForTest(gfx::Point* point) const;
+
+ // Clears internal mouse state (such as mouse ups should be sent to the same
+ // window that ate mouse downs).
+ void ClearMouseHandlers();
+
+ void SetRootWindowTransformer(scoped_ptr<RootWindowTransformer> transformer);
+ gfx::Transform GetRootTransform() const;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(RootWindowTest, KeepTranslatedEventInRoot);
+
+ friend class Window;
+ friend class TestScreen;
+
+ // The parameter for OnWindowHidden() to specify why window is hidden.
+ enum WindowHiddenReason {
+ WINDOW_DESTROYED, // Window is destroyed.
+ WINDOW_HIDDEN, // Window is hidden.
+ WINDOW_MOVING, // Window is temporarily marked as hidden due to move
+ // across root windows.
+ };
+
+ // Updates the event with the appropriate transform for the device scale
+ // factor. The RootWindowHostDelegate dispatches events in the physical pixel
+ // coordinate. But the event processing from RootWindow onwards happen in
+ // device-independent pixel coordinate. So it is necessary to update the event
+ // received from the host.
+ void TransformEventForDeviceScaleFactor(ui::LocatedEvent* event);
+
+ // Moves the cursor to the specified location. This method is internally used
+ // by MoveCursorTo() and MoveCursorToHostLocation().
+ void MoveCursorToInternal(const gfx::Point& root_location,
+ const gfx::Point& host_location);
+
+ // Called whenever the mouse moves, tracks the current |mouse_moved_handler_|,
+ // sending exited and entered events as its value changes.
+ void HandleMouseMoved(const ui::MouseEvent& event, Window* target);
+
+ // Dispatches the specified event type (intended for enter/exit) to the
+ // |mouse_moved_handler_|.
+ void DispatchMouseEnterOrExit(const ui::MouseEvent& event,
+ ui::EventType type);
+
+ void ProcessEvent(Window* target, ui::Event* event);
+
+ bool ProcessGestures(ui::GestureRecognizer::Gestures* gestures);
+
+ // Called when a Window is attached or detached from the RootWindow.
+ void OnWindowAddedToRootWindow(Window* window);
+ void OnWindowRemovedFromRootWindow(Window* window, RootWindow* new_root);
+
+ // Called when a window becomes invisible, either by being removed
+ // from root window hierarchy, via SetVisible(false) or being destroyed.
+ // |reason| specifies what triggered the hiding.
+ void OnWindowHidden(Window* invisible, WindowHiddenReason reason);
+
+ // Cleans up the gesture recognizer for all windows in |window| (including
+ // |window| itself).
+ void CleanupGestureRecognizerState(Window* window);
+
+ // Updates the root window's size using |host_size|, current
+ // transform and insets.
+ void UpdateRootWindowSize(const gfx::Size& host_size);
+
+ // Overridden from ui::EventDispatcherDelegate.
+ virtual bool CanDispatchToTarget(EventTarget* target) OVERRIDE;
+
+ // Overridden from ui::GestureEventHelper.
+ virtual bool DispatchLongPressGestureEvent(ui::GestureEvent* event) OVERRIDE;
+ virtual bool DispatchCancelTouchEvent(ui::TouchEvent* event) OVERRIDE;
+
+ // Overridden from ui::LayerAnimationObserver:
+ virtual void OnLayerAnimationEnded(
+ ui::LayerAnimationSequence* animation) OVERRIDE;
+ virtual void OnLayerAnimationScheduled(
+ ui::LayerAnimationSequence* animation) OVERRIDE;
+ virtual void OnLayerAnimationAborted(
+ ui::LayerAnimationSequence* animation) OVERRIDE;
+
+ // Overridden from aura::RootWindowHostDelegate:
+ virtual bool OnHostKeyEvent(ui::KeyEvent* event) OVERRIDE;
+ virtual bool OnHostMouseEvent(ui::MouseEvent* event) OVERRIDE;
+ virtual bool OnHostScrollEvent(ui::ScrollEvent* event) OVERRIDE;
+ virtual bool OnHostTouchEvent(ui::TouchEvent* event) OVERRIDE;
+ virtual void OnHostCancelMode() OVERRIDE;
+ virtual void OnHostActivated() OVERRIDE;
+ virtual void OnHostLostWindowCapture() OVERRIDE;
+ virtual void OnHostLostMouseGrab() OVERRIDE;
+ virtual void OnHostPaint(const gfx::Rect& damage_rect) OVERRIDE;
+ virtual void OnHostMoved(const gfx::Point& origin) OVERRIDE;
+ virtual void OnHostResized(const gfx::Size& size) OVERRIDE;
+ virtual float GetDeviceScaleFactor() OVERRIDE;
+ virtual RootWindow* AsRootWindow() OVERRIDE;
+
+ // We hold and aggregate mouse drags and touch moves as a way of throttling
+ // resizes when HoldMouseMoves() is called. The following methods are used to
+ // dispatch held and newly incoming mouse and touch events, typically when an
+ // event other than one of these needs dispatching or a matching
+ // ReleaseMouseMoves()/ReleaseTouchMoves() is called. NOTE: because these
+ // methods dispatch events from RootWindowHost the coordinates are in terms of
+ // the root.
+ bool DispatchMouseEventImpl(ui::MouseEvent* event);
+ bool DispatchMouseEventRepost(ui::MouseEvent* event);
+ bool DispatchMouseEventToTarget(ui::MouseEvent* event, Window* target);
+ bool DispatchTouchEventImpl(ui::TouchEvent* event);
+ void DispatchHeldEvents();
+
+ // Parses the switch describing the initial size for the host window and
+ // returns bounds for the window.
+ gfx::Rect GetInitialHostWindowBounds() const;
+
+ // Posts a task to send synthesized mouse move event if there
+ // is no a pending task.
+ void PostMouseMoveEventAfterWindowChange();
+
+ // Creates and dispatches synthesized mouse move event using the
+ // current mouse location.
+ void SynthesizeMouseMoveEvent();
+
+ gfx::Transform GetInverseRootTransform() const;
+
+ scoped_ptr<ui::Compositor> compositor_;
+
+ scoped_ptr<RootWindowHost> host_;
+
+ // Used to schedule painting.
+ base::WeakPtrFactory<RootWindow> schedule_paint_factory_;
+
+ // Use to post mouse move event.
+ base::WeakPtrFactory<RootWindow> event_factory_;
+
+ // Touch ids that are currently down.
+ uint32 touch_ids_down_;
+
+ // Last cursor set. Used for testing.
+ gfx::NativeCursor last_cursor_;
+
+ ObserverList<RootWindowObserver> observers_;
+
+ Window* mouse_pressed_handler_;
+ Window* mouse_moved_handler_;
+ Window* mouse_event_dispatch_target_;
+ Window* event_dispatch_target_;
+
+ // The gesture_recognizer_ for this.
+ scoped_ptr<ui::GestureRecognizer> gesture_recognizer_;
+
+ bool synthesize_mouse_move_;
+ bool waiting_on_compositing_end_;
+ bool draw_on_compositing_end_;
+
+ bool defer_draw_scheduling_;
+
+ // How many move holds are outstanding. We try to defer dispatching
+ // touch/mouse moves while the count is > 0.
+ int move_hold_count_;
+ // Used to schedule DispatchHeldEvents() when |move_hold_count_| goes to 0.
+ base::WeakPtrFactory<RootWindow> held_event_factory_;
+ scoped_ptr<ui::LocatedEvent> held_move_event_;
+
+ // Allowing for reposting of events. Used when exiting context menus.
+ scoped_ptr<ui::LocatedEvent> held_repostable_event_;
+ base::WeakPtrFactory<RootWindow> repostable_event_factory_;
+
+ scoped_ptr<ui::ViewProp> prop_;
+
+ scoped_ptr<RootWindowTransformer> transformer_;
+
+ DISALLOW_COPY_AND_ASSIGN(RootWindow);
+};
+
+} // namespace aura
+
+#endif // UI_AURA_ROOT_WINDOW_H_
diff --git a/chromium/ui/aura/root_window_host.h b/chromium/ui/aura/root_window_host.h
new file mode 100644
index 00000000000..f1b52041707
--- /dev/null
+++ b/chromium/ui/aura/root_window_host.h
@@ -0,0 +1,114 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_ROOT_WINDOW_HOST_H_
+#define UI_AURA_ROOT_WINDOW_HOST_H_
+
+#include <vector>
+
+#include "base/message_loop/message_loop.h"
+#include "ui/aura/aura_export.h"
+#include "ui/base/cursor/cursor.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace gfx {
+class Insets;
+class Point;
+class Rect;
+class Size;
+}
+
+namespace aura {
+
+class RootWindow;
+class RootWindowHostDelegate;
+
+// RootWindowHost bridges between a native window and the embedded RootWindow.
+// It provides the accelerated widget and maps events from the native os to
+// aura.
+class AURA_EXPORT RootWindowHost {
+ public:
+ virtual ~RootWindowHost() {}
+
+ // Creates a new RootWindowHost. The caller owns the returned value.
+ static RootWindowHost* Create(const gfx::Rect& bounds);
+
+ // Returns the actual size of the screen.
+ // (gfx::Screen only reports on the virtual desktop exposed by Aura.)
+ static gfx::Size GetNativeScreenSize();
+
+ // Sets the delegate, which is normally done by the root window.
+ virtual void SetDelegate(RootWindowHostDelegate* delegate) = 0;
+
+ virtual RootWindow* GetRootWindow() = 0;
+
+ // Returns the accelerated widget.
+ virtual gfx::AcceleratedWidget GetAcceleratedWidget() = 0;
+
+ // Shows the RootWindowHost.
+ virtual void Show() = 0;
+
+ // Hides the RootWindowHost.
+ virtual void Hide() = 0;
+
+ // Toggles the host's full screen state.
+ virtual void ToggleFullScreen() = 0;
+
+ // Gets/Sets the size of the RootWindowHost.
+ virtual gfx::Rect GetBounds() const = 0;
+ virtual void SetBounds(const gfx::Rect& bounds) = 0;
+
+ // Sets/Gets the insets that specifies the effective root window area
+ // in the host window.
+ virtual gfx::Insets GetInsets() const = 0;
+ virtual void SetInsets(const gfx::Insets& insets) = 0;
+
+ // Returns the location of the RootWindow on native screen.
+ virtual gfx::Point GetLocationOnNativeScreen() const = 0;
+
+ // Sets the OS capture to the root window.
+ virtual void SetCapture() = 0;
+
+ // Releases OS capture of the root window.
+ virtual void ReleaseCapture() = 0;
+
+ // Sets the currently displayed cursor.
+ virtual void SetCursor(gfx::NativeCursor cursor) = 0;
+
+ // Queries the mouse's current position relative to the host window and sets
+ // it in |location_return|. Returns true if the cursor is within the host
+ // window. The position set to |location_return| is constrained within the
+ // host window. If the cursor is disabled, returns false and (0, 0) is set to
+ // |location_return|.
+ // This method is expensive, instead use gfx::Screen::GetCursorScreenPoint().
+ virtual bool QueryMouseLocation(gfx::Point* location_return) = 0;
+
+ // Clips the cursor to the bounds of the root window until UnConfineCursor().
+ virtual bool ConfineCursorToRootWindow() = 0;
+ virtual void UnConfineCursor() = 0;
+
+ // Called when the cursor visibility has changed.
+ virtual void OnCursorVisibilityChanged(bool show) = 0;
+
+ // Moves the cursor to the specified location relative to the root window.
+ virtual void MoveCursorTo(const gfx::Point& location) = 0;
+
+ // Sets if the window should be focused when shown.
+ virtual void SetFocusWhenShown(bool focus_when_shown) = 0;
+
+ // Posts |native_event| to the platform's event queue.
+#if !defined(OS_MACOSX)
+ virtual void PostNativeEvent(const base::NativeEvent& native_event) = 0;
+#endif
+
+ // Called when the device scale factor of the root window has chagned.
+ virtual void OnDeviceScaleFactorChanged(float device_scale_factor) = 0;
+
+ // Stop listening events in preparation for shutdown.
+ virtual void PrepareForShutdown() = 0;
+};
+
+} // namespace aura
+
+#endif // UI_AURA_ROOT_WINDOW_HOST_H_
diff --git a/chromium/ui/aura/root_window_host_delegate.h b/chromium/ui/aura/root_window_host_delegate.h
new file mode 100644
index 00000000000..a7876512354
--- /dev/null
+++ b/chromium/ui/aura/root_window_host_delegate.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_ROOT_WINDOW_HOST_DELEGATE_H_
+#define UI_AURA_ROOT_WINDOW_HOST_DELEGATE_H_
+
+#include "ui/aura/aura_export.h"
+
+namespace gfx {
+class Point;
+class Rect;
+class Size;
+}
+
+namespace ui {
+class Event;
+class KeyEvent;
+class MouseEvent;
+class ScrollEvent;
+class TouchEvent;
+}
+
+namespace aura {
+
+class RootWindow;
+
+// A private interface used by RootWindowHost implementations to communicate
+// with their owning RootWindow.
+class AURA_EXPORT RootWindowHostDelegate {
+ public:
+ virtual bool OnHostKeyEvent(ui::KeyEvent* event) = 0;
+ virtual bool OnHostMouseEvent(ui::MouseEvent* event) = 0;
+ virtual bool OnHostScrollEvent(ui::ScrollEvent* event) = 0;
+ virtual bool OnHostTouchEvent(ui::TouchEvent* event) = 0;
+ virtual void OnHostCancelMode() = 0;
+
+ // Called when the windowing system activates the window.
+ virtual void OnHostActivated() = 0;
+
+ // Called when system focus is changed to another window.
+ virtual void OnHostLostWindowCapture() = 0;
+
+ // Called when the windowing system has mouse grab because it's performing a
+ // window move on our behalf, but we should still paint as if we're active.
+ virtual void OnHostLostMouseGrab() = 0;
+
+ virtual void OnHostPaint(const gfx::Rect& damage_rect) = 0;
+
+ virtual void OnHostMoved(const gfx::Point& origin) = 0;
+ virtual void OnHostResized(const gfx::Size& size) = 0;
+
+ virtual float GetDeviceScaleFactor() = 0;
+
+ virtual RootWindow* AsRootWindow() = 0;
+
+ protected:
+ virtual ~RootWindowHostDelegate() {}
+};
+
+} // namespace aura
+
+#endif // UI_AURA_ROOT_WINDOW_HOST_DELEGATE_H_
diff --git a/chromium/ui/aura/root_window_host_mac.h b/chromium/ui/aura/root_window_host_mac.h
new file mode 100644
index 00000000000..e7c26c35482
--- /dev/null
+++ b/chromium/ui/aura/root_window_host_mac.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_ROOT_WINDOW_HOST_MAC_H_
+#define UI_AURA_ROOT_WINDOW_HOST_MAC_H_
+
+#include "base/basictypes.h"
+#include "base/event_types.h"
+
+namespace aura {
+
+// An interface establishing event dispatch from the Mac native window and the
+// Aura host.
+class RootWindowHostMacDelegate {
+ public:
+ RootWindowHostMacDelegate();
+ virtual ~RootWindowHostMacDelegate();
+
+ // Route events from platform code to the RootWindowHost.
+ virtual void SendEvent(const base::NativeEvent& native_event) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RootWindowHostMacDelegate);
+};
+
+} // namespace aura
+
+#endif // UI_AURA_ROOT_WINDOW_HOST_MAC_H_
diff --git a/chromium/ui/aura/root_window_host_mac.mm b/chromium/ui/aura/root_window_host_mac.mm
new file mode 100644
index 00000000000..de00e9b244f
--- /dev/null
+++ b/chromium/ui/aura/root_window_host_mac.mm
@@ -0,0 +1,215 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/root_window_host_mac.h"
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/compiler_specific.h"
+#include "base/mac/bundle_locations.h"
+#include "base/mac/scoped_nsobject.h"
+#include "ui/aura/event.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/root_window_host.h"
+#include "ui/aura/root_window_mac.h"
+#include "ui/aura/root_window_view_mac.h"
+#include "ui/base/events/event_utils.h"
+#include "ui/gfx/point.h"
+
+namespace aura {
+
+// The Mac-specific implementation of the RootWindowHost interface. This class
+// acts at an intermediary between the Aura shell and an NSWindow. The
+// association between the Aura compositor and the native window's view is
+// established with this class as is the association between the native window's
+// event dispatch and the Aura event processing.
+class RootWindowHostMac : public RootWindowHost,
+ public RootWindowHostMacDelegate {
+ public:
+ explicit RootWindowHostMac(const gfx::Rect& bounds);
+ virtual ~RootWindowHostMac();
+
+ // RootWindowHost:
+ virtual void SetRootWindow(RootWindow* root_window) OVERRIDE;
+ virtual gfx::AcceleratedWidget GetAcceleratedWidget() OVERRIDE;
+ virtual void Show() OVERRIDE;
+ virtual void ToggleFullScreen() OVERRIDE;
+ virtual gfx::Size GetSize() const OVERRIDE;
+ virtual void SetSize(const gfx::Size& size) OVERRIDE;
+ virtual gfx::Point GetLocationOnNativeScreen() const OVERRIDE;
+ virtual void SetCapture() OVERRIDE;
+ virtual void ReleaseCapture() OVERRIDE;
+ virtual void SetCursor(gfx::NativeCursor cursor) OVERRIDE;
+ virtual void ShowCursor(bool show) OVERRIDE;
+ virtual bool QueryMouseLocation(gfx::Point* location_return) OVERRIDE;
+ virtual void MoveCursorTo(const gfx::Point& location) OVERRIDE;
+ virtual bool ConfineCursorToRootWindow() OVERRIDE;
+ virtual void UnConfineCursor() OVERRIDE;
+ // RootWindowHostMacDelegate:
+ virtual void SendEvent(const base::NativeEvent& native_event) OVERRIDE;
+
+ // Set the initial location of the root window. The origin of |bounds| is
+ // top-left. This gets converted to bottom-left to match Mac coordinates on
+ // the main screen.
+ void SetLocation(const gfx::Rect& bounds);
+
+ private:
+ // Weak reference.
+ RootWindow* root_window_;
+
+ // The bounds of the Aura desktop. Relative to Aura's coordinate system.
+ // This is currently used only for size information, not location.
+ gfx::Rect bounds_;
+
+ // An NSWindowController for the root window. Controls the actual Cocoa
+ // window on Mac.
+ base::scoped_nsobject<NSWindowController> controller_;
+
+ DISALLOW_COPY_AND_ASSIGN(RootWindowHostMac);
+};
+
+RootWindowHostMacDelegate::RootWindowHostMacDelegate() {
+}
+
+RootWindowHostMacDelegate::~RootWindowHostMacDelegate() {
+}
+
+RootWindowHostMac::RootWindowHostMac(const gfx::Rect& bounds)
+ : root_window_(NULL), bounds_(bounds) {
+ NSString* nibpath = [base::mac::FrameworkBundle()
+ pathForResource:@"RootWindow"
+ ofType:@"nib"];
+ NSWindowController* controller = [NSWindowController alloc];
+ controller_.reset([controller initWithWindowNibPath:nibpath
+ owner:controller]);
+ SetSize(bounds.size());
+ SetLocation(bounds);
+}
+
+RootWindowHostMac::~RootWindowHostMac() {
+ RootWindowView* view = [[controller_ window] contentView];
+ [view setCompositor:NULL];
+ [controller_ close];
+}
+
+// static
+RootWindowHost* RootWindowHost::Create(const gfx::Rect& bounds) {
+ return new RootWindowHostMac(bounds);
+}
+
+// static
+gfx::Size RootWindowHost::GetNativeScreenSize() {
+ NSRect screen = [[NSScreen mainScreen] visibleFrame];
+ return gfx::Size(NSSizeToCGSize(screen.size));
+}
+
+void RootWindowHostMac::SetRootWindow(RootWindow* root_window) {
+ root_window_ = root_window;
+
+ RootWindowView* view = [[controller_ window] contentView];
+ DCHECK([view respondsToSelector:@selector(setCompositor:)]);
+ [view setCompositor:root_window->compositor()];
+
+ RootWindowMac* window = static_cast<RootWindowMac*>([controller_ window]);
+ DCHECK([window respondsToSelector:@selector(setHostDelegate:)]);
+ [window setHostDelegate:this];
+}
+
+gfx::AcceleratedWidget RootWindowHostMac::GetAcceleratedWidget() {
+ return [[controller_ window] contentView];
+}
+
+void RootWindowHostMac::Show() {
+ [controller_ showWindow:controller_];
+}
+
+void RootWindowHostMac::ToggleFullScreen() {
+}
+
+gfx::Size RootWindowHostMac::GetSize() const {
+ NSSize size = [[[controller_ window] contentView] bounds].size;
+ return gfx::Size(NSSizeToCGSize(size));
+}
+
+void RootWindowHostMac::SetSize(const gfx::Size& size) {
+ NSSize nssize = NSSizeFromCGSize(size.ToCGSize());
+ [[controller_ window] setContentSize:nssize];
+ [[controller_ window] setContentMaxSize:nssize];
+ [[controller_ window] setContentMinSize:nssize];
+}
+
+gfx::Point RootWindowHostMac::GetLocationOnNativeScreen() const {
+ return gfx::Point();
+}
+
+void RootWindowHostMac::SetCapture() {
+}
+
+void RootWindowHostMac::ReleaseCapture() {
+}
+
+void RootWindowHostMac::SetCursor(gfx::NativeCursor cursor) {
+}
+
+void RootWindowHostMac::ShowCursor(bool show) {
+}
+
+bool RootWindowHostMac::QueryMouseLocation(gfx::Point* location_return) {
+ *location_return = gfx::Point();
+ return true;
+}
+
+void RootWindowHostMac::MoveCursorTo(const gfx::Point& location) {
+}
+
+bool RootWindowHostMac::ConfineCursorToRootWindow() {
+ return false;
+}
+
+void RootWindowHostMac::UnConfineCursor() {
+}
+
+void RootWindowHostMac::SendEvent(const base::NativeEvent& native_event) {
+ ui::EventType type = ui::EventTypeFromNative(native_event);
+ switch (type) {
+ case ui::ET_MOUSE_PRESSED:
+ case ui::ET_MOUSE_DRAGGED:
+ case ui::ET_MOUSE_RELEASED:
+ case ui::ET_MOUSE_MOVED:
+ case ui::ET_MOUSE_ENTERED:
+ case ui::ET_MOUSE_EXITED: {
+ MouseEvent mouse_event(native_event);
+ root_window_->DispatchMouseEvent(&mouse_event);
+ break;
+ }
+ case ui::ET_KEY_PRESSED:
+ case ui::ET_KEY_RELEASED: {
+ KeyEvent key_event(native_event, false);
+ root_window_->DispatchKeyEvent(&key_event);
+ break;
+ }
+ case ui::ET_MOUSEWHEEL:
+ case ui::ET_TOUCH_RELEASED:
+ case ui::ET_TOUCH_PRESSED:
+ case ui::ET_TOUCH_MOVED:
+ case ui::ET_TOUCH_STATIONARY:
+ case ui::ET_TOUCH_CANCELLED:
+ case ui::ET_DROP_TARGET_EVENT:
+ case ui::ET_FOCUS_CHANGE:
+ case ui::ET_SCROLL:
+ case ui::ET_UNKNOWN:
+ default:
+ break;
+ }
+}
+
+void RootWindowHostMac::SetLocation(const gfx::Rect& bounds) {
+ NSRect screen = [[NSScreen mainScreen] visibleFrame];
+ NSPoint origin = NSMakePoint(screen.origin.x + bounds.x(),
+ screen.origin.y + screen.size.height -
+ bounds.y() - bounds.height());
+ [[controller_ window] setFrameOrigin:origin];
+}
+
+} // namespace aura
diff --git a/chromium/ui/aura/root_window_host_ozone.cc b/chromium/ui/aura/root_window_host_ozone.cc
new file mode 100644
index 00000000000..d3fe7c3865d
--- /dev/null
+++ b/chromium/ui/aura/root_window_host_ozone.cc
@@ -0,0 +1,132 @@
+// 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 "ui/aura/root_window_host_ozone.h"
+
+#include "ui/aura/root_window.h"
+#include "ui/base/ozone/surface_factory_ozone.h"
+
+namespace aura {
+
+RootWindowHostOzone::RootWindowHostOzone(const gfx::Rect& bounds)
+ : delegate_(NULL),
+ widget_(0),
+ bounds_(bounds),
+ factory_(new ui::EventFactoryOzone()) {
+ factory_->CreateStartupEventConverters();
+ ui::SurfaceFactoryOzone* surface_factory =
+ ui::SurfaceFactoryOzone::GetInstance();
+ widget_ = surface_factory->GetAcceleratedWidget();
+
+ surface_factory->AttemptToResizeAcceleratedWidget(widget_, bounds_);
+
+ base::MessagePumpOzone::Current()->AddDispatcherForRootWindow(this);
+}
+
+RootWindowHostOzone::~RootWindowHostOzone() {
+ base::MessagePumpOzone::Current()->RemoveDispatcherForRootWindow(0);
+}
+
+bool RootWindowHostOzone::Dispatch(const base::NativeEvent& ne) {
+ ui::Event* event = static_cast<ui::Event*>(ne);
+ if (event->IsTouchEvent()) {
+ ui::TouchEvent* touchev = static_cast<ui::TouchEvent*>(ne);
+ delegate_->OnHostTouchEvent(touchev);
+ } else if (event->IsKeyEvent()) {
+ ui::KeyEvent* keyev = static_cast<ui::KeyEvent*>(ne);
+ delegate_->OnHostKeyEvent(keyev);
+ }
+ return true;
+}
+
+void RootWindowHostOzone::SetDelegate(RootWindowHostDelegate* delegate) {
+ delegate_ = delegate;
+}
+
+RootWindow* RootWindowHostOzone::GetRootWindow() {
+ return delegate_->AsRootWindow();
+}
+
+gfx::AcceleratedWidget RootWindowHostOzone::GetAcceleratedWidget() {
+ return widget_;
+}
+
+void RootWindowHostOzone::Show() { NOTIMPLEMENTED(); }
+
+void RootWindowHostOzone::Hide() { NOTIMPLEMENTED(); }
+
+void RootWindowHostOzone::ToggleFullScreen() { NOTIMPLEMENTED(); }
+
+gfx::Rect RootWindowHostOzone::GetBounds() const { return bounds_; }
+
+void RootWindowHostOzone::SetBounds(const gfx::Rect& bounds) {
+ NOTIMPLEMENTED();
+}
+
+gfx::Insets RootWindowHostOzone::GetInsets() const { return gfx::Insets(); }
+
+void RootWindowHostOzone::SetInsets(const gfx::Insets& insets) {
+ NOTIMPLEMENTED();
+}
+
+gfx::Point RootWindowHostOzone::GetLocationOnNativeScreen() const {
+ return bounds_.origin();
+}
+
+void RootWindowHostOzone::SetCapture() { NOTIMPLEMENTED(); }
+
+void RootWindowHostOzone::ReleaseCapture() { NOTIMPLEMENTED(); }
+
+void RootWindowHostOzone::SetCursor(gfx::NativeCursor cursor) {
+ NOTIMPLEMENTED();
+}
+
+bool RootWindowHostOzone::QueryMouseLocation(gfx::Point* location_return) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool RootWindowHostOzone::ConfineCursorToRootWindow() {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+void RootWindowHostOzone::UnConfineCursor() { NOTIMPLEMENTED(); }
+
+void RootWindowHostOzone::OnCursorVisibilityChanged(bool show) {
+ NOTIMPLEMENTED();
+}
+
+void RootWindowHostOzone::MoveCursorTo(const gfx::Point& location) {
+ NOTIMPLEMENTED();
+}
+
+void RootWindowHostOzone::SetFocusWhenShown(bool focus_when_shown) {
+ NOTIMPLEMENTED();
+}
+
+void RootWindowHostOzone::PostNativeEvent(
+ const base::NativeEvent& native_event) {
+ NOTIMPLEMENTED();
+}
+
+void RootWindowHostOzone::OnDeviceScaleFactorChanged(
+ float device_scale_factor) {
+ NOTIMPLEMENTED();
+}
+
+void RootWindowHostOzone::PrepareForShutdown() { NOTIMPLEMENTED(); }
+
+// static
+RootWindowHost* RootWindowHost::Create(const gfx::Rect& bounds) {
+ return new RootWindowHostOzone(bounds);
+}
+
+// static
+gfx::Size RootWindowHost::GetNativeScreenSize() {
+ NOTIMPLEMENTED();
+ return gfx::Size();
+}
+
+} // namespace aura
diff --git a/chromium/ui/aura/root_window_host_ozone.h b/chromium/ui/aura/root_window_host_ozone.h
new file mode 100644
index 00000000000..f510d418678
--- /dev/null
+++ b/chromium/ui/aura/root_window_host_ozone.h
@@ -0,0 +1,67 @@
+// 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 UI_AURA_ROOT_WINDOW_HOST_OZONE_H_
+#define UI_AURA_ROOT_WINDOW_HOST_OZONE_H_
+
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "ui/aura/root_window_host.h"
+#include "ui/base/ozone/event_factory_ozone.h"
+#include "ui/gfx/rect.h"
+
+namespace aura {
+
+class RootWindowHostOzone : public RootWindowHost,
+ public base::MessageLoop::Dispatcher {
+ public:
+ explicit RootWindowHostOzone(const gfx::Rect& bounds);
+ virtual ~RootWindowHostOzone();
+
+ private:
+ // Overridden from Dispatcher overrides:
+ virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE;
+
+ // RootWindowHost Overrides.
+ virtual void SetDelegate(RootWindowHostDelegate* delegate) OVERRIDE;
+ virtual RootWindow* GetRootWindow() OVERRIDE;
+ virtual gfx::AcceleratedWidget GetAcceleratedWidget() OVERRIDE;
+ virtual void Show() OVERRIDE;
+ virtual void Hide() OVERRIDE;
+ virtual void ToggleFullScreen() OVERRIDE;
+ virtual gfx::Rect GetBounds() const OVERRIDE;
+ virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE;
+ virtual gfx::Insets GetInsets() const OVERRIDE;
+ virtual void SetInsets(const gfx::Insets& bounds) OVERRIDE;
+ virtual gfx::Point GetLocationOnNativeScreen() const OVERRIDE;
+ virtual void SetCapture() OVERRIDE;
+ virtual void ReleaseCapture() OVERRIDE;
+ virtual void SetCursor(gfx::NativeCursor cursor_type) OVERRIDE;
+ virtual bool QueryMouseLocation(gfx::Point* location_return) OVERRIDE;
+ virtual bool ConfineCursorToRootWindow() OVERRIDE;
+ virtual void UnConfineCursor() OVERRIDE;
+ virtual void OnCursorVisibilityChanged(bool show) OVERRIDE;
+ virtual void MoveCursorTo(const gfx::Point& location) OVERRIDE;
+ virtual void SetFocusWhenShown(bool focus_when_shown) OVERRIDE;
+ virtual void PostNativeEvent(const base::NativeEvent& event) OVERRIDE;
+ virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE;
+ virtual void PrepareForShutdown() OVERRIDE;
+
+ RootWindowHostDelegate* delegate_;
+ gfx::AcceleratedWidget widget_;
+ gfx::Rect bounds_;
+
+ // EventFactoryOzone creates converters that obtain input events from the
+ // underlying input system and dispatch them as |ui::Event| instances into
+ // Aura.
+ scoped_ptr<ui::EventFactoryOzone> factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(RootWindowHostOzone);
+};
+
+} // namespace aura
+
+#endif // UI_AURA_ROOT_WINDOW_HOST_OZONE_H_
diff --git a/chromium/ui/aura/root_window_host_win.cc b/chromium/ui/aura/root_window_host_win.cc
new file mode 100644
index 00000000000..723de57fe0a
--- /dev/null
+++ b/chromium/ui/aura/root_window_host_win.cc
@@ -0,0 +1,318 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/root_window_host_win.h"
+
+#include <windows.h>
+
+#include <algorithm>
+
+#include "base/message_loop/message_loop.h"
+#include "ui/aura/client/capture_client.h"
+#include "ui/aura/client/cursor_client.h"
+#include "ui/aura/root_window.h"
+#include "ui/base/cursor/cursor_loader_win.h"
+#include "ui/base/events/event.h"
+#include "ui/base/view_prop.h"
+#include "ui/gfx/display.h"
+#include "ui/gfx/insets.h"
+#include "ui/gfx/screen.h"
+
+using std::max;
+using std::min;
+
+namespace aura {
+namespace {
+
+bool use_popup_as_root_window_for_test = false;
+
+} // namespace
+
+// static
+RootWindowHost* RootWindowHost::Create(const gfx::Rect& bounds) {
+ return new RootWindowHostWin(bounds);
+}
+
+// static
+gfx::Size RootWindowHost::GetNativeScreenSize() {
+ return gfx::Size(GetSystemMetrics(SM_CXSCREEN),
+ GetSystemMetrics(SM_CYSCREEN));
+}
+
+RootWindowHostWin::RootWindowHostWin(const gfx::Rect& bounds)
+ : delegate_(NULL),
+ fullscreen_(false),
+ has_capture_(false),
+ saved_window_style_(0),
+ saved_window_ex_style_(0) {
+ if (use_popup_as_root_window_for_test)
+ set_window_style(WS_POPUP);
+ Init(NULL, bounds);
+ SetWindowText(hwnd(), L"aura::RootWindow!");
+}
+
+RootWindowHostWin::~RootWindowHostWin() {
+ DestroyWindow(hwnd());
+}
+
+void RootWindowHostWin::SetDelegate(RootWindowHostDelegate* delegate) {
+ delegate_ = delegate;
+}
+
+RootWindow* RootWindowHostWin::GetRootWindow() {
+ return delegate_->AsRootWindow();
+}
+
+gfx::AcceleratedWidget RootWindowHostWin::GetAcceleratedWidget() {
+ return hwnd();
+}
+
+void RootWindowHostWin::Show() {
+ ShowWindow(hwnd(), SW_SHOWNORMAL);
+}
+
+void RootWindowHostWin::Hide() {
+ NOTIMPLEMENTED();
+}
+
+void RootWindowHostWin::ToggleFullScreen() {
+ gfx::Rect target_rect;
+ if (!fullscreen_) {
+ fullscreen_ = true;
+ saved_window_style_ = GetWindowLong(hwnd(), GWL_STYLE);
+ saved_window_ex_style_ = GetWindowLong(hwnd(), GWL_EXSTYLE);
+ GetWindowRect(hwnd(), &saved_window_rect_);
+ SetWindowLong(hwnd(), GWL_STYLE,
+ saved_window_style_ & ~(WS_CAPTION | WS_THICKFRAME));
+ SetWindowLong(hwnd(), GWL_EXSTYLE,
+ saved_window_ex_style_ & ~(WS_EX_DLGMODALFRAME |
+ WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
+
+ MONITORINFO mi;
+ mi.cbSize = sizeof(mi);
+ GetMonitorInfo(MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONEAREST), &mi);
+ target_rect = gfx::Rect(mi.rcMonitor);
+ } else {
+ fullscreen_ = false;
+ SetWindowLong(hwnd(), GWL_STYLE, saved_window_style_);
+ SetWindowLong(hwnd(), GWL_EXSTYLE, saved_window_ex_style_);
+ target_rect = gfx::Rect(saved_window_rect_);
+ }
+ SetWindowPos(hwnd(),
+ NULL,
+ target_rect.x(),
+ target_rect.y(),
+ target_rect.width(),
+ target_rect.height(),
+ SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
+}
+
+gfx::Rect RootWindowHostWin::GetBounds() const {
+ RECT r;
+ GetClientRect(hwnd(), &r);
+ return gfx::Rect(r);
+}
+
+void RootWindowHostWin::SetBounds(const gfx::Rect& bounds) {
+ if (fullscreen_) {
+ saved_window_rect_.right = saved_window_rect_.left + bounds.width();
+ saved_window_rect_.bottom = saved_window_rect_.top + bounds.height();
+ return;
+ }
+ RECT window_rect;
+ window_rect.left = bounds.x();
+ window_rect.top = bounds.y();
+ window_rect.right = bounds.right() ;
+ window_rect.bottom = bounds.bottom();
+ AdjustWindowRectEx(&window_rect,
+ GetWindowLong(hwnd(), GWL_STYLE),
+ FALSE,
+ GetWindowLong(hwnd(), GWL_EXSTYLE));
+ SetWindowPos(
+ hwnd(),
+ NULL,
+ window_rect.left,
+ window_rect.top,
+ window_rect.right - window_rect.left,
+ window_rect.bottom - window_rect.top,
+ SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOREDRAW | SWP_NOREPOSITION);
+
+ // Explicity call OnHostResized when the scale has changed because
+ // the window size may not have changed.
+ float current_scale = delegate_->GetDeviceScaleFactor();
+ float new_scale = gfx::Screen::GetScreenFor(delegate_->AsRootWindow())->
+ GetDisplayNearestWindow(delegate_->AsRootWindow()).device_scale_factor();
+ if (current_scale != new_scale)
+ delegate_->OnHostResized(bounds.size());
+}
+
+gfx::Insets RootWindowHostWin::GetInsets() const {
+ return gfx::Insets();
+}
+
+void RootWindowHostWin::SetInsets(const gfx::Insets& insets) {
+}
+
+gfx::Point RootWindowHostWin::GetLocationOnNativeScreen() const {
+ RECT r;
+ GetClientRect(hwnd(), &r);
+ return gfx::Point(r.left, r.top);
+}
+
+
+void RootWindowHostWin::SetCursor(gfx::NativeCursor native_cursor) {
+ // Custom web cursors are handled directly.
+ if (native_cursor == ui::kCursorCustom)
+ return;
+
+ ui::CursorLoaderWin cursor_loader;
+ cursor_loader.SetPlatformCursor(&native_cursor);
+ ::SetCursor(native_cursor.platform());
+}
+
+void RootWindowHostWin::SetCapture() {
+ if (!has_capture_) {
+ has_capture_ = true;
+ ::SetCapture(hwnd());
+ }
+}
+
+void RootWindowHostWin::ReleaseCapture() {
+ if (has_capture_) {
+ has_capture_ = false;
+ ::ReleaseCapture();
+ }
+}
+
+bool RootWindowHostWin::QueryMouseLocation(gfx::Point* location_return) {
+ client::CursorClient* cursor_client =
+ client::GetCursorClient(GetRootWindow());
+ if (cursor_client && !cursor_client->IsMouseEventsEnabled()) {
+ *location_return = gfx::Point(0, 0);
+ return false;
+ }
+
+ POINT pt;
+ GetCursorPos(&pt);
+ ScreenToClient(hwnd(), &pt);
+ const gfx::Size size = GetBounds().size();
+ *location_return =
+ gfx::Point(max(0, min(size.width(), static_cast<int>(pt.x))),
+ max(0, min(size.height(), static_cast<int>(pt.y))));
+ return (pt.x >= 0 && static_cast<int>(pt.x) < size.width() &&
+ pt.y >= 0 && static_cast<int>(pt.y) < size.height());
+}
+
+bool RootWindowHostWin::ConfineCursorToRootWindow() {
+ RECT window_rect;
+ GetWindowRect(hwnd(), &window_rect);
+ return ClipCursor(&window_rect) != 0;
+}
+
+void RootWindowHostWin::UnConfineCursor() {
+ ClipCursor(NULL);
+}
+
+void RootWindowHostWin::OnCursorVisibilityChanged(bool show) {
+ NOTIMPLEMENTED();
+}
+
+void RootWindowHostWin::MoveCursorTo(const gfx::Point& location) {
+ // Deliberately not implemented.
+}
+
+void RootWindowHostWin::SetFocusWhenShown(bool focus_when_shown) {
+ NOTIMPLEMENTED();
+}
+
+void RootWindowHostWin::PostNativeEvent(const base::NativeEvent& native_event) {
+ ::PostMessage(
+ hwnd(), native_event.message, native_event.wParam, native_event.lParam);
+}
+
+void RootWindowHostWin::OnDeviceScaleFactorChanged(
+ float device_scale_factor) {
+ NOTIMPLEMENTED();
+}
+
+void RootWindowHostWin::PrepareForShutdown() {
+ NOTIMPLEMENTED();
+}
+
+void RootWindowHostWin::OnClose() {
+ // TODO: this obviously shouldn't be here.
+ base::MessageLoopForUI::current()->Quit();
+}
+
+LRESULT RootWindowHostWin::OnKeyEvent(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ MSG msg = { hwnd(), message, w_param, l_param };
+ ui::KeyEvent keyev(msg, message == WM_CHAR);
+ SetMsgHandled(delegate_->OnHostKeyEvent(&keyev));
+ return 0;
+}
+
+LRESULT RootWindowHostWin::OnMouseRange(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ MSG msg = { hwnd(), message, w_param, l_param, 0,
+ { GET_X_LPARAM(l_param), GET_Y_LPARAM(l_param) } };
+ ui::MouseEvent event(msg);
+ bool handled = false;
+ if (!(event.flags() & ui::EF_IS_NON_CLIENT))
+ handled = delegate_->OnHostMouseEvent(&event);
+ SetMsgHandled(handled);
+ return 0;
+}
+
+LRESULT RootWindowHostWin::OnCaptureChanged(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ if (has_capture_) {
+ has_capture_ = false;
+ delegate_->OnHostLostWindowCapture();
+ }
+ return 0;
+}
+
+LRESULT RootWindowHostWin::OnNCActivate(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ if (!!w_param)
+ delegate_->OnHostActivated();
+ return DefWindowProc(hwnd(), message, w_param, l_param);
+}
+
+void RootWindowHostWin::OnMove(const CPoint& point) {
+ if (delegate_)
+ delegate_->OnHostMoved(gfx::Point(point.x, point.y));
+}
+
+void RootWindowHostWin::OnPaint(HDC dc) {
+ gfx::Rect damage_rect;
+ RECT update_rect = {0};
+ if (GetUpdateRect(hwnd(), &update_rect, FALSE))
+ damage_rect = gfx::Rect(update_rect);
+ delegate_->OnHostPaint(damage_rect);
+ ValidateRect(hwnd(), NULL);
+}
+
+void RootWindowHostWin::OnSize(UINT param, const CSize& size) {
+ // Minimizing resizes the window to 0x0 which causes our layout to go all
+ // screwy, so we just ignore it.
+ if (delegate_ && param != SIZE_MINIMIZED)
+ delegate_->OnHostResized(gfx::Size(size.cx, size.cy));
+}
+
+namespace test {
+
+// static
+void SetUsePopupAsRootWindowForTest(bool use) {
+ use_popup_as_root_window_for_test = use;
+}
+
+} // namespace test
+
+} // namespace aura
diff --git a/chromium/ui/aura/root_window_host_win.h b/chromium/ui/aura/root_window_host_win.h
new file mode 100644
index 00000000000..e2a60ddf738
--- /dev/null
+++ b/chromium/ui/aura/root_window_host_win.h
@@ -0,0 +1,100 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_ROOT_WINDOW_HOST_WIN_H_
+#define UI_AURA_ROOT_WINDOW_HOST_WIN_H_
+
+#include "base/compiler_specific.h"
+#include "ui/aura/root_window_host.h"
+#include "ui/base/ui_export.h"
+#include "ui/base/win/window_impl.h"
+
+namespace aura {
+
+class RootWindowHostWin : public RootWindowHost, public ui::WindowImpl {
+ public:
+ RootWindowHostWin(const gfx::Rect& bounds);
+ virtual ~RootWindowHostWin();
+ // RootWindowHost:
+ virtual void SetDelegate(RootWindowHostDelegate* delegate) OVERRIDE;
+ virtual RootWindow* GetRootWindow() OVERRIDE;
+ virtual gfx::AcceleratedWidget GetAcceleratedWidget() OVERRIDE;
+ virtual void Show() OVERRIDE;
+ virtual void Hide() OVERRIDE;
+ virtual void ToggleFullScreen() OVERRIDE;
+ virtual gfx::Rect GetBounds() const OVERRIDE;
+ virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE;
+ virtual gfx::Insets GetInsets() const OVERRIDE;
+ virtual void SetInsets(const gfx::Insets& insets) OVERRIDE;
+ virtual gfx::Point GetLocationOnNativeScreen() const OVERRIDE;
+ virtual void SetCapture() OVERRIDE;
+ virtual void ReleaseCapture() OVERRIDE;
+ virtual void SetCursor(gfx::NativeCursor cursor) OVERRIDE;
+ virtual bool QueryMouseLocation(gfx::Point* location_return) OVERRIDE;
+ virtual bool ConfineCursorToRootWindow() OVERRIDE;
+ virtual void UnConfineCursor() OVERRIDE;
+ virtual void OnCursorVisibilityChanged(bool show) OVERRIDE;
+ virtual void MoveCursorTo(const gfx::Point& location) OVERRIDE;
+ virtual void SetFocusWhenShown(bool focus_when_shown) OVERRIDE;
+ virtual void PostNativeEvent(const base::NativeEvent& native_event) OVERRIDE;
+ virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE;
+ virtual void PrepareForShutdown() OVERRIDE;
+
+ private:
+ BEGIN_MSG_MAP_EX(RootWindowHostWin)
+ // Range handlers must go first!
+ MESSAGE_RANGE_HANDLER_EX(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseRange)
+ MESSAGE_RANGE_HANDLER_EX(WM_NCMOUSEMOVE, WM_NCXBUTTONDBLCLK, OnMouseRange)
+
+ // Mouse capture events.
+ MESSAGE_HANDLER_EX(WM_CAPTURECHANGED, OnCaptureChanged)
+
+ // Key events.
+ MESSAGE_HANDLER_EX(WM_KEYDOWN, OnKeyEvent)
+ MESSAGE_HANDLER_EX(WM_KEYUP, OnKeyEvent)
+ MESSAGE_HANDLER_EX(WM_SYSKEYDOWN, OnKeyEvent)
+ MESSAGE_HANDLER_EX(WM_SYSKEYUP, OnKeyEvent)
+ MESSAGE_HANDLER_EX(WM_CHAR, OnKeyEvent)
+ MESSAGE_HANDLER_EX(WM_SYSCHAR, OnKeyEvent)
+ MESSAGE_HANDLER_EX(WM_IME_CHAR, OnKeyEvent)
+ MESSAGE_HANDLER_EX(WM_NCACTIVATE, OnNCActivate)
+
+ MSG_WM_CLOSE(OnClose)
+ MSG_WM_MOVE(OnMove)
+ MSG_WM_PAINT(OnPaint)
+ MSG_WM_SIZE(OnSize)
+ END_MSG_MAP()
+
+ void OnClose();
+ LRESULT OnKeyEvent(UINT message, WPARAM w_param, LPARAM l_param);
+ LRESULT OnMouseRange(UINT message, WPARAM w_param, LPARAM l_param);
+ LRESULT OnCaptureChanged(UINT message, WPARAM w_param, LPARAM l_param);
+ LRESULT OnNCActivate(UINT message, WPARAM w_param, LPARAM l_param);
+ void OnMove(const CPoint& point);
+ void OnPaint(HDC dc);
+ void OnSize(UINT param, const CSize& size);
+
+ RootWindowHostDelegate* delegate_;
+
+ bool fullscreen_;
+ bool has_capture_;
+ RECT saved_window_rect_;
+ DWORD saved_window_style_;
+ DWORD saved_window_ex_style_;
+
+ DISALLOW_COPY_AND_ASSIGN(RootWindowHostWin);
+};
+
+namespace test {
+
+// Set true to let RootWindowHostWin use a popup window
+// with no frame/title so that the window size and test's
+// expectations matches.
+AURA_EXPORT void SetUsePopupAsRootWindowForTest(bool use);
+
+} // namespace
+
+} // namespace aura
+
+#endif // UI_AURA_ROOT_WINDOW_HOST_WIN_H_
diff --git a/chromium/ui/aura/root_window_host_x11.cc b/chromium/ui/aura/root_window_host_x11.cc
new file mode 100644
index 00000000000..783e960095d
--- /dev/null
+++ b/chromium/ui/aura/root_window_host_x11.cc
@@ -0,0 +1,1096 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/root_window_host_x11.h"
+
+#include <strings.h>
+#include <X11/cursorfont.h>
+#include <X11/extensions/Xfixes.h>
+#include <X11/extensions/XInput2.h>
+#include <X11/extensions/Xrandr.h>
+#include <X11/Xatom.h>
+#include <X11/Xcursor/Xcursor.h>
+#include <X11/Xlib.h>
+
+#include <algorithm>
+#include <limits>
+#include <string>
+
+#include "base/command_line.h"
+#include "base/debug/trace_event.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_pump_aurax11.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "ui/aura/client/capture_client.h"
+#include "ui/aura/client/cursor_client.h"
+#include "ui/aura/client/screen_position_client.h"
+#include "ui/aura/client/user_action_client.h"
+#include "ui/aura/env.h"
+#include "ui/aura/root_window.h"
+#include "ui/base/cursor/cursor.h"
+#include "ui/base/events/event.h"
+#include "ui/base/events/event_utils.h"
+#include "ui/base/keycodes/keyboard_codes.h"
+#include "ui/base/touch/touch_factory_x11.h"
+#include "ui/base/ui_base_switches.h"
+#include "ui/base/view_prop.h"
+#include "ui/base/x/device_list_cache_x.h"
+#include "ui/base/x/x11_util.h"
+#include "ui/compositor/dip_util.h"
+#include "ui/compositor/layer.h"
+#include "ui/gfx/screen.h"
+
+#if defined(OS_CHROMEOS)
+#include "base/chromeos/chromeos_version.h"
+#endif
+
+using std::max;
+using std::min;
+
+namespace aura {
+
+namespace {
+
+// Standard Linux mouse buttons for going back and forward.
+const int kBackMouseButton = 8;
+const int kForwardMouseButton = 9;
+
+const char* kAtomsToCache[] = {
+ "WM_DELETE_WINDOW",
+ "_NET_WM_PING",
+ "_NET_WM_PID",
+ "WM_S0",
+#if defined(OS_CHROMEOS)
+ "Tap Paused", // Defined in the gestures library.
+#endif
+ NULL
+};
+
+::Window FindEventTarget(const base::NativeEvent& xev) {
+ ::Window target = xev->xany.window;
+ if (xev->type == GenericEvent)
+ target = static_cast<XIDeviceEvent*>(xev->xcookie.data)->event;
+ return target;
+}
+
+#if defined(USE_XI2_MT)
+bool IsSideBezelsEnabled() {
+ static bool side_bezels_enabled =
+ CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kTouchSideBezels) == "1";
+ return side_bezels_enabled;
+}
+#endif
+
+void SelectEventsForRootWindow() {
+ Display* display = ui::GetXDisplay();
+ ::Window root_window = ui::GetX11RootWindow();
+
+ // Receive resize events for the root-window so |x_root_bounds_| can be
+ // updated.
+ XWindowAttributes attr;
+ XGetWindowAttributes(display, root_window, &attr);
+ if (!(attr.your_event_mask & StructureNotifyMask)) {
+ XSelectInput(display, root_window,
+ StructureNotifyMask | attr.your_event_mask);
+ }
+
+ if (!base::MessagePumpForUI::HasXInput2())
+ return;
+
+ unsigned char mask[XIMaskLen(XI_LASTEVENT)] = {};
+ memset(mask, 0, sizeof(mask));
+
+ XISetMask(mask, XI_HierarchyChanged);
+ XISetMask(mask, XI_KeyPress);
+ XISetMask(mask, XI_KeyRelease);
+
+ XIEventMask evmask;
+ evmask.deviceid = XIAllDevices;
+ evmask.mask_len = sizeof(mask);
+ evmask.mask = mask;
+ XISelectEvents(display, root_window, &evmask, 1);
+
+ // Selecting for touch events seems to fail on some cases (e.g. when logging
+ // in incognito). So select for non-touch events first, and then select for
+ // touch-events (but keep the other events in the mask, i.e. do not memset
+ // |mask| back to 0).
+ // TODO(sad): Figure out why this happens. http://crbug.com/153976
+#if defined(USE_XI2_MT)
+ XISetMask(mask, XI_TouchBegin);
+ XISetMask(mask, XI_TouchUpdate);
+ XISetMask(mask, XI_TouchEnd);
+ XISelectEvents(display, root_window, &evmask, 1);
+#endif
+}
+
+// We emulate Windows' WM_KEYDOWN and WM_CHAR messages. WM_CHAR events are only
+// generated for certain keys; see
+// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646268.aspx. Per
+// discussion on http://crbug.com/108480, char events should furthermore not be
+// generated for Tab, Escape, and Backspace.
+bool ShouldSendCharEventForKeyboardCode(ui::KeyboardCode keycode) {
+ if ((keycode >= ui::VKEY_0 && keycode <= ui::VKEY_9) ||
+ (keycode >= ui::VKEY_A && keycode <= ui::VKEY_Z) ||
+ (keycode >= ui::VKEY_NUMPAD0 && keycode <= ui::VKEY_NUMPAD9)) {
+ return true;
+ }
+
+ switch (keycode) {
+ case ui::VKEY_RETURN:
+ case ui::VKEY_SPACE:
+ // In addition to the keys listed at MSDN, we include other
+ // graphic-character and numpad keys.
+ case ui::VKEY_MULTIPLY:
+ case ui::VKEY_ADD:
+ case ui::VKEY_SUBTRACT:
+ case ui::VKEY_DECIMAL:
+ case ui::VKEY_DIVIDE:
+ case ui::VKEY_OEM_1:
+ case ui::VKEY_OEM_2:
+ case ui::VKEY_OEM_3:
+ case ui::VKEY_OEM_4:
+ case ui::VKEY_OEM_5:
+ case ui::VKEY_OEM_6:
+ case ui::VKEY_OEM_7:
+ case ui::VKEY_OEM_102:
+ case ui::VKEY_OEM_PLUS:
+ case ui::VKEY_OEM_COMMA:
+ case ui::VKEY_OEM_MINUS:
+ case ui::VKEY_OEM_PERIOD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool default_override_redirect = false;
+
+} // namespace
+
+namespace internal {
+
+// Accomplishes 2 tasks concerning touch event calibration:
+// 1. Being a message-pump observer,
+// routes all the touch events to the X root window,
+// where they can be calibrated later.
+// 2. Has the Calibrate method that does the actual bezel calibration,
+// when invoked from X root window's event dispatcher.
+class TouchEventCalibrate : public base::MessagePumpObserver {
+ public:
+ TouchEventCalibrate()
+ : left_(0),
+ right_(0),
+ top_(0),
+ bottom_(0) {
+ base::MessageLoopForUI::current()->AddObserver(this);
+#if defined(USE_XI2_MT)
+ std::vector<std::string> parts;
+ if (Tokenize(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kTouchCalibration), ",", &parts) >= 4) {
+ if (!base::StringToInt(parts[0], &left_))
+ DLOG(ERROR) << "Incorrect left border calibration value passed.";
+ if (!base::StringToInt(parts[1], &right_))
+ DLOG(ERROR) << "Incorrect right border calibration value passed.";
+ if (!base::StringToInt(parts[2], &top_))
+ DLOG(ERROR) << "Incorrect top border calibration value passed.";
+ if (!base::StringToInt(parts[3], &bottom_))
+ DLOG(ERROR) << "Incorrect bottom border calibration value passed.";
+ }
+#endif // defined(USE_XI2_MT)
+ }
+
+ virtual ~TouchEventCalibrate() {
+ base::MessageLoopForUI::current()->RemoveObserver(this);
+ }
+
+#if defined(USE_XI2_MT)
+ bool IsEventOnSideBezels(
+ const base::NativeEvent& xev,
+ const gfx::Rect& bounds) {
+ if (!left_ && !right_)
+ return false;
+
+ gfx::Point location = ui::EventLocationFromNative(xev);
+ int x = location.x();
+ return x < left_ || x > bounds.width() - right_;
+ }
+#endif // defined(USE_XI2_MT)
+
+ // Modify the location of the |event|,
+ // expanding it from |bounds| to (|bounds| + bezels).
+ // Required when touchscreen is bigger than screen (i.e. has bezels),
+ // because we receive events in touchscreen coordinates,
+ // which need to be expanded when converting to screen coordinates,
+ // so that location on bezels will be outside of screen area.
+ void Calibrate(ui::TouchEvent* event, const gfx::Rect& bounds) {
+#if defined(USE_XI2_MT)
+ int x = event->x();
+ int y = event->y();
+
+ if (!left_ && !right_ && !top_ && !bottom_)
+ return;
+
+ const int resolution_x = bounds.width();
+ const int resolution_y = bounds.height();
+ // The "grace area" (10% in this case) is to make it easier for the user to
+ // navigate to the corner.
+ const double kGraceAreaFraction = 0.1;
+ if (left_ || right_) {
+ // Offset the x position to the real
+ x -= left_;
+ // Check if we are in the grace area of the left side.
+ // Note: We might not want to do this when the gesture is locked?
+ if (x < 0 && x > -left_ * kGraceAreaFraction)
+ x = 0;
+ // Check if we are in the grace area of the right side.
+ // Note: We might not want to do this when the gesture is locked?
+ if (x > resolution_x - left_ &&
+ x < resolution_x - left_ + right_ * kGraceAreaFraction)
+ x = resolution_x - left_;
+ // Scale the screen area back to the full resolution of the screen.
+ x = (x * resolution_x) / (resolution_x - (right_ + left_));
+ }
+ if (top_ || bottom_) {
+ // When there is a top bezel we add our border,
+ y -= top_;
+
+ // Check if we are in the grace area of the top side.
+ // Note: We might not want to do this when the gesture is locked?
+ if (y < 0 && y > -top_ * kGraceAreaFraction)
+ y = 0;
+
+ // Check if we are in the grace area of the bottom side.
+ // Note: We might not want to do this when the gesture is locked?
+ if (y > resolution_y - top_ &&
+ y < resolution_y - top_ + bottom_ * kGraceAreaFraction)
+ y = resolution_y - top_;
+ // Scale the screen area back to the full resolution of the screen.
+ y = (y * resolution_y) / (resolution_y - (bottom_ + top_));
+ }
+
+ // Set the modified coordinate back to the event.
+ if (event->root_location() == event->location()) {
+ // Usually those will be equal,
+ // if not, I am not sure what the correct value should be.
+ event->set_root_location(gfx::Point(x, y));
+ }
+ event->set_location(gfx::Point(x, y));
+#endif // defined(USE_XI2_MT)
+ }
+
+ private:
+ // Overridden from base::MessagePumpObserver:
+ virtual base::EventStatus WillProcessEvent(
+ const base::NativeEvent& event) OVERRIDE {
+#if defined(USE_XI2_MT)
+ if (event->type == GenericEvent &&
+ (event->xgeneric.evtype == XI_TouchBegin ||
+ event->xgeneric.evtype == XI_TouchUpdate ||
+ event->xgeneric.evtype == XI_TouchEnd)) {
+ XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(event->xcookie.data);
+ xievent->event = xievent->root;
+ xievent->event_x = xievent->root_x;
+ xievent->event_y = xievent->root_y;
+ }
+#endif // defined(USE_XI2_MT)
+ return base::EVENT_CONTINUE;
+ }
+
+ virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE {
+ }
+
+ // The difference in screen's native resolution pixels between
+ // the border of the touchscreen and the border of the screen,
+ // aka bezel sizes.
+ int left_;
+ int right_;
+ int top_;
+ int bottom_;
+
+ DISALLOW_COPY_AND_ASSIGN(TouchEventCalibrate);
+};
+
+} // namespace internal
+
+////////////////////////////////////////////////////////////////////////////////
+// RootWindowHostX11::MouseMoveFilter filters out the move events that
+// jump back and forth between two points. This happens when sub pixel mouse
+// move is enabled and mouse move events could be jumping between two neighbor
+// pixels, e.g. move(0,0), move(1,0), move(0,0), move(1,0) and on and on.
+// The filtering is done by keeping track of the last two event locations and
+// provides a Filter method to find out whether a mouse event is in a different
+// location and should be processed.
+
+class RootWindowHostX11::MouseMoveFilter {
+ public:
+ MouseMoveFilter() : insert_index_(0) {
+ for (size_t i = 0; i < kMaxEvents; ++i) {
+ const int int_max = std::numeric_limits<int>::max();
+ recent_locations_[i] = gfx::Point(int_max, int_max);
+ }
+ }
+ ~MouseMoveFilter() {}
+
+ // Returns true if |event| is known and should be ignored.
+ bool Filter(const base::NativeEvent& event) {
+ const gfx::Point& location = ui::EventLocationFromNative(event);
+ for (size_t i = 0; i < kMaxEvents; ++i) {
+ if (location == recent_locations_[i])
+ return true;
+ }
+
+ recent_locations_[insert_index_] = location;
+ insert_index_ = (insert_index_ + 1) % kMaxEvents;
+ return false;
+ }
+
+ private:
+ static const size_t kMaxEvents = 2;
+
+ gfx::Point recent_locations_[kMaxEvents];
+ size_t insert_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(MouseMoveFilter);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// RootWindowHostX11
+
+RootWindowHostX11::RootWindowHostX11(const gfx::Rect& bounds)
+ : delegate_(NULL),
+ xdisplay_(base::MessagePumpAuraX11::GetDefaultXDisplay()),
+ xwindow_(0),
+ x_root_window_(DefaultRootWindow(xdisplay_)),
+ current_cursor_(ui::kCursorNull),
+ window_mapped_(false),
+ bounds_(bounds),
+ is_internal_display_(false),
+ focus_when_shown_(false),
+ touch_calibrate_(new internal::TouchEventCalibrate),
+ mouse_move_filter_(new MouseMoveFilter),
+ atom_cache_(xdisplay_, kAtomsToCache) {
+ XSetWindowAttributes swa;
+ memset(&swa, 0, sizeof(swa));
+ swa.background_pixmap = None;
+ swa.override_redirect = default_override_redirect;
+ xwindow_ = XCreateWindow(
+ xdisplay_, x_root_window_,
+ bounds.x(), bounds.y(), bounds.width(), bounds.height(),
+ 0, // border width
+ CopyFromParent, // depth
+ InputOutput,
+ CopyFromParent, // visual
+ CWBackPixmap | CWOverrideRedirect,
+ &swa);
+ base::MessagePumpAuraX11::Current()->AddDispatcherForWindow(this, xwindow_);
+ base::MessagePumpAuraX11::Current()->AddDispatcherForRootWindow(this);
+
+ long event_mask = ButtonPressMask | ButtonReleaseMask | FocusChangeMask |
+ KeyPressMask | KeyReleaseMask |
+ EnterWindowMask | LeaveWindowMask |
+ ExposureMask | VisibilityChangeMask |
+ StructureNotifyMask | PropertyChangeMask |
+ PointerMotionMask;
+ XSelectInput(xdisplay_, xwindow_, event_mask);
+ XFlush(xdisplay_);
+
+ if (base::MessagePumpForUI::HasXInput2())
+ ui::TouchFactory::GetInstance()->SetupXI2ForXWindow(xwindow_);
+
+ SelectEventsForRootWindow();
+
+ // Get the initial size of the X root window.
+ XWindowAttributes attrs;
+ XGetWindowAttributes(xdisplay_, x_root_window_, &attrs);
+ x_root_bounds_.SetRect(attrs.x, attrs.y, attrs.width, attrs.height);
+
+ // TODO(erg): We currently only request window deletion events. We also
+ // should listen for activation events and anything else that GTK+ listens
+ // for, and do something useful.
+ ::Atom protocols[2];
+ protocols[0] = atom_cache_.GetAtom("WM_DELETE_WINDOW");
+ protocols[1] = atom_cache_.GetAtom("_NET_WM_PING");
+ XSetWMProtocols(xdisplay_, xwindow_, protocols, 2);
+
+ // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with
+ // the desktop environment.
+ XSetWMProperties(xdisplay_, xwindow_, NULL, NULL, NULL, 0, NULL, NULL, NULL);
+
+ // Likewise, the X server needs to know this window's pid so it knows which
+ // program to kill if the window hangs.
+ pid_t pid = getpid();
+ XChangeProperty(xdisplay_,
+ xwindow_,
+ atom_cache_.GetAtom("_NET_WM_PID"),
+ XA_CARDINAL,
+ 32,
+ PropModeReplace,
+ reinterpret_cast<unsigned char*>(&pid), 1);
+
+ XRRSelectInput(xdisplay_, x_root_window_,
+ RRScreenChangeNotifyMask | RROutputChangeNotifyMask);
+ Env::GetInstance()->AddObserver(this);
+}
+
+RootWindowHostX11::~RootWindowHostX11() {
+ Env::GetInstance()->RemoveObserver(this);
+ base::MessagePumpAuraX11::Current()->RemoveDispatcherForRootWindow(this);
+ base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow(xwindow_);
+
+ UnConfineCursor();
+
+ XDestroyWindow(xdisplay_, xwindow_);
+}
+
+bool RootWindowHostX11::Dispatch(const base::NativeEvent& event) {
+ XEvent* xev = event;
+
+ if (FindEventTarget(event) == x_root_window_)
+ return DispatchEventForRootWindow(event);
+
+ switch (xev->type) {
+ case EnterNotify: {
+ ui::MouseEvent mouse_event(xev);
+ // EnterNotify creates ET_MOUSE_MOVE. Mark as synthesized as this is not
+ // real mouse move event.
+ mouse_event.set_flags(mouse_event.flags() | ui::EF_IS_SYNTHESIZED);
+ TranslateAndDispatchMouseEvent(&mouse_event);
+ break;
+ }
+ case LeaveNotify: {
+ ui::MouseEvent mouse_event(xev);
+ TranslateAndDispatchMouseEvent(&mouse_event);
+ break;
+ }
+ case Expose: {
+ gfx::Rect damage_rect(xev->xexpose.x, xev->xexpose.y,
+ xev->xexpose.width, xev->xexpose.height);
+ delegate_->AsRootWindow()->ScheduleRedrawRect(damage_rect);
+ break;
+ }
+ case KeyPress: {
+ ui::KeyEvent keydown_event(xev, false);
+ delegate_->OnHostKeyEvent(&keydown_event);
+ break;
+ }
+ case KeyRelease: {
+ ui::KeyEvent keyup_event(xev, false);
+ delegate_->OnHostKeyEvent(&keyup_event);
+ break;
+ }
+ case ButtonPress: {
+ if (static_cast<int>(xev->xbutton.button) == kBackMouseButton ||
+ static_cast<int>(xev->xbutton.button) == kForwardMouseButton) {
+ client::UserActionClient* gesture_client =
+ client::GetUserActionClient(delegate_->AsRootWindow());
+ if (gesture_client) {
+ gesture_client->OnUserAction(
+ static_cast<int>(xev->xbutton.button) == kBackMouseButton ?
+ client::UserActionClient::BACK :
+ client::UserActionClient::FORWARD);
+ }
+ break;
+ }
+ } // fallthrough
+ case ButtonRelease: {
+ ui::MouseEvent mouseev(xev);
+ TranslateAndDispatchMouseEvent(&mouseev);
+ break;
+ }
+ case FocusOut:
+ if (xev->xfocus.mode != NotifyGrab)
+ delegate_->OnHostLostWindowCapture();
+ break;
+ case ConfigureNotify: {
+ DCHECK_EQ(xwindow_, xev->xconfigure.event);
+ DCHECK_EQ(xwindow_, xev->xconfigure.window);
+ // It's possible that the X window may be resized by some other means
+ // than from within aura (e.g. the X window manager can change the
+ // size). Make sure the root window size is maintained properly.
+ gfx::Rect bounds(xev->xconfigure.x, xev->xconfigure.y,
+ xev->xconfigure.width, xev->xconfigure.height);
+ bool size_changed = bounds_.size() != bounds.size();
+ bool origin_changed = bounds_.origin() != bounds.origin();
+ bounds_ = bounds;
+ UpdateIsInternalDisplay();
+ // Always update barrier and mouse location because |bounds_| might
+ // have already been updated in |SetBounds|.
+ if (pointer_barriers_) {
+ UnConfineCursor();
+ ConfineCursorToRootWindow();
+ }
+ if (size_changed)
+ delegate_->OnHostResized(bounds.size());
+ if (origin_changed)
+ delegate_->OnHostMoved(bounds_.origin());
+ break;
+ }
+ case GenericEvent:
+ DispatchXI2Event(event);
+ break;
+ case MapNotify: {
+ // If there's no window manager running, we need to assign the X input
+ // focus to our host window.
+ if (!IsWindowManagerPresent() && focus_when_shown_)
+ XSetInputFocus(xdisplay_, xwindow_, RevertToNone, CurrentTime);
+ break;
+ }
+ case ClientMessage: {
+ Atom message_type = static_cast<Atom>(xev->xclient.data.l[0]);
+ if (message_type == atom_cache_.GetAtom("WM_DELETE_WINDOW")) {
+ // We have received a close message from the window manager.
+ delegate_->AsRootWindow()->OnRootWindowHostCloseRequested();
+ } else if (message_type == atom_cache_.GetAtom("_NET_WM_PING")) {
+ XEvent reply_event = *xev;
+ reply_event.xclient.window = x_root_window_;
+
+ XSendEvent(xdisplay_,
+ reply_event.xclient.window,
+ False,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ &reply_event);
+ }
+ break;
+ }
+ case MappingNotify: {
+ switch (xev->xmapping.request) {
+ case MappingModifier:
+ case MappingKeyboard:
+ XRefreshKeyboardMapping(&xev->xmapping);
+ delegate_->AsRootWindow()->OnKeyboardMappingChanged();
+ break;
+ case MappingPointer:
+ ui::UpdateButtonMap();
+ break;
+ default:
+ NOTIMPLEMENTED() << " Unknown request: " << xev->xmapping.request;
+ break;
+ }
+ break;
+ }
+ case MotionNotify: {
+ // Discard all but the most recent motion event that targets the same
+ // window with unchanged state.
+ XEvent last_event;
+ while (XPending(xev->xany.display)) {
+ XEvent next_event;
+ XPeekEvent(xev->xany.display, &next_event);
+ if (next_event.type == MotionNotify &&
+ next_event.xmotion.window == xev->xmotion.window &&
+ next_event.xmotion.subwindow == xev->xmotion.subwindow &&
+ next_event.xmotion.state == xev->xmotion.state) {
+ XNextEvent(xev->xany.display, &last_event);
+ xev = &last_event;
+ } else {
+ break;
+ }
+ }
+
+ ui::MouseEvent mouseev(xev);
+ TranslateAndDispatchMouseEvent(&mouseev);
+ break;
+ }
+ }
+ return true;
+}
+
+void RootWindowHostX11::SetDelegate(RootWindowHostDelegate* delegate) {
+ delegate_ = delegate;
+}
+
+RootWindow* RootWindowHostX11::GetRootWindow() {
+ return delegate_->AsRootWindow();
+}
+
+gfx::AcceleratedWidget RootWindowHostX11::GetAcceleratedWidget() {
+ return xwindow_;
+}
+
+void RootWindowHostX11::Show() {
+ if (!window_mapped_) {
+ // Before we map the window, set size hints. Otherwise, some window managers
+ // will ignore toplevel XMoveWindow commands.
+ XSizeHints size_hints;
+ size_hints.flags = PPosition | PWinGravity;
+ size_hints.x = bounds_.x();
+ size_hints.y = bounds_.y();
+ // Set StaticGravity so that the window position is not affected by the
+ // frame width when running with window manager.
+ size_hints.win_gravity = StaticGravity;
+ XSetWMNormalHints(xdisplay_, xwindow_, &size_hints);
+
+ XMapWindow(xdisplay_, xwindow_);
+
+ // We now block until our window is mapped. Some X11 APIs will crash and
+ // burn if passed |xwindow_| before the window is mapped, and XMapWindow is
+ // asynchronous.
+ base::MessagePumpAuraX11::Current()->BlockUntilWindowMapped(xwindow_);
+ window_mapped_ = true;
+ }
+}
+
+void RootWindowHostX11::Hide() {
+ if (window_mapped_) {
+ XWithdrawWindow(xdisplay_, xwindow_, 0);
+ window_mapped_ = false;
+ }
+}
+
+void RootWindowHostX11::ToggleFullScreen() {
+ NOTIMPLEMENTED();
+}
+
+gfx::Rect RootWindowHostX11::GetBounds() const {
+ return bounds_;
+}
+
+void RootWindowHostX11::SetBounds(const gfx::Rect& bounds) {
+ // Even if the host window's size doesn't change, aura's root window
+ // size, which is in DIP, changes when the scale changes.
+ float current_scale = delegate_->GetDeviceScaleFactor();
+ float new_scale = gfx::Screen::GetScreenFor(delegate_->AsRootWindow())->
+ GetDisplayNearestWindow(delegate_->AsRootWindow()).device_scale_factor();
+ bool origin_changed = bounds_.origin() != bounds.origin();
+ bool size_changed = bounds_.size() != bounds.size();
+ XWindowChanges changes = {0};
+ unsigned value_mask = 0;
+
+ if (size_changed) {
+ changes.width = bounds.width();
+ changes.height = bounds.height();
+ value_mask = CWHeight | CWWidth;
+ }
+
+ if (origin_changed) {
+ changes.x = bounds.x();
+ changes.y = bounds.y();
+ value_mask |= CWX | CWY;
+ }
+ if (value_mask)
+ XConfigureWindow(xdisplay_, xwindow_, value_mask, &changes);
+
+ // Assume that the resize will go through as requested, which should be the
+ // case if we're running without a window manager. If there's a window
+ // manager, it can modify or ignore the request, but (per ICCCM) we'll get a
+ // (possibly synthetic) ConfigureNotify about the actual size and correct
+ // |bounds_| later.
+ bounds_ = bounds;
+ UpdateIsInternalDisplay();
+ if (origin_changed)
+ delegate_->OnHostMoved(bounds.origin());
+ if (size_changed || current_scale != new_scale) {
+ delegate_->OnHostResized(bounds.size());
+ } else {
+ delegate_->AsRootWindow()->SchedulePaintInRect(
+ delegate_->AsRootWindow()->bounds());
+ }
+}
+
+gfx::Insets RootWindowHostX11::GetInsets() const {
+ return insets_;
+}
+
+void RootWindowHostX11::SetInsets(const gfx::Insets& insets) {
+ insets_ = insets;
+ if (pointer_barriers_) {
+ UnConfineCursor();
+ ConfineCursorToRootWindow();
+ }
+}
+
+gfx::Point RootWindowHostX11::GetLocationOnNativeScreen() const {
+ return bounds_.origin();
+}
+
+void RootWindowHostX11::SetCapture() {
+ // TODO(oshima): Grab x input.
+}
+
+void RootWindowHostX11::ReleaseCapture() {
+ // TODO(oshima): Release x input.
+}
+
+void RootWindowHostX11::SetCursor(gfx::NativeCursor cursor) {
+ if (cursor == current_cursor_)
+ return;
+ current_cursor_ = cursor;
+ SetCursorInternal(cursor);
+}
+
+bool RootWindowHostX11::QueryMouseLocation(gfx::Point* location_return) {
+ client::CursorClient* cursor_client =
+ client::GetCursorClient(GetRootWindow());
+ if (cursor_client && !cursor_client->IsMouseEventsEnabled()) {
+ *location_return = gfx::Point(0, 0);
+ return false;
+ }
+
+ ::Window root_return, child_return;
+ int root_x_return, root_y_return, win_x_return, win_y_return;
+ unsigned int mask_return;
+ XQueryPointer(xdisplay_,
+ xwindow_,
+ &root_return,
+ &child_return,
+ &root_x_return, &root_y_return,
+ &win_x_return, &win_y_return,
+ &mask_return);
+ *location_return = gfx::Point(max(0, min(bounds_.width(), win_x_return)),
+ max(0, min(bounds_.height(), win_y_return)));
+ return (win_x_return >= 0 && win_x_return < bounds_.width() &&
+ win_y_return >= 0 && win_y_return < bounds_.height());
+}
+
+bool RootWindowHostX11::ConfineCursorToRootWindow() {
+#if XFIXES_MAJOR >= 5
+ DCHECK(!pointer_barriers_.get());
+ if (pointer_barriers_)
+ return false;
+ pointer_barriers_.reset(new XID[4]);
+ gfx::Rect bounds(bounds_);
+ bounds.Inset(insets_);
+ // Horizontal, top barriers.
+ pointer_barriers_[0] = XFixesCreatePointerBarrier(
+ xdisplay_, x_root_window_,
+ bounds.x(), bounds.y(), bounds.right(), bounds.y(),
+ BarrierPositiveY,
+ 0, XIAllDevices);
+ // Horizontal, bottom barriers.
+ pointer_barriers_[1] = XFixesCreatePointerBarrier(
+ xdisplay_, x_root_window_,
+ bounds.x(), bounds.bottom(), bounds.right(), bounds.bottom(),
+ BarrierNegativeY,
+ 0, XIAllDevices);
+ // Vertical, left barriers.
+ pointer_barriers_[2] = XFixesCreatePointerBarrier(
+ xdisplay_, x_root_window_,
+ bounds.x(), bounds.y(), bounds.x(), bounds.bottom(),
+ BarrierPositiveX,
+ 0, XIAllDevices);
+ // Vertical, right barriers.
+ pointer_barriers_[3] = XFixesCreatePointerBarrier(
+ xdisplay_, x_root_window_,
+ bounds.right(), bounds.y(), bounds.right(), bounds.bottom(),
+ BarrierNegativeX,
+ 0, XIAllDevices);
+#endif
+ return true;
+}
+
+void RootWindowHostX11::UnConfineCursor() {
+#if XFIXES_MAJOR >= 5
+ if (pointer_barriers_) {
+ XFixesDestroyPointerBarrier(xdisplay_, pointer_barriers_[0]);
+ XFixesDestroyPointerBarrier(xdisplay_, pointer_barriers_[1]);
+ XFixesDestroyPointerBarrier(xdisplay_, pointer_barriers_[2]);
+ XFixesDestroyPointerBarrier(xdisplay_, pointer_barriers_[3]);
+ pointer_barriers_.reset();
+ }
+#endif
+}
+
+void RootWindowHostX11::OnCursorVisibilityChanged(bool show) {
+ SetCrOSTapPaused(!show);
+}
+
+void RootWindowHostX11::MoveCursorTo(const gfx::Point& location) {
+ XWarpPointer(xdisplay_, None, x_root_window_, 0, 0, 0, 0,
+ bounds_.x() + location.x(),
+ bounds_.y() + location.y());
+}
+
+void RootWindowHostX11::SetFocusWhenShown(bool focus_when_shown) {
+ static const char* k_NET_WM_USER_TIME = "_NET_WM_USER_TIME";
+ focus_when_shown_ = focus_when_shown;
+ if (IsWindowManagerPresent() && !focus_when_shown_) {
+ ui::SetIntProperty(xwindow_,
+ k_NET_WM_USER_TIME,
+ k_NET_WM_USER_TIME,
+ 0);
+ }
+}
+
+void RootWindowHostX11::PostNativeEvent(
+ const base::NativeEvent& native_event) {
+ DCHECK(xwindow_);
+ DCHECK(xdisplay_);
+ XEvent xevent = *native_event;
+ xevent.xany.display = xdisplay_;
+ xevent.xany.window = xwindow_;
+
+ switch (xevent.type) {
+ case EnterNotify:
+ case LeaveNotify:
+ case MotionNotify:
+ case KeyPress:
+ case KeyRelease:
+ case ButtonPress:
+ case ButtonRelease: {
+ // The fields used below are in the same place for all of events
+ // above. Using xmotion from XEvent's unions to avoid repeating
+ // the code.
+ xevent.xmotion.root = x_root_window_;
+ xevent.xmotion.time = CurrentTime;
+
+ gfx::Point point(xevent.xmotion.x, xevent.xmotion.y);
+ delegate_->AsRootWindow()->ConvertPointToNativeScreen(&point);
+ xevent.xmotion.x_root = point.x();
+ xevent.xmotion.y_root = point.y();
+ }
+ default:
+ break;
+ }
+ XSendEvent(xdisplay_, xwindow_, False, 0, &xevent);
+}
+
+void RootWindowHostX11::OnDeviceScaleFactorChanged(
+ float device_scale_factor) {
+}
+
+void RootWindowHostX11::PrepareForShutdown() {
+ base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow(xwindow_);
+}
+
+void RootWindowHostX11::OnWindowInitialized(Window* window) {
+}
+
+void RootWindowHostX11::OnRootWindowInitialized(RootWindow* root_window) {
+ // UpdateIsInternalDisplay relies on:
+ // 1. delegate_ pointing to RootWindow - available after SetDelegate.
+ // 2. RootWindow's kDisplayIdKey property set - available by the time
+ // RootWindow::Init is called.
+ // (set in DisplayManager::CreateRootWindowForDisplay)
+ // Ready when NotifyRootWindowInitialized is called from RootWindow::Init.
+ if (!delegate_ || root_window != GetRootWindow())
+ return;
+ UpdateIsInternalDisplay();
+
+ // We have to enable Tap-to-click by default because the cursor is set to
+ // visible in Shell::InitRootWindowController.
+ SetCrOSTapPaused(false);
+}
+
+bool RootWindowHostX11::DispatchEventForRootWindow(
+ const base::NativeEvent& event) {
+ switch (event->type) {
+ case ConfigureNotify:
+ DCHECK_EQ(x_root_window_, event->xconfigure.event);
+ x_root_bounds_.SetRect(event->xconfigure.x, event->xconfigure.y,
+ event->xconfigure.width, event->xconfigure.height);
+ break;
+
+ case GenericEvent:
+ DispatchXI2Event(event);
+ break;
+ }
+
+ return true;
+}
+
+void RootWindowHostX11::DispatchXI2Event(const base::NativeEvent& event) {
+ ui::TouchFactory* factory = ui::TouchFactory::GetInstance();
+ XEvent* xev = event;
+ if (!factory->ShouldProcessXI2Event(xev))
+ return;
+
+ TRACE_EVENT1("input", "RootWindowHostX11::DispatchXI2Event",
+ "event_latency_us",
+ (ui::EventTimeForNow() - ui::EventTimeFromNative(event)).
+ InMicroseconds());
+
+ ui::EventType type = ui::EventTypeFromNative(xev);
+ XEvent last_event;
+ int num_coalesced = 0;
+
+ switch (type) {
+ case ui::ET_TOUCH_MOVED:
+ case ui::ET_TOUCH_PRESSED:
+ case ui::ET_TOUCH_CANCELLED:
+ case ui::ET_TOUCH_RELEASED: {
+#if defined(USE_XI2_MT)
+ // Ignore events from the bezel when the side bezel flag is not explicitly
+ // enabled.
+ if (!IsSideBezelsEnabled() &&
+ touch_calibrate_->IsEventOnSideBezels(xev, bounds_)) {
+ break;
+ }
+#endif // defined(USE_XI2_MT)
+ ui::TouchEvent touchev(xev);
+#if defined(OS_CHROMEOS)
+ if (base::chromeos::IsRunningOnChromeOS()) {
+ if (!bounds_.Contains(touchev.location()))
+ break;
+ // X maps the touch-surface to the size of the X root-window.
+ // In multi-monitor setup, Coordinate Transformation Matrix
+ // repositions the touch-surface onto part of X root-window
+ // containing aura root-window corresponding to the touchscreen.
+ // However, if aura root-window has non-zero origin,
+ // we need to relocate the event into aura root-window coordinates.
+ touchev.Relocate(bounds_.origin());
+#if defined(USE_XI2_MT)
+ if (is_internal_display_)
+ touch_calibrate_->Calibrate(&touchev, bounds_);
+#endif // defined(USE_XI2_MT)
+ }
+#endif // defined(OS_CHROMEOS)
+ delegate_->OnHostTouchEvent(&touchev);
+ break;
+ }
+ case ui::ET_MOUSE_MOVED:
+ case ui::ET_MOUSE_DRAGGED:
+ case ui::ET_MOUSE_PRESSED:
+ case ui::ET_MOUSE_RELEASED:
+ case ui::ET_MOUSE_ENTERED:
+ case ui::ET_MOUSE_EXITED: {
+ if (type == ui::ET_MOUSE_MOVED || type == ui::ET_MOUSE_DRAGGED) {
+ // If this is a motion event, we want to coalesce all pending motion
+ // events that are at the top of the queue.
+ num_coalesced = ui::CoalescePendingMotionEvents(xev, &last_event);
+ if (num_coalesced > 0)
+ xev = &last_event;
+
+ if (mouse_move_filter_ && mouse_move_filter_->Filter(xev))
+ break;
+ } else if (type == ui::ET_MOUSE_PRESSED ||
+ type == ui::ET_MOUSE_RELEASED) {
+ XIDeviceEvent* xievent =
+ static_cast<XIDeviceEvent*>(xev->xcookie.data);
+ int button = xievent->detail;
+ if (button == kBackMouseButton || button == kForwardMouseButton) {
+ if (type == ui::ET_MOUSE_RELEASED)
+ break;
+ client::UserActionClient* gesture_client =
+ client::GetUserActionClient(delegate_->AsRootWindow());
+ if (gesture_client) {
+ bool reverse_direction =
+ ui::IsTouchpadEvent(xev) && ui::IsNaturalScrollEnabled();
+ gesture_client->OnUserAction(
+ (button == kBackMouseButton && !reverse_direction) ||
+ (button == kForwardMouseButton && reverse_direction) ?
+ client::UserActionClient::BACK :
+ client::UserActionClient::FORWARD);
+ }
+ break;
+ }
+ }
+ ui::MouseEvent mouseev(xev);
+ TranslateAndDispatchMouseEvent(&mouseev);
+ break;
+ }
+ case ui::ET_MOUSEWHEEL: {
+ ui::MouseWheelEvent mouseev(xev);
+ TranslateAndDispatchMouseEvent(&mouseev);
+ break;
+ }
+ case ui::ET_SCROLL_FLING_START:
+ case ui::ET_SCROLL_FLING_CANCEL:
+ case ui::ET_SCROLL: {
+ ui::ScrollEvent scrollev(xev);
+ delegate_->OnHostScrollEvent(&scrollev);
+ break;
+ }
+ case ui::ET_UMA_DATA:
+ break;
+ case ui::ET_UNKNOWN:
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ // If we coalesced an event we need to free its cookie.
+ if (num_coalesced > 0)
+ XFreeEventData(xev->xgeneric.display, &last_event.xcookie);
+}
+
+bool RootWindowHostX11::IsWindowManagerPresent() {
+ // Per ICCCM 2.8, "Manager Selections", window managers should take ownership
+ // of WM_Sn selections (where n is a screen number).
+ return XGetSelectionOwner(
+ xdisplay_, atom_cache_.GetAtom("WM_S0")) != None;
+}
+
+void RootWindowHostX11::SetCursorInternal(gfx::NativeCursor cursor) {
+ XDefineCursor(xdisplay_, xwindow_, cursor.platform());
+}
+
+void RootWindowHostX11::TranslateAndDispatchMouseEvent(
+ ui::MouseEvent* event) {
+ RootWindow* root_window = GetRootWindow();
+ client::ScreenPositionClient* screen_position_client =
+ GetScreenPositionClient(root_window);
+ gfx::Rect local(bounds_.size());
+
+ if (screen_position_client && !local.Contains(event->location())) {
+ gfx::Point location(event->location());
+ // In order to get the correct point in screen coordinates
+ // during passive grab, we first need to find on which host window
+ // the mouse is on, and find out the screen coordinates on that
+ // host window, then convert it back to this host window's coordinate.
+ screen_position_client->ConvertHostPointToScreen(root_window, &location);
+ screen_position_client->ConvertPointFromScreen(root_window, &location);
+ root_window->ConvertPointToHost(&location);
+ event->set_location(location);
+ event->set_root_location(location);
+ }
+ delegate_->OnHostMouseEvent(event);
+}
+
+void RootWindowHostX11::UpdateIsInternalDisplay() {
+ RootWindow* root_window = GetRootWindow();
+ gfx::Screen* screen = gfx::Screen::GetScreenFor(root_window);
+ gfx::Display display = screen->GetDisplayNearestWindow(root_window);
+ is_internal_display_ = display.IsInternal();
+}
+
+void RootWindowHostX11::SetCrOSTapPaused(bool state) {
+#if defined(OS_CHROMEOS)
+ // Temporarily pause tap-to-click when the cursor is hidden.
+ Atom prop = atom_cache_.GetAtom("Tap Paused");
+ unsigned char value = state;
+ XIDeviceList dev_list =
+ ui::DeviceListCacheX::GetInstance()->GetXI2DeviceList(xdisplay_);
+
+ // Only slave pointer devices could possibly have tap-paused property.
+ for (int i = 0; i < dev_list.count; i++) {
+ if (dev_list[i].use == XISlavePointer) {
+ Atom old_type;
+ int old_format;
+ unsigned long old_nvalues, bytes;
+ unsigned char* data;
+ int result = XIGetProperty(xdisplay_, dev_list[i].deviceid, prop, 0, 0,
+ False, AnyPropertyType, &old_type, &old_format,
+ &old_nvalues, &bytes, &data);
+ if (result != Success)
+ continue;
+ XFree(data);
+ XIChangeProperty(xdisplay_, dev_list[i].deviceid, prop, XA_INTEGER, 8,
+ PropModeReplace, &value, 1);
+ }
+ }
+#endif
+}
+
+// static
+RootWindowHost* RootWindowHost::Create(const gfx::Rect& bounds) {
+ return new RootWindowHostX11(bounds);
+}
+
+// static
+gfx::Size RootWindowHost::GetNativeScreenSize() {
+ ::Display* xdisplay = base::MessagePumpAuraX11::GetDefaultXDisplay();
+ return gfx::Size(DisplayWidth(xdisplay, 0), DisplayHeight(xdisplay, 0));
+}
+
+namespace test {
+
+void SetUseOverrideRedirectWindowByDefault(bool override_redirect) {
+ default_override_redirect = override_redirect;
+}
+
+} // namespace test
+} // namespace aura
diff --git a/chromium/ui/aura/root_window_host_x11.h b/chromium/ui/aura/root_window_host_x11.h
new file mode 100644
index 00000000000..ac9cc74933e
--- /dev/null
+++ b/chromium/ui/aura/root_window_host_x11.h
@@ -0,0 +1,153 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_ROOT_WINDOW_HOST_X11_H_
+#define UI_AURA_ROOT_WINDOW_HOST_X11_H_
+
+#include <X11/Xlib.h>
+
+#include <vector>
+
+// Get rid of a macro from Xlib.h that conflicts with Aura's RootWindow class.
+#undef RootWindow
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "ui/aura/aura_export.h"
+#include "ui/aura/env_observer.h"
+#include "ui/aura/root_window_host.h"
+#include "ui/base/x/x11_atom_cache.h"
+#include "ui/base/x/x11_util.h"
+#include "ui/gfx/insets.h"
+#include "ui/gfx/rect.h"
+
+namespace ui {
+class MouseEvent;
+}
+
+namespace aura {
+
+namespace internal {
+class TouchEventCalibrate;
+}
+
+class RootWindowHostX11 : public RootWindowHost,
+ public base::MessageLoop::Dispatcher,
+ public EnvObserver {
+ public:
+ explicit RootWindowHostX11(const gfx::Rect& bounds);
+ virtual ~RootWindowHostX11();
+
+ // Overridden from Dispatcher overrides:
+ virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE;
+
+ // RootWindowHost Overrides.
+ virtual void SetDelegate(RootWindowHostDelegate* delegate) OVERRIDE;
+ virtual RootWindow* GetRootWindow() OVERRIDE;
+ virtual gfx::AcceleratedWidget GetAcceleratedWidget() OVERRIDE;
+ virtual void Show() OVERRIDE;
+ virtual void Hide() OVERRIDE;
+ virtual void ToggleFullScreen() OVERRIDE;
+ virtual gfx::Rect GetBounds() const OVERRIDE;
+ virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE;
+ virtual gfx::Insets GetInsets() const OVERRIDE;
+ virtual void SetInsets(const gfx::Insets& insets) OVERRIDE;
+ virtual gfx::Point GetLocationOnNativeScreen() const OVERRIDE;
+ virtual void SetCapture() OVERRIDE;
+ virtual void ReleaseCapture() OVERRIDE;
+ virtual void SetCursor(gfx::NativeCursor cursor_type) OVERRIDE;
+ virtual bool QueryMouseLocation(gfx::Point* location_return) OVERRIDE;
+ virtual bool ConfineCursorToRootWindow() OVERRIDE;
+ virtual void UnConfineCursor() OVERRIDE;
+ virtual void OnCursorVisibilityChanged(bool show) OVERRIDE;
+ virtual void MoveCursorTo(const gfx::Point& location) OVERRIDE;
+ virtual void SetFocusWhenShown(bool focus_when_shown) OVERRIDE;
+ virtual void PostNativeEvent(const base::NativeEvent& event) OVERRIDE;
+ virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE;
+ virtual void PrepareForShutdown() OVERRIDE;
+
+ // EnvObserver overrides.
+ virtual void OnWindowInitialized(Window* window) OVERRIDE;
+ virtual void OnRootWindowInitialized(RootWindow* root_window) OVERRIDE;
+ private:
+ class MouseMoveFilter;
+
+ bool DispatchEventForRootWindow(const base::NativeEvent& event);
+
+ // Dispatches XI2 events. Note that some events targetted for the X root
+ // window are dispatched to the aura root window (e.g. touch events after
+ // calibration).
+ void DispatchXI2Event(const base::NativeEvent& event);
+
+ // Returns true if there's an X window manager present... in most cases. Some
+ // window managers (notably, ion3) don't implement enough of ICCCM for us to
+ // detect that they're there.
+ bool IsWindowManagerPresent();
+
+ // Sets the cursor on |xwindow_| to |cursor|. Does not check or update
+ // |current_cursor_|.
+ void SetCursorInternal(gfx::NativeCursor cursor);
+
+ // Translates the native mouse location into screen coordinates and and
+ // dispatches the event to RootWindowHostDelegate.
+ void TranslateAndDispatchMouseEvent(ui::MouseEvent* event);
+
+ // Update is_internal_display_ based on delegate_ state
+ void UpdateIsInternalDisplay();
+
+ // Set the CrOS touchpad "tap paused" property. It is used to temporarily
+ // turn off the Tap-to-click feature when the mouse pointer is invisible.
+ void SetCrOSTapPaused(bool state);
+
+ RootWindowHostDelegate* delegate_;
+
+ // The display and the native X window hosting the root window.
+ Display* xdisplay_;
+ ::Window xwindow_;
+
+ // The native root window.
+ ::Window x_root_window_;
+
+ // Current Aura cursor.
+ gfx::NativeCursor current_cursor_;
+
+ // Is the window mapped to the screen?
+ bool window_mapped_;
+
+ // The bounds of |xwindow_|.
+ gfx::Rect bounds_;
+
+ // The insets that specifies the effective area within the |window_|.
+ gfx::Insets insets_;
+
+ // The bounds of |x_root_window_|.
+ gfx::Rect x_root_bounds_;
+
+ // True if the root host resides on the internal display
+ bool is_internal_display_;
+
+ // True if the window should be focused when the window is shown.
+ bool focus_when_shown_;
+
+ scoped_ptr<XID[]> pointer_barriers_;
+
+ scoped_ptr<internal::TouchEventCalibrate> touch_calibrate_;
+
+ scoped_ptr<MouseMoveFilter> mouse_move_filter_;
+
+ ui::X11AtomCache atom_cache_;
+
+ DISALLOW_COPY_AND_ASSIGN(RootWindowHostX11);
+};
+
+namespace test {
+
+// Set the default value of the override redirect flag used to
+// create a X window for RootWindowHostX11.
+AURA_EXPORT void SetUseOverrideRedirectWindowByDefault(bool override_redirect);
+
+} // namespace test
+} // namespace aura
+
+#endif // UI_AURA_ROOT_WINDOW_HOST_X11_H_
diff --git a/chromium/ui/aura/root_window_mac.h b/chromium/ui/aura/root_window_mac.h
new file mode 100644
index 00000000000..13a044c21d2
--- /dev/null
+++ b/chromium/ui/aura/root_window_mac.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_ROOT_WINDOW_MAC_H_
+#define UI_AURA_ROOT_WINDOW_MAC_H_
+
+#import <Cocoa/Cocoa.h>
+
+namespace aura {
+class RootWindowHostMacDelegate;
+} // aura
+
+// RootWindow routes NSWindow events back to the RootWindowHost for dispatch
+// to the Aura event handling system.
+@interface RootWindowMac : NSWindow {
+ @private
+ // Weak. May be NULL. The host delegate acts as a conduit for event routing
+ // back to the host.
+ aura::RootWindowHostMacDelegate* hostDelegate_;
+}
+
+// Designated initializer.
+- (id)initWithContentRect:(NSRect)contentRect
+ styleMask:(NSUInteger)windowStyle
+ backing:(NSBackingStoreType)bufferingType
+ defer:(BOOL)deferCreation;
+
+// Sets the |hostDelegate_|
+- (void)setHostDelegate:(aura::RootWindowHostMacDelegate*)hostDelegate;
+
+// Overrides main event dispatch to route NSWindow events to host delegate.
+- (void)sendEvent:(NSEvent*)event;
+
+@end
+
+#endif // UI_AURA_ROOT_WINDOW_MAC_H_
diff --git a/chromium/ui/aura/root_window_mac.mm b/chromium/ui/aura/root_window_mac.mm
new file mode 100644
index 00000000000..32e42b7ad9d
--- /dev/null
+++ b/chromium/ui/aura/root_window_mac.mm
@@ -0,0 +1,35 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ui/aura/root_window_mac.h"
+
+#include "ui/aura/root_window_host_mac.h"
+
+@implementation RootWindowMac
+
+- (id)initWithContentRect:(NSRect)contentRect
+ styleMask:(NSUInteger)windowStyle
+ backing:(NSBackingStoreType)bufferingType
+ defer:(BOOL)deferCreation {
+ if ((self = [super initWithContentRect:contentRect
+ styleMask:windowStyle
+ backing:bufferingType
+ defer:deferCreation])) {
+ hostDelegate_ = NULL;
+ }
+ return self;
+}
+
+- (void)setHostDelegate:(aura::RootWindowHostMacDelegate*)hostDelegate {
+ hostDelegate_ = hostDelegate;
+}
+
+- (void)sendEvent:(NSEvent*)event {
+ // Allow both the Cocoa machinery and the Aura machinery to handle the event.
+ [super sendEvent:event];
+ if (hostDelegate_)
+ hostDelegate_->SendEvent(event);
+}
+
+@end
diff --git a/chromium/ui/aura/root_window_observer.h b/chromium/ui/aura/root_window_observer.h
new file mode 100644
index 00000000000..bfb8e65c518
--- /dev/null
+++ b/chromium/ui/aura/root_window_observer.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_ROOT_WINDOW_OBSERVER_H_
+#define UI_AURA_ROOT_WINDOW_OBSERVER_H_
+
+#include "ui/aura/aura_export.h"
+
+namespace gfx {
+class Point;
+class Size;
+}
+
+namespace aura {
+class RootWindow;
+class Window;
+
+class AURA_EXPORT RootWindowObserver {
+ public:
+ // Invoked after the RootWindow's host has been resized.
+ virtual void OnRootWindowHostResized(const RootWindow* root) {}
+
+ // Invoked after the RootWindow's host has been moved on screen.
+ virtual void OnRootWindowHostMoved(const RootWindow* root,
+ const gfx::Point& new_origin) {}
+
+ // Invoked when the native windowing system sends us a request to close our
+ // window.
+ virtual void OnRootWindowHostCloseRequested(const RootWindow* root) {}
+
+ // Invoked when the keyboard mapping has changed.
+ virtual void OnKeyboardMappingChanged(const RootWindow* root) {}
+
+ protected:
+ virtual ~RootWindowObserver() {}
+};
+
+} // namespace aura
+
+#endif // UI_AURA_ROOT_WINDOW_OBSERVER_H_
diff --git a/chromium/ui/aura/root_window_transformer.h b/chromium/ui/aura/root_window_transformer.h
new file mode 100644
index 00000000000..89d67f28e6d
--- /dev/null
+++ b/chromium/ui/aura/root_window_transformer.h
@@ -0,0 +1,45 @@
+// Copyright (c) 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 UI_AURA_ROOT_WINDOW_TRANSFORMER_H_
+#define UI_AURA_ROOT_WINDOW_TRANSFORMER_H_
+
+#include "ui/aura/aura_export.h"
+
+namespace gfx {
+class Insets;
+class Rect;
+class Size;
+class Transform;
+}
+
+namespace aura {
+
+// RootWindowTransformer controls how RootWindow should be placed and
+// transformed inside the host window.
+class AURA_EXPORT RootWindowTransformer {
+ public:
+ virtual ~RootWindowTransformer() {}
+
+ // Returns the transform the root window in DIP.
+ virtual gfx::Transform GetTransform() const = 0;
+
+ // Returns the inverse of the transform above. This method is to
+ // provie an accurate inverse of the transform because the result of
+ // |gfx::Transform::GetInverse| may contains computational error.
+ virtual gfx::Transform GetInverseTransform() const = 0;
+
+ // Returns the root window's bounds for given host window size in DIP.
+ // This is necessary to handle the case where the root window's size
+ // is bigger than the host window. (Screen magnifier for example).
+ virtual gfx::Rect GetRootWindowBounds(const gfx::Size& host_size) const = 0;
+
+ // Returns the insets that specifies the effective area of
+ // the host window.
+ virtual gfx::Insets GetHostInsets() const = 0;
+};
+
+} // namespace aura
+
+#endif // UI_AURA_ROOT_WINDOW_TRANSFORMER_H_
diff --git a/chromium/ui/aura/root_window_unittest.cc b/chromium/ui/aura/root_window_unittest.cc
new file mode 100644
index 00000000000..739de66811f
--- /dev/null
+++ b/chromium/ui/aura/root_window_unittest.cc
@@ -0,0 +1,885 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/root_window.h"
+
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/aura/client/event_client.h"
+#include "ui/aura/env.h"
+#include "ui/aura/focus_manager.h"
+#include "ui/aura/test/aura_test_base.h"
+#include "ui/aura/test/event_generator.h"
+#include "ui/aura/test/test_cursor_client.h"
+#include "ui/aura/test/test_event_handler.h"
+#include "ui/aura/test/test_window_delegate.h"
+#include "ui/aura/test/test_windows.h"
+#include "ui/aura/window_tracker.h"
+#include "ui/base/events/event.h"
+#include "ui/base/events/event_handler.h"
+#include "ui/base/events/event_utils.h"
+#include "ui/base/gestures/gesture_configuration.h"
+#include "ui/base/hit_test.h"
+#include "ui/base/keycodes/keyboard_codes.h"
+#include "ui/gfx/point.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/screen.h"
+#include "ui/gfx/transform.h"
+
+namespace aura {
+namespace {
+
+// A delegate that always returns a non-client component for hit tests.
+class NonClientDelegate : public test::TestWindowDelegate {
+ public:
+ NonClientDelegate()
+ : non_client_count_(0),
+ mouse_event_count_(0),
+ mouse_event_flags_(0x0) {
+ }
+ virtual ~NonClientDelegate() {}
+
+ int non_client_count() const { return non_client_count_; }
+ gfx::Point non_client_location() const { return non_client_location_; }
+ int mouse_event_count() const { return mouse_event_count_; }
+ gfx::Point mouse_event_location() const { return mouse_event_location_; }
+ int mouse_event_flags() const { return mouse_event_flags_; }
+
+ virtual int GetNonClientComponent(const gfx::Point& location) const OVERRIDE {
+ NonClientDelegate* self = const_cast<NonClientDelegate*>(this);
+ self->non_client_count_++;
+ self->non_client_location_ = location;
+ return HTTOPLEFT;
+ }
+ virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
+ mouse_event_count_++;
+ mouse_event_location_ = event->location();
+ mouse_event_flags_ = event->flags();
+ event->SetHandled();
+ }
+
+ private:
+ int non_client_count_;
+ gfx::Point non_client_location_;
+ int mouse_event_count_;
+ gfx::Point mouse_event_location_;
+ int mouse_event_flags_;
+
+ DISALLOW_COPY_AND_ASSIGN(NonClientDelegate);
+};
+
+// A simple event handler that consumes key events.
+class ConsumeKeyHandler : public test::TestEventHandler {
+ public:
+ ConsumeKeyHandler() {}
+ virtual ~ConsumeKeyHandler() {}
+
+ // Overridden from ui::EventHandler:
+ virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE {
+ test::TestEventHandler::OnKeyEvent(event);
+ event->StopPropagation();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ConsumeKeyHandler);
+};
+
+bool IsFocusedWindow(aura::Window* window) {
+ return client::GetFocusClient(window)->GetFocusedWindow() == window;
+}
+
+} // namespace
+
+typedef test::AuraTestBase RootWindowTest;
+
+TEST_F(RootWindowTest, OnHostMouseEvent) {
+ // Create two non-overlapping windows so we don't have to worry about which
+ // is on top.
+ scoped_ptr<NonClientDelegate> delegate1(new NonClientDelegate());
+ scoped_ptr<NonClientDelegate> delegate2(new NonClientDelegate());
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+ gfx::Rect bounds1(100, 200, kWindowWidth, kWindowHeight);
+ gfx::Rect bounds2(300, 400, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> window1(CreateTestWindowWithDelegate(
+ delegate1.get(), -1234, bounds1, root_window()));
+ scoped_ptr<aura::Window> window2(CreateTestWindowWithDelegate(
+ delegate2.get(), -5678, bounds2, root_window()));
+
+ // Send a mouse event to window1.
+ gfx::Point point(101, 201);
+ ui::MouseEvent event1(
+ ui::ET_MOUSE_PRESSED, point, point, ui::EF_LEFT_MOUSE_BUTTON);
+ root_window()->AsRootWindowHostDelegate()->OnHostMouseEvent(&event1);
+
+ // Event was tested for non-client area for the target window.
+ EXPECT_EQ(1, delegate1->non_client_count());
+ EXPECT_EQ(0, delegate2->non_client_count());
+ // The non-client component test was in local coordinates.
+ EXPECT_EQ(gfx::Point(1, 1), delegate1->non_client_location());
+ // Mouse event was received by target window.
+ EXPECT_EQ(1, delegate1->mouse_event_count());
+ EXPECT_EQ(0, delegate2->mouse_event_count());
+ // Event was in local coordinates.
+ EXPECT_EQ(gfx::Point(1, 1), delegate1->mouse_event_location());
+ // Non-client flag was set.
+ EXPECT_TRUE(delegate1->mouse_event_flags() & ui::EF_IS_NON_CLIENT);
+}
+
+TEST_F(RootWindowTest, RepostEvent) {
+ // Test RepostEvent in RootWindow. It only works for Mouse Press.
+ EXPECT_FALSE(Env::GetInstance()->is_mouse_button_down());
+ gfx::Point point(10, 10);
+ ui::MouseEvent event(
+ ui::ET_MOUSE_PRESSED, point, point, ui::EF_LEFT_MOUSE_BUTTON);
+ root_window()->RepostEvent(event);
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(Env::GetInstance()->is_mouse_button_down());
+}
+
+// Check that we correctly track the state of the mouse buttons in response to
+// button press and release events.
+TEST_F(RootWindowTest, MouseButtonState) {
+ EXPECT_FALSE(Env::GetInstance()->is_mouse_button_down());
+
+ gfx::Point location;
+ scoped_ptr<ui::MouseEvent> event;
+
+ // Press the left button.
+ event.reset(new ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED,
+ location,
+ location,
+ ui::EF_LEFT_MOUSE_BUTTON));
+ root_window()->AsRootWindowHostDelegate()->OnHostMouseEvent(event.get());
+ EXPECT_TRUE(Env::GetInstance()->is_mouse_button_down());
+
+ // Additionally press the right.
+ event.reset(new ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED,
+ location,
+ location,
+ ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON));
+ root_window()->AsRootWindowHostDelegate()->OnHostMouseEvent(event.get());
+ EXPECT_TRUE(Env::GetInstance()->is_mouse_button_down());
+
+ // Release the left button.
+ event.reset(new ui::MouseEvent(
+ ui::ET_MOUSE_RELEASED,
+ location,
+ location,
+ ui::EF_RIGHT_MOUSE_BUTTON));
+ root_window()->AsRootWindowHostDelegate()->OnHostMouseEvent(event.get());
+ EXPECT_TRUE(Env::GetInstance()->is_mouse_button_down());
+
+ // Release the right button. We should ignore the Shift-is-down flag.
+ event.reset(new ui::MouseEvent(
+ ui::ET_MOUSE_RELEASED,
+ location,
+ location,
+ ui::EF_SHIFT_DOWN));
+ root_window()->AsRootWindowHostDelegate()->OnHostMouseEvent(event.get());
+ EXPECT_FALSE(Env::GetInstance()->is_mouse_button_down());
+
+ // Press the middle button.
+ event.reset(new ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED,
+ location,
+ location,
+ ui::EF_MIDDLE_MOUSE_BUTTON));
+ root_window()->AsRootWindowHostDelegate()->OnHostMouseEvent(event.get());
+ EXPECT_TRUE(Env::GetInstance()->is_mouse_button_down());
+}
+
+TEST_F(RootWindowTest, TranslatedEvent) {
+ scoped_ptr<Window> w1(test::CreateTestWindowWithDelegate(NULL, 1,
+ gfx::Rect(50, 50, 100, 100), root_window()));
+
+ gfx::Point origin(100, 100);
+ ui::MouseEvent root(ui::ET_MOUSE_PRESSED, origin, origin, 0);
+
+ EXPECT_EQ("100,100", root.location().ToString());
+ EXPECT_EQ("100,100", root.root_location().ToString());
+
+ ui::MouseEvent translated_event(
+ root, static_cast<Window*>(root_window()), w1.get(),
+ ui::ET_MOUSE_ENTERED, root.flags());
+ EXPECT_EQ("50,50", translated_event.location().ToString());
+ EXPECT_EQ("100,100", translated_event.root_location().ToString());
+}
+
+namespace {
+
+class TestEventClient : public client::EventClient {
+ public:
+ static const int kNonLockWindowId = 100;
+ static const int kLockWindowId = 200;
+
+ explicit TestEventClient(RootWindow* root_window)
+ : root_window_(root_window),
+ lock_(false) {
+ client::SetEventClient(root_window_, this);
+ Window* lock_window =
+ test::CreateTestWindowWithBounds(root_window_->bounds(), root_window_);
+ lock_window->set_id(kLockWindowId);
+ Window* non_lock_window =
+ test::CreateTestWindowWithBounds(root_window_->bounds(), root_window_);
+ non_lock_window->set_id(kNonLockWindowId);
+ }
+ virtual ~TestEventClient() {
+ client::SetEventClient(root_window_, NULL);
+ }
+
+ // Starts/stops locking. Locking prevents windows other than those inside
+ // the lock container from receiving events, getting focus etc.
+ void Lock() {
+ lock_ = true;
+ }
+ void Unlock() {
+ lock_ = false;
+ }
+
+ Window* GetLockWindow() {
+ return const_cast<Window*>(
+ static_cast<const TestEventClient*>(this)->GetLockWindow());
+ }
+ const Window* GetLockWindow() const {
+ return root_window_->GetChildById(kLockWindowId);
+ }
+ Window* GetNonLockWindow() {
+ return root_window_->GetChildById(kNonLockWindowId);
+ }
+
+ private:
+ // Overridden from client::EventClient:
+ virtual bool CanProcessEventsWithinSubtree(
+ const Window* window) const OVERRIDE {
+ return lock_ ?
+ window->Contains(GetLockWindow()) || GetLockWindow()->Contains(window) :
+ true;
+ }
+
+ virtual ui::EventTarget* GetToplevelEventTarget() OVERRIDE {
+ return NULL;
+ }
+
+ RootWindow* root_window_;
+ bool lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestEventClient);
+};
+
+} // namespace
+
+TEST_F(RootWindowTest, CanProcessEventsWithinSubtree) {
+ TestEventClient client(root_window());
+ test::TestWindowDelegate d;
+
+ test::TestEventHandler* nonlock_ef = new test::TestEventHandler;
+ test::TestEventHandler* lock_ef = new test::TestEventHandler;
+ client.GetNonLockWindow()->SetEventFilter(nonlock_ef);
+ client.GetLockWindow()->SetEventFilter(lock_ef);
+
+ Window* w1 = test::CreateTestWindowWithBounds(gfx::Rect(10, 10, 20, 20),
+ client.GetNonLockWindow());
+ w1->set_id(1);
+ Window* w2 = test::CreateTestWindowWithBounds(gfx::Rect(30, 30, 20, 20),
+ client.GetNonLockWindow());
+ w2->set_id(2);
+ scoped_ptr<Window> w3(
+ test::CreateTestWindowWithDelegate(&d, 3, gfx::Rect(30, 30, 20, 20),
+ client.GetLockWindow()));
+
+ w1->Focus();
+ EXPECT_TRUE(IsFocusedWindow(w1));
+
+ client.Lock();
+
+ // Since we're locked, the attempt to focus w2 will be ignored.
+ w2->Focus();
+ EXPECT_TRUE(IsFocusedWindow(w1));
+ EXPECT_FALSE(IsFocusedWindow(w2));
+
+ {
+ // Attempting to send a key event to w1 (not in the lock container) should
+ // cause focus to be reset.
+ test::EventGenerator generator(root_window());
+ generator.PressKey(ui::VKEY_SPACE, 0);
+ EXPECT_EQ(NULL, client::GetFocusClient(w1)->GetFocusedWindow());
+ }
+
+ {
+ // Events sent to a window not in the lock container will not be processed.
+ // i.e. never sent to the non-lock container's event filter.
+ test::EventGenerator generator(root_window(), w1);
+ generator.PressLeftButton();
+ EXPECT_EQ(0, nonlock_ef->num_mouse_events());
+
+ // Events sent to a window in the lock container will be processed.
+ test::EventGenerator generator3(root_window(), w3.get());
+ generator3.PressLeftButton();
+ EXPECT_EQ(1, lock_ef->num_mouse_events());
+ }
+
+ // Prevent w3 from being deleted by the hierarchy since its delegate is owned
+ // by this scope.
+ w3->parent()->RemoveChild(w3.get());
+}
+
+TEST_F(RootWindowTest, IgnoreUnknownKeys) {
+ test::TestEventHandler* filter = new ConsumeKeyHandler;
+ root_window()->SetEventFilter(filter); // passes ownership
+
+ ui::KeyEvent unknown_event(ui::ET_KEY_PRESSED, ui::VKEY_UNKNOWN, 0, false);
+ EXPECT_FALSE(root_window()->AsRootWindowHostDelegate()->OnHostKeyEvent(
+ &unknown_event));
+ EXPECT_EQ(0, filter->num_key_events());
+
+ ui::KeyEvent known_event(ui::ET_KEY_PRESSED, ui::VKEY_A, 0, false);
+ EXPECT_TRUE(root_window()->AsRootWindowHostDelegate()->OnHostKeyEvent(
+ &known_event));
+ EXPECT_EQ(1, filter->num_key_events());
+}
+
+// Tests that touch-events that are beyond the bounds of the root-window do get
+// propagated to the event filters correctly with the root as the target.
+TEST_F(RootWindowTest, TouchEventsOutsideBounds) {
+ test::TestEventHandler* filter = new test::TestEventHandler;
+ root_window()->SetEventFilter(filter); // passes ownership
+
+ gfx::Point position = root_window()->bounds().origin();
+ position.Offset(-10, -10);
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED, position, 0, base::TimeDelta());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ EXPECT_EQ(1, filter->num_touch_events());
+
+ position = root_window()->bounds().origin();
+ position.Offset(root_window()->bounds().width() + 10,
+ root_window()->bounds().height() + 10);
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED, position, 0, base::TimeDelta());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ EXPECT_EQ(2, filter->num_touch_events());
+}
+
+// Tests that scroll events are dispatched correctly.
+TEST_F(RootWindowTest, ScrollEventDispatch) {
+ base::TimeDelta now = ui::EventTimeForNow();
+ test::TestEventHandler* filter = new test::TestEventHandler;
+ root_window()->SetEventFilter(filter);
+
+ test::TestWindowDelegate delegate;
+ scoped_ptr<Window> w1(CreateNormalWindow(1, root_window(), &delegate));
+ w1->SetBounds(gfx::Rect(20, 20, 40, 40));
+
+ // A scroll event on the root-window itself is dispatched.
+ ui::ScrollEvent scroll1(ui::ET_SCROLL,
+ gfx::Point(10, 10),
+ now,
+ 0,
+ 0, -10,
+ 0, -10,
+ 2);
+ root_window()->AsRootWindowHostDelegate()->OnHostScrollEvent(&scroll1);
+ EXPECT_EQ(1, filter->num_scroll_events());
+
+ // Scroll event on a window should be dispatched properly.
+ ui::ScrollEvent scroll2(ui::ET_SCROLL,
+ gfx::Point(25, 30),
+ now,
+ 0,
+ -10, 0,
+ -10, 0,
+ 2);
+ root_window()->AsRootWindowHostDelegate()->OnHostScrollEvent(&scroll2);
+ EXPECT_EQ(2, filter->num_scroll_events());
+}
+
+namespace {
+
+// FilterFilter that tracks the types of events it's seen.
+class EventFilterRecorder : public ui::EventHandler {
+ public:
+ typedef std::vector<ui::EventType> Events;
+
+ EventFilterRecorder() {}
+
+ Events& events() { return events_; }
+
+ // ui::EventHandler overrides:
+ virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE {
+ events_.push_back(event->type());
+ }
+
+ virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
+ events_.push_back(event->type());
+ }
+
+ virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE {
+ events_.push_back(event->type());
+ }
+
+ virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE {
+ events_.push_back(event->type());
+ }
+
+ virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
+ events_.push_back(event->type());
+ }
+
+ private:
+ Events events_;
+
+ DISALLOW_COPY_AND_ASSIGN(EventFilterRecorder);
+};
+
+// Converts an EventType to a string.
+std::string EventTypeToString(ui::EventType type) {
+ switch (type) {
+ case ui::ET_TOUCH_RELEASED:
+ return "TOUCH_RELEASED";
+
+ case ui::ET_TOUCH_PRESSED:
+ return "TOUCH_PRESSED";
+
+ case ui::ET_TOUCH_MOVED:
+ return "TOUCH_MOVED";
+
+ case ui::ET_MOUSE_PRESSED:
+ return "MOUSE_PRESSED";
+
+ case ui::ET_MOUSE_DRAGGED:
+ return "MOUSE_DRAGGED";
+
+ case ui::ET_MOUSE_RELEASED:
+ return "MOUSE_RELEASED";
+
+ case ui::ET_MOUSE_MOVED:
+ return "MOUSE_MOVED";
+
+ case ui::ET_MOUSE_ENTERED:
+ return "MOUSE_ENTERED";
+
+ case ui::ET_MOUSE_EXITED:
+ return "MOUSE_EXITED";
+
+ case ui::ET_GESTURE_SCROLL_BEGIN:
+ return "GESTURE_SCROLL_BEGIN";
+
+ case ui::ET_GESTURE_SCROLL_END:
+ return "GESTURE_SCROLL_END";
+
+ case ui::ET_GESTURE_SCROLL_UPDATE:
+ return "GESTURE_SCROLL_UPDATE";
+
+ case ui::ET_GESTURE_TAP:
+ return "GESTURE_TAP";
+
+ case ui::ET_GESTURE_TAP_DOWN:
+ return "GESTURE_TAP_DOWN";
+
+ case ui::ET_GESTURE_BEGIN:
+ return "GESTURE_BEGIN";
+
+ case ui::ET_GESTURE_END:
+ return "GESTURE_END";
+
+ default:
+ break;
+ }
+ return "";
+}
+
+std::string EventTypesToString(const EventFilterRecorder::Events& events) {
+ std::string result;
+ for (size_t i = 0; i < events.size(); ++i) {
+ if (i != 0)
+ result += " ";
+ result += EventTypeToString(events[i]);
+ }
+ return result;
+}
+
+} // namespace
+
+TEST_F(RootWindowTest, MouseMovesHeld) {
+ EventFilterRecorder* filter = new EventFilterRecorder;
+ root_window()->SetEventFilter(filter); // passes ownership
+
+ test::TestWindowDelegate delegate;
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ &delegate, 1, gfx::Rect(0, 0, 100, 100), root_window()));
+
+ ui::MouseEvent mouse_move_event(ui::ET_MOUSE_MOVED, gfx::Point(0, 0),
+ gfx::Point(0, 0), 0);
+ root_window()->AsRootWindowHostDelegate()->OnHostMouseEvent(
+ &mouse_move_event);
+ // Discard MOUSE_ENTER.
+ filter->events().clear();
+
+ root_window()->HoldPointerMoves();
+
+ // Check that we don't immediately dispatch the MOUSE_DRAGGED event.
+ ui::MouseEvent mouse_dragged_event(ui::ET_MOUSE_DRAGGED, gfx::Point(0, 0),
+ gfx::Point(0, 0), 0);
+ root_window()->AsRootWindowHostDelegate()->OnHostMouseEvent(
+ &mouse_dragged_event);
+ EXPECT_TRUE(filter->events().empty());
+
+ // Check that we do dispatch the held MOUSE_DRAGGED event before another type
+ // of event.
+ ui::MouseEvent mouse_pressed_event(ui::ET_MOUSE_PRESSED, gfx::Point(0, 0),
+ gfx::Point(0, 0), 0);
+ root_window()->AsRootWindowHostDelegate()->OnHostMouseEvent(
+ &mouse_pressed_event);
+ EXPECT_EQ("MOUSE_DRAGGED MOUSE_PRESSED",
+ EventTypesToString(filter->events()));
+ filter->events().clear();
+
+ // Check that we coalesce held MOUSE_DRAGGED events.
+ ui::MouseEvent mouse_dragged_event2(ui::ET_MOUSE_DRAGGED, gfx::Point(1, 1),
+ gfx::Point(1, 1), 0);
+ root_window()->AsRootWindowHostDelegate()->OnHostMouseEvent(
+ &mouse_dragged_event);
+ root_window()->AsRootWindowHostDelegate()->OnHostMouseEvent(
+ &mouse_dragged_event2);
+ EXPECT_TRUE(filter->events().empty());
+ root_window()->AsRootWindowHostDelegate()->OnHostMouseEvent(
+ &mouse_pressed_event);
+ EXPECT_EQ("MOUSE_DRAGGED MOUSE_PRESSED",
+ EventTypesToString(filter->events()));
+ filter->events().clear();
+
+ // Check that on ReleasePointerMoves, held events are not dispatched
+ // immediately, but posted instead.
+ root_window()->AsRootWindowHostDelegate()->OnHostMouseEvent(
+ &mouse_dragged_event);
+ root_window()->ReleasePointerMoves();
+ EXPECT_TRUE(filter->events().empty());
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ("MOUSE_DRAGGED", EventTypesToString(filter->events()));
+ filter->events().clear();
+
+ // However if another message comes in before the dispatch,
+ // the Check that on ReleasePointerMoves, held events are not dispatched
+ // immediately, but posted instead.
+ root_window()->HoldPointerMoves();
+ root_window()->AsRootWindowHostDelegate()->OnHostMouseEvent(
+ &mouse_dragged_event);
+ root_window()->ReleasePointerMoves();
+ root_window()->AsRootWindowHostDelegate()->OnHostMouseEvent(
+ &mouse_pressed_event);
+ EXPECT_EQ("MOUSE_DRAGGED MOUSE_PRESSED",
+ EventTypesToString(filter->events()));
+ filter->events().clear();
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(filter->events().empty());
+
+ // Check that if the other message is another MOUSE_DRAGGED, we still coalesce
+ // them.
+ root_window()->HoldPointerMoves();
+ root_window()->AsRootWindowHostDelegate()->OnHostMouseEvent(
+ &mouse_dragged_event);
+ root_window()->ReleasePointerMoves();
+ root_window()->AsRootWindowHostDelegate()->OnHostMouseEvent(
+ &mouse_dragged_event2);
+ EXPECT_EQ("MOUSE_DRAGGED", EventTypesToString(filter->events()));
+ filter->events().clear();
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(filter->events().empty());
+}
+
+TEST_F(RootWindowTest, TouchMovesHeld) {
+ EventFilterRecorder* filter = new EventFilterRecorder;
+ root_window()->SetEventFilter(filter); // passes ownership
+
+ test::TestWindowDelegate delegate;
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ &delegate, 1, gfx::Rect(0, 0, 100, 100), root_window()));
+
+ // Starting the touch and throwing out the first few events, since the system
+ // is going to generate synthetic mouse events that are not relevant to the
+ // test.
+ ui::TouchEvent touch_pressed_event(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0),
+ 0, base::TimeDelta());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(
+ &touch_pressed_event);
+ RunAllPendingInMessageLoop();
+ filter->events().clear();
+
+ root_window()->HoldPointerMoves();
+
+ // Check that we don't immediately dispatch the TOUCH_MOVED event.
+ ui::TouchEvent touch_moved_event(ui::ET_TOUCH_MOVED, gfx::Point(0, 0),
+ 0, base::TimeDelta());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(
+ &touch_moved_event);
+ EXPECT_TRUE(filter->events().empty());
+
+ // Check that on ReleasePointerMoves, held events are not dispatched
+ // immediately, but posted instead.
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(
+ &touch_moved_event);
+ root_window()->ReleasePointerMoves();
+ EXPECT_TRUE(filter->events().empty());
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ("TOUCH_MOVED", EventTypesToString(filter->events()));
+ filter->events().clear();
+
+ // If another touch event occurs then the held touch should be dispatched
+ // immediately before it.
+ ui::TouchEvent touch_released_event(ui::ET_TOUCH_RELEASED, gfx::Point(0, 0),
+ 0, base::TimeDelta());
+ filter->events().clear();
+ root_window()->HoldPointerMoves();
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(
+ &touch_moved_event);
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(
+ &touch_released_event);
+ EXPECT_EQ("TOUCH_MOVED TOUCH_RELEASED GESTURE_END",
+ EventTypesToString(filter->events()));
+ filter->events().clear();
+ root_window()->ReleasePointerMoves();
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(filter->events().empty());
+}
+
+// Tests that synthetic mouse events are ignored when mouse
+// events are disabled.
+TEST_F(RootWindowTest, DispatchSyntheticMouseEvents) {
+ EventFilterRecorder* filter = new EventFilterRecorder;
+ root_window()->SetEventFilter(filter); // passes ownership
+
+ test::TestWindowDelegate delegate;
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ &delegate, 1234, gfx::Rect(5, 5, 100, 100), root_window()));
+ window->Show();
+ window->SetCapture();
+
+ test::TestCursorClient cursor_client(root_window());
+
+ // Dispatch a non-synthetic mouse event when mouse events are enabled.
+ ui::MouseEvent mouse1(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
+ gfx::Point(10, 10), 0);
+ root_window()->AsRootWindowHostDelegate()->OnHostMouseEvent(&mouse1);
+ EXPECT_FALSE(filter->events().empty());
+ filter->events().clear();
+
+ // Dispatch a synthetic mouse event when mouse events are enabled.
+ ui::MouseEvent mouse2(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
+ gfx::Point(10, 10), ui::EF_IS_SYNTHESIZED);
+ root_window()->AsRootWindowHostDelegate()->OnHostMouseEvent(&mouse2);
+ EXPECT_FALSE(filter->events().empty());
+ filter->events().clear();
+
+ // Dispatch a synthetic mouse event when mouse events are disabled.
+ cursor_client.DisableMouseEvents();
+ root_window()->AsRootWindowHostDelegate()->OnHostMouseEvent(&mouse2);
+ EXPECT_TRUE(filter->events().empty());
+}
+
+class DeletingEventFilter : public ui::EventHandler {
+ public:
+ DeletingEventFilter()
+ : delete_during_pre_handle_(false) {}
+ virtual ~DeletingEventFilter() {}
+
+ void Reset(bool delete_during_pre_handle) {
+ delete_during_pre_handle_ = delete_during_pre_handle;
+ }
+
+ private:
+ // Overridden from ui::EventHandler:
+ virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE {
+ if (delete_during_pre_handle_)
+ delete event->target();
+ }
+
+ virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
+ if (delete_during_pre_handle_)
+ delete event->target();
+ }
+
+ bool delete_during_pre_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeletingEventFilter);
+};
+
+class DeletingWindowDelegate : public test::TestWindowDelegate {
+ public:
+ DeletingWindowDelegate()
+ : window_(NULL),
+ delete_during_handle_(false),
+ got_event_(false) {}
+ virtual ~DeletingWindowDelegate() {}
+
+ void Reset(Window* window, bool delete_during_handle) {
+ window_ = window;
+ delete_during_handle_ = delete_during_handle;
+ got_event_ = false;
+ }
+ bool got_event() const { return got_event_; }
+
+ private:
+ // Overridden from WindowDelegate:
+ virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE {
+ if (delete_during_handle_)
+ delete window_;
+ got_event_ = true;
+ }
+
+ virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
+ if (delete_during_handle_)
+ delete window_;
+ got_event_ = true;
+ }
+
+ Window* window_;
+ bool delete_during_handle_;
+ bool got_event_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeletingWindowDelegate);
+};
+
+TEST_F(RootWindowTest, DeleteWindowDuringDispatch) {
+ // Verifies that we can delete a window during each phase of event handling.
+ // Deleting the window should not cause a crash, only prevent further
+ // processing from occurring.
+ scoped_ptr<Window> w1(CreateNormalWindow(1, root_window(), NULL));
+ DeletingWindowDelegate d11;
+ Window* w11 = CreateNormalWindow(11, w1.get(), &d11);
+ WindowTracker tracker;
+ DeletingEventFilter* w1_filter = new DeletingEventFilter;
+ w1->SetEventFilter(w1_filter);
+ client::GetFocusClient(w1.get())->FocusWindow(w11);
+
+ test::EventGenerator generator(root_window(), w11);
+
+ // First up, no one deletes anything.
+ tracker.Add(w11);
+ d11.Reset(w11, false);
+
+ generator.PressLeftButton();
+ EXPECT_TRUE(tracker.Contains(w11));
+ EXPECT_TRUE(d11.got_event());
+ generator.ReleaseLeftButton();
+
+ // Delegate deletes w11. This will prevent the post-handle step from applying.
+ w1_filter->Reset(false);
+ d11.Reset(w11, true);
+ generator.PressKey(ui::VKEY_A, 0);
+ EXPECT_FALSE(tracker.Contains(w11));
+ EXPECT_TRUE(d11.got_event());
+
+ // Pre-handle step deletes w11. This will prevent the delegate and the post-
+ // handle steps from applying.
+ w11 = CreateNormalWindow(11, w1.get(), &d11);
+ w1_filter->Reset(true);
+ d11.Reset(w11, false);
+ generator.PressLeftButton();
+ EXPECT_FALSE(tracker.Contains(w11));
+ EXPECT_FALSE(d11.got_event());
+}
+
+namespace {
+
+// A window delegate that detaches the parent of the target's parent window when
+// it receives a tap event.
+class DetachesParentOnTapDelegate : public test::TestWindowDelegate {
+ public:
+ DetachesParentOnTapDelegate() {}
+ virtual ~DetachesParentOnTapDelegate() {}
+
+ private:
+ virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
+ if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
+ event->SetHandled();
+ return;
+ }
+
+ if (event->type() == ui::ET_GESTURE_TAP) {
+ Window* parent = static_cast<Window*>(event->target())->parent();
+ parent->parent()->RemoveChild(parent);
+ event->SetHandled();
+ }
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(DetachesParentOnTapDelegate);
+};
+
+} // namespace
+
+// Tests that the gesture recognizer is reset for all child windows when a
+// window hides. No expectations, just checks that the test does not crash.
+TEST_F(RootWindowTest, GestureRecognizerResetsTargetWhenParentHides) {
+ scoped_ptr<Window> w1(CreateNormalWindow(1, root_window(), NULL));
+ DetachesParentOnTapDelegate delegate;
+ scoped_ptr<Window> parent(CreateNormalWindow(22, w1.get(), NULL));
+ Window* child = CreateNormalWindow(11, parent.get(), &delegate);
+ test::EventGenerator generator(root_window(), child);
+ generator.GestureTapAt(gfx::Point(40, 40));
+}
+
+namespace {
+
+// A window delegate that processes nested gestures on tap.
+class NestedGestureDelegate : public test::TestWindowDelegate {
+ public:
+ NestedGestureDelegate(test::EventGenerator* generator,
+ const gfx::Point tap_location)
+ : generator_(generator),
+ tap_location_(tap_location),
+ gesture_end_count_(0) {}
+ virtual ~NestedGestureDelegate() {}
+
+ int gesture_end_count() const { return gesture_end_count_; }
+
+ private:
+ virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
+ switch (event->type()) {
+ case ui::ET_GESTURE_TAP_DOWN:
+ event->SetHandled();
+ break;
+ case ui::ET_GESTURE_TAP:
+ if (generator_)
+ generator_->GestureTapAt(tap_location_);
+ event->SetHandled();
+ break;
+ case ui::ET_GESTURE_END:
+ ++gesture_end_count_;
+ break;
+ default:
+ break;
+ }
+ }
+
+ test::EventGenerator* generator_;
+ const gfx::Point tap_location_;
+ int gesture_end_count_;
+ DISALLOW_COPY_AND_ASSIGN(NestedGestureDelegate);
+};
+
+} // namespace
+
+// Tests that gesture end is delivered after nested gesture processing.
+TEST_F(RootWindowTest, GestureEndDeliveredAfterNestedGestures) {
+ NestedGestureDelegate d1(NULL, gfx::Point());
+ scoped_ptr<Window> w1(CreateNormalWindow(1, root_window(), &d1));
+ w1->SetBounds(gfx::Rect(0, 0, 100, 100));
+
+ test::EventGenerator nested_generator(root_window(), w1.get());
+ NestedGestureDelegate d2(&nested_generator, w1->bounds().CenterPoint());
+ scoped_ptr<Window> w2(CreateNormalWindow(1, root_window(), &d2));
+ w2->SetBounds(gfx::Rect(100, 0, 100, 100));
+
+ // Tap on w2 which triggers nested gestures for w1.
+ test::EventGenerator generator(root_window(), w2.get());
+ generator.GestureTapAt(w2->bounds().CenterPoint());
+
+ // Both windows should get their gesture end events.
+ EXPECT_EQ(1, d1.gesture_end_count());
+ EXPECT_EQ(1, d2.gesture_end_count());
+}
+
+} // namespace aura
diff --git a/chromium/ui/aura/root_window_view_mac.h b/chromium/ui/aura/root_window_view_mac.h
new file mode 100644
index 00000000000..6617f36dad9
--- /dev/null
+++ b/chromium/ui/aura/root_window_view_mac.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_ROOT_WINDOW_VIEW_MAC_H_
+#define UI_AURA_ROOT_WINDOW_VIEW_MAC_H_
+
+#import <Cocoa/Cocoa.h>
+
+#include "ui/compositor/compositor.h"
+
+// RootWindowView provides an NSView class that delegates drawing to a
+// ui::Compositor delegate, setting up the NSOpenGLContext as required.
+@interface RootWindowView : NSView {
+ @private
+ ui::Compositor* compositor_;
+}
+-(void)setCompositor:(ui::Compositor*)compositor;
+@end
+
+#endif // UI_AURA_ROOT_WINDOW_VIEW_MAC_H_
diff --git a/chromium/ui/aura/root_window_view_mac.mm b/chromium/ui/aura/root_window_view_mac.mm
new file mode 100644
index 00000000000..a82100e8434
--- /dev/null
+++ b/chromium/ui/aura/root_window_view_mac.mm
@@ -0,0 +1,16 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ui/aura/root_window_view_mac.h"
+
+@implementation RootWindowView
+-(void)setCompositor:(ui::Compositor*)compositor {
+ compositor_ = compositor;
+}
+
+- (void)drawRect:(NSRect)rect {
+ if (compositor_)
+ compositor_->Draw(false);
+}
+@end
diff --git a/chromium/ui/aura/window.cc b/chromium/ui/aura/window.cc
new file mode 100644
index 00000000000..e5289e46a29
--- /dev/null
+++ b/chromium/ui/aura/window.cc
@@ -0,0 +1,1137 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/window.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "ui/aura/client/capture_client.h"
+#include "ui/aura/client/event_client.h"
+#include "ui/aura/client/screen_position_client.h"
+#include "ui/aura/client/stacking_client.h"
+#include "ui/aura/client/visibility_client.h"
+#include "ui/aura/env.h"
+#include "ui/aura/focus_manager.h"
+#include "ui/aura/layout_manager.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/window_delegate.h"
+#include "ui/aura/window_observer.h"
+#include "ui/aura/window_tracker.h"
+#include "ui/base/animation/multi_animation.h"
+#include "ui/compositor/compositor.h"
+#include "ui/compositor/layer.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/path.h"
+#include "ui/gfx/screen.h"
+
+namespace aura {
+
+namespace {
+
+void MailboxReleaseCallback(scoped_ptr<base::SharedMemory> shared_memory,
+ unsigned sync_point, bool lost_resource) {
+ // NOTE: shared_memory will get released when we go out of scope.
+}
+
+} // namespace
+
+Window::Window(WindowDelegate* delegate)
+ : type_(client::WINDOW_TYPE_UNKNOWN),
+ owned_by_parent_(true),
+ delegate_(delegate),
+ parent_(NULL),
+ transient_parent_(NULL),
+ visible_(false),
+ id_(-1),
+ transparent_(false),
+ user_data_(NULL),
+ ignore_events_(false),
+ // 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 the workspace code).
+ observers_(ObserverList<WindowObserver>::NOTIFY_EXISTING_ONLY) {
+ set_target_handler(delegate_);
+}
+
+Window::~Window() {
+ // layer_ can be NULL if Init() wasn't invoked, which can happen
+ // only in tests.
+ if (layer_)
+ layer_->SuppressPaint();
+
+ // Let the delegate know we're in the processing of destroying.
+ if (delegate_)
+ delegate_->OnWindowDestroying();
+ FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowDestroying(this));
+
+ // Let the root know so that it can remove any references to us.
+ RootWindow* root_window = GetRootWindow();
+ if (root_window)
+ root_window->OnWindowDestroying(this);
+
+ // Then destroy the children.
+ while (!children_.empty()) {
+ Window* child = children_[0];
+ if (child->owned_by_parent_) {
+ delete child;
+ // Deleting the child so remove it from out children_ list.
+ DCHECK(std::find(children_.begin(), children_.end(), child) ==
+ children_.end());
+ } else {
+ // Even if we can't delete the child, we still need to remove it from the
+ // parent so that relevant bookkeeping (parent_ back-pointers etc) are
+ // updated.
+ RemoveChild(child);
+ }
+ }
+
+ // Removes ourselves from our transient parent (if it hasn't been done by the
+ // RootWindow).
+ if (transient_parent_)
+ transient_parent_->RemoveTransientChild(this);
+
+ // The window needs to be removed from the parent before calling the
+ // WindowDestroyed callbacks of delegate and the observers.
+ if (parent_)
+ parent_->RemoveChild(this);
+
+ // And let the delegate do any post cleanup.
+ // TODO(beng): Figure out if this notification needs to happen here, or if it
+ // can be moved down adjacent to the observer notification. If it has to be
+ // done here, the reason why should be documented.
+ if (delegate_)
+ delegate_->OnWindowDestroyed();
+
+ // 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());
+
+ FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowDestroyed(this));
+
+ // Clear properties.
+ for (std::map<const void*, Value>::const_iterator iter = prop_map_.begin();
+ iter != prop_map_.end();
+ ++iter) {
+ if (iter->second.deallocator)
+ (*iter->second.deallocator)(iter->second.value);
+ }
+ prop_map_.clear();
+
+ // If we have layer it will either be destroyed by layer_owner_'s dtor, or by
+ // whoever acquired it. We don't have a layer if Init() wasn't invoked, which
+ // can happen in tests.
+ if (layer_)
+ layer_->set_delegate(NULL);
+ layer_ = NULL;
+}
+
+void Window::Init(ui::LayerType layer_type) {
+ layer_ = new ui::Layer(layer_type);
+ layer_owner_.reset(layer_);
+ layer_->SetVisible(false);
+ layer_->set_delegate(this);
+ UpdateLayerName(name_);
+ layer_->SetFillsBoundsOpaquely(!transparent_);
+
+ Env::GetInstance()->NotifyWindowInitialized(this);
+}
+
+ui::Layer* Window::RecreateLayer() {
+ // Disconnect the old layer, but don't delete it.
+ ui::Layer* old_layer = AcquireLayer();
+ if (!old_layer)
+ return NULL;
+
+ old_layer->set_delegate(NULL);
+ float mailbox_scale_factor;
+ cc::TextureMailbox old_mailbox =
+ old_layer->GetTextureMailbox(&mailbox_scale_factor);
+ scoped_refptr<ui::Texture> old_texture = old_layer->external_texture();
+ if (delegate_ && old_texture.get())
+ old_layer->SetExternalTexture(delegate_->CopyTexture().get());
+
+ layer_ = new ui::Layer(old_layer->type());
+ layer_owner_.reset(layer_);
+ layer_->SetVisible(old_layer->visible());
+ layer_->set_scale_content(old_layer->scale_content());
+ layer_->set_delegate(this);
+ layer_->SetMasksToBounds(old_layer->GetMasksToBounds());
+ // Move the original texture to the new layer if the old layer has a
+ // texture and we could copy it into the old layer,
+ // crbug.com/175211.
+ if (delegate_ && old_texture.get()) {
+ layer_->SetExternalTexture(old_texture.get());
+ } else if (old_mailbox.IsSharedMemory()) {
+ base::SharedMemory* old_buffer = old_mailbox.shared_memory();
+ const size_t size = old_mailbox.shared_memory_size_in_bytes();
+
+ scoped_ptr<base::SharedMemory> new_buffer(new base::SharedMemory);
+ new_buffer->CreateAndMapAnonymous(size);
+
+ if (old_buffer->memory() && new_buffer->memory()) {
+ memcpy(new_buffer->memory(), old_buffer->memory(), size);
+ base::SharedMemory* new_buffer_raw_ptr = new_buffer.get();
+ cc::TextureMailbox::ReleaseCallback callback =
+ base::Bind(MailboxReleaseCallback, Passed(&new_buffer));
+ cc::TextureMailbox new_mailbox(new_buffer_raw_ptr,
+ old_mailbox.shared_memory_size(),
+ callback);
+ layer_->SetTextureMailbox(new_mailbox, mailbox_scale_factor);
+ }
+ }
+
+ UpdateLayerName(name_);
+ layer_->SetFillsBoundsOpaquely(!transparent_);
+ // Install new layer as a sibling of the old layer, stacked below it.
+ if (old_layer->parent()) {
+ old_layer->parent()->Add(layer_);
+ old_layer->parent()->StackBelow(layer_, old_layer);
+ }
+ // Migrate all the child layers over to the new layer. Copy the list because
+ // the items are removed during iteration.
+ std::vector<ui::Layer*> children_copy = old_layer->children();
+ for (std::vector<ui::Layer*>::const_iterator it = children_copy.begin();
+ it != children_copy.end();
+ ++it) {
+ ui::Layer* child = *it;
+ layer_->Add(child);
+ }
+ return old_layer;
+}
+
+void Window::SetType(client::WindowType type) {
+ // Cannot change type after the window is initialized.
+ DCHECK(!layer());
+ type_ = type;
+}
+
+void Window::SetName(const std::string& name) {
+ name_ = name;
+
+ if (layer())
+ UpdateLayerName(name_);
+}
+
+void Window::SetTransparent(bool transparent) {
+ // Cannot change transparent flag after the window is initialized.
+ DCHECK(!layer());
+ transparent_ = transparent;
+}
+
+RootWindow* Window::GetRootWindow() {
+ return const_cast<RootWindow*>(
+ static_cast<const Window*>(this)->GetRootWindow());
+}
+
+const RootWindow* Window::GetRootWindow() const {
+ return parent_ ? parent_->GetRootWindow() : NULL;
+}
+
+void Window::Show() {
+ SetVisible(true);
+}
+
+void Window::Hide() {
+ for (Windows::iterator it = transient_children_.begin();
+ it != transient_children_.end(); ++it) {
+ (*it)->Hide();
+ }
+ SetVisible(false);
+ ReleaseCapture();
+}
+
+bool Window::IsVisible() const {
+ // Layer visibility can be inconsistent with window visibility, for example
+ // when a Window is hidden, we want this function to return false immediately
+ // after, even though the client may decide to animate the hide effect (and
+ // so the layer will be visible for some time after Hide() is called).
+ return visible_ && layer_ && layer_->IsDrawn();
+}
+
+gfx::Rect Window::GetBoundsInRootWindow() const {
+ // TODO(beng): There may be a better way to handle this, and the existing code
+ // is likely wrong anyway in a multi-display world, but this will
+ // do for now.
+ if (!GetRootWindow())
+ return bounds();
+ gfx::Point origin = bounds().origin();
+ ConvertPointToTarget(parent_, GetRootWindow(), &origin);
+ return gfx::Rect(origin, bounds().size());
+}
+
+gfx::Rect Window::GetBoundsInScreen() const {
+ gfx::Rect bounds(GetBoundsInRootWindow());
+ const RootWindow* root = GetRootWindow();
+ if (root) {
+ aura::client::ScreenPositionClient* screen_position_client =
+ aura::client::GetScreenPositionClient(root);
+ if (screen_position_client) {
+ gfx::Point origin = bounds.origin();
+ screen_position_client->ConvertPointToScreen(root, &origin);
+ bounds.set_origin(origin);
+ }
+ }
+ return bounds;
+}
+
+void Window::SetTransform(const gfx::Transform& transform) {
+ RootWindow* root_window = GetRootWindow();
+ bool contained_mouse = IsVisible() && root_window &&
+ ContainsPointInRoot(root_window->GetLastMouseLocationInRoot());
+ layer()->SetTransform(transform);
+ if (root_window)
+ root_window->OnWindowTransformed(this, contained_mouse);
+}
+
+void Window::SetLayoutManager(LayoutManager* layout_manager) {
+ if (layout_manager == layout_manager_)
+ return;
+ layout_manager_.reset(layout_manager);
+ if (!layout_manager)
+ return;
+ // If we're changing to a new layout manager, ensure it is aware of all the
+ // existing child windows.
+ for (Windows::const_iterator it = children_.begin();
+ it != children_.end();
+ ++it)
+ layout_manager_->OnWindowAddedToLayout(*it);
+}
+
+void Window::SetBounds(const gfx::Rect& new_bounds) {
+ if (parent_ && parent_->layout_manager())
+ parent_->layout_manager()->SetChildBounds(this, new_bounds);
+ else
+ SetBoundsInternal(new_bounds);
+}
+
+void Window::SetBoundsInScreen(const gfx::Rect& new_bounds_in_screen,
+ const gfx::Display& dst_display) {
+ RootWindow* root = GetRootWindow();
+ if (root) {
+ gfx::Point origin = new_bounds_in_screen.origin();
+ aura::client::ScreenPositionClient* screen_position_client =
+ aura::client::GetScreenPositionClient(root);
+ screen_position_client->SetBounds(this, new_bounds_in_screen, dst_display);
+ return;
+ }
+ SetBounds(new_bounds_in_screen);
+}
+
+gfx::Rect Window::GetTargetBounds() const {
+ return layer_->GetTargetBounds();
+}
+
+const gfx::Rect& Window::bounds() const {
+ return layer_->bounds();
+}
+
+void Window::SchedulePaintInRect(const gfx::Rect& rect) {
+ if (layer_->SchedulePaint(rect)) {
+ FOR_EACH_OBSERVER(
+ WindowObserver, observers_, OnWindowPaintScheduled(this, rect));
+ }
+}
+
+void Window::SetDefaultParentByRootWindow(RootWindow* root_window,
+ const gfx::Rect& bounds_in_screen) {
+ DCHECK(root_window);
+
+ // Stacking clients are mandatory on RootWindow objects.
+ client::StackingClient* client = client::GetStackingClient(root_window);
+ DCHECK(client);
+
+ aura::Window* default_parent = client->GetDefaultParent(
+ root_window, this, bounds_in_screen);
+ default_parent->AddChild(this);
+}
+
+void Window::StackChildAtTop(Window* child) {
+ if (children_.size() <= 1 || child == children_.back())
+ return; // In the front already.
+ StackChildAbove(child, children_.back());
+}
+
+void Window::StackChildAbove(Window* child, Window* target) {
+ StackChildRelativeTo(child, target, STACK_ABOVE);
+}
+
+void Window::StackChildAtBottom(Window* child) {
+ if (children_.size() <= 1 || child == children_.front())
+ return; // At the bottom already.
+ StackChildBelow(child, children_.front());
+}
+
+void Window::StackChildBelow(Window* child, Window* target) {
+ StackChildRelativeTo(child, target, STACK_BELOW);
+}
+
+void Window::AddChild(Window* child) {
+ WindowObserver::HierarchyChangeParams params;
+ params.target = child;
+ params.new_parent = this;
+ params.old_parent = child->parent();
+ params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGING;
+ NotifyWindowHierarchyChange(params);
+
+ RootWindow* old_root = child->GetRootWindow();
+
+ DCHECK(std::find(children_.begin(), children_.end(), child) ==
+ children_.end());
+ if (child->parent())
+ child->parent()->RemoveChildImpl(child, this);
+ child->parent_ = this;
+
+ layer_->Add(child->layer_);
+
+ children_.push_back(child);
+ if (layout_manager_)
+ layout_manager_->OnWindowAddedToLayout(child);
+ FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowAdded(child));
+ child->OnParentChanged();
+
+ RootWindow* root_window = GetRootWindow();
+ if (root_window && old_root != root_window) {
+ root_window->OnWindowAddedToRootWindow(child);
+ child->NotifyAddedToRootWindow();
+ }
+
+ params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGED;
+ NotifyWindowHierarchyChange(params);
+}
+
+void Window::RemoveChild(Window* child) {
+ WindowObserver::HierarchyChangeParams params;
+ params.target = child;
+ params.new_parent = NULL;
+ params.old_parent = this;
+ params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGING;
+ NotifyWindowHierarchyChange(params);
+
+ RemoveChildImpl(child, NULL);
+
+ params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGED;
+ NotifyWindowHierarchyChange(params);
+}
+
+bool Window::Contains(const Window* other) const {
+ for (const Window* parent = other; parent; parent = parent->parent_) {
+ if (parent == this)
+ return true;
+ }
+ return false;
+}
+
+void Window::AddTransientChild(Window* child) {
+ if (child->transient_parent_)
+ child->transient_parent_->RemoveTransientChild(child);
+ DCHECK(std::find(transient_children_.begin(), transient_children_.end(),
+ child) == transient_children_.end());
+ transient_children_.push_back(child);
+ child->transient_parent_ = this;
+ FOR_EACH_OBSERVER(WindowObserver, observers_,
+ OnAddTransientChild(this, child));
+}
+
+void Window::RemoveTransientChild(Window* child) {
+ Windows::iterator i =
+ std::find(transient_children_.begin(), transient_children_.end(), child);
+ DCHECK(i != transient_children_.end());
+ transient_children_.erase(i);
+ if (child->transient_parent_ == this)
+ child->transient_parent_ = NULL;
+ FOR_EACH_OBSERVER(WindowObserver, observers_,
+ OnRemoveTransientChild(this, child));
+}
+
+Window* Window::GetChildById(int id) {
+ return const_cast<Window*>(const_cast<const Window*>(this)->GetChildById(id));
+}
+
+const Window* Window::GetChildById(int id) const {
+ Windows::const_iterator i;
+ for (i = children_.begin(); i != children_.end(); ++i) {
+ if ((*i)->id() == id)
+ return *i;
+ const Window* result = (*i)->GetChildById(id);
+ if (result)
+ return result;
+ }
+ return NULL;
+}
+
+// static
+void Window::ConvertPointToTarget(const Window* source,
+ const Window* target,
+ gfx::Point* point) {
+ if (!source)
+ return;
+ if (source->GetRootWindow() != target->GetRootWindow()) {
+ client::ScreenPositionClient* source_client =
+ GetScreenPositionClient(source->GetRootWindow());
+ source_client->ConvertPointToScreen(source, point);
+
+ client::ScreenPositionClient* target_client =
+ GetScreenPositionClient(target->GetRootWindow());
+ target_client->ConvertPointFromScreen(target, point);
+ } else {
+ ui::Layer::ConvertPointToLayer(source->layer(), target->layer(), point);
+ }
+}
+
+void Window::MoveCursorTo(const gfx::Point& point_in_window) {
+ RootWindow* root_window = GetRootWindow();
+ DCHECK(root_window);
+ gfx::Point point_in_root(point_in_window);
+ ConvertPointToTarget(this, root_window, &point_in_root);
+ root_window->MoveCursorTo(point_in_root);
+}
+
+gfx::NativeCursor Window::GetCursor(const gfx::Point& point) const {
+ return delegate_ ? delegate_->GetCursor(point) : gfx::kNullCursor;
+}
+
+void Window::SetEventFilter(ui::EventHandler* event_filter) {
+ if (event_filter_)
+ RemovePreTargetHandler(event_filter_.get());
+ event_filter_.reset(event_filter);
+ if (event_filter)
+ AddPreTargetHandler(event_filter);
+}
+
+void Window::AddObserver(WindowObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void Window::RemoveObserver(WindowObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+bool Window::HasObserver(WindowObserver* observer) {
+ return observers_.HasObserver(observer);
+}
+
+bool Window::ContainsPointInRoot(const gfx::Point& point_in_root) const {
+ const Window* root_window = GetRootWindow();
+ if (!root_window)
+ return false;
+ gfx::Point local_point(point_in_root);
+ ConvertPointToTarget(root_window, this, &local_point);
+ return gfx::Rect(GetTargetBounds().size()).Contains(local_point);
+}
+
+bool Window::ContainsPoint(const gfx::Point& local_point) const {
+ return gfx::Rect(bounds().size()).Contains(local_point);
+}
+
+bool Window::HitTest(const gfx::Point& local_point) {
+ // Expand my bounds for hit testing (override is usually zero but it's
+ // probably cheaper to do the math every time than to branch).
+ gfx::Rect local_bounds(gfx::Point(), bounds().size());
+ local_bounds.Inset(aura::Env::GetInstance()->is_touch_down() ?
+ hit_test_bounds_override_outer_touch_ :
+ hit_test_bounds_override_outer_mouse_);
+
+ if (!delegate_ || !delegate_->HasHitTestMask())
+ return local_bounds.Contains(local_point);
+
+ gfx::Path mask;
+ delegate_->GetHitTestMask(&mask);
+
+ SkRegion clip_region;
+ clip_region.setRect(local_bounds.x(), local_bounds.y(),
+ local_bounds.width(), local_bounds.height());
+ SkRegion mask_region;
+ return mask_region.setPath(mask, clip_region) &&
+ mask_region.contains(local_point.x(), local_point.y());
+}
+
+Window* Window::GetEventHandlerForPoint(const gfx::Point& local_point) {
+ return GetWindowForPoint(local_point, true, true);
+}
+
+Window* Window::GetTopWindowContainingPoint(const gfx::Point& local_point) {
+ return GetWindowForPoint(local_point, false, false);
+}
+
+Window* Window::GetToplevelWindow() {
+ Window* topmost_window_with_delegate = NULL;
+ for (aura::Window* window = this; window != NULL; window = window->parent()) {
+ if (window->delegate())
+ topmost_window_with_delegate = window;
+ }
+ return topmost_window_with_delegate;
+}
+
+void Window::Focus() {
+ client::FocusClient* client = client::GetFocusClient(this);
+ DCHECK(client);
+ client->FocusWindow(this);
+}
+
+void Window::Blur() {
+ client::FocusClient* client = client::GetFocusClient(this);
+ DCHECK(client);
+ client->FocusWindow(NULL);
+}
+
+bool Window::HasFocus() const {
+ client::FocusClient* client = client::GetFocusClient(this);
+ return client && client->GetFocusedWindow() == this;
+}
+
+bool Window::CanFocus() const {
+ // NOTE: as part of focusing the window the ActivationClient may make the
+ // window visible (by way of making a hidden ancestor visible). For this
+ // reason we can't check visibility here and assume the client is doing it.
+ if (!parent_ || (delegate_ && !delegate_->CanFocus()))
+ return false;
+
+ // The client may forbid certain windows from receiving focus at a given point
+ // in time.
+ client::EventClient* client = client::GetEventClient(GetRootWindow());
+ if (client && !client->CanProcessEventsWithinSubtree(this))
+ return false;
+
+ return parent_->CanFocus();
+}
+
+bool Window::CanReceiveEvents() const {
+ // The client may forbid certain windows from receiving events at a given
+ // point in time.
+ client::EventClient* client = client::GetEventClient(GetRootWindow());
+ if (client && !client->CanProcessEventsWithinSubtree(this))
+ return false;
+
+ return parent_ && IsVisible() && parent_->CanReceiveEvents();
+}
+
+void Window::SetCapture() {
+ if (!IsVisible())
+ return;
+
+ RootWindow* root_window = GetRootWindow();
+ if (!root_window)
+ return;
+ client::GetCaptureClient(root_window)->SetCapture(this);
+}
+
+void Window::ReleaseCapture() {
+ RootWindow* root_window = GetRootWindow();
+ if (!root_window)
+ return;
+ client::GetCaptureClient(root_window)->ReleaseCapture(this);
+}
+
+bool Window::HasCapture() {
+ RootWindow* root_window = GetRootWindow();
+ return root_window &&
+ client::GetCaptureClient(root_window)->GetCaptureWindow() == this;
+}
+
+void Window::SuppressPaint() {
+ layer_->SuppressPaint();
+}
+
+// {Set,Get,Clear}Property are implemented in window_property.h.
+
+void Window::SetNativeWindowProperty(const char* key, void* value) {
+ SetPropertyInternal(
+ key, key, NULL, reinterpret_cast<int64>(value), 0);
+}
+
+void* Window::GetNativeWindowProperty(const char* key) const {
+ return reinterpret_cast<void*>(GetPropertyInternal(key, 0));
+}
+
+void Window::OnDeviceScaleFactorChanged(float device_scale_factor) {
+ if (delegate_)
+ delegate_->OnDeviceScaleFactorChanged(device_scale_factor);
+}
+
+#ifndef NDEBUG
+std::string Window::GetDebugInfo() const {
+ return base::StringPrintf(
+ "%s<%d> bounds(%d, %d, %d, %d) %s %s opacity=%.1f",
+ name().empty() ? "Unknown" : name().c_str(), id(),
+ bounds().x(), bounds().y(), bounds().width(), bounds().height(),
+ visible_ ? "WindowVisible" : "WindowHidden",
+ layer_->GetTargetVisibility() ? "LayerVisible" : "LayerHidden",
+ layer_->opacity());
+}
+
+void Window::PrintWindowHierarchy(int depth) const {
+ printf("%*s%s\n", depth * 2, "", GetDebugInfo().c_str());
+ for (Windows::const_iterator it = children_.begin();
+ it != children_.end(); ++it) {
+ Window* child = *it;
+ child->PrintWindowHierarchy(depth + 1);
+ }
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Window, private:
+
+int64 Window::SetPropertyInternal(const void* key,
+ const char* name,
+ PropertyDeallocator deallocator,
+ int64 value,
+ int64 default_value) {
+ int64 old = GetPropertyInternal(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_,
+ OnWindowPropertyChanged(this, key, old));
+ return old;
+}
+
+int64 Window::GetPropertyInternal(const void* key,
+ int64 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::SetBoundsInternal(const gfx::Rect& new_bounds) {
+ gfx::Rect actual_new_bounds(new_bounds);
+
+ // Ensure we don't go smaller than our minimum bounds.
+ if (delegate_) {
+ const gfx::Size& min_size = delegate_->GetMinimumSize();
+ actual_new_bounds.set_width(
+ std::max(min_size.width(), actual_new_bounds.width()));
+ actual_new_bounds.set_height(
+ std::max(min_size.height(), actual_new_bounds.height()));
+ }
+
+ gfx::Rect old_bounds = GetTargetBounds();
+
+ // Always need to set the layer's bounds -- even if it is to the same thing.
+ // This may cause important side effects such as stopping animation.
+ layer_->SetBounds(actual_new_bounds);
+
+ // If we are currently not the layer's delegate, we will not get bounds
+ // changed notification from the layer (this typically happens after animating
+ // hidden). We must notify ourselves.
+ if (layer_->delegate() != this)
+ OnLayerBoundsChanged(old_bounds, ContainsMouse());
+}
+
+void Window::SetVisible(bool visible) {
+ if (visible == layer_->GetTargetVisibility())
+ return; // No change.
+
+ FOR_EACH_OBSERVER(WindowObserver, observers_,
+ OnWindowVisibilityChanging(this, visible));
+
+ RootWindow* root_window = GetRootWindow();
+ if (root_window)
+ root_window->DispatchMouseExitToHidingWindow(this);
+
+ client::VisibilityClient* visibility_client =
+ client::GetVisibilityClient(this);
+ if (visibility_client)
+ visibility_client->UpdateLayerVisibility(this, visible);
+ else
+ layer_->SetVisible(visible);
+ visible_ = visible;
+ SchedulePaint();
+ if (parent_ && parent_->layout_manager_)
+ parent_->layout_manager_->OnChildWindowVisibilityChanged(this, visible);
+
+ if (delegate_)
+ delegate_->OnWindowTargetVisibilityChanged(visible);
+
+ NotifyWindowVisibilityChanged(this, visible);
+
+ if (root_window)
+ root_window->OnWindowVisibilityChanged(this, visible);
+}
+
+void Window::SchedulePaint() {
+ SchedulePaintInRect(gfx::Rect(0, 0, bounds().width(), bounds().height()));
+}
+
+Window* Window::GetWindowForPoint(const gfx::Point& local_point,
+ bool return_tightest,
+ bool for_event_handling) {
+ if (!IsVisible())
+ return NULL;
+
+ if ((for_event_handling && !HitTest(local_point)) ||
+ (!for_event_handling && !ContainsPoint(local_point)))
+ return NULL;
+
+ // Check if I should claim this event and not pass it to my children because
+ // the location is inside my hit test override area. For details, see
+ // set_hit_test_bounds_override_inner().
+ if (for_event_handling && !hit_test_bounds_override_inner_.empty()) {
+ gfx::Rect inset_local_bounds(gfx::Point(), bounds().size());
+ inset_local_bounds.Inset(hit_test_bounds_override_inner_);
+ // We know we're inside the normal local bounds, so if we're outside the
+ // inset bounds we must be in the special hit test override area.
+ DCHECK(HitTest(local_point));
+ if (!inset_local_bounds.Contains(local_point))
+ return delegate_ ? this : NULL;
+ }
+
+ if (!return_tightest && delegate_)
+ return this;
+
+ for (Windows::const_reverse_iterator it = children_.rbegin(),
+ rend = children_.rend();
+ it != rend; ++it) {
+ Window* child = *it;
+
+ if (for_event_handling) {
+ if (child->ignore_events_)
+ continue;
+ // The client may not allow events to be processed by certain subtrees.
+ client::EventClient* client = client::GetEventClient(GetRootWindow());
+ if (client && !client->CanProcessEventsWithinSubtree(child))
+ continue;
+ if (delegate_ && !delegate_->ShouldDescendIntoChildForEventHandling(
+ child, local_point)) {
+ continue;
+ }
+ }
+
+ gfx::Point point_in_child_coords(local_point);
+ ConvertPointToTarget(this, child, &point_in_child_coords);
+ Window* match = child->GetWindowForPoint(point_in_child_coords,
+ return_tightest,
+ for_event_handling);
+ if (match)
+ return match;
+ }
+
+ return delegate_ ? this : NULL;
+}
+
+void Window::RemoveChildImpl(Window* child, Window* new_parent) {
+ if (layout_manager_)
+ layout_manager_->OnWillRemoveWindowFromLayout(child);
+ FOR_EACH_OBSERVER(WindowObserver, observers_, OnWillRemoveWindow(child));
+ RootWindow* root_window = child->GetRootWindow();
+ RootWindow* new_root_window = new_parent ? new_parent->GetRootWindow() : NULL;
+ if (root_window && root_window != new_root_window) {
+ root_window->OnWindowRemovedFromRootWindow(child, new_root_window);
+ child->NotifyRemovingFromRootWindow();
+ }
+ child->parent_ = NULL;
+ // We should only remove the child's layer if the child still owns that layer.
+ // Someone else may have acquired ownership of it via AcquireLayer() and may
+ // expect the hierarchy to go unchanged as the Window is destroyed.
+ if (child->layer_owner_)
+ layer_->Remove(child->layer_);
+ Windows::iterator i = std::find(children_.begin(), children_.end(), child);
+ DCHECK(i != children_.end());
+ children_.erase(i);
+ child->OnParentChanged();
+ if (layout_manager_)
+ layout_manager_->OnWindowRemovedFromLayout(child);
+}
+
+void Window::OnParentChanged() {
+ FOR_EACH_OBSERVER(
+ WindowObserver, observers_, OnWindowParentChanged(this, parent_));
+}
+
+void Window::StackChildRelativeTo(Window* child,
+ Window* target,
+ StackDirection direction) {
+ DCHECK_NE(child, target);
+ DCHECK(child);
+ DCHECK(target);
+ DCHECK_EQ(this, child->parent());
+ DCHECK_EQ(this, target->parent());
+
+ const size_t target_i =
+ std::find(children_.begin(), children_.end(), target) - children_.begin();
+
+ // By convention we don't stack on top of windows with layers with NULL
+ // delegates. Walk backward to find a valid target window.
+ // See tests WindowTest.StackingMadrigal and StackOverClosingTransient
+ // for an explanation of this.
+ size_t final_target_i = target_i;
+ while (final_target_i > 0 &&
+ children_[final_target_i]->layer()->delegate() == NULL) {
+ --final_target_i;
+ }
+
+ // Allow stacking immediately below a window with a NULL layer.
+ if (direction == STACK_BELOW && target_i != final_target_i)
+ direction = STACK_ABOVE;
+
+ Window* final_target = children_[final_target_i];
+
+ // If we couldn't find a valid target position, don't move anything.
+ if (final_target->layer()->delegate() == NULL)
+ return;
+
+ // Don't try to stack a child above itself.
+ if (child == final_target)
+ return;
+
+ // Move the child and all its transients.
+ StackChildRelativeToImpl(child, final_target, direction);
+}
+
+void Window::StackChildRelativeToImpl(Window* child,
+ Window* target,
+ StackDirection direction) {
+ DCHECK_NE(child, target);
+ DCHECK(child);
+ DCHECK(target);
+ DCHECK_EQ(this, child->parent());
+ DCHECK_EQ(this, target->parent());
+
+ const size_t child_i =
+ std::find(children_.begin(), children_.end(), child) - children_.begin();
+ const size_t target_i =
+ std::find(children_.begin(), children_.end(), target) - children_.begin();
+
+ // Don't move the child if it is already in the right place.
+ if ((direction == STACK_ABOVE && child_i == target_i + 1) ||
+ (direction == STACK_BELOW && child_i + 1 == target_i))
+ return;
+
+ const size_t dest_i =
+ direction == STACK_ABOVE ?
+ (child_i < target_i ? target_i : target_i + 1) :
+ (child_i < target_i ? target_i - 1 : target_i);
+ children_.erase(children_.begin() + child_i);
+ children_.insert(children_.begin() + dest_i, child);
+
+ if (direction == STACK_ABOVE)
+ layer()->StackAbove(child->layer(), target->layer());
+ else
+ layer()->StackBelow(child->layer(), target->layer());
+
+ // Stack any transient children that share the same parent to be in front of
+ // 'child'.
+ Window* last_transient = child;
+ for (Windows::iterator it = child->transient_children_.begin();
+ it != child->transient_children_.end(); ++it) {
+ Window* transient_child = *it;
+ if (transient_child->parent_ == this) {
+ StackChildRelativeToImpl(transient_child, last_transient, STACK_ABOVE);
+ last_transient = transient_child;
+ }
+ }
+
+ child->OnStackingChanged();
+}
+
+void Window::OnStackingChanged() {
+ FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowStackingChanged(this));
+}
+
+void Window::NotifyRemovingFromRootWindow() {
+ FOR_EACH_OBSERVER(WindowObserver, observers_,
+ OnWindowRemovingFromRootWindow(this));
+ for (Window::Windows::const_iterator it = children_.begin();
+ it != children_.end(); ++it) {
+ (*it)->NotifyRemovingFromRootWindow();
+ }
+}
+
+void Window::NotifyAddedToRootWindow() {
+ FOR_EACH_OBSERVER(WindowObserver, observers_,
+ OnWindowAddedToRootWindow(this));
+ for (Window::Windows::const_iterator it = children_.begin();
+ it != children_.end(); ++it) {
+ (*it)->NotifyAddedToRootWindow();
+ }
+}
+
+void Window::NotifyWindowHierarchyChange(
+ const WindowObserver::HierarchyChangeParams& params) {
+ params.target->NotifyWindowHierarchyChangeDown(params);
+ switch (params.phase) {
+ case WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGING:
+ if (params.old_parent)
+ params.old_parent->NotifyWindowHierarchyChangeUp(params);
+ break;
+ case WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGED:
+ if (params.new_parent)
+ params.new_parent->NotifyWindowHierarchyChangeUp(params);
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+void Window::NotifyWindowHierarchyChangeDown(
+ const WindowObserver::HierarchyChangeParams& params) {
+ NotifyWindowHierarchyChangeAtReceiver(params);
+ for (Window::Windows::const_iterator it = children_.begin();
+ it != children_.end(); ++it) {
+ (*it)->NotifyWindowHierarchyChangeDown(params);
+ }
+}
+
+void Window::NotifyWindowHierarchyChangeUp(
+ const WindowObserver::HierarchyChangeParams& params) {
+ for (Window* window = this; window; window = window->parent())
+ window->NotifyWindowHierarchyChangeAtReceiver(params);
+}
+
+void Window::NotifyWindowHierarchyChangeAtReceiver(
+ const WindowObserver::HierarchyChangeParams& params) {
+ WindowObserver::HierarchyChangeParams local_params = params;
+ local_params.receiver = this;
+
+ switch (params.phase) {
+ case WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGING:
+ FOR_EACH_OBSERVER(WindowObserver, observers_,
+ OnWindowHierarchyChanging(local_params));
+ break;
+ case WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGED:
+ FOR_EACH_OBSERVER(WindowObserver, observers_,
+ OnWindowHierarchyChanged(local_params));
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+void Window::NotifyWindowVisibilityChanged(aura::Window* target,
+ bool visible) {
+ if (!NotifyWindowVisibilityChangedDown(target, visible)) {
+ return; // |this| has been deleted.
+ }
+ NotifyWindowVisibilityChangedUp(target, visible);
+}
+
+bool Window::NotifyWindowVisibilityChangedAtReceiver(aura::Window* target,
+ bool visible) {
+ // |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, visible));
+ return tracker.Contains(this);
+}
+
+bool Window::NotifyWindowVisibilityChangedDown(aura::Window* target,
+ bool visible) {
+ if (!NotifyWindowVisibilityChangedAtReceiver(target, visible))
+ return false; // |this| was deleted.
+ std::set<const Window*> child_already_processed;
+ bool child_destroyed = false;
+ do {
+ child_destroyed = false;
+ for (Window::Windows::const_iterator it = children_.begin();
+ it != children_.end(); ++it) {
+ if (!child_already_processed.insert(*it).second)
+ continue;
+ if (!(*it)->NotifyWindowVisibilityChangedDown(target, visible)) {
+ // |*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(aura::Window* target,
+ bool visible) {
+ for (Window* window = this; window; window = window->parent()) {
+ bool ret = window->NotifyWindowVisibilityChangedAtReceiver(target, visible);
+ DCHECK(ret);
+ }
+}
+
+void Window::OnLayerBoundsChanged(const gfx::Rect& old_bounds,
+ bool contained_mouse) {
+ if (layout_manager_)
+ layout_manager_->OnWindowResized();
+ if (delegate_)
+ delegate_->OnBoundsChanged(old_bounds, bounds());
+ FOR_EACH_OBSERVER(WindowObserver,
+ observers_,
+ OnWindowBoundsChanged(this, old_bounds, bounds()));
+ RootWindow* root_window = GetRootWindow();
+ if (root_window)
+ root_window->OnWindowBoundsChanged(this, contained_mouse);
+}
+
+void Window::OnPaintLayer(gfx::Canvas* canvas) {
+ if (delegate_)
+ delegate_->OnPaint(canvas);
+}
+
+base::Closure Window::PrepareForLayerBoundsChange() {
+ return base::Bind(&Window::OnLayerBoundsChanged, base::Unretained(this),
+ bounds(), ContainsMouse());
+}
+
+bool Window::CanAcceptEvent(const ui::Event& event) {
+ // The client may forbid certain windows from receiving events at a given
+ // point in time.
+ client::EventClient* client = client::GetEventClient(GetRootWindow());
+ if (client && !client->CanProcessEventsWithinSubtree(this))
+ return false;
+
+ bool visible = event.dispatch_to_hidden_targets() || IsVisible();
+ return visible && (!parent_ || parent_->CanAcceptEvent(event));
+}
+
+ui::EventTarget* Window::GetParentTarget() {
+ return parent_;
+}
+
+void Window::UpdateLayerName(const std::string& name) {
+#if !defined(NDEBUG)
+ DCHECK(layer());
+
+ std::string layer_name(name_);
+ if (layer_name.empty())
+ layer_name.append("Unnamed Window");
+
+ if (id_ != -1) {
+ char id_buf[10];
+ base::snprintf(id_buf, sizeof(id_buf), " %d", id_);
+ layer_name.append(id_buf);
+ }
+ layer()->set_name(layer_name);
+#endif
+}
+
+bool Window::ContainsMouse() {
+ bool contains_mouse = false;
+ if (IsVisible()) {
+ RootWindow* root_window = GetRootWindow();
+ contains_mouse = root_window &&
+ ContainsPointInRoot(root_window->GetLastMouseLocationInRoot());
+ }
+ return contains_mouse;
+}
+
+} // namespace aura
diff --git a/chromium/ui/aura/window.h b/chromium/ui/aura/window.h
new file mode 100644
index 00000000000..92dfa90b9c4
--- /dev/null
+++ b/chromium/ui/aura/window.h
@@ -0,0 +1,529 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_WINDOW_H_
+#define UI_AURA_WINDOW_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "base/strings/string16.h"
+#include "ui/aura/aura_export.h"
+#include "ui/aura/client/window_types.h"
+#include "ui/aura/window_observer.h"
+#include "ui/base/events/event_constants.h"
+#include "ui/base/events/event_target.h"
+#include "ui/base/gestures/gesture_types.h"
+#include "ui/compositor/layer_animator.h"
+#include "ui/compositor/layer_delegate.h"
+#include "ui/compositor/layer_owner.h"
+#include "ui/compositor/layer_type.h"
+#include "ui/gfx/insets.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/rect.h"
+
+namespace gfx {
+class Display;
+class Transform;
+}
+
+namespace ui {
+class EventHandler;
+class Layer;
+class Texture;
+}
+
+namespace aura {
+
+class LayoutManager;
+class RootWindow;
+class WindowDelegate;
+class WindowObserver;
+
+// Defined in window_property.h (which we do not include)
+template<typename T>
+struct WindowProperty;
+
+namespace test {
+class WindowTestApi;
+}
+
+// Aura window implementation. Interesting events are sent to the
+// WindowDelegate.
+// TODO(beng): resolve ownership.
+class AURA_EXPORT Window : public ui::LayerDelegate,
+ public ui::LayerOwner,
+ public ui::EventTarget,
+ public ui::GestureConsumer {
+ public:
+ typedef std::vector<Window*> Windows;
+
+ explicit Window(WindowDelegate* delegate);
+ virtual ~Window();
+
+ // Initializes the window. This creates the window's layer.
+ void Init(ui::LayerType layer_type);
+
+ // Creates a new layer for the window. Erases the layer-owned bounds, so the
+ // caller may wish to set new bounds and other state on the window/layer.
+ // Returns the old layer, which can be used for animations. Caller owns the
+ // memory for the returned layer and must delete it when animation completes.
+ // Returns NULL and does not recreate layer if window does not own its layer.
+ ui::Layer* RecreateLayer() WARN_UNUSED_RESULT;
+
+ void set_owned_by_parent(bool owned_by_parent) {
+ owned_by_parent_ = owned_by_parent;
+ }
+
+ // A type is used to identify a class of Windows and customize behavior such
+ // as event handling and parenting. This field should only be consumed by the
+ // shell -- Aura itself shouldn't contain type-specific logic.
+ client::WindowType type() const { return type_; }
+ void SetType(client::WindowType type);
+
+ int id() const { return id_; }
+ void set_id(int id) { id_ = id; }
+
+ const std::string& name() const { return name_; }
+ void SetName(const std::string& name);
+
+ const base::string16 title() const { return title_; }
+ void set_title(const base::string16& title) { title_ = title; }
+
+ bool transparent() const { return transparent_; }
+ void SetTransparent(bool transparent);
+
+ WindowDelegate* delegate() { return delegate_; }
+
+ const gfx::Rect& bounds() const;
+
+ Window* parent() { return parent_; }
+ const Window* parent() const { return parent_; }
+
+ // Returns the RootWindow that contains this Window or NULL if the Window is
+ // not contained by a RootWindow.
+ virtual RootWindow* GetRootWindow();
+ virtual const RootWindow* GetRootWindow() const;
+
+ // The Window does not own this object.
+ void set_user_data(void* user_data) { user_data_ = user_data; }
+ void* user_data() const { return user_data_; }
+
+ // Changes the visibility of the window.
+ void Show();
+ void Hide();
+ // Returns true if this window and all its ancestors are visible.
+ bool IsVisible() const;
+ // Returns the visibility requested by this window. IsVisible() takes into
+ // account the visibility of the layer and ancestors, where as this tracks
+ // whether Show() without a Hide() has been invoked.
+ bool TargetVisibility() const { return visible_; }
+
+ // Returns the window's bounds in root window's coordinates.
+ gfx::Rect GetBoundsInRootWindow() const;
+
+ // Returns the window's bounds in screen coordinates.
+ // How the root window's coordinates is mapped to screen's coordinates
+ // is platform dependent and defined in the implementation of the
+ // |aura::client::ScreenPositionClient| interface.
+ gfx::Rect GetBoundsInScreen() const;
+
+ virtual void SetTransform(const gfx::Transform& transform);
+
+ // Assigns a LayoutManager to size and place child windows.
+ // The Window takes ownership of the LayoutManager.
+ void SetLayoutManager(LayoutManager* layout_manager);
+ LayoutManager* layout_manager() { return layout_manager_.get(); }
+
+ // Changes the bounds of the window. If present, the window's parent's
+ // LayoutManager may adjust the bounds.
+ void SetBounds(const gfx::Rect& new_bounds);
+
+ // Changes the bounds of the window in the screen coordintates.
+ // If present, the window's parent's LayoutManager may adjust the bounds.
+ void SetBoundsInScreen(const gfx::Rect& new_bounds_in_screen_coords,
+ const gfx::Display& dst_display);
+
+ // Returns the target bounds of the window. If the window's layer is
+ // not animating, it simply returns the current bounds.
+ gfx::Rect GetTargetBounds() const;
+
+ // Marks the a portion of window as needing to be painted.
+ void SchedulePaintInRect(const gfx::Rect& rect);
+
+ // Places this window per |root_window|'s stacking client. The final location
+ // may be a RootWindow other than the one passed in. |root_window| may not be
+ // NULL. |bounds_in_screen| may be empty; it is more optional context that
+ // may, but isn't necessarily used.
+ void SetDefaultParentByRootWindow(RootWindow* root_window,
+ const gfx::Rect& bounds_in_screen);
+
+ // Stacks the specified child of this Window at the front of the z-order.
+ void StackChildAtTop(Window* child);
+
+ // Stacks |child| above |target|. Does nothing if |child| is already above
+ // |target|. Does not stack on top of windows with NULL layer delegates,
+ // see WindowTest.StackingMadrigal for details.
+ void StackChildAbove(Window* child, Window* target);
+
+ // Stacks the specified child of this window at the bottom of the z-order.
+ void StackChildAtBottom(Window* child);
+
+ // Stacks |child| below |target|. Does nothing if |child| is already below
+ // |target|.
+ void StackChildBelow(Window* child, Window* target);
+
+ // Tree operations.
+ void AddChild(Window* child);
+ void RemoveChild(Window* child);
+
+ const Windows& children() const { return children_; }
+
+ // Returns true if this Window contains |other| somewhere in its children.
+ bool Contains(const Window* other) const;
+
+ // Adds or removes |child| as a transient child of this window. Transient
+ // children get the following behavior:
+ // . The transient parent destroys any transient children when it is
+ // destroyed. This means a transient child is destroyed if either its parent
+ // or transient parent is destroyed.
+ // . If a transient child and its transient parent share the same parent, then
+ // transient children are always ordered above the transient parent.
+ // Transient windows are typically used for popups and menus.
+ void AddTransientChild(Window* child);
+ void RemoveTransientChild(Window* child);
+
+ const Windows& transient_children() const { return transient_children_; }
+
+ Window* transient_parent() { return transient_parent_; }
+ const Window* transient_parent() const { return transient_parent_; }
+
+ // Retrieves the first-level child with the specified id, or NULL if no first-
+ // level child is found matching |id|.
+ Window* GetChildById(int id);
+ const Window* GetChildById(int id) const;
+
+ // Converts |point| from |source|'s coordinates to |target|'s. If |source| is
+ // NULL, the function returns without modifying |point|. |target| cannot be
+ // NULL.
+ static void ConvertPointToTarget(const Window* source,
+ const Window* target,
+ gfx::Point* point);
+
+ // Moves the cursor to the specified location relative to the window.
+ virtual void MoveCursorTo(const gfx::Point& point_in_window);
+
+ // Returns the cursor for the specified point, in window coordinates.
+ gfx::NativeCursor GetCursor(const gfx::Point& point) const;
+
+ // Sets an 'event filter' for the window. An 'event filter' for a Window is
+ // a pre-target event handler, where the window owns the handler. A window
+ // can have only one such event filter. Setting a new filter removes and
+ // destroys any previously installed filter.
+ void SetEventFilter(ui::EventHandler* event_filter);
+
+ // Add/remove observer.
+ void AddObserver(WindowObserver* observer);
+ void RemoveObserver(WindowObserver* observer);
+ bool HasObserver(WindowObserver* observer);
+
+ void set_ignore_events(bool ignore_events) { ignore_events_ = ignore_events; }
+
+ // Sets the window to grab hits for mouse and touch to an area extending
+ // -|mouse_insets| and -|touch_insets| pixels outside its bounds. This can be
+ // used to create an invisible non-client area, for example if your windows
+ // have no visible frames but still need to have resize edges.
+ void SetHitTestBoundsOverrideOuter(const gfx::Insets& mouse_insets,
+ const gfx::Insets& touch_insets) {
+ hit_test_bounds_override_outer_mouse_ = mouse_insets;
+ hit_test_bounds_override_outer_touch_ = touch_insets;
+ }
+
+ gfx::Insets hit_test_bounds_override_outer_touch() const {
+ return hit_test_bounds_override_outer_touch_;
+ }
+
+ gfx::Insets hit_test_bounds_override_outer_mouse() const {
+ return hit_test_bounds_override_outer_mouse_;
+ }
+
+ // Sets the window to grab hits for an area extending |insets| pixels inside
+ // its bounds (even if that inner region overlaps a child window). This can be
+ // used to create an invisible non-client area that overlaps the client area.
+ void set_hit_test_bounds_override_inner(const gfx::Insets& insets) {
+ hit_test_bounds_override_inner_ = insets;
+ }
+ gfx::Insets hit_test_bounds_override_inner() const {
+ return hit_test_bounds_override_inner_;
+ }
+
+ // Returns true if the |point_in_root| in root window's coordinate falls
+ // within this window's bounds. Returns false if the window is detached
+ // from root window.
+ bool ContainsPointInRoot(const gfx::Point& point_in_root) const;
+
+ // Returns true if relative-to-this-Window's-origin |local_point| falls
+ // within this Window's bounds.
+ bool ContainsPoint(const gfx::Point& local_point) const;
+
+ // Returns true if the mouse pointer at relative-to-this-Window's-origin
+ // |local_point| can trigger an event for this Window.
+ // TODO(beng): A Window can supply a hit-test mask to cause some portions of
+ // itself to not trigger events, causing the events to fall through to the
+ // Window behind.
+ bool HitTest(const gfx::Point& local_point);
+
+ // Returns the Window that most closely encloses |local_point| for the
+ // purposes of event targeting.
+ Window* GetEventHandlerForPoint(const gfx::Point& local_point);
+
+ // Returns the topmost Window with a delegate containing |local_point|.
+ Window* GetTopWindowContainingPoint(const gfx::Point& local_point);
+
+ // Returns this window's toplevel window (the highest-up-the-tree anscestor
+ // that has a delegate set). The toplevel window may be |this|.
+ Window* GetToplevelWindow();
+
+ // Claims or relinquishes the claim to focus.
+ void Focus();
+ void Blur();
+
+ // Returns true if the Window is currently the focused window.
+ bool HasFocus() const;
+
+ // Returns true if the Window can be focused.
+ virtual bool CanFocus() const;
+
+ // Returns true if the Window can receive events.
+ virtual bool CanReceiveEvents() const;
+
+ // Does a capture on the window. This does nothing if the window isn't showing
+ // (VISIBILITY_SHOWN) or isn't contained in a valid window hierarchy.
+ void SetCapture();
+
+ // Releases a capture.
+ void ReleaseCapture();
+
+ // Returns true if this window has capture.
+ bool HasCapture();
+
+ // Suppresses painting window content by disgarding damaged rect and ignoring
+ // new paint requests.
+ void SuppressPaint();
+
+ // 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.
+ template<typename T>
+ void SetProperty(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.
+ template<typename T>
+ T GetProperty(const WindowProperty<T>* property) const;
+
+ // Sets the |property| to its default value. Useful for avoiding a cast when
+ // setting to NULL.
+ template<typename T>
+ void ClearProperty(const WindowProperty<T>* property);
+
+ // NativeWidget::[GS]etNativeWindowProperty use strings as keys, and this is
+ // difficult to change while retaining compatibility with other platforms.
+ // TODO(benrg): Find a better solution.
+ void SetNativeWindowProperty(const char* key, void* value);
+ void* GetNativeWindowProperty(const char* key) const;
+
+ // Type of a function to delete a property that this window owns.
+ typedef void (*PropertyDeallocator)(int64 value);
+
+ // Overridden from ui::LayerDelegate:
+ virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE;
+
+#ifndef NDEBUG
+ // These methods are useful when debugging.
+ std::string GetDebugInfo() const;
+ void PrintWindowHierarchy(int depth) const;
+#endif
+
+ private:
+ friend class test::WindowTestApi;
+ friend class LayoutManager;
+
+ // Used when stacking windows.
+ enum StackDirection {
+ STACK_ABOVE,
+ STACK_BELOW
+ };
+
+ // Called by the public {Set,Get,Clear}Property functions.
+ int64 SetPropertyInternal(const void* key,
+ const char* name,
+ PropertyDeallocator deallocator,
+ int64 value,
+ int64 default_value);
+ int64 GetPropertyInternal(const void* key, int64 default_value) const;
+
+ // Changes the bounds of the window without condition.
+ void SetBoundsInternal(const gfx::Rect& new_bounds);
+
+ // Updates the visible state of the layer, but does not make visible-state
+ // specific changes. Called from Show()/Hide().
+ void SetVisible(bool visible);
+
+ // Schedules a paint for the Window's entire bounds.
+ void SchedulePaint();
+
+ // Gets a Window (either this one or a subwindow) containing |local_point|.
+ // If |return_tightest| is true, returns the tightest-containing (i.e.
+ // furthest down the hierarchy) Window containing the point; otherwise,
+ // returns the loosest. If |for_event_handling| is true, then hit-test masks
+ // are honored; otherwise, only bounds checks are performed.
+ Window* GetWindowForPoint(const gfx::Point& local_point,
+ bool return_tightest,
+ bool for_event_handling);
+
+ // Implementation of RemoveChild(). If |child| is being removed as the result
+ // of an add, |new_parent| is the new parent |child| is going to be parented
+ // to.
+ void RemoveChildImpl(Window* child, Window* new_parent);
+
+ // Called when this window's parent has changed.
+ void OnParentChanged();
+
+ // Determines the real location for stacking |child| and invokes
+ // StackChildRelativeToImpl().
+ void StackChildRelativeTo(Window* child,
+ Window* target,
+ StackDirection direction);
+
+ // Implementation of StackChildRelativeTo().
+ void StackChildRelativeToImpl(Window* child,
+ Window* target,
+ StackDirection direction);
+
+ // Called when this window's stacking order among its siblings is changed.
+ void OnStackingChanged();
+
+ // Notifies observers registered with this Window (and its subtree) when the
+ // Window has been added or is about to be removed from a RootWindow.
+ void NotifyRemovingFromRootWindow();
+ void NotifyAddedToRootWindow();
+
+ // Methods implementing hierarchy change notifications. See WindowObserver for
+ // more details.
+ void NotifyWindowHierarchyChange(
+ const WindowObserver::HierarchyChangeParams& params);
+ // Notifies this window and its child hierarchy.
+ void NotifyWindowHierarchyChangeDown(
+ const WindowObserver::HierarchyChangeParams& params);
+ // Notifies this window and its parent hierarchy.
+ void NotifyWindowHierarchyChangeUp(
+ const WindowObserver::HierarchyChangeParams& params);
+ // Notifies this window's observers.
+ void NotifyWindowHierarchyChangeAtReceiver(
+ const WindowObserver::HierarchyChangeParams& params);
+
+ // Methods implementing visibility change notifications. See WindowObserver
+ // for more details.
+ void NotifyWindowVisibilityChanged(aura::Window* target, bool visible);
+ // Notifies this window's observers. Returns false if |this| was deleted
+ // during the call (by an observer), otherwise true.
+ bool NotifyWindowVisibilityChangedAtReceiver(aura::Window* target,
+ bool visible);
+ // Notifies this window and its child hierarchy. Returns false if
+ // |this| was deleted during the call (by an observer), otherwise
+ // true.
+ bool NotifyWindowVisibilityChangedDown(aura::Window* target, bool visible);
+ // Notifies this window and its parent hierarchy.
+ void NotifyWindowVisibilityChangedUp(aura::Window* target, bool visible);
+
+ // Invoked from the closure returned by PrepareForLayerBoundsChange() after
+ // the bounds of the layer has changed. |old_bounds| is the previous bounds of
+ // the layer, and |contained_mouse| is true if the mouse was previously within
+ // the window's bounds.
+ void OnLayerBoundsChanged(const gfx::Rect& old_bounds, bool contained_mouse);
+
+ // Overridden from ui::LayerDelegate:
+ virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE;
+ virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE;
+
+ // Overridden from ui::EventTarget:
+ virtual bool CanAcceptEvent(const ui::Event& event) OVERRIDE;
+ virtual EventTarget* GetParentTarget() OVERRIDE;
+
+ // Updates the layer name with a name based on the window's name and id.
+ void UpdateLayerName(const std::string& name);
+
+ // Returns true if the mouse is currently within our bounds.
+ bool ContainsMouse();
+
+ client::WindowType type_;
+
+ // True if the Window is owned by its parent - i.e. it will be deleted by its
+ // parent during its parents destruction. True is the default.
+ bool owned_by_parent_;
+
+ WindowDelegate* delegate_;
+
+ // The Window's parent.
+ Window* parent_;
+
+ // Child windows. Topmost is last.
+ Windows children_;
+
+ // Transient windows.
+ Windows transient_children_;
+
+ Window* transient_parent_;
+
+ // The visibility state of the window as set by Show()/Hide(). This may differ
+ // from the visibility of the underlying layer, which may remain visible after
+ // the window is hidden (e.g. to animate its disappearance).
+ bool visible_;
+
+ int id_;
+ std::string name_;
+
+ base::string16 title_;
+
+ // Whether layer is initialized as non-opaque.
+ bool transparent_;
+
+ scoped_ptr<ui::EventHandler> event_filter_;
+ scoped_ptr<LayoutManager> layout_manager_;
+
+ void* user_data_;
+
+ // Makes the window pass all events through to any windows behind it.
+ bool ignore_events_;
+
+ // See set_hit_test_outer_override().
+ gfx::Insets hit_test_bounds_override_outer_mouse_;
+ gfx::Insets hit_test_bounds_override_outer_touch_;
+ gfx::Insets hit_test_bounds_override_inner_;
+
+ ObserverList<WindowObserver> observers_;
+
+ // 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 value;
+ PropertyDeallocator deallocator;
+ };
+
+ std::map<const void*, Value> prop_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(Window);
+};
+
+} // namespace aura
+
+#endif // UI_AURA_WINDOW_H_
diff --git a/chromium/ui/aura/window_delegate.h b/chromium/ui/aura/window_delegate.h
new file mode 100644
index 00000000000..bcbd2a6984c
--- /dev/null
+++ b/chromium/ui/aura/window_delegate.h
@@ -0,0 +1,108 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_WINDOW_DELEGATE_H_
+#define UI_AURA_WINDOW_DELEGATE_H_
+
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "ui/aura/aura_export.h"
+#include "ui/base/events/event_constants.h"
+#include "ui/base/events/event_handler.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace gfx {
+class Canvas;
+class Path;
+class Point;
+class Rect;
+class Size;
+}
+
+namespace ui {
+class GestureEvent;
+class KeyEvent;
+class MouseEvent;
+class Texture;
+class TouchEvent;
+}
+
+namespace aura {
+
+// Delegate interface for aura::Window.
+class AURA_EXPORT WindowDelegate : public ui::EventHandler {
+ public:
+ // Returns the window's minimum size, or size 0,0 if there is no limit.
+ virtual gfx::Size GetMinimumSize() const = 0;
+
+ // Returns the window's maximum size, or size 0,0 if there is no limit.
+ virtual gfx::Size GetMaximumSize() const = 0;
+
+ // Called when the Window's position and/or size changes.
+ virtual void OnBoundsChanged(const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) = 0;
+
+ // Returns the native cursor for the specified point, in window coordinates,
+ // or NULL for the default cursor.
+ virtual gfx::NativeCursor GetCursor(const gfx::Point& point) = 0;
+
+ // Returns the non-client component (see hit_test.h) containing |point|, in
+ // window coordinates.
+ virtual int GetNonClientComponent(const gfx::Point& point) const = 0;
+
+ // Returns true if event handling should descend into |child|. |location| is
+ // in terms of the Window.
+ virtual bool ShouldDescendIntoChildForEventHandling(
+ Window* child,
+ const gfx::Point& location) = 0;
+
+ // Returns true of the window can be focused.
+ virtual bool CanFocus() = 0;
+
+ // Invoked when mouse capture is lost on the window.
+ virtual void OnCaptureLost() = 0;
+
+ // Asks the delegate to paint window contents into the supplied canvas.
+ virtual void OnPaint(gfx::Canvas* canvas) = 0;
+
+ // Called when the window's device scale factor has changed.
+ virtual void OnDeviceScaleFactorChanged(float device_scale_factor) = 0;
+
+ // Called from Window's destructor before OnWindowDestroyed and before the
+ // children have been destroyed and the window has been removed from its
+ // parent.
+ virtual void OnWindowDestroying() = 0;
+
+ // Called when the Window has been destroyed (i.e. from its destructor). This
+ // is called after OnWindowDestroying and after the children have been
+ // deleted and the window has been removed from its parent.
+ // The delegate can use this as an opportunity to delete itself if necessary.
+ virtual void OnWindowDestroyed() = 0;
+
+ // Called when the TargetVisibility() of a Window changes. |visible|
+ // corresponds to the target visibility of the window. See
+ // Window::TargetVisibility() for details.
+ virtual void OnWindowTargetVisibilityChanged(bool visible) = 0;
+
+ // Called from Window::HitTest to check if the window has a custom hit test
+ // mask. It works similar to the views counterparts. That is, if the function
+ // returns true, GetHitTestMask below will be called to get the mask.
+ // Otherwise, Window will hit-test against its bounds.
+ virtual bool HasHitTestMask() const = 0;
+
+ // Called from Window::HitTest to retrieve hit test mask when HasHitTestMask
+ // above returns true.
+ virtual void GetHitTestMask(gfx::Path* mask) const = 0;
+
+ // Called from RecreateLayer() if the layer the window is associated with has
+ // an external texture.
+ virtual scoped_refptr<ui::Texture> CopyTexture() = 0;
+
+ protected:
+ virtual ~WindowDelegate() {}
+};
+
+} // namespace aura
+
+#endif // UI_AURA_WINDOW_DELEGATE_H_
diff --git a/chromium/ui/aura/window_observer.h b/chromium/ui/aura/window_observer.h
new file mode 100644
index 00000000000..5008ac514d5
--- /dev/null
+++ b/chromium/ui/aura/window_observer.h
@@ -0,0 +1,112 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_WINDOW_OBSERVER_H_
+#define UI_AURA_WINDOW_OBSERVER_H_
+
+#include "base/basictypes.h"
+#include "ui/aura/aura_export.h"
+
+namespace gfx {
+class Rect;
+} // namespace gfx
+
+namespace aura {
+
+class Window;
+
+class AURA_EXPORT WindowObserver {
+ public:
+ struct HierarchyChangeParams {
+ enum HierarchyChangePhase {
+ HIERARCHY_CHANGING,
+ HIERARCHY_CHANGED
+ };
+
+ Window* target; // The window that was added or removed.
+ Window* new_parent;
+ Window* old_parent;
+ HierarchyChangePhase phase;
+ Window* receiver; // The window receiving the notification.
+ };
+
+ // Called when a window is added or removed. Notifications are sent to the
+ // following hierarchies in this order:
+ // 1. |target|.
+ // 2. |target|'s child hierarchy.
+ // 3. |target|'s parent hierarchy in its |old_parent|
+ // (only for Changing notifications).
+ // 3. |target|'s parent hierarchy in its |new_parent|.
+ // (only for Changed notifications).
+ // This sequence is performed via the Changing and Changed notifications below
+ // before and after the change is committed.
+ virtual void OnWindowHierarchyChanging(const HierarchyChangeParams& params) {}
+ virtual void OnWindowHierarchyChanged(const HierarchyChangeParams& params) {}
+
+ // Invoked when |new_window| has been added as a child of this window.
+ virtual void OnWindowAdded(Window* new_window) {}
+
+ // Invoked prior to removing |window| as a child of this window.
+ virtual void OnWillRemoveWindow(Window* window) {}
+
+ // Invoked when this window's parent window changes. |parent| may be NULL.
+ virtual void OnWindowParentChanged(Window* window, Window* parent) {}
+
+ // Invoked when SetProperty(), ClearProperty(), or
+ // NativeWidgetAura::SetNativeWindowProperty() is called on the window.
+ // |key| is either a WindowProperty<T>* (SetProperty, ClearProperty)
+ // or a const char* (SetNativeWindowProperty). 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 OnWindowPropertyChanged(Window* window,
+ const void* key,
+ intptr_t old) {}
+
+ // Invoked when SetVisible() is invoked on a window. |visible| is the
+ // value supplied to SetVisible(). If |visible| is true, window->IsVisible()
+ // may still return false. See description in Window::IsVisible() for details.
+ virtual void OnWindowVisibilityChanging(Window* window, bool visible) {}
+ virtual void OnWindowVisibilityChanged(Window* window, bool visible) {}
+
+ // Invoked when SetBounds() is invoked on |window|. |old_bounds| and
+ // |new_bounds| are in parent coordinates.
+ virtual void OnWindowBoundsChanged(Window* window,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {}
+
+ // Invoked when |window|'s position among its siblings in the stacking order
+ // has changed.
+ virtual void OnWindowStackingChanged(Window* window) {}
+
+ // Invoked when a region of |window| is scheduled to be redrawn.
+ virtual void OnWindowPaintScheduled(Window* window,
+ const gfx::Rect& region) {}
+
+ // Invoked when the Window is being destroyed (i.e. from the start of its
+ // destructor). This is called before the window is removed from its parent.
+ virtual void OnWindowDestroying(Window* window) {}
+
+ // Invoked when the Window has been destroyed (i.e. at the end of its
+ // destructor). This is called after the window is removed from its parent.
+ virtual void OnWindowDestroyed(Window* window) {}
+
+ // Called when a Window has been added to a RootWindow.
+ virtual void OnWindowAddedToRootWindow(Window* window) {}
+
+ // Called when a Window is about to be removed from a RootWindow.
+ virtual void OnWindowRemovingFromRootWindow(Window* window) {}
+
+ // Called when a transient child is added to |window|.
+ virtual void OnAddTransientChild(Window* window, Window* transient) {}
+
+ // Called when a transient child is removed from |window|.
+ virtual void OnRemoveTransientChild(Window* window, Window* transient) {}
+
+ protected:
+ virtual ~WindowObserver() {}
+};
+
+} // namespace aura
+
+#endif // UI_AURA_WINDOW_OBSERVER_H_
diff --git a/chromium/ui/aura/window_property.h b/chromium/ui/aura/window_property.h
new file mode 100644
index 00000000000..0b1574e4f36
--- /dev/null
+++ b/chromium/ui/aura/window_property.h
@@ -0,0 +1,144 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_WINDOW_PROPERTY_H_
+#define UI_AURA_WINDOW_PROPERTY_H_
+
+#include "base/basictypes.h"
+#include "ui/aura/aura_export.h"
+#include "ui/aura/window.h"
+
+// This header should be included by code that defines WindowProperties. It
+// should not be included by code that only gets and sets WindowProperties.
+//
+// To define a new WindowProperty:
+//
+// #include "foo/foo_export.h"
+// #include "ui/aura/window_property.h"
+//
+// DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(FOO_EXPORT, MyType);
+// namespace foo {
+// // Use this to define an exported property that is premitive,
+// // or a pointer you don't want automatically deleted.
+// 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.
+// DEFINE_OWNED_WINDOW_PROPERTY_KEY(gfx::Rect, kRestoreBoundsKey, NULL);
+//
+// // 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.
+// DEFINE_LOCAL_WINDOW_PROPERTY_KEY(MyType, kMyKey, MyDefault);
+//
+// } // foo namespace
+//
+// To define a new type used for WindowProperty.
+//
+// // outside all namespaces:
+// DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(FOO_EXPORT, MyType)
+//
+// If a property type is not exported, use DECLARE_WINDOW_PROPERTY_TYPE(MyType)
+// which is a shorthand for DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(, MyType).
+
+namespace aura {
+namespace {
+
+// No single new-style cast works for every conversion to/from int64, 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 ToInt64(T x) { return static_cast<int64>(x); }
+ static T FromInt64(int64 x) { return static_cast<T>(x); }
+};
+template<typename T>
+class WindowPropertyCaster<T*> {
+ public:
+ static int64 ToInt64(T* x) { return reinterpret_cast<int64>(x); }
+ static T* FromInt64(int64 x) { return reinterpret_cast<T*>(x); }
+};
+template<>
+class WindowPropertyCaster<bool> {
+ public:
+ static int64 ToInt64(bool x) { return static_cast<int64>(x); }
+ static bool FromInt64(int64 x) { return x != 0; }
+};
+
+} // namespace
+
+template<typename T>
+struct WindowProperty {
+ T default_value;
+ const char* name;
+ Window::PropertyDeallocator deallocator;
+};
+
+template<typename T>
+void Window::SetProperty(const WindowProperty<T>* property, T value) {
+ int64 old = SetPropertyInternal(
+ property,
+ property->name,
+ value == property->default_value ? NULL : 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::GetProperty(const WindowProperty<T>* property) const {
+ return WindowPropertyCaster<T>::FromInt64(GetPropertyInternal(
+ property, WindowPropertyCaster<T>::ToInt64(property->default_value)));
+}
+
+template<typename T>
+void Window::ClearProperty(const WindowProperty<T>* property) {
+ SetProperty(property, property->default_value);
+}
+
+} // namespace aura
+
+// Macros to instantiate the property getter/setter template functions.
+#define DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(EXPORT, T) \
+ template EXPORT void aura::Window::SetProperty( \
+ const aura::WindowProperty<T >*, T); \
+ template EXPORT T aura::Window::GetProperty( \
+ const aura::WindowProperty<T >*) const; \
+ template EXPORT void aura::Window::ClearProperty( \
+ const aura::WindowProperty<T >*);
+#define DECLARE_WINDOW_PROPERTY_TYPE(T) \
+ DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(, T)
+
+#define DEFINE_WINDOW_PROPERTY_KEY(TYPE, NAME, DEFAULT) \
+ COMPILE_ASSERT(sizeof(TYPE) <= sizeof(int64), property_type_too_large); \
+ namespace { \
+ const aura::WindowProperty<TYPE> NAME ## _Value = {DEFAULT, #NAME, NULL}; \
+ } \
+ const aura::WindowProperty<TYPE>* const NAME = & NAME ## _Value;
+
+#define DEFINE_LOCAL_WINDOW_PROPERTY_KEY(TYPE, NAME, DEFAULT) \
+ COMPILE_ASSERT(sizeof(TYPE) <= sizeof(int64), property_type_too_large); \
+ namespace { \
+ const aura::WindowProperty<TYPE> NAME ## _Value = {DEFAULT, #NAME, NULL}; \
+ const aura::WindowProperty<TYPE>* const NAME = & NAME ## _Value; \
+ }
+
+#define DEFINE_OWNED_WINDOW_PROPERTY_KEY(TYPE, NAME, DEFAULT) \
+ namespace { \
+ void Deallocator ## NAME (int64 p) { \
+ enum { type_must_be_complete = sizeof(TYPE) }; \
+ delete aura::WindowPropertyCaster<TYPE*>::FromInt64(p); \
+ } \
+ const aura::WindowProperty<TYPE*> NAME ## _Value = \
+ {DEFAULT,#NAME,&Deallocator ## NAME}; \
+ } \
+ const aura::WindowProperty<TYPE*>* const NAME = & NAME ## _Value;
+
+#endif // UI_AURA_WINDOW_PROPERTY_H_
diff --git a/chromium/ui/aura/window_tracker.cc b/chromium/ui/aura/window_tracker.cc
new file mode 100644
index 00000000000..9653e5fcf9e
--- /dev/null
+++ b/chromium/ui/aura/window_tracker.cc
@@ -0,0 +1,43 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/window_tracker.h"
+
+#include "ui/aura/window.h"
+
+namespace aura {
+
+WindowTracker::WindowTracker() {
+}
+
+WindowTracker::~WindowTracker() {
+ for (Windows::iterator i = windows_.begin(); i != windows_.end(); ++i)
+ (*i)->RemoveObserver(this);
+}
+
+void WindowTracker::Add(Window* window) {
+ if (windows_.count(window))
+ return;
+
+ window->AddObserver(this);
+ windows_.insert(window);
+}
+
+void WindowTracker::Remove(Window* window) {
+ if (windows_.count(window)) {
+ windows_.erase(window);
+ window->RemoveObserver(this);
+ }
+}
+
+bool WindowTracker::Contains(Window* window) {
+ return windows_.count(window) > 0;
+}
+
+void WindowTracker::OnWindowDestroying(Window* window) {
+ DCHECK_GT(windows_.count(window), 0u);
+ Remove(window);
+}
+
+} // namespace aura
diff --git a/chromium/ui/aura/window_tracker.h b/chromium/ui/aura/window_tracker.h
new file mode 100644
index 00000000000..960549a0474
--- /dev/null
+++ b/chromium/ui/aura/window_tracker.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_WINDOW_LIFE_TRACKER_H_
+#define UI_AURA_WINDOW_LIFE_TRACKER_H_
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "ui/aura/aura_export.h"
+#include "ui/aura/window_observer.h"
+
+namespace aura {
+
+// This class keeps track of a set of windows. A Window is removed either
+// explicitly by Remove(), or implicitly when the window is destroyed.
+class AURA_EXPORT WindowTracker : public WindowObserver {
+ public:
+ typedef std::set<Window*> Windows;
+
+ WindowTracker();
+ virtual ~WindowTracker();
+
+ // Returns the set of windows being observed.
+ const std::set<Window*>& windows() const { return windows_; }
+
+ // Adds |window| to the set of Windows being tracked.
+ void Add(Window* window);
+
+ // Removes |window| from the set of windows being tracked.
+ void Remove(Window* window);
+
+ // Returns true if |window| was previously added and has not been removed or
+ // deleted.
+ bool Contains(Window* window);
+
+ // WindowObserver overrides:
+ virtual void OnWindowDestroying(Window* window) OVERRIDE;
+
+ private:
+ Windows windows_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowTracker);
+};
+
+} // namespace aura
+
+#endif // UI_AURA_WINDOW_LIFE_TRACKER_H_
diff --git a/chromium/ui/aura/window_unittest.cc b/chromium/ui/aura/window_unittest.cc
new file mode 100644
index 00000000000..320d1a61f7b
--- /dev/null
+++ b/chromium/ui/aura/window_unittest.cc
@@ -0,0 +1,3054 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/window.h"
+
+#include <utility>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/aura/client/capture_client.h"
+#include "ui/aura/client/focus_change_observer.h"
+#include "ui/aura/client/stacking_client.h"
+#include "ui/aura/client/visibility_client.h"
+#include "ui/aura/layout_manager.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/root_window_host.h"
+#include "ui/aura/root_window_observer.h"
+#include "ui/aura/test/aura_test_base.h"
+#include "ui/aura/test/event_generator.h"
+#include "ui/aura/test/test_window_delegate.h"
+#include "ui/aura/test/test_windows.h"
+#include "ui/aura/test/window_test_api.h"
+#include "ui/aura/window_delegate.h"
+#include "ui/aura/window_observer.h"
+#include "ui/aura/window_property.h"
+#include "ui/base/events/event.h"
+#include "ui/base/events/event_utils.h"
+#include "ui/base/gestures/gesture_configuration.h"
+#include "ui/base/hit_test.h"
+#include "ui/base/keycodes/keyboard_codes.h"
+#include "ui/compositor/layer.h"
+#include "ui/compositor/scoped_animation_duration_scale_mode.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/compositor/test/test_layers.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/screen.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(const char*)
+DECLARE_WINDOW_PROPERTY_TYPE(int)
+
+namespace aura {
+namespace test {
+
+class WindowTest : public AuraTestBase {
+ public:
+ WindowTest() : max_separation_(0) {
+ }
+
+ virtual void SetUp() OVERRIDE {
+ AuraTestBase::SetUp();
+ // TODO: there needs to be an easier way to do this.
+ max_separation_ = ui::GestureConfiguration::
+ max_separation_for_gesture_touches_in_pixels();
+ ui::GestureConfiguration::
+ set_max_separation_for_gesture_touches_in_pixels(0);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ AuraTestBase::TearDown();
+ ui::GestureConfiguration::
+ set_max_separation_for_gesture_touches_in_pixels(max_separation_);
+ }
+
+ // Adds |window| to |root_window_|, through the StackingClient.
+ void SetDefaultParentByPrimaryRootWindow(aura::Window* window) {
+ window->SetDefaultParentByRootWindow(root_window(), gfx::Rect());
+ }
+
+ private:
+ int max_separation_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowTest);
+};
+
+namespace {
+
+// Used for verifying destruction methods are invoked.
+class DestroyTrackingDelegateImpl : public TestWindowDelegate {
+ public:
+ DestroyTrackingDelegateImpl()
+ : destroying_count_(0),
+ destroyed_count_(0),
+ in_destroying_(false) {}
+
+ void clear_destroying_count() { destroying_count_ = 0; }
+ int destroying_count() const { return destroying_count_; }
+
+ void clear_destroyed_count() { destroyed_count_ = 0; }
+ int destroyed_count() const { return destroyed_count_; }
+
+ bool in_destroying() const { return in_destroying_; }
+
+ virtual void OnWindowDestroying() OVERRIDE {
+ EXPECT_FALSE(in_destroying_);
+ in_destroying_ = true;
+ destroying_count_++;
+ }
+
+ virtual void OnWindowDestroyed() OVERRIDE {
+ EXPECT_TRUE(in_destroying_);
+ in_destroying_ = false;
+ destroyed_count_++;
+ }
+
+ private:
+ int destroying_count_;
+ int destroyed_count_;
+ bool in_destroying_;
+
+ DISALLOW_COPY_AND_ASSIGN(DestroyTrackingDelegateImpl);
+};
+
+// Used to verify that when OnWindowDestroying is invoked the parent is also
+// is in the process of being destroyed.
+class ChildWindowDelegateImpl : public DestroyTrackingDelegateImpl {
+ public:
+ explicit ChildWindowDelegateImpl(
+ DestroyTrackingDelegateImpl* parent_delegate)
+ : parent_delegate_(parent_delegate) {
+ }
+
+ virtual void OnWindowDestroying() OVERRIDE {
+ EXPECT_TRUE(parent_delegate_->in_destroying());
+ DestroyTrackingDelegateImpl::OnWindowDestroying();
+ }
+
+ private:
+ DestroyTrackingDelegateImpl* parent_delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChildWindowDelegateImpl);
+};
+
+// Used to verify that a Window is removed from its parent when
+// OnWindowDestroyed is called.
+class DestroyOrphanDelegate : public TestWindowDelegate {
+ public:
+ DestroyOrphanDelegate() : window_(NULL) {
+ }
+
+ void set_window(Window* window) { window_ = window; }
+
+ virtual void OnWindowDestroyed() OVERRIDE {
+ EXPECT_FALSE(window_->parent());
+ }
+
+ private:
+ Window* window_;
+ DISALLOW_COPY_AND_ASSIGN(DestroyOrphanDelegate);
+};
+
+// Used in verifying mouse capture.
+class CaptureWindowDelegateImpl : public TestWindowDelegate {
+ public:
+ CaptureWindowDelegateImpl() {
+ ResetCounts();
+ }
+
+ void ResetCounts() {
+ capture_changed_event_count_ = 0;
+ capture_lost_count_ = 0;
+ mouse_event_count_ = 0;
+ touch_event_count_ = 0;
+ gesture_event_count_ = 0;
+ }
+
+ int capture_changed_event_count() const {
+ return capture_changed_event_count_;
+ }
+ int capture_lost_count() const { return capture_lost_count_; }
+ int mouse_event_count() const { return mouse_event_count_; }
+ int touch_event_count() const { return touch_event_count_; }
+ int gesture_event_count() const { return gesture_event_count_; }
+
+ virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
+ if (event->type() == ui::ET_MOUSE_CAPTURE_CHANGED)
+ capture_changed_event_count_++;
+ mouse_event_count_++;
+ }
+ virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE {
+ touch_event_count_++;
+ }
+ virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
+ gesture_event_count_++;
+ }
+ virtual void OnCaptureLost() OVERRIDE {
+ capture_lost_count_++;
+ }
+
+ private:
+ int capture_changed_event_count_;
+ int capture_lost_count_;
+ int mouse_event_count_;
+ int touch_event_count_;
+ int gesture_event_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(CaptureWindowDelegateImpl);
+};
+
+// aura::WindowDelegate that tracks the window that was reported as having the
+// focus before us.
+class FocusDelegate : public TestWindowDelegate,
+ public aura::client::FocusChangeObserver {
+ public:
+ FocusDelegate() : previous_focused_window_(NULL) {
+ }
+
+ aura::Window* previous_focused_window() const {
+ return previous_focused_window_;
+ }
+
+ // Overridden from client::FocusChangeObserver:
+ virtual void OnWindowFocused(Window* gained_focus,
+ Window* lost_focus) OVERRIDE {
+ previous_focused_window_ = lost_focus;
+ }
+
+ private:
+ aura::Window* previous_focused_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(FocusDelegate);
+};
+
+// Keeps track of the location of the gesture.
+class GestureTrackPositionDelegate : public TestWindowDelegate {
+ public:
+ GestureTrackPositionDelegate() {}
+
+ virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
+ position_ = event->location();
+ event->StopPropagation();
+ }
+
+ const gfx::Point& position() const { return position_; }
+
+ private:
+ gfx::Point position_;
+
+ DISALLOW_COPY_AND_ASSIGN(GestureTrackPositionDelegate);
+};
+
+base::TimeDelta getTime() {
+ return ui::EventTimeForNow();
+}
+
+class SelfEventHandlingWindowDelegate : public TestWindowDelegate {
+ public:
+ SelfEventHandlingWindowDelegate() {}
+
+ virtual bool ShouldDescendIntoChildForEventHandling(
+ Window* child,
+ const gfx::Point& location) OVERRIDE {
+ return false;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SelfEventHandlingWindowDelegate);
+};
+
+// The delegate deletes itself when the window is being destroyed.
+class DestroyWindowDelegate : public TestWindowDelegate {
+ public:
+ DestroyWindowDelegate() {}
+
+ private:
+ virtual ~DestroyWindowDelegate() {}
+
+ // Overridden from WindowDelegate.
+ virtual void OnWindowDestroyed() OVERRIDE {
+ delete this;
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(DestroyWindowDelegate);
+};
+
+} // namespace
+
+TEST_F(WindowTest, GetChildById) {
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, root_window()));
+ scoped_ptr<Window> w11(CreateTestWindowWithId(11, w1.get()));
+ scoped_ptr<Window> w111(CreateTestWindowWithId(111, w11.get()));
+ scoped_ptr<Window> w12(CreateTestWindowWithId(12, w1.get()));
+
+ EXPECT_EQ(NULL, w1->GetChildById(57));
+ EXPECT_EQ(w12.get(), w1->GetChildById(12));
+ EXPECT_EQ(w111.get(), w1->GetChildById(111));
+}
+
+// Make sure that Window::Contains correctly handles children, grandchildren,
+// and not containing NULL or parents.
+TEST_F(WindowTest, Contains) {
+ Window parent(NULL);
+ parent.Init(ui::LAYER_NOT_DRAWN);
+ Window child1(NULL);
+ child1.Init(ui::LAYER_NOT_DRAWN);
+ Window child2(NULL);
+ child2.Init(ui::LAYER_NOT_DRAWN);
+
+ parent.AddChild(&child1);
+ child1.AddChild(&child2);
+
+ EXPECT_TRUE(parent.Contains(&parent));
+ EXPECT_TRUE(parent.Contains(&child1));
+ EXPECT_TRUE(parent.Contains(&child2));
+
+ EXPECT_FALSE(parent.Contains(NULL));
+ EXPECT_FALSE(child1.Contains(&parent));
+ EXPECT_FALSE(child2.Contains(&child1));
+}
+
+TEST_F(WindowTest, ContainsPointInRoot) {
+ scoped_ptr<Window> w(
+ CreateTestWindow(SK_ColorWHITE, 1, gfx::Rect(10, 10, 5, 5),
+ root_window()));
+ EXPECT_FALSE(w->ContainsPointInRoot(gfx::Point(9, 9)));
+ EXPECT_TRUE(w->ContainsPointInRoot(gfx::Point(10, 10)));
+ EXPECT_TRUE(w->ContainsPointInRoot(gfx::Point(14, 14)));
+ EXPECT_FALSE(w->ContainsPointInRoot(gfx::Point(15, 15)));
+ EXPECT_FALSE(w->ContainsPointInRoot(gfx::Point(20, 20)));
+}
+
+TEST_F(WindowTest, ContainsPoint) {
+ scoped_ptr<Window> w(
+ CreateTestWindow(SK_ColorWHITE, 1, gfx::Rect(10, 10, 5, 5),
+ root_window()));
+ EXPECT_TRUE(w->ContainsPoint(gfx::Point(0, 0)));
+ EXPECT_TRUE(w->ContainsPoint(gfx::Point(4, 4)));
+ EXPECT_FALSE(w->ContainsPoint(gfx::Point(5, 5)));
+ EXPECT_FALSE(w->ContainsPoint(gfx::Point(10, 10)));
+}
+
+TEST_F(WindowTest, ConvertPointToWindow) {
+ // Window::ConvertPointToWindow is mostly identical to
+ // Layer::ConvertPointToLayer, except NULL values for |source| are permitted,
+ // in which case the function just returns.
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, root_window()));
+ gfx::Point reference_point(100, 100);
+ gfx::Point test_point = reference_point;
+ Window::ConvertPointToTarget(NULL, w1.get(), &test_point);
+ EXPECT_EQ(reference_point, test_point);
+}
+
+TEST_F(WindowTest, MoveCursorTo) {
+ scoped_ptr<Window> w1(
+ CreateTestWindow(SK_ColorWHITE, 1, gfx::Rect(10, 10, 500, 500),
+ root_window()));
+ scoped_ptr<Window> w11(
+ CreateTestWindow(SK_ColorGREEN, 11, gfx::Rect(5, 5, 100, 100), w1.get()));
+ scoped_ptr<Window> w111(
+ CreateTestWindow(SK_ColorCYAN, 111, gfx::Rect(5, 5, 75, 75), w11.get()));
+ scoped_ptr<Window> w1111(
+ CreateTestWindow(SK_ColorRED, 1111, gfx::Rect(5, 5, 50, 50), w111.get()));
+
+ RootWindow* root = root_window();
+ root->MoveCursorTo(gfx::Point(10, 10));
+ EXPECT_EQ("10,10",
+ gfx::Screen::GetScreenFor(root)->GetCursorScreenPoint().ToString());
+ w1->MoveCursorTo(gfx::Point(10, 10));
+ EXPECT_EQ("20,20",
+ gfx::Screen::GetScreenFor(root)->GetCursorScreenPoint().ToString());
+ w11->MoveCursorTo(gfx::Point(10, 10));
+ EXPECT_EQ("25,25",
+ gfx::Screen::GetScreenFor(root)->GetCursorScreenPoint().ToString());
+ w111->MoveCursorTo(gfx::Point(10, 10));
+ EXPECT_EQ("30,30",
+ gfx::Screen::GetScreenFor(root)->GetCursorScreenPoint().ToString());
+ w1111->MoveCursorTo(gfx::Point(10, 10));
+ EXPECT_EQ("35,35",
+ gfx::Screen::GetScreenFor(root)->GetCursorScreenPoint().ToString());
+}
+
+TEST_F(WindowTest, ContainsMouse) {
+ scoped_ptr<Window> w(
+ CreateTestWindow(SK_ColorWHITE, 1, gfx::Rect(10, 10, 500, 500),
+ root_window()));
+ w->Show();
+ WindowTestApi w_test_api(w.get());
+ RootWindow* root = root_window();
+ root->MoveCursorTo(gfx::Point(10, 10));
+ EXPECT_TRUE(w_test_api.ContainsMouse());
+ root->MoveCursorTo(gfx::Point(9, 10));
+ EXPECT_FALSE(w_test_api.ContainsMouse());
+}
+
+// Test Window::ConvertPointToWindow() with transform to root_window.
+#if defined(USE_OZONE)
+// TODO(rjkroege): Add cursor support in ozone: http://crbug.com/252315.
+TEST_F(WindowTest, DISABLED_MoveCursorToWithTransformRootWindow) {
+#else
+TEST_F(WindowTest, MoveCursorToWithTransformRootWindow) {
+#endif
+ RootWindow* root = root_window();
+ gfx::Transform transform;
+ transform.Translate(100.0, 100.0);
+ transform.Rotate(90.0);
+ transform.Scale(2.0, 5.0);
+ root->SetTransform(transform);
+ root->MoveCursorTo(gfx::Point(10, 10));
+#if !defined(OS_WIN)
+ gfx::Point mouse_location;
+ EXPECT_TRUE(root->QueryMouseLocationForTest(&mouse_location));
+ // TODO(yoshiki): fix this to build on Windows. See crbug.com/133413.OD
+ EXPECT_EQ("50,120", mouse_location.ToString());
+#endif
+ EXPECT_EQ("10,10",
+ gfx::Screen::GetScreenFor(root)->GetCursorScreenPoint().ToString());
+}
+
+// Tests Window::ConvertPointToWindow() with transform to non-root windows.
+TEST_F(WindowTest, MoveCursorToWithTransformWindow) {
+ scoped_ptr<Window> w1(
+ CreateTestWindow(SK_ColorWHITE, 1, gfx::Rect(10, 10, 500, 500),
+ root_window()));
+
+ gfx::Transform transform1;
+ transform1.Scale(2, 2);
+ w1->SetTransform(transform1);
+ w1->MoveCursorTo(gfx::Point(10, 10));
+ EXPECT_EQ("30,30",
+ gfx::Screen::GetScreenFor(w1.get())->GetCursorScreenPoint().ToString());
+
+ gfx::Transform transform2;
+ transform2.Translate(-10, 20);
+ w1->SetTransform(transform2);
+ w1->MoveCursorTo(gfx::Point(10, 10));
+ EXPECT_EQ("10,40",
+ gfx::Screen::GetScreenFor(w1.get())->GetCursorScreenPoint().ToString());
+
+ gfx::Transform transform3;
+ transform3.Rotate(90.0);
+ w1->SetTransform(transform3);
+ w1->MoveCursorTo(gfx::Point(5, 5));
+ EXPECT_EQ("5,15",
+ gfx::Screen::GetScreenFor(w1.get())->GetCursorScreenPoint().ToString());
+
+ gfx::Transform transform4;
+ transform4.Translate(100.0, 100.0);
+ transform4.Rotate(90.0);
+ transform4.Scale(2.0, 5.0);
+ w1->SetTransform(transform4);
+ w1->MoveCursorTo(gfx::Point(10, 10));
+ EXPECT_EQ("60,130",
+ gfx::Screen::GetScreenFor(w1.get())->GetCursorScreenPoint().ToString());
+}
+
+// Test Window::ConvertPointToWindow() with complex transforms to both root and
+// non-root windows.
+// Test Window::ConvertPointToWindow() with transform to root_window.
+#if defined(USE_OZONE)
+// TODO(rjkroege): Add cursor support in ozone: http://crbug.com/252315.
+TEST_F(WindowTest, DISABLED_MoveCursorToWithComplexTransform) {
+#else
+TEST_F(WindowTest, MoveCursorToWithComplexTransform) {
+#endif
+ scoped_ptr<Window> w1(
+ CreateTestWindow(SK_ColorWHITE, 1, gfx::Rect(10, 10, 500, 500),
+ root_window()));
+ scoped_ptr<Window> w11(
+ CreateTestWindow(SK_ColorGREEN, 11, gfx::Rect(5, 5, 100, 100), w1.get()));
+ scoped_ptr<Window> w111(
+ CreateTestWindow(SK_ColorCYAN, 111, gfx::Rect(5, 5, 75, 75), w11.get()));
+ scoped_ptr<Window> w1111(
+ CreateTestWindow(SK_ColorRED, 1111, gfx::Rect(5, 5, 50, 50), w111.get()));
+
+ RootWindow* root = root_window();
+
+ // The root window expects transforms that produce integer rects.
+ gfx::Transform root_transform;
+ root_transform.Translate(60.0, 70.0);
+ root_transform.Rotate(-90.0);
+ root_transform.Translate(-50.0, -50.0);
+ root_transform.Scale(2.0, 3.0);
+
+ gfx::Transform transform;
+ transform.Translate(10.0, 20.0);
+ transform.Rotate(10.0);
+ transform.Scale(0.3, 0.5);
+ root->SetTransform(root_transform);
+ w1->SetTransform(transform);
+ w11->SetTransform(transform);
+ w111->SetTransform(transform);
+ w1111->SetTransform(transform);
+
+ w1111->MoveCursorTo(gfx::Point(10, 10));
+
+#if !defined(OS_WIN)
+ // TODO(yoshiki): fix this to build on Windows. See crbug.com/133413.
+ gfx::Point mouse_location;
+ EXPECT_TRUE(root->QueryMouseLocationForTest(&mouse_location));
+ EXPECT_EQ("169,80", mouse_location.ToString());
+#endif
+ EXPECT_EQ("20,53",
+ gfx::Screen::GetScreenFor(root)->GetCursorScreenPoint().ToString());
+}
+
+TEST_F(WindowTest, HitTest) {
+ Window w1(new ColorTestWindowDelegate(SK_ColorWHITE));
+ w1.set_id(1);
+ w1.Init(ui::LAYER_TEXTURED);
+ w1.SetBounds(gfx::Rect(10, 20, 50, 60));
+ w1.Show();
+ SetDefaultParentByPrimaryRootWindow(&w1);
+
+ // Points are in the Window's coordinates.
+ EXPECT_TRUE(w1.HitTest(gfx::Point(1, 1)));
+ EXPECT_FALSE(w1.HitTest(gfx::Point(-1, -1)));
+
+ // We can expand the bounds slightly to track events outside our border.
+ w1.SetHitTestBoundsOverrideOuter(gfx::Insets(-1, -1, -1, -1),
+ gfx::Insets(-5, -5, -5, -5));
+ EXPECT_TRUE(w1.HitTest(gfx::Point(-1, -1)));
+ EXPECT_FALSE(w1.HitTest(gfx::Point(-2, -2)));
+
+ ui::TouchEvent pressed(
+ ui::ET_TOUCH_PRESSED, gfx::Point(50, 50), 0, getTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&pressed);
+ EXPECT_TRUE(w1.HitTest(gfx::Point(-2, -2)));
+ EXPECT_TRUE(w1.HitTest(gfx::Point(-5, -5)));
+ EXPECT_FALSE(w1.HitTest(gfx::Point(-5, -6)));
+ ui::TouchEvent released(
+ ui::ET_TOUCH_RELEASED, gfx::Point(50, 50), 0, getTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&released);
+ EXPECT_FALSE(w1.HitTest(gfx::Point(-2, -2)));
+
+ // TODO(beng): clip Window to parent.
+}
+
+TEST_F(WindowTest, HitTestMask) {
+ MaskedWindowDelegate d1(gfx::Rect(5, 6, 20, 30));
+ Window w1(&d1);
+ w1.Init(ui::LAYER_NOT_DRAWN);
+ w1.SetBounds(gfx::Rect(10, 20, 50, 60));
+ w1.Show();
+ SetDefaultParentByPrimaryRootWindow(&w1);
+
+ // Points inside the mask.
+ EXPECT_TRUE(w1.HitTest(gfx::Point(5, 6))); // top-left
+ EXPECT_TRUE(w1.HitTest(gfx::Point(15, 21))); // center
+ EXPECT_TRUE(w1.HitTest(gfx::Point(24, 35))); // bottom-right
+
+ // Points outside the mask.
+ EXPECT_FALSE(w1.HitTest(gfx::Point(0, 0)));
+ EXPECT_FALSE(w1.HitTest(gfx::Point(60, 80)));
+ EXPECT_FALSE(w1.HitTest(gfx::Point(4, 6)));
+ EXPECT_FALSE(w1.HitTest(gfx::Point(5, 5)));
+ EXPECT_FALSE(w1.HitTest(gfx::Point(25, 36)));
+}
+
+TEST_F(WindowTest, GetEventHandlerForPoint) {
+ scoped_ptr<Window> w1(
+ CreateTestWindow(SK_ColorWHITE, 1, gfx::Rect(10, 10, 500, 500),
+ root_window()));
+ scoped_ptr<Window> w11(
+ CreateTestWindow(SK_ColorGREEN, 11, gfx::Rect(5, 5, 100, 100), w1.get()));
+ scoped_ptr<Window> w111(
+ CreateTestWindow(SK_ColorCYAN, 111, gfx::Rect(5, 5, 75, 75), w11.get()));
+ scoped_ptr<Window> w1111(
+ CreateTestWindow(SK_ColorRED, 1111, gfx::Rect(5, 5, 50, 50), w111.get()));
+ scoped_ptr<Window> w12(
+ CreateTestWindow(SK_ColorMAGENTA, 12, gfx::Rect(10, 420, 25, 25),
+ w1.get()));
+ scoped_ptr<Window> w121(
+ CreateTestWindow(SK_ColorYELLOW, 121, gfx::Rect(5, 5, 5, 5), w12.get()));
+ scoped_ptr<Window> w13(
+ CreateTestWindow(SK_ColorGRAY, 13, gfx::Rect(5, 470, 50, 50), w1.get()));
+
+ Window* root = root_window();
+ w1->parent()->SetBounds(gfx::Rect(500, 500));
+ EXPECT_EQ(NULL, root->GetEventHandlerForPoint(gfx::Point(5, 5)));
+ EXPECT_EQ(w1.get(), root->GetEventHandlerForPoint(gfx::Point(11, 11)));
+ EXPECT_EQ(w11.get(), root->GetEventHandlerForPoint(gfx::Point(16, 16)));
+ EXPECT_EQ(w111.get(), root->GetEventHandlerForPoint(gfx::Point(21, 21)));
+ EXPECT_EQ(w1111.get(), root->GetEventHandlerForPoint(gfx::Point(26, 26)));
+ EXPECT_EQ(w12.get(), root->GetEventHandlerForPoint(gfx::Point(21, 431)));
+ EXPECT_EQ(w121.get(), root->GetEventHandlerForPoint(gfx::Point(26, 436)));
+ EXPECT_EQ(w13.get(), root->GetEventHandlerForPoint(gfx::Point(26, 481)));
+}
+
+TEST_F(WindowTest, GetEventHandlerForPointWithOverride) {
+ // If our child is flush to our top-left corner he gets events just inside the
+ // window edges.
+ scoped_ptr<Window> parent(
+ CreateTestWindow(SK_ColorWHITE, 1, gfx::Rect(10, 20, 400, 500),
+ root_window()));
+ scoped_ptr<Window> child(
+ CreateTestWindow(SK_ColorRED, 2, gfx::Rect(0, 0, 60, 70), parent.get()));
+ EXPECT_EQ(child.get(), parent->GetEventHandlerForPoint(gfx::Point(0, 0)));
+ EXPECT_EQ(child.get(), parent->GetEventHandlerForPoint(gfx::Point(1, 1)));
+
+ // We can override the hit test bounds of the parent to make the parent grab
+ // events along that edge.
+ parent->set_hit_test_bounds_override_inner(gfx::Insets(1, 1, 1, 1));
+ EXPECT_EQ(parent.get(), parent->GetEventHandlerForPoint(gfx::Point(0, 0)));
+ EXPECT_EQ(child.get(), parent->GetEventHandlerForPoint(gfx::Point(1, 1)));
+}
+
+TEST_F(WindowTest, GetEventHandlerForPointWithOverrideDescendingOrder) {
+ scoped_ptr<SelfEventHandlingWindowDelegate> parent_delegate(
+ new SelfEventHandlingWindowDelegate);
+ scoped_ptr<Window> parent(CreateTestWindowWithDelegate(
+ parent_delegate.get(), 1, gfx::Rect(10, 20, 400, 500), root_window()));
+ scoped_ptr<Window> child(
+ CreateTestWindow(SK_ColorRED, 2, gfx::Rect(0, 0, 390, 480),
+ parent.get()));
+
+ // We can override ShouldDescendIntoChildForEventHandling to make the parent
+ // grab all events.
+ EXPECT_EQ(parent.get(), parent->GetEventHandlerForPoint(gfx::Point(0, 0)));
+ EXPECT_EQ(parent.get(), parent->GetEventHandlerForPoint(gfx::Point(50, 50)));
+}
+
+TEST_F(WindowTest, GetTopWindowContainingPoint) {
+ Window* root = root_window();
+ root->SetBounds(gfx::Rect(0, 0, 300, 300));
+
+ scoped_ptr<Window> w1(
+ CreateTestWindow(SK_ColorWHITE, 1, gfx::Rect(10, 10, 100, 100),
+ root_window()));
+ scoped_ptr<Window> w11(
+ CreateTestWindow(SK_ColorGREEN, 11, gfx::Rect(0, 0, 120, 120), w1.get()));
+
+ scoped_ptr<Window> w2(
+ CreateTestWindow(SK_ColorRED, 2, gfx::Rect(5, 5, 55, 55),
+ root_window()));
+
+ scoped_ptr<Window> w3(
+ CreateTestWindowWithDelegate(
+ NULL, 3, gfx::Rect(200, 200, 100, 100), root_window()));
+ scoped_ptr<Window> w31(
+ CreateTestWindow(SK_ColorCYAN, 31, gfx::Rect(0, 0, 50, 50), w3.get()));
+ scoped_ptr<Window> w311(
+ CreateTestWindow(SK_ColorBLUE, 311, gfx::Rect(0, 0, 10, 10), w31.get()));
+
+ EXPECT_EQ(NULL, root->GetTopWindowContainingPoint(gfx::Point(0, 0)));
+ EXPECT_EQ(w2.get(), root->GetTopWindowContainingPoint(gfx::Point(5, 5)));
+ EXPECT_EQ(w2.get(), root->GetTopWindowContainingPoint(gfx::Point(10, 10)));
+ EXPECT_EQ(w2.get(), root->GetTopWindowContainingPoint(gfx::Point(59, 59)));
+ EXPECT_EQ(w1.get(), root->GetTopWindowContainingPoint(gfx::Point(60, 60)));
+ EXPECT_EQ(w1.get(), root->GetTopWindowContainingPoint(gfx::Point(109, 109)));
+ EXPECT_EQ(NULL, root->GetTopWindowContainingPoint(gfx::Point(110, 110)));
+ EXPECT_EQ(w31.get(), root->GetTopWindowContainingPoint(gfx::Point(200, 200)));
+ EXPECT_EQ(w31.get(), root->GetTopWindowContainingPoint(gfx::Point(220, 220)));
+ EXPECT_EQ(NULL, root->GetTopWindowContainingPoint(gfx::Point(260, 260)));
+}
+
+TEST_F(WindowTest, GetToplevelWindow) {
+ const gfx::Rect kBounds(0, 0, 10, 10);
+ TestWindowDelegate delegate;
+
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, root_window()));
+ scoped_ptr<Window> w11(
+ CreateTestWindowWithDelegate(&delegate, 11, kBounds, w1.get()));
+ scoped_ptr<Window> w111(CreateTestWindowWithId(111, w11.get()));
+ scoped_ptr<Window> w1111(
+ CreateTestWindowWithDelegate(&delegate, 1111, kBounds, w111.get()));
+
+ EXPECT_TRUE(root_window()->GetToplevelWindow() == NULL);
+ EXPECT_TRUE(w1->GetToplevelWindow() == NULL);
+ EXPECT_EQ(w11.get(), w11->GetToplevelWindow());
+ EXPECT_EQ(w11.get(), w111->GetToplevelWindow());
+ EXPECT_EQ(w11.get(), w1111->GetToplevelWindow());
+}
+
+class AddedToRootWindowObserver : public WindowObserver {
+ public:
+ AddedToRootWindowObserver() : called_(false) {}
+
+ virtual void OnWindowAddedToRootWindow(Window* window) OVERRIDE {
+ called_ = true;
+ }
+
+ bool called() const { return called_; }
+
+ private:
+ bool called_;
+
+ DISALLOW_COPY_AND_ASSIGN(AddedToRootWindowObserver);
+};
+
+TEST_F(WindowTest, WindowAddedToRootWindowShouldNotifyChildAndNotParent) {
+ AddedToRootWindowObserver parent_observer;
+ AddedToRootWindowObserver child_observer;
+ scoped_ptr<Window> parent_window(CreateTestWindowWithId(1, root_window()));
+ scoped_ptr<Window> child_window(new Window(NULL));
+ child_window->Init(ui::LAYER_TEXTURED);
+ child_window->Show();
+
+ parent_window->AddObserver(&parent_observer);
+ child_window->AddObserver(&child_observer);
+
+ parent_window->AddChild(child_window.get());
+
+ EXPECT_FALSE(parent_observer.called());
+ EXPECT_TRUE(child_observer.called());
+
+ parent_window->RemoveObserver(&parent_observer);
+ child_window->RemoveObserver(&child_observer);
+}
+
+// Various destruction assertions.
+TEST_F(WindowTest, DestroyTest) {
+ DestroyTrackingDelegateImpl parent_delegate;
+ ChildWindowDelegateImpl child_delegate(&parent_delegate);
+ {
+ scoped_ptr<Window> parent(
+ CreateTestWindowWithDelegate(&parent_delegate, 0, gfx::Rect(),
+ root_window()));
+ CreateTestWindowWithDelegate(&child_delegate, 0, gfx::Rect(), parent.get());
+ }
+ // Both the parent and child should have been destroyed.
+ EXPECT_EQ(1, parent_delegate.destroying_count());
+ EXPECT_EQ(1, parent_delegate.destroyed_count());
+ EXPECT_EQ(1, child_delegate.destroying_count());
+ EXPECT_EQ(1, child_delegate.destroyed_count());
+}
+
+// Tests that a window is orphaned before OnWindowDestroyed is called.
+TEST_F(WindowTest, OrphanedBeforeOnDestroyed) {
+ TestWindowDelegate parent_delegate;
+ DestroyOrphanDelegate child_delegate;
+ {
+ scoped_ptr<Window> parent(
+ CreateTestWindowWithDelegate(&parent_delegate, 0, gfx::Rect(),
+ root_window()));
+ scoped_ptr<Window> child(CreateTestWindowWithDelegate(&child_delegate, 0,
+ gfx::Rect(), parent.get()));
+ child_delegate.set_window(child.get());
+ }
+}
+
+// Make sure StackChildAtTop moves both the window and layer to the front.
+TEST_F(WindowTest, StackChildAtTop) {
+ Window parent(NULL);
+ parent.Init(ui::LAYER_NOT_DRAWN);
+ Window child1(NULL);
+ child1.Init(ui::LAYER_NOT_DRAWN);
+ Window child2(NULL);
+ child2.Init(ui::LAYER_NOT_DRAWN);
+
+ parent.AddChild(&child1);
+ parent.AddChild(&child2);
+ ASSERT_EQ(2u, parent.children().size());
+ EXPECT_EQ(&child1, parent.children()[0]);
+ EXPECT_EQ(&child2, parent.children()[1]);
+ ASSERT_EQ(2u, parent.layer()->children().size());
+ EXPECT_EQ(child1.layer(), parent.layer()->children()[0]);
+ EXPECT_EQ(child2.layer(), parent.layer()->children()[1]);
+
+ parent.StackChildAtTop(&child1);
+ ASSERT_EQ(2u, parent.children().size());
+ EXPECT_EQ(&child1, parent.children()[1]);
+ EXPECT_EQ(&child2, parent.children()[0]);
+ ASSERT_EQ(2u, parent.layer()->children().size());
+ EXPECT_EQ(child1.layer(), parent.layer()->children()[1]);
+ EXPECT_EQ(child2.layer(), parent.layer()->children()[0]);
+}
+
+// Make sure StackChildBelow works.
+TEST_F(WindowTest, StackChildBelow) {
+ Window parent(NULL);
+ parent.Init(ui::LAYER_NOT_DRAWN);
+ Window child1(NULL);
+ child1.Init(ui::LAYER_NOT_DRAWN);
+ child1.set_id(1);
+ Window child2(NULL);
+ child2.Init(ui::LAYER_NOT_DRAWN);
+ child2.set_id(2);
+ Window child3(NULL);
+ child3.Init(ui::LAYER_NOT_DRAWN);
+ child3.set_id(3);
+
+ parent.AddChild(&child1);
+ parent.AddChild(&child2);
+ parent.AddChild(&child3);
+ EXPECT_EQ("1 2 3", ChildWindowIDsAsString(&parent));
+
+ parent.StackChildBelow(&child1, &child2);
+ EXPECT_EQ("1 2 3", ChildWindowIDsAsString(&parent));
+
+ parent.StackChildBelow(&child2, &child1);
+ EXPECT_EQ("2 1 3", ChildWindowIDsAsString(&parent));
+
+ parent.StackChildBelow(&child3, &child2);
+ EXPECT_EQ("3 2 1", ChildWindowIDsAsString(&parent));
+
+ parent.StackChildBelow(&child3, &child1);
+ EXPECT_EQ("2 3 1", ChildWindowIDsAsString(&parent));
+}
+
+// Various assertions for StackChildAbove.
+TEST_F(WindowTest, StackChildAbove) {
+ Window parent(NULL);
+ parent.Init(ui::LAYER_NOT_DRAWN);
+ Window child1(NULL);
+ child1.Init(ui::LAYER_NOT_DRAWN);
+ Window child2(NULL);
+ child2.Init(ui::LAYER_NOT_DRAWN);
+ Window child3(NULL);
+ child3.Init(ui::LAYER_NOT_DRAWN);
+
+ parent.AddChild(&child1);
+ parent.AddChild(&child2);
+
+ // Move 1 in front of 2.
+ parent.StackChildAbove(&child1, &child2);
+ ASSERT_EQ(2u, parent.children().size());
+ EXPECT_EQ(&child2, parent.children()[0]);
+ EXPECT_EQ(&child1, parent.children()[1]);
+ ASSERT_EQ(2u, parent.layer()->children().size());
+ EXPECT_EQ(child2.layer(), parent.layer()->children()[0]);
+ EXPECT_EQ(child1.layer(), parent.layer()->children()[1]);
+
+ // Add 3, resulting in order [2, 1, 3], then move 2 in front of 1, resulting
+ // in [1, 2, 3].
+ parent.AddChild(&child3);
+ parent.StackChildAbove(&child2, &child1);
+ ASSERT_EQ(3u, parent.children().size());
+ EXPECT_EQ(&child1, parent.children()[0]);
+ EXPECT_EQ(&child2, parent.children()[1]);
+ EXPECT_EQ(&child3, parent.children()[2]);
+ ASSERT_EQ(3u, parent.layer()->children().size());
+ EXPECT_EQ(child1.layer(), parent.layer()->children()[0]);
+ EXPECT_EQ(child2.layer(), parent.layer()->children()[1]);
+ EXPECT_EQ(child3.layer(), parent.layer()->children()[2]);
+
+ // Move 1 in front of 3, resulting in [2, 3, 1].
+ parent.StackChildAbove(&child1, &child3);
+ ASSERT_EQ(3u, parent.children().size());
+ EXPECT_EQ(&child2, parent.children()[0]);
+ EXPECT_EQ(&child3, parent.children()[1]);
+ EXPECT_EQ(&child1, parent.children()[2]);
+ ASSERT_EQ(3u, parent.layer()->children().size());
+ EXPECT_EQ(child2.layer(), parent.layer()->children()[0]);
+ EXPECT_EQ(child3.layer(), parent.layer()->children()[1]);
+ EXPECT_EQ(child1.layer(), parent.layer()->children()[2]);
+
+ // Moving 1 in front of 2 should lower it, resulting in [2, 1, 3].
+ parent.StackChildAbove(&child1, &child2);
+ ASSERT_EQ(3u, parent.children().size());
+ EXPECT_EQ(&child2, parent.children()[0]);
+ EXPECT_EQ(&child1, parent.children()[1]);
+ EXPECT_EQ(&child3, parent.children()[2]);
+ ASSERT_EQ(3u, parent.layer()->children().size());
+ EXPECT_EQ(child2.layer(), parent.layer()->children()[0]);
+ EXPECT_EQ(child1.layer(), parent.layer()->children()[1]);
+ EXPECT_EQ(child3.layer(), parent.layer()->children()[2]);
+}
+
+// Various capture assertions.
+TEST_F(WindowTest, CaptureTests) {
+ CaptureWindowDelegateImpl delegate;
+ scoped_ptr<Window> window(CreateTestWindowWithDelegate(
+ &delegate, 0, gfx::Rect(0, 0, 20, 20), root_window()));
+ EXPECT_FALSE(window->HasCapture());
+
+ delegate.ResetCounts();
+
+ // Do a capture.
+ window->SetCapture();
+ EXPECT_TRUE(window->HasCapture());
+ EXPECT_EQ(0, delegate.capture_lost_count());
+ EXPECT_EQ(0, delegate.capture_changed_event_count());
+ EventGenerator generator(root_window(), gfx::Point(50, 50));
+ generator.PressLeftButton();
+ EXPECT_EQ(1, delegate.mouse_event_count());
+ generator.ReleaseLeftButton();
+
+ EXPECT_EQ(2, delegate.mouse_event_count());
+ delegate.ResetCounts();
+
+ ui::TouchEvent touchev(
+ ui::ET_TOUCH_PRESSED, gfx::Point(50, 50), 0, getTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&touchev);
+ EXPECT_EQ(1, delegate.touch_event_count());
+ delegate.ResetCounts();
+
+ window->ReleaseCapture();
+ EXPECT_FALSE(window->HasCapture());
+ EXPECT_EQ(1, delegate.capture_lost_count());
+ EXPECT_EQ(1, delegate.capture_changed_event_count());
+ EXPECT_EQ(1, delegate.mouse_event_count());
+ EXPECT_EQ(0, delegate.touch_event_count());
+
+ generator.PressLeftButton();
+ EXPECT_EQ(1, delegate.mouse_event_count());
+
+ ui::TouchEvent touchev2(
+ ui::ET_TOUCH_PRESSED, gfx::Point(250, 250), 1, getTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&touchev2);
+ EXPECT_EQ(0, delegate.touch_event_count());
+
+ // Removing the capture window from parent should reset the capture window
+ // in the root window.
+ window->SetCapture();
+ EXPECT_EQ(window.get(), aura::client::GetCaptureWindow(root_window()));
+ window->parent()->RemoveChild(window.get());
+ EXPECT_FALSE(window->HasCapture());
+ EXPECT_EQ(NULL, aura::client::GetCaptureWindow(root_window()));
+}
+
+TEST_F(WindowTest, TouchCaptureCancelsOtherTouches) {
+ CaptureWindowDelegateImpl delegate1;
+ scoped_ptr<Window> w1(CreateTestWindowWithDelegate(
+ &delegate1, 0, gfx::Rect(0, 0, 50, 50), root_window()));
+ CaptureWindowDelegateImpl delegate2;
+ scoped_ptr<Window> w2(CreateTestWindowWithDelegate(
+ &delegate2, 0, gfx::Rect(50, 50, 50, 50), root_window()));
+
+ // Press on w1.
+ ui::TouchEvent press(
+ ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), 0, getTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ // We will get both GESTURE_BEGIN and GESTURE_TAP_DOWN.
+ EXPECT_EQ(2, delegate1.gesture_event_count());
+ delegate1.ResetCounts();
+
+ // Capturing to w2 should cause the touch to be canceled.
+ w2->SetCapture();
+ EXPECT_EQ(1, delegate1.touch_event_count());
+ EXPECT_EQ(0, delegate2.touch_event_count());
+ delegate1.ResetCounts();
+ delegate2.ResetCounts();
+
+ // Events now go to w2.
+ ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(10, 20), 0, getTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move);
+ EXPECT_EQ(0, delegate1.gesture_event_count());
+ EXPECT_EQ(0, delegate1.touch_event_count());
+ EXPECT_EQ(0, delegate2.gesture_event_count());
+ EXPECT_EQ(1, delegate2.touch_event_count());
+
+ ui::TouchEvent release(
+ ui::ET_TOUCH_RELEASED, gfx::Point(10, 20), 0, getTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ EXPECT_EQ(0, delegate1.gesture_event_count());
+ EXPECT_EQ(0, delegate2.gesture_event_count());
+
+ // A new press is captured by w2.
+ ui::TouchEvent press2(
+ ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), 0, getTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press2);
+ EXPECT_EQ(0, delegate1.gesture_event_count());
+ // We will get both GESTURE_BEGIN and GESTURE_TAP_DOWN.
+ EXPECT_EQ(2, delegate2.gesture_event_count());
+ delegate1.ResetCounts();
+ delegate2.ResetCounts();
+
+ // And releasing capture changes nothing.
+ w2->ReleaseCapture();
+ EXPECT_EQ(0, delegate1.gesture_event_count());
+ EXPECT_EQ(0, delegate1.touch_event_count());
+ EXPECT_EQ(0, delegate2.gesture_event_count());
+ EXPECT_EQ(0, delegate2.touch_event_count());
+}
+
+TEST_F(WindowTest, TouchCaptureDoesntCancelCapturedTouches) {
+ CaptureWindowDelegateImpl delegate;
+ scoped_ptr<Window> window(CreateTestWindowWithDelegate(
+ &delegate, 0, gfx::Rect(0, 0, 50, 50), root_window()));
+
+ ui::TouchEvent press(
+ ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), 0, getTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+
+ // We will get both GESTURE_BEGIN and GESTURE_TAP_DOWN.
+ EXPECT_EQ(2, delegate.gesture_event_count());
+ EXPECT_EQ(1, delegate.touch_event_count());
+ delegate.ResetCounts();
+
+ window->SetCapture();
+ EXPECT_EQ(0, delegate.gesture_event_count());
+ EXPECT_EQ(0, delegate.touch_event_count());
+ delegate.ResetCounts();
+
+ // On move We will get TOUCH_MOVED, GESTURE_TAP_CANCEL,
+ // GESTURE_SCROLL_START and GESTURE_SCROLL_UPDATE.
+ ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(10, 20), 0, getTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move);
+ EXPECT_EQ(1, delegate.touch_event_count());
+ EXPECT_EQ(3, delegate.gesture_event_count());
+ delegate.ResetCounts();
+
+ // Release capture shouldn't change anything.
+ window->ReleaseCapture();
+ EXPECT_EQ(0, delegate.touch_event_count());
+ EXPECT_EQ(0, delegate.gesture_event_count());
+ delegate.ResetCounts();
+
+ // On move we still get TOUCH_MOVED and GESTURE_SCROLL_UPDATE.
+ ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(10, 30), 0, getTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move2);
+ EXPECT_EQ(1, delegate.touch_event_count());
+ EXPECT_EQ(1, delegate.gesture_event_count());
+ delegate.ResetCounts();
+
+ // And on release we get TOUCH_RELEASED, GESTURE_SCROLL_END, GESTURE_END
+ ui::TouchEvent release(
+ ui::ET_TOUCH_RELEASED, gfx::Point(10, 20), 0, getTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release);
+ EXPECT_EQ(1, delegate.touch_event_count());
+ EXPECT_EQ(2, delegate.gesture_event_count());
+}
+
+
+// Assertions around SetCapture() and touch/gestures.
+TEST_F(WindowTest, TransferCaptureTouchEvents) {
+ // Touch on |w1|.
+ CaptureWindowDelegateImpl d1;
+ scoped_ptr<Window> w1(CreateTestWindowWithDelegate(
+ &d1, 0, gfx::Rect(0, 0, 20, 20), root_window()));
+ ui::TouchEvent p1(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), 0, getTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&p1);
+ // We will get both GESTURE_BEGIN and GESTURE_TAP_DOWN.
+ EXPECT_EQ(1, d1.touch_event_count());
+ EXPECT_EQ(2, d1.gesture_event_count());
+ d1.ResetCounts();
+
+ // Touch on |w2| with a different id.
+ CaptureWindowDelegateImpl d2;
+ scoped_ptr<Window> w2(CreateTestWindowWithDelegate(
+ &d2, 0, gfx::Rect(40, 0, 40, 20), root_window()));
+ ui::TouchEvent p2(ui::ET_TOUCH_PRESSED, gfx::Point(41, 10), 1, getTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&p2);
+ EXPECT_EQ(0, d1.touch_event_count());
+ EXPECT_EQ(0, d1.gesture_event_count());
+ // We will get both GESTURE_BEGIN and GESTURE_TAP_DOWN for new target window.
+ EXPECT_EQ(1, d2.touch_event_count());
+ EXPECT_EQ(2, d2.gesture_event_count());
+ d1.ResetCounts();
+ d2.ResetCounts();
+
+ // Set capture on |w2|, this should send a cancel (TAP_CANCEL, END) to |w1|
+ // but not |w2|.
+ w2->SetCapture();
+ EXPECT_EQ(1, d1.touch_event_count());
+ EXPECT_EQ(2, d1.gesture_event_count());
+ EXPECT_EQ(0, d2.touch_event_count());
+ EXPECT_EQ(0, d2.gesture_event_count());
+ d1.ResetCounts();
+ d2.ResetCounts();
+
+ CaptureWindowDelegateImpl d3;
+ scoped_ptr<Window> w3(CreateTestWindowWithDelegate(
+ &d3, 0, gfx::Rect(0, 0, 100, 101), root_window()));
+ // Set capture on w3. No new events should be received.
+ // Note this difference in behavior between the first and second capture
+ // is confusing and error prone. http://crbug.com/236930
+ w3->SetCapture();
+ EXPECT_EQ(0, d1.touch_event_count());
+ EXPECT_EQ(0, d1.gesture_event_count());
+ EXPECT_EQ(0, d2.touch_event_count());
+ EXPECT_EQ(0, d2.gesture_event_count());
+ EXPECT_EQ(0, d3.touch_event_count());
+ EXPECT_EQ(0, d3.gesture_event_count());
+
+ // Move touch id originally associated with |w2|. Since capture was transfered
+ // from 2 to 3 only |w3| should get the event.
+ ui::TouchEvent m3(ui::ET_TOUCH_MOVED, gfx::Point(110, 105), 1, getTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&m3);
+ EXPECT_EQ(0, d1.touch_event_count());
+ EXPECT_EQ(0, d1.gesture_event_count());
+ EXPECT_EQ(0, d2.touch_event_count());
+ EXPECT_EQ(0, d2.gesture_event_count());
+ // |w3| gets a TOUCH_MOVE, TAP_CANCEL and two scroll related events.
+ EXPECT_EQ(1, d3.touch_event_count());
+ EXPECT_EQ(3, d3.gesture_event_count());
+ d1.ResetCounts();
+ d2.ResetCounts();
+ d3.ResetCounts();
+
+ // When we release capture, no touches are canceled.
+ w3->ReleaseCapture();
+ EXPECT_EQ(0, d1.touch_event_count());
+ EXPECT_EQ(0, d1.gesture_event_count());
+ EXPECT_EQ(0, d2.touch_event_count());
+ EXPECT_EQ(0, d2.gesture_event_count());
+ EXPECT_EQ(0, d3.touch_event_count());
+ EXPECT_EQ(0, d3.gesture_event_count());
+
+ // And when we move the touch again, |w3| still gets the events.
+ ui::TouchEvent m4(ui::ET_TOUCH_MOVED, gfx::Point(120, 105), 1, getTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&m4);
+ EXPECT_EQ(0, d1.touch_event_count());
+ EXPECT_EQ(0, d1.gesture_event_count());
+ EXPECT_EQ(0, d2.touch_event_count());
+ EXPECT_EQ(0, d2.gesture_event_count());
+ EXPECT_EQ(1, d3.touch_event_count());
+ EXPECT_EQ(1, d3.gesture_event_count());
+ d1.ResetCounts();
+ d2.ResetCounts();
+ d3.ResetCounts();
+}
+
+// Changes capture while capture is already ongoing.
+TEST_F(WindowTest, ChangeCaptureWhileMouseDown) {
+ CaptureWindowDelegateImpl delegate;
+ scoped_ptr<Window> window(CreateTestWindowWithDelegate(
+ &delegate, 0, gfx::Rect(0, 0, 20, 20), root_window()));
+ CaptureWindowDelegateImpl delegate2;
+ scoped_ptr<Window> w2(CreateTestWindowWithDelegate(
+ &delegate2, 0, gfx::Rect(20, 20, 20, 20), root_window()));
+
+ // Execute the scheduled draws so that mouse events are not
+ // aggregated.
+ RunAllPendingInMessageLoop();
+
+ EXPECT_FALSE(window->HasCapture());
+
+ // Do a capture.
+ delegate.ResetCounts();
+ window->SetCapture();
+ EXPECT_TRUE(window->HasCapture());
+ EXPECT_EQ(0, delegate.capture_lost_count());
+ EXPECT_EQ(0, delegate.capture_changed_event_count());
+ EventGenerator generator(root_window(), gfx::Point(50, 50));
+ generator.PressLeftButton();
+ EXPECT_EQ(0, delegate.capture_lost_count());
+ EXPECT_EQ(0, delegate.capture_changed_event_count());
+ EXPECT_EQ(1, delegate.mouse_event_count());
+
+ // Set capture to |w2|, should implicitly unset capture for |window|.
+ delegate.ResetCounts();
+ delegate2.ResetCounts();
+ w2->SetCapture();
+
+ generator.MoveMouseTo(gfx::Point(40, 40), 2);
+ EXPECT_EQ(1, delegate.capture_lost_count());
+ EXPECT_EQ(1, delegate.capture_changed_event_count());
+ EXPECT_EQ(1, delegate.mouse_event_count());
+ EXPECT_EQ(2, delegate2.mouse_event_count());
+}
+
+// Verifies capture is reset when a window is destroyed.
+TEST_F(WindowTest, ReleaseCaptureOnDestroy) {
+ CaptureWindowDelegateImpl delegate;
+ scoped_ptr<Window> window(CreateTestWindowWithDelegate(
+ &delegate, 0, gfx::Rect(0, 0, 20, 20), root_window()));
+ EXPECT_FALSE(window->HasCapture());
+
+ // Do a capture.
+ window->SetCapture();
+ EXPECT_TRUE(window->HasCapture());
+
+ // Destroy the window.
+ window.reset();
+
+ // Make sure the root window doesn't reference the window anymore.
+ EXPECT_EQ(NULL, root_window()->mouse_pressed_handler());
+ EXPECT_EQ(NULL, aura::client::GetCaptureWindow(root_window()));
+}
+
+TEST_F(WindowTest, GetBoundsInRootWindow) {
+ scoped_ptr<Window> viewport(CreateTestWindowWithBounds(
+ gfx::Rect(0, 0, 300, 300), root_window()));
+ scoped_ptr<Window> child(CreateTestWindowWithBounds(
+ gfx::Rect(0, 0, 100, 100), viewport.get()));
+ // Sanity check.
+ EXPECT_EQ("0,0 100x100", child->GetBoundsInRootWindow().ToString());
+
+ // The |child| window's screen bounds should move along with the |viewport|.
+ viewport->SetBounds(gfx::Rect(-100, -100, 300, 300));
+ EXPECT_EQ("-100,-100 100x100", child->GetBoundsInRootWindow().ToString());
+
+ // The |child| window is moved to the 0,0 in screen coordinates.
+ // |GetBoundsInRootWindow()| should return 0,0.
+ child->SetBounds(gfx::Rect(100, 100, 100, 100));
+ EXPECT_EQ("0,0 100x100", child->GetBoundsInRootWindow().ToString());
+}
+
+class MouseEnterExitWindowDelegate : public TestWindowDelegate {
+ public:
+ MouseEnterExitWindowDelegate() : entered_(false), exited_(false) {}
+
+ virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
+ switch (event->type()) {
+ case ui::ET_MOUSE_ENTERED:
+ EXPECT_TRUE(event->flags() & ui::EF_IS_SYNTHESIZED);
+ entered_ = true;
+ break;
+ case ui::ET_MOUSE_EXITED:
+ EXPECT_TRUE(event->flags() & ui::EF_IS_SYNTHESIZED);
+ exited_ = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ bool entered() const { return entered_; }
+ bool exited() const { return exited_; }
+
+ // Clear the entered / exited states.
+ void ResetExpectations() {
+ entered_ = false;
+ exited_ = false;
+ }
+
+ private:
+ bool entered_;
+ bool exited_;
+
+ DISALLOW_COPY_AND_ASSIGN(MouseEnterExitWindowDelegate);
+};
+
+
+// Verifies that the WindowDelegate receives MouseExit and MouseEnter events for
+// mouse transitions from window to window.
+TEST_F(WindowTest, MouseEnterExit) {
+ MouseEnterExitWindowDelegate d1;
+ scoped_ptr<Window> w1(
+ CreateTestWindowWithDelegate(&d1, 1, gfx::Rect(10, 10, 50, 50),
+ root_window()));
+ MouseEnterExitWindowDelegate d2;
+ scoped_ptr<Window> w2(
+ CreateTestWindowWithDelegate(&d2, 2, gfx::Rect(70, 70, 50, 50),
+ root_window()));
+
+ test::EventGenerator generator(root_window());
+ generator.MoveMouseToCenterOf(w1.get());
+ EXPECT_TRUE(d1.entered());
+ EXPECT_FALSE(d1.exited());
+ EXPECT_FALSE(d2.entered());
+ EXPECT_FALSE(d2.exited());
+
+ generator.MoveMouseToCenterOf(w2.get());
+ EXPECT_TRUE(d1.entered());
+ EXPECT_TRUE(d1.exited());
+ EXPECT_TRUE(d2.entered());
+ EXPECT_FALSE(d2.exited());
+}
+
+// Verifies that the WindowDelegate receives MouseExit from ET_MOUSE_EXITED.
+TEST_F(WindowTest, RootWindowHostExit) {
+ MouseEnterExitWindowDelegate d1;
+ scoped_ptr<Window> w1(
+ CreateTestWindowWithDelegate(&d1, 1, gfx::Rect(10, 10, 50, 50),
+ root_window()));
+
+ test::EventGenerator generator(root_window());
+ generator.MoveMouseToCenterOf(w1.get());
+ EXPECT_TRUE(d1.entered());
+ EXPECT_FALSE(d1.exited());
+ d1.ResetExpectations();
+
+ ui::MouseEvent exit_event(
+ ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(), 0);
+ root_window()->AsRootWindowHostDelegate()->OnHostMouseEvent(&exit_event);
+ EXPECT_FALSE(d1.entered());
+ EXPECT_TRUE(d1.exited());
+}
+
+// Verifies that the WindowDelegate receives MouseExit and MouseEnter events for
+// mouse transitions from window to window, even if the entered window sets
+// and releases capture.
+TEST_F(WindowTest, MouseEnterExitWithClick) {
+ MouseEnterExitWindowDelegate d1;
+ scoped_ptr<Window> w1(
+ CreateTestWindowWithDelegate(&d1, 1, gfx::Rect(10, 10, 50, 50),
+ root_window()));
+ MouseEnterExitWindowDelegate d2;
+ scoped_ptr<Window> w2(
+ CreateTestWindowWithDelegate(&d2, 2, gfx::Rect(70, 70, 50, 50),
+ root_window()));
+
+ test::EventGenerator generator(root_window());
+ generator.MoveMouseToCenterOf(w1.get());
+ EXPECT_TRUE(d1.entered());
+ EXPECT_FALSE(d1.exited());
+ EXPECT_FALSE(d2.entered());
+ EXPECT_FALSE(d2.exited());
+
+ // Emmulate what Views does on a click by grabbing and releasing capture.
+ generator.PressLeftButton();
+ w1->SetCapture();
+ w1->ReleaseCapture();
+ generator.ReleaseLeftButton();
+
+ generator.MoveMouseToCenterOf(w2.get());
+ EXPECT_TRUE(d1.entered());
+ EXPECT_TRUE(d1.exited());
+ EXPECT_TRUE(d2.entered());
+ EXPECT_FALSE(d2.exited());
+}
+
+TEST_F(WindowTest, MouseEnterExitWhenDeleteWithCapture) {
+ MouseEnterExitWindowDelegate delegate;
+ scoped_ptr<Window> window(
+ CreateTestWindowWithDelegate(&delegate, 1, gfx::Rect(10, 10, 50, 50),
+ root_window()));
+
+ test::EventGenerator generator(root_window());
+ generator.MoveMouseToCenterOf(window.get());
+ EXPECT_TRUE(delegate.entered());
+ EXPECT_FALSE(delegate.exited());
+
+ // Emmulate what Views does on a click by grabbing and releasing capture.
+ generator.PressLeftButton();
+ window->SetCapture();
+
+ delegate.ResetExpectations();
+ generator.MoveMouseTo(0, 0);
+ EXPECT_FALSE(delegate.entered());
+ EXPECT_FALSE(delegate.exited());
+
+ delegate.ResetExpectations();
+ window.reset();
+ EXPECT_FALSE(delegate.entered());
+ EXPECT_FALSE(delegate.exited());
+}
+
+// Verifies that enter / exits are sent if windows appear and are deleted
+// under the current mouse position..
+TEST_F(WindowTest, MouseEnterExitWithDelete) {
+ MouseEnterExitWindowDelegate d1;
+ scoped_ptr<Window> w1(
+ CreateTestWindowWithDelegate(&d1, 1, gfx::Rect(10, 10, 50, 50),
+ root_window()));
+
+ test::EventGenerator generator(root_window());
+ generator.MoveMouseToCenterOf(w1.get());
+ EXPECT_TRUE(d1.entered());
+ EXPECT_FALSE(d1.exited());
+
+ MouseEnterExitWindowDelegate d2;
+ {
+ scoped_ptr<Window> w2(
+ CreateTestWindowWithDelegate(&d2, 2, gfx::Rect(10, 10, 50, 50),
+ root_window()));
+ // Enters / exits can be send asynchronously.
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(d1.entered());
+ EXPECT_TRUE(d1.exited());
+ EXPECT_TRUE(d2.entered());
+ EXPECT_FALSE(d2.exited());
+ d1.ResetExpectations();
+ }
+ // Enters / exits can be send asynchronously.
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(d2.exited());
+ EXPECT_TRUE(d1.entered());
+}
+
+// Verifies that enter / exits are sent if windows appear and are hidden
+// under the current mouse position..
+TEST_F(WindowTest, MouseEnterExitWithHide) {
+ MouseEnterExitWindowDelegate d1;
+ scoped_ptr<Window> w1(
+ CreateTestWindowWithDelegate(&d1, 1, gfx::Rect(10, 10, 50, 50),
+ root_window()));
+
+ test::EventGenerator generator(root_window());
+ generator.MoveMouseToCenterOf(w1.get());
+ EXPECT_TRUE(d1.entered());
+ EXPECT_FALSE(d1.exited());
+
+ MouseEnterExitWindowDelegate d2;
+ scoped_ptr<Window> w2(
+ CreateTestWindowWithDelegate(&d2, 2, gfx::Rect(10, 10, 50, 50),
+ root_window()));
+ // Enters / exits can be send asynchronously.
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(d1.entered());
+ EXPECT_TRUE(d1.exited());
+ EXPECT_TRUE(d2.entered());
+ EXPECT_FALSE(d2.exited());
+
+ d1.ResetExpectations();
+ w2->Hide();
+ // Enters / exits can be send asynchronously.
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(d2.exited());
+ EXPECT_TRUE(d1.entered());
+}
+
+TEST_F(WindowTest, MouseEnterExitWithParentHide) {
+ MouseEnterExitWindowDelegate d1;
+ scoped_ptr<Window> w1(
+ CreateTestWindowWithDelegate(&d1, 1, gfx::Rect(10, 10, 50, 50),
+ root_window()));
+ MouseEnterExitWindowDelegate d2;
+ Window* w2 = CreateTestWindowWithDelegate(&d2, 2, gfx::Rect(10, 10, 50, 50),
+ w1.get());
+ test::EventGenerator generator(root_window());
+ generator.MoveMouseToCenterOf(w2);
+ // Enters / exits can be send asynchronously.
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(d2.entered());
+ EXPECT_FALSE(d2.exited());
+
+ d2.ResetExpectations();
+ w1->Hide();
+ RunAllPendingInMessageLoop();
+ EXPECT_FALSE(d2.entered());
+ EXPECT_TRUE(d2.exited());
+
+ w1.reset();
+}
+
+TEST_F(WindowTest, MouseEnterExitWithParentDelete) {
+ MouseEnterExitWindowDelegate d1;
+ scoped_ptr<Window> w1(
+ CreateTestWindowWithDelegate(&d1, 1, gfx::Rect(10, 10, 50, 50),
+ root_window()));
+ MouseEnterExitWindowDelegate d2;
+ Window* w2 = CreateTestWindowWithDelegate(&d2, 2, gfx::Rect(10, 10, 50, 50),
+ w1.get());
+ test::EventGenerator generator(root_window());
+ generator.MoveMouseToCenterOf(w2);
+
+ // Enters / exits can be send asynchronously.
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(d2.entered());
+ EXPECT_FALSE(d2.exited());
+
+ d2.ResetExpectations();
+ w1.reset();
+ RunAllPendingInMessageLoop();
+ EXPECT_FALSE(d2.entered());
+ EXPECT_TRUE(d2.exited());
+}
+
+// Creates a window with a delegate (w111) that can handle events at a lower
+// z-index than a window without a delegate (w12). w12 is sized to fill the
+// entire bounds of the container. This test verifies that
+// GetEventHandlerForPoint() skips w12 even though its bounds contain the event,
+// because it has no children that can handle the event and it has no delegate
+// allowing it to handle the event itself.
+TEST_F(WindowTest, GetEventHandlerForPoint_NoDelegate) {
+ TestWindowDelegate d111;
+ scoped_ptr<Window> w1(CreateTestWindowWithDelegate(NULL, 1,
+ gfx::Rect(0, 0, 500, 500), root_window()));
+ scoped_ptr<Window> w11(CreateTestWindowWithDelegate(NULL, 11,
+ gfx::Rect(0, 0, 500, 500), w1.get()));
+ scoped_ptr<Window> w111(CreateTestWindowWithDelegate(&d111, 111,
+ gfx::Rect(50, 50, 450, 450), w11.get()));
+ scoped_ptr<Window> w12(CreateTestWindowWithDelegate(NULL, 12,
+ gfx::Rect(0, 0, 500, 500), w1.get()));
+
+ gfx::Point target_point = w111->bounds().CenterPoint();
+ EXPECT_EQ(w111.get(), w1->GetEventHandlerForPoint(target_point));
+}
+
+class VisibilityWindowDelegate : public TestWindowDelegate {
+ public:
+ VisibilityWindowDelegate()
+ : shown_(0),
+ hidden_(0) {
+ }
+
+ int shown() const { return shown_; }
+ int hidden() const { return hidden_; }
+ void Clear() {
+ shown_ = 0;
+ hidden_ = 0;
+ }
+
+ virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE {
+ if (visible)
+ shown_++;
+ else
+ hidden_++;
+ }
+
+ private:
+ int shown_;
+ int hidden_;
+
+ DISALLOW_COPY_AND_ASSIGN(VisibilityWindowDelegate);
+};
+
+// Verifies show/hide propagate correctly to children and the layer.
+TEST_F(WindowTest, Visibility) {
+ VisibilityWindowDelegate d;
+ VisibilityWindowDelegate d2;
+ scoped_ptr<Window> w1(CreateTestWindowWithDelegate(&d, 1, gfx::Rect(),
+ root_window()));
+ scoped_ptr<Window> w2(
+ CreateTestWindowWithDelegate(&d2, 2, gfx::Rect(), w1.get()));
+ scoped_ptr<Window> w3(CreateTestWindowWithId(3, w2.get()));
+
+ // Create shows all the windows.
+ EXPECT_TRUE(w1->IsVisible());
+ EXPECT_TRUE(w2->IsVisible());
+ EXPECT_TRUE(w3->IsVisible());
+ EXPECT_EQ(1, d.shown());
+
+ d.Clear();
+ w1->Hide();
+ EXPECT_FALSE(w1->IsVisible());
+ EXPECT_FALSE(w2->IsVisible());
+ EXPECT_FALSE(w3->IsVisible());
+ EXPECT_EQ(1, d.hidden());
+ EXPECT_EQ(0, d.shown());
+
+ w2->Show();
+ EXPECT_FALSE(w1->IsVisible());
+ EXPECT_FALSE(w2->IsVisible());
+ EXPECT_FALSE(w3->IsVisible());
+
+ w3->Hide();
+ EXPECT_FALSE(w1->IsVisible());
+ EXPECT_FALSE(w2->IsVisible());
+ EXPECT_FALSE(w3->IsVisible());
+
+ d.Clear();
+ w1->Show();
+ EXPECT_TRUE(w1->IsVisible());
+ EXPECT_TRUE(w2->IsVisible());
+ EXPECT_FALSE(w3->IsVisible());
+ EXPECT_EQ(0, d.hidden());
+ EXPECT_EQ(1, d.shown());
+
+ w3->Show();
+ EXPECT_TRUE(w1->IsVisible());
+ EXPECT_TRUE(w2->IsVisible());
+ EXPECT_TRUE(w3->IsVisible());
+
+ // Verify that if an ancestor isn't visible and we change the visibility of a
+ // child window that OnChildWindowVisibilityChanged() is still invoked.
+ w1->Hide();
+ d2.Clear();
+ w2->Hide();
+ EXPECT_EQ(1, d2.hidden());
+ EXPECT_EQ(0, d2.shown());
+ d2.Clear();
+ w2->Show();
+ EXPECT_EQ(0, d2.hidden());
+ EXPECT_EQ(1, d2.shown());
+}
+
+TEST_F(WindowTest, IgnoreEventsTest) {
+ TestWindowDelegate d11;
+ TestWindowDelegate d12;
+ TestWindowDelegate d111;
+ TestWindowDelegate d121;
+ scoped_ptr<Window> w1(CreateTestWindowWithDelegate(NULL, 1,
+ gfx::Rect(0, 0, 500, 500), root_window()));
+ scoped_ptr<Window> w11(CreateTestWindowWithDelegate(&d11, 11,
+ gfx::Rect(0, 0, 500, 500), w1.get()));
+ scoped_ptr<Window> w111(CreateTestWindowWithDelegate(&d111, 111,
+ gfx::Rect(50, 50, 450, 450), w11.get()));
+ scoped_ptr<Window> w12(CreateTestWindowWithDelegate(&d12, 12,
+ gfx::Rect(0, 0, 500, 500), w1.get()));
+ scoped_ptr<Window> w121(CreateTestWindowWithDelegate(&d121, 121,
+ gfx::Rect(150, 150, 50, 50), w12.get()));
+
+ EXPECT_EQ(w12.get(), w1->GetEventHandlerForPoint(gfx::Point(10, 10)));
+ w12->set_ignore_events(true);
+ EXPECT_EQ(w11.get(), w1->GetEventHandlerForPoint(gfx::Point(10, 10)));
+ w12->set_ignore_events(false);
+
+ EXPECT_EQ(w121.get(), w1->GetEventHandlerForPoint(gfx::Point(160, 160)));
+ w121->set_ignore_events(true);
+ EXPECT_EQ(w12.get(), w1->GetEventHandlerForPoint(gfx::Point(160, 160)));
+ w12->set_ignore_events(true);
+ EXPECT_EQ(w111.get(), w1->GetEventHandlerForPoint(gfx::Point(160, 160)));
+ w111->set_ignore_events(true);
+ EXPECT_EQ(w11.get(), w1->GetEventHandlerForPoint(gfx::Point(160, 160)));
+}
+
+// Tests transformation on the root window.
+TEST_F(WindowTest, Transform) {
+ gfx::Size size = root_window()->GetHostSize();
+ EXPECT_EQ(gfx::Rect(size),
+ gfx::Screen::GetScreenFor(root_window())->GetDisplayNearestPoint(
+ gfx::Point()).bounds());
+
+ // Rotate it clock-wise 90 degrees.
+ gfx::Transform transform;
+ transform.Translate(size.height(), 0);
+ transform.Rotate(90.0);
+ root_window()->SetTransform(transform);
+
+ // The size should be the transformed size.
+ gfx::Size transformed_size(size.height(), size.width());
+ EXPECT_EQ(transformed_size.ToString(),
+ root_window()->bounds().size().ToString());
+ EXPECT_EQ(
+ gfx::Rect(transformed_size).ToString(),
+ gfx::Screen::GetScreenFor(root_window())->GetDisplayNearestPoint(
+ gfx::Point()).bounds().ToString());
+
+ // Host size shouldn't change.
+ EXPECT_EQ(size.ToString(),
+ root_window()->GetHostSize().ToString());
+}
+
+TEST_F(WindowTest, TransformGesture) {
+ gfx::Size size = root_window()->GetHostSize();
+
+ scoped_ptr<GestureTrackPositionDelegate> delegate(
+ new GestureTrackPositionDelegate);
+ scoped_ptr<Window> window(CreateTestWindowWithDelegate(delegate.get(), -1234,
+ gfx::Rect(0, 0, 20, 20), root_window()));
+
+ // Rotate the root-window clock-wise 90 degrees.
+ gfx::Transform transform;
+ transform.Translate(size.height(), 0.0);
+ transform.Rotate(90.0);
+ root_window()->SetTransform(transform);
+
+ ui::TouchEvent press(
+ ui::ET_TOUCH_PRESSED, gfx::Point(size.height() - 10, 10), 0, getTime());
+ root_window()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ EXPECT_EQ(gfx::Point(10, 10).ToString(), delegate->position().ToString());
+}
+
+// Various assertions for transient children.
+TEST_F(WindowTest, TransientChildren) {
+ scoped_ptr<Window> parent(CreateTestWindowWithId(0, root_window()));
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get()));
+ scoped_ptr<Window> w3(CreateTestWindowWithId(3, parent.get()));
+ Window* w2 = CreateTestWindowWithId(2, parent.get());
+ w1->AddTransientChild(w2); // w2 is now owned by w1.
+ // 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 = NULL;
+ ASSERT_EQ(1u, parent->children().size());
+ EXPECT_EQ(w3.get(), parent->children()[0]);
+
+ w1.reset(CreateTestWindowWithId(4, parent.get()));
+ w2 = CreateTestWindowWithId(5, w3.get());
+ w1->AddTransientChild(w2);
+ parent->StackChildAtTop(w3.get());
+ // Stack w1 at the top (end), this shouldn't affect w2 since it has a
+ // different parent.
+ parent->StackChildAtTop(w1.get());
+ ASSERT_EQ(2u, parent->children().size());
+ EXPECT_EQ(w3.get(), parent->children()[0]);
+ EXPECT_EQ(w1.get(), parent->children()[1]);
+
+ // Hiding parent should hide transient children.
+ EXPECT_TRUE(w2->IsVisible());
+ w1->Hide();
+ EXPECT_FALSE(w2->IsVisible());
+}
+
+// Tests that when a focused window is closed, its parent inherits the focus.
+TEST_F(WindowTest, FocusedWindowTest) {
+ scoped_ptr<Window> parent(CreateTestWindowWithId(0, root_window()));
+ scoped_ptr<Window> child(CreateTestWindowWithId(1, parent.get()));
+
+ parent->Show();
+
+ child->Focus();
+ EXPECT_TRUE(child->HasFocus());
+ EXPECT_FALSE(parent->HasFocus());
+
+ child.reset();
+ EXPECT_TRUE(parent->HasFocus());
+}
+
+// Tests that the previously-focused window is passed to OnWindowFocused.
+// TODO(beng): Remove once the FocusController lands.
+TEST_F(WindowTest, OldFocusedWindowTest) {
+ const gfx::Rect kBounds(0, 0, 100, 100);
+
+ FocusDelegate delegate1;
+ scoped_ptr<Window> window1(
+ CreateTestWindowWithDelegate(&delegate1, 0, kBounds, root_window()));
+ client::SetFocusChangeObserver(window1.get(), &delegate1);
+ window1->Focus();
+ ASSERT_TRUE(window1->HasFocus());
+ EXPECT_TRUE(delegate1.previous_focused_window() == NULL);
+
+ FocusDelegate delegate2;
+ scoped_ptr<Window> window2(
+ CreateTestWindowWithDelegate(&delegate2, 1, kBounds, root_window()));
+ client::SetFocusChangeObserver(window2.get(), &delegate2);
+ window2->Focus();
+ ASSERT_TRUE(window2->HasFocus());
+ EXPECT_FALSE(window1->HasFocus());
+ EXPECT_EQ(window1.get(), delegate2.previous_focused_window());
+}
+
+namespace {
+DEFINE_WINDOW_PROPERTY_KEY(int, kIntKey, -2);
+DEFINE_WINDOW_PROPERTY_KEY(const char*, kStringKey, "squeamish");
+}
+
+TEST_F(WindowTest, Property) {
+ scoped_ptr<Window> w(CreateTestWindowWithId(0, root_window()));
+
+ static const char native_prop_key[] = "fnord";
+
+ // Non-existent properties should return the default values.
+ EXPECT_EQ(-2, w->GetProperty(kIntKey));
+ EXPECT_EQ(std::string("squeamish"), w->GetProperty(kStringKey));
+ EXPECT_EQ(NULL, w->GetNativeWindowProperty(native_prop_key));
+
+ // A set property value should be returned again (even if it's the default
+ // value).
+ w->SetProperty(kIntKey, INT_MAX);
+ EXPECT_EQ(INT_MAX, w->GetProperty(kIntKey));
+ w->SetProperty(kIntKey, -2);
+ EXPECT_EQ(-2, w->GetProperty(kIntKey));
+ w->SetProperty(kIntKey, INT_MIN);
+ EXPECT_EQ(INT_MIN, w->GetProperty(kIntKey));
+
+ w->SetProperty(kStringKey, static_cast<const char*>(NULL));
+ EXPECT_EQ(NULL, w->GetProperty(kStringKey));
+ w->SetProperty(kStringKey, "squeamish");
+ EXPECT_EQ(std::string("squeamish"), w->GetProperty(kStringKey));
+ w->SetProperty(kStringKey, "ossifrage");
+ EXPECT_EQ(std::string("ossifrage"), w->GetProperty(kStringKey));
+
+ w->SetNativeWindowProperty(native_prop_key, &*w);
+ EXPECT_EQ(&*w, w->GetNativeWindowProperty(native_prop_key));
+ w->SetNativeWindowProperty(native_prop_key, NULL);
+ EXPECT_EQ(NULL, w->GetNativeWindowProperty(native_prop_key));
+
+ // ClearProperty should restore the default value.
+ w->ClearProperty(kIntKey);
+ EXPECT_EQ(-2, w->GetProperty(kIntKey));
+ w->ClearProperty(kStringKey);
+ EXPECT_EQ(std::string("squeamish"), w->GetProperty(kStringKey));
+}
+
+namespace {
+
+class TestProperty {
+ public:
+ TestProperty() {}
+ virtual ~TestProperty() {
+ last_deleted_ = this;
+ }
+ static TestProperty* last_deleted() { return last_deleted_; }
+
+ private:
+ static TestProperty* last_deleted_;
+ DISALLOW_COPY_AND_ASSIGN(TestProperty);
+};
+
+TestProperty* TestProperty::last_deleted_ = NULL;
+
+DEFINE_OWNED_WINDOW_PROPERTY_KEY(TestProperty, kOwnedKey, NULL);
+
+} // namespace
+
+TEST_F(WindowTest, OwnedProperty) {
+ scoped_ptr<Window> w(CreateTestWindowWithId(0, root_window()));
+ EXPECT_EQ(NULL, w->GetProperty(kOwnedKey));
+ TestProperty* p1 = new TestProperty();
+ w->SetProperty(kOwnedKey, p1);
+ EXPECT_EQ(p1, w->GetProperty(kOwnedKey));
+ EXPECT_EQ(NULL, TestProperty::last_deleted());
+
+ TestProperty* p2 = new TestProperty();
+ w->SetProperty(kOwnedKey, p2);
+ EXPECT_EQ(p2, w->GetProperty(kOwnedKey));
+ EXPECT_EQ(p1, TestProperty::last_deleted());
+
+ w->ClearProperty(kOwnedKey);
+ EXPECT_EQ(NULL, w->GetProperty(kOwnedKey));
+ EXPECT_EQ(p2, TestProperty::last_deleted());
+
+ TestProperty* p3 = new TestProperty();
+ w->SetProperty(kOwnedKey, p3);
+ EXPECT_EQ(p3, w->GetProperty(kOwnedKey));
+ EXPECT_EQ(p2, TestProperty::last_deleted());
+ w.reset();
+ EXPECT_EQ(p3, TestProperty::last_deleted());
+}
+
+TEST_F(WindowTest, SetBoundsInternalShouldCheckTargetBounds) {
+ // We cannot short-circuit animations in this test.
+ ui::ScopedAnimationDurationScaleMode normal_duration_mode(
+ ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
+
+ scoped_ptr<Window> w1(
+ CreateTestWindowWithBounds(gfx::Rect(0, 0, 100, 100), root_window()));
+
+ EXPECT_FALSE(!w1->layer());
+ w1->layer()->GetAnimator()->set_disable_timer_for_test(true);
+ ui::AnimationContainerElement* element = w1->layer()->GetAnimator();
+
+ EXPECT_EQ("0,0 100x100", w1->bounds().ToString());
+ EXPECT_EQ("0,0 100x100", w1->layer()->GetTargetBounds().ToString());
+
+ // Animate to a different position.
+ {
+ ui::ScopedLayerAnimationSettings settings(w1->layer()->GetAnimator());
+ w1->SetBounds(gfx::Rect(100, 100, 100, 100));
+ }
+
+ EXPECT_EQ("0,0 100x100", w1->bounds().ToString());
+ EXPECT_EQ("100,100 100x100", w1->layer()->GetTargetBounds().ToString());
+
+ // Animate back to the first position. The animation hasn't started yet, so
+ // the current bounds are still (0, 0, 100, 100), but the target bounds are
+ // (100, 100, 100, 100). If we step the animator ahead, we should find that
+ // we're at (0, 0, 100, 100). That is, the second animation should be applied.
+ {
+ ui::ScopedLayerAnimationSettings settings(w1->layer()->GetAnimator());
+ w1->SetBounds(gfx::Rect(0, 0, 100, 100));
+ }
+
+ EXPECT_EQ("0,0 100x100", w1->bounds().ToString());
+ EXPECT_EQ("0,0 100x100", w1->layer()->GetTargetBounds().ToString());
+
+ // Confirm that the target bounds are reached.
+ base::TimeTicks start_time =
+ w1->layer()->GetAnimator()->last_step_time();
+
+ element->Step(start_time + base::TimeDelta::FromMilliseconds(1000));
+
+ EXPECT_EQ("0,0 100x100", w1->bounds().ToString());
+}
+
+
+typedef std::pair<const void*, intptr_t> PropertyChangeInfo;
+
+class WindowObserverTest : public WindowTest,
+ public WindowObserver {
+ public:
+ struct VisibilityInfo {
+ bool window_visible;
+ bool visible_param;
+ };
+
+ WindowObserverTest()
+ : added_count_(0),
+ removed_count_(0),
+ destroyed_count_(0),
+ old_property_value_(-3) {
+ }
+
+ virtual ~WindowObserverTest() {}
+
+ const VisibilityInfo* GetVisibilityInfo() const {
+ return visibility_info_.get();
+ }
+
+ void ResetVisibilityInfo() {
+ visibility_info_.reset();
+ }
+
+ // Returns a description of the WindowObserver methods that have been invoked.
+ std::string WindowObserverCountStateAndClear() {
+ std::string result(
+ base::StringPrintf("added=%d removed=%d",
+ added_count_, removed_count_));
+ added_count_ = removed_count_ = 0;
+ return result;
+ }
+
+ int DestroyedCountAndClear() {
+ int result = destroyed_count_;
+ destroyed_count_ = 0;
+ return result;
+ }
+
+ // Return a tuple of the arguments passed in OnPropertyChanged callback.
+ PropertyChangeInfo PropertyChangeInfoAndClear() {
+ PropertyChangeInfo result(property_key_, old_property_value_);
+ property_key_ = NULL;
+ old_property_value_ = -3;
+ return result;
+ }
+
+ private:
+ virtual void OnWindowAdded(Window* new_window) OVERRIDE {
+ added_count_++;
+ }
+
+ virtual void OnWillRemoveWindow(Window* window) OVERRIDE {
+ removed_count_++;
+ }
+
+ virtual void OnWindowVisibilityChanged(Window* window,
+ bool visible) OVERRIDE {
+ visibility_info_.reset(new VisibilityInfo);
+ visibility_info_->window_visible = window->IsVisible();
+ visibility_info_->visible_param = visible;
+ }
+
+ virtual void OnWindowDestroyed(Window* window) OVERRIDE {
+ EXPECT_FALSE(window->parent());
+ destroyed_count_++;
+ }
+
+ virtual void OnWindowPropertyChanged(Window* window,
+ const void* key,
+ intptr_t old) OVERRIDE {
+ property_key_ = key;
+ old_property_value_ = old;
+ }
+
+ int added_count_;
+ int removed_count_;
+ int destroyed_count_;
+ scoped_ptr<VisibilityInfo> visibility_info_;
+ const void* property_key_;
+ intptr_t old_property_value_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowObserverTest);
+};
+
+// Various assertions for WindowObserver.
+TEST_F(WindowObserverTest, WindowObserver) {
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, root_window()));
+ w1->AddObserver(this);
+
+ // Create a new window as a child of w1, our observer should be notified.
+ scoped_ptr<Window> w2(CreateTestWindowWithId(2, w1.get()));
+ EXPECT_EQ("added=1 removed=0", WindowObserverCountStateAndClear());
+
+ // Delete w2, which should result in the remove notification.
+ w2.reset();
+ EXPECT_EQ("added=0 removed=1", WindowObserverCountStateAndClear());
+
+ // Create a window that isn't parented to w1, we shouldn't get any
+ // notification.
+ scoped_ptr<Window> w3(CreateTestWindowWithId(3, root_window()));
+ EXPECT_EQ("added=0 removed=0", WindowObserverCountStateAndClear());
+
+ // Similarly destroying w3 shouldn't notify us either.
+ w3.reset();
+ EXPECT_EQ("added=0 removed=0", WindowObserverCountStateAndClear());
+ w1->RemoveObserver(this);
+}
+
+// Test if OnWindowVisibilityChagned is invoked with expected
+// parameters.
+TEST_F(WindowObserverTest, WindowVisibility) {
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, root_window()));
+ scoped_ptr<Window> w2(CreateTestWindowWithId(1, w1.get()));
+ w2->AddObserver(this);
+
+ // Hide should make the window invisible and the passed visible
+ // parameter is false.
+ w2->Hide();
+ EXPECT_FALSE(!GetVisibilityInfo());
+ EXPECT_FALSE(!GetVisibilityInfo());
+ if (!GetVisibilityInfo())
+ return;
+ EXPECT_FALSE(GetVisibilityInfo()->window_visible);
+ EXPECT_FALSE(GetVisibilityInfo()->visible_param);
+
+ // If parent isn't visible, showing window won't make the window visible, but
+ // passed visible value must be true.
+ w1->Hide();
+ ResetVisibilityInfo();
+ EXPECT_TRUE(!GetVisibilityInfo());
+ w2->Show();
+ EXPECT_FALSE(!GetVisibilityInfo());
+ if (!GetVisibilityInfo())
+ return;
+ EXPECT_FALSE(GetVisibilityInfo()->window_visible);
+ EXPECT_TRUE(GetVisibilityInfo()->visible_param);
+
+ // If parent is visible, showing window will make the window
+ // visible and the passed visible value is true.
+ w1->Show();
+ w2->Hide();
+ ResetVisibilityInfo();
+ w2->Show();
+ EXPECT_FALSE(!GetVisibilityInfo());
+ if (!GetVisibilityInfo())
+ return;
+ EXPECT_TRUE(GetVisibilityInfo()->window_visible);
+ EXPECT_TRUE(GetVisibilityInfo()->visible_param);
+}
+
+// Test if OnWindowDestroyed is invoked as expected.
+TEST_F(WindowObserverTest, WindowDestroyed) {
+ // Delete a window should fire a destroyed notification.
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, root_window()));
+ w1->AddObserver(this);
+ w1.reset();
+ EXPECT_EQ(1, DestroyedCountAndClear());
+
+ // Observe on child and delete parent window should fire a notification.
+ scoped_ptr<Window> parent(CreateTestWindowWithId(1, root_window()));
+ Window* child = CreateTestWindowWithId(1, parent.get()); // owned by parent
+ child->AddObserver(this);
+ parent.reset();
+ EXPECT_EQ(1, DestroyedCountAndClear());
+}
+
+TEST_F(WindowObserverTest, PropertyChanged) {
+ // Setting property should fire a property change notification.
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, root_window()));
+ w1->AddObserver(this);
+
+ static const WindowProperty<int> prop = {-2};
+ static const char native_prop_key[] = "fnord";
+
+ w1->SetProperty(&prop, 1);
+ EXPECT_EQ(PropertyChangeInfo(&prop, -2), PropertyChangeInfoAndClear());
+ w1->SetProperty(&prop, -2);
+ EXPECT_EQ(PropertyChangeInfo(&prop, 1), PropertyChangeInfoAndClear());
+ w1->SetProperty(&prop, 3);
+ EXPECT_EQ(PropertyChangeInfo(&prop, -2), PropertyChangeInfoAndClear());
+ w1->ClearProperty(&prop);
+ EXPECT_EQ(PropertyChangeInfo(&prop, 3), PropertyChangeInfoAndClear());
+
+ w1->SetNativeWindowProperty(native_prop_key, &*w1);
+ EXPECT_EQ(PropertyChangeInfo(native_prop_key, 0),
+ PropertyChangeInfoAndClear());
+ w1->SetNativeWindowProperty(native_prop_key, NULL);
+ EXPECT_EQ(PropertyChangeInfo(native_prop_key,
+ reinterpret_cast<intptr_t>(&*w1)),
+ PropertyChangeInfoAndClear());
+
+ // Sanity check to see if |PropertyChangeInfoAndClear| really clears.
+ EXPECT_EQ(PropertyChangeInfo(
+ reinterpret_cast<const void*>(NULL), -3), PropertyChangeInfoAndClear());
+}
+
+TEST_F(WindowTest, AcquireLayer) {
+ scoped_ptr<Window> window1(CreateTestWindowWithId(1, root_window()));
+ scoped_ptr<Window> window2(CreateTestWindowWithId(2, root_window()));
+ ui::Layer* parent = window1->parent()->layer();
+ EXPECT_EQ(2U, parent->children().size());
+
+ WindowTestApi window1_test_api(window1.get());
+ WindowTestApi window2_test_api(window2.get());
+
+ EXPECT_TRUE(window1_test_api.OwnsLayer());
+ EXPECT_TRUE(window2_test_api.OwnsLayer());
+
+ // After acquisition, window1 should not own its layer, but it should still
+ // be available to the window.
+ scoped_ptr<ui::Layer> window1_layer(window1->AcquireLayer());
+ EXPECT_FALSE(window1_test_api.OwnsLayer());
+ EXPECT_TRUE(window1_layer.get() == window1->layer());
+
+ // Upon destruction, window1's layer should still be valid, and in the layer
+ // hierarchy, but window2's should be gone, and no longer in the hierarchy.
+ window1.reset();
+ window2.reset();
+
+ // This should be set by the window's destructor.
+ EXPECT_TRUE(window1_layer->delegate() == NULL);
+ EXPECT_EQ(1U, parent->children().size());
+}
+
+// Make sure that properties which should persist from the old layer to the new
+// layer actually do.
+TEST_F(WindowTest, RecreateLayer) {
+ // Set properties to non default values.
+ Window w(new ColorTestWindowDelegate(SK_ColorWHITE));
+ w.set_id(1);
+ w.Init(ui::LAYER_SOLID_COLOR);
+ w.SetBounds(gfx::Rect(0, 0, 100, 100));
+
+ ui::Layer* layer = w.layer();
+ layer->set_scale_content(false);
+ layer->SetVisible(false);
+ layer->SetMasksToBounds(true);
+
+ ui::Layer child_layer;
+ layer->Add(&child_layer);
+
+ scoped_ptr<ui::Layer> old_layer(w.RecreateLayer());
+ layer = w.layer();
+ EXPECT_EQ(ui::LAYER_SOLID_COLOR, layer->type());
+ EXPECT_FALSE(layer->scale_content());
+ EXPECT_FALSE(layer->visible());
+ EXPECT_EQ(1u, layer->children().size());
+ EXPECT_TRUE(layer->GetMasksToBounds());
+}
+
+// Verify that RecreateLayer() stacks the old layer above the newly creatd
+// layer.
+TEST_F(WindowTest, RecreateLayerZOrder) {
+ scoped_ptr<Window> w(
+ CreateTestWindow(SK_ColorWHITE, 1, gfx::Rect(0, 0, 100, 100),
+ root_window()));
+ scoped_ptr<ui::Layer> old_layer(w->RecreateLayer());
+
+ const std::vector<ui::Layer*>& child_layers =
+ root_window()->layer()->children();
+ ASSERT_EQ(2u, child_layers.size());
+ EXPECT_EQ(w->layer(), child_layers[0]);
+ EXPECT_EQ(old_layer.get(), child_layers[1]);
+}
+
+// Ensure that acquiring a layer then recreating a layer does not crash
+// and that RecreateLayer returns null.
+TEST_F(WindowTest, AcquireThenRecreateLayer) {
+ scoped_ptr<Window> w(
+ CreateTestWindow(SK_ColorWHITE, 1, gfx::Rect(0, 0, 100, 100),
+ root_window()));
+ scoped_ptr<ui::Layer>acquired_layer(w->AcquireLayer());
+ scoped_ptr<ui::Layer>doubly_acquired_layer(w->RecreateLayer());
+ EXPECT_EQ(NULL, doubly_acquired_layer.get());
+
+ // Destroy window before layer gets destroyed.
+ w.reset();
+}
+
+TEST_F(WindowTest, StackWindowsWhoseLayersHaveNoDelegate) {
+ scoped_ptr<Window> window1(CreateTestWindowWithId(1, root_window()));
+ window1->layer()->set_name("1");
+ scoped_ptr<Window> window2(CreateTestWindowWithId(2, root_window()));
+ window2->layer()->set_name("2");
+ scoped_ptr<Window> window3(CreateTestWindowWithId(3, root_window()));
+ window3->layer()->set_name("3");
+
+ // This brings |window1| (and its layer) to the front.
+ root_window()->StackChildAbove(window1.get(), window3.get());
+ EXPECT_EQ("2 3 1", ChildWindowIDsAsString(root_window()));
+ EXPECT_EQ("2 3 1",
+ ui::test::ChildLayerNamesAsString(*root_window()->layer()));
+
+ // Since |window1| does not have a delegate, |window2| should not move in
+ // front of it, nor should its layer.
+ window1->layer()->set_delegate(NULL);
+ root_window()->StackChildAbove(window2.get(), window1.get());
+ EXPECT_EQ("3 2 1", ChildWindowIDsAsString(root_window()));
+ EXPECT_EQ("3 2 1",
+ ui::test::ChildLayerNamesAsString(*root_window()->layer()));
+
+ // It should still be possible to stack |window3| immediately below |window1|.
+ root_window()->StackChildBelow(window3.get(), window1.get());
+ EXPECT_EQ("2 3 1", ChildWindowIDsAsString(root_window()));
+ EXPECT_EQ("2 3 1",
+ ui::test::ChildLayerNamesAsString(*root_window()->layer()));
+
+ // Since neither |window3| nor |window1| have a delegate, |window2| should
+ // not move in front of either.
+ window3->layer()->set_delegate(NULL);
+ root_window()->StackChildBelow(window2.get(), window1.get());
+ EXPECT_EQ("2 3 1", ChildWindowIDsAsString(root_window()));
+ EXPECT_EQ("2 3 1",
+ ui::test::ChildLayerNamesAsString(*root_window()->layer()));
+}
+
+TEST_F(WindowTest, StackTransientsWhoseLayersHaveNoDelegate) {
+ RootWindow* root = root_window();
+
+ // Create a window with several transients, then a couple windows on top.
+ scoped_ptr<Window> window1(CreateTestWindowWithId(1, root_window()));
+ scoped_ptr<Window> window11(CreateTransientChild(11, window1.get()));
+ scoped_ptr<Window> window12(CreateTransientChild(12, window1.get()));
+ scoped_ptr<Window> window13(CreateTransientChild(13, window1.get()));
+ scoped_ptr<Window> window2(CreateTestWindowWithId(2, root_window()));
+ scoped_ptr<Window> window3(CreateTestWindowWithId(3, root_window()));
+
+ EXPECT_EQ("1 11 12 13 2 3", ChildWindowIDsAsString(root));
+
+ // Remove the delegates of a couple of transients, as if they are closing
+ // and animating out.
+ window11->layer()->set_delegate(NULL);
+ window13->layer()->set_delegate(NULL);
+
+ // Move window1 to the front. All transients should move with it, and their
+ // order should be preserved.
+ root->StackChildAtTop(window1.get());
+
+ EXPECT_EQ("2 3 1 11 12 13", ChildWindowIDsAsString(root));
+}
+
+class TestVisibilityClient : public client::VisibilityClient {
+ public:
+ explicit TestVisibilityClient(RootWindow* root_window)
+ : ignore_visibility_changes_(false) {
+ client::SetVisibilityClient(root_window, this);
+ }
+ virtual ~TestVisibilityClient() {
+ }
+
+ void set_ignore_visibility_changes(bool ignore_visibility_changes) {
+ ignore_visibility_changes_ = ignore_visibility_changes;
+ }
+
+ // Overridden from client::VisibilityClient:
+ virtual void UpdateLayerVisibility(aura::Window* window,
+ bool visible) OVERRIDE {
+ if (!ignore_visibility_changes_)
+ window->layer()->SetVisible(visible);
+ }
+
+ private:
+ bool ignore_visibility_changes_;
+ DISALLOW_COPY_AND_ASSIGN(TestVisibilityClient);
+};
+
+TEST_F(WindowTest, VisibilityClientIsVisible) {
+ TestVisibilityClient client(root_window());
+
+ scoped_ptr<Window> window(CreateTestWindowWithId(1, root_window()));
+ EXPECT_TRUE(window->IsVisible());
+ EXPECT_TRUE(window->layer()->visible());
+
+ window->Hide();
+ EXPECT_FALSE(window->IsVisible());
+ EXPECT_FALSE(window->layer()->visible());
+ window->Show();
+
+ client.set_ignore_visibility_changes(true);
+ window->Hide();
+ EXPECT_FALSE(window->IsVisible());
+ EXPECT_TRUE(window->layer()->visible());
+}
+
+// Tests mouse events on window change.
+TEST_F(WindowTest, MouseEventsOnWindowChange) {
+ gfx::Size size = root_window()->GetHostSize();
+
+ EventGenerator generator(root_window());
+ generator.MoveMouseTo(50, 50);
+
+ EventCountDelegate d1;
+ scoped_ptr<Window> w1(CreateTestWindowWithDelegate(&d1, 1,
+ gfx::Rect(0, 0, 100, 100), root_window()));
+ RunAllPendingInMessageLoop();
+ // The format of result is "Enter/Mouse/Leave".
+ EXPECT_EQ("1 1 0", d1.GetMouseMotionCountsAndReset());
+
+ // Adding new window.
+ EventCountDelegate d11;
+ scoped_ptr<Window> w11(CreateTestWindowWithDelegate(
+ &d11, 1, gfx::Rect(0, 0, 100, 100), w1.get()));
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ("0 0 1", d1.GetMouseMotionCountsAndReset());
+ EXPECT_EQ("1 1 0", d11.GetMouseMotionCountsAndReset());
+
+ // Move bounds.
+ w11->SetBounds(gfx::Rect(0, 0, 10, 10));
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ("1 1 0", d1.GetMouseMotionCountsAndReset());
+ EXPECT_EQ("0 0 1", d11.GetMouseMotionCountsAndReset());
+
+ w11->SetBounds(gfx::Rect(0, 0, 60, 60));
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ("0 0 1", d1.GetMouseMotionCountsAndReset());
+ EXPECT_EQ("1 1 0", d11.GetMouseMotionCountsAndReset());
+
+ // Detach, then re-attach.
+ w1->RemoveChild(w11.get());
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ("1 1 0", d1.GetMouseMotionCountsAndReset());
+ // Window is detached, so no event is set.
+ EXPECT_EQ("0 0 1", d11.GetMouseMotionCountsAndReset());
+
+ w1->AddChild(w11.get());
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ("0 0 1", d1.GetMouseMotionCountsAndReset());
+ // Window is detached, so no event is set.
+ EXPECT_EQ("1 1 0", d11.GetMouseMotionCountsAndReset());
+
+ // Visibility Change
+ w11->Hide();
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ("1 1 0", d1.GetMouseMotionCountsAndReset());
+ EXPECT_EQ("0 0 1", d11.GetMouseMotionCountsAndReset());
+
+ w11->Show();
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ("0 0 1", d1.GetMouseMotionCountsAndReset());
+ EXPECT_EQ("1 1 0", d11.GetMouseMotionCountsAndReset());
+
+ // Transform: move d11 by 100 100.
+ gfx::Transform transform;
+ transform.Translate(100, 100);
+ w11->SetTransform(transform);
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ("1 1 0", d1.GetMouseMotionCountsAndReset());
+ EXPECT_EQ("0 0 1", d11.GetMouseMotionCountsAndReset());
+
+ w11->SetTransform(gfx::Transform());
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ("0 0 1", d1.GetMouseMotionCountsAndReset());
+ EXPECT_EQ("1 1 0", d11.GetMouseMotionCountsAndReset());
+
+ // Closing a window.
+ w11.reset();
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ("1 1 0", d1.GetMouseMotionCountsAndReset());
+
+ // Make sure we don't synthesize events if the mouse
+ // is outside of the root window.
+ generator.MoveMouseTo(-10, -10);
+ EXPECT_EQ("0 0 1", d1.GetMouseMotionCountsAndReset());
+
+ // Adding new windows.
+ w11.reset(CreateTestWindowWithDelegate(
+ &d11, 1, gfx::Rect(0, 0, 100, 100), w1.get()));
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ("0 0 0", d1.GetMouseMotionCountsAndReset());
+ EXPECT_EQ("0 0 1", d11.GetMouseMotionCountsAndReset());
+
+ // Closing windows
+ w11.reset();
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ("0 0 0", d1.GetMouseMotionCountsAndReset());
+ EXPECT_EQ("0 0 0", d11.GetMouseMotionCountsAndReset());
+}
+
+class StackingMadrigalLayoutManager : public LayoutManager {
+ public:
+ explicit StackingMadrigalLayoutManager(RootWindow* root_window)
+ : root_window_(root_window) {
+ root_window_->SetLayoutManager(this);
+ }
+ virtual ~StackingMadrigalLayoutManager() {
+ }
+
+ private:
+ // Overridden from LayoutManager:
+ virtual void OnWindowResized() OVERRIDE {}
+ virtual void OnWindowAddedToLayout(Window* child) OVERRIDE {}
+ virtual void OnWillRemoveWindowFromLayout(Window* child) OVERRIDE {}
+ virtual void OnWindowRemovedFromLayout(Window* child) OVERRIDE {}
+ virtual void OnChildWindowVisibilityChanged(Window* child,
+ bool visible) OVERRIDE {
+ Window::Windows::const_iterator it = root_window_->children().begin();
+ Window* last_window = NULL;
+ for (; it != root_window_->children().end(); ++it) {
+ if (*it == child && last_window) {
+ if (!visible)
+ root_window_->StackChildAbove(last_window, *it);
+ else
+ root_window_->StackChildAbove(*it, last_window);
+ break;
+ }
+ last_window = *it;
+ }
+ }
+ virtual void SetChildBounds(Window* child,
+ const gfx::Rect& requested_bounds) OVERRIDE {
+ SetChildBoundsDirect(child, requested_bounds);
+ }
+
+ RootWindow* root_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(StackingMadrigalLayoutManager);
+};
+
+class StackingMadrigalVisibilityClient : public client::VisibilityClient {
+ public:
+ explicit StackingMadrigalVisibilityClient(RootWindow* root_window)
+ : ignored_window_(NULL) {
+ client::SetVisibilityClient(root_window, this);
+ }
+ virtual ~StackingMadrigalVisibilityClient() {
+ }
+
+ void set_ignored_window(Window* ignored_window) {
+ ignored_window_ = ignored_window;
+ }
+
+ private:
+ // Overridden from client::VisibilityClient:
+ virtual void UpdateLayerVisibility(Window* window, bool visible) OVERRIDE {
+ if (!visible) {
+ if (window == ignored_window_)
+ window->layer()->set_delegate(NULL);
+ else
+ window->layer()->SetVisible(visible);
+ } else {
+ window->layer()->SetVisible(visible);
+ }
+ }
+
+ Window* ignored_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(StackingMadrigalVisibilityClient);
+};
+
+// This test attempts to reconstruct a circumstance that can happen when the
+// aura client attempts to manipulate the visibility and delegate of a layer
+// independent of window visibility.
+// A use case is where the client attempts to keep a window visible onscreen
+// even after code has called Hide() on the window. The use case for this would
+// be that window hides are animated (e.g. the window fades out). To prevent
+// spurious updating the client code may also clear window's layer's delegate,
+// so that the window cannot attempt to paint or update it further. The window
+// uses the presence of a NULL layer delegate as a signal in stacking to note
+// that the window is being manipulated by such a use case and its stacking
+// should not be adjusted.
+// One issue that can arise when a window opens two transient children, and the
+// first is hidden. Subsequent attempts to activate the transient parent can
+// result in the transient parent being stacked above the second transient
+// child. A fix is made to Window::StackAbove to prevent this, and this test
+// verifies this fix.
+TEST_F(WindowTest, StackingMadrigal) {
+ new StackingMadrigalLayoutManager(root_window());
+ StackingMadrigalVisibilityClient visibility_client(root_window());
+
+ scoped_ptr<Window> window1(CreateTestWindowWithId(1, root_window()));
+ scoped_ptr<Window> window11(CreateTransientChild(11, window1.get()));
+
+ visibility_client.set_ignored_window(window11.get());
+
+ window11->Show();
+ window11->Hide();
+
+ // As a transient, window11 should still be stacked above window1, even when
+ // hidden.
+ EXPECT_TRUE(WindowIsAbove(window11.get(), window1.get()));
+ EXPECT_TRUE(LayerIsAbove(window11.get(), window1.get()));
+
+ // A new transient should still be above window1. It will appear behind
+ // window11 because we don't stack windows on top of targets with NULL
+ // delegates.
+ scoped_ptr<Window> window12(CreateTransientChild(12, window1.get()));
+ window12->Show();
+
+ EXPECT_TRUE(WindowIsAbove(window12.get(), window1.get()));
+ EXPECT_TRUE(LayerIsAbove(window12.get(), window1.get()));
+
+ // In earlier versions of the StackChildAbove() method, attempting to stack
+ // window1 above window12 at this point would actually restack the layers
+ // resulting in window12's layer being below window1's layer (though the
+ // windows themselves would still be correctly stacked, so events would pass
+ // through.)
+ root_window()->StackChildAbove(window1.get(), window12.get());
+
+ // Both window12 and its layer should be stacked above window1.
+ EXPECT_TRUE(WindowIsAbove(window12.get(), window1.get()));
+ EXPECT_TRUE(LayerIsAbove(window12.get(), window1.get()));
+}
+
+// Test for an issue where attempting to stack a primary window on top of a
+// transient with a NULL layer delegate causes that primary window to be moved,
+// but the layer order not changed to match. http://crbug.com/112562
+TEST_F(WindowTest, StackOverClosingTransient) {
+ scoped_ptr<Window> window1(CreateTestWindowWithId(1, root_window()));
+ scoped_ptr<Window> transient1(CreateTransientChild(11, window1.get()));
+ scoped_ptr<Window> window2(CreateTestWindowWithId(2, root_window()));
+ scoped_ptr<Window> transient2(CreateTransientChild(21, window2.get()));
+
+ // Both windows and layers are stacked in creation order.
+ RootWindow* root = root_window();
+ ASSERT_EQ(4u, root->children().size());
+ EXPECT_EQ(root->children()[0], window1.get());
+ EXPECT_EQ(root->children()[1], transient1.get());
+ EXPECT_EQ(root->children()[2], window2.get());
+ EXPECT_EQ(root->children()[3], transient2.get());
+ ASSERT_EQ(4u, root->layer()->children().size());
+ EXPECT_EQ(root->layer()->children()[0], window1->layer());
+ EXPECT_EQ(root->layer()->children()[1], transient1->layer());
+ EXPECT_EQ(root->layer()->children()[2], window2->layer());
+ EXPECT_EQ(root->layer()->children()[3], transient2->layer());
+
+ // This brings window1 and its transient to the front.
+ root->StackChildAtTop(window1.get());
+
+ EXPECT_EQ(root->children()[0], window2.get());
+ EXPECT_EQ(root->children()[1], transient2.get());
+ EXPECT_EQ(root->children()[2], window1.get());
+ EXPECT_EQ(root->children()[3], transient1.get());
+ EXPECT_EQ(root->layer()->children()[0], window2->layer());
+ EXPECT_EQ(root->layer()->children()[1], transient2->layer());
+ EXPECT_EQ(root->layer()->children()[2], window1->layer());
+ EXPECT_EQ(root->layer()->children()[3], transient1->layer());
+
+ // Pretend we're closing the top-most transient, then bring window2 to the
+ // front. This mimics activating a browser window while the status bubble
+ // is fading out. The transient should stay topmost.
+ transient1->layer()->set_delegate(NULL);
+ root->StackChildAtTop(window2.get());
+
+ EXPECT_EQ(root->children()[0], window1.get());
+ EXPECT_EQ(root->children()[1], window2.get());
+ EXPECT_EQ(root->children()[2], transient2.get());
+ EXPECT_EQ(root->children()[3], transient1.get());
+ EXPECT_EQ(root->layer()->children()[0], window1->layer());
+ EXPECT_EQ(root->layer()->children()[1], window2->layer());
+ EXPECT_EQ(root->layer()->children()[2], transient2->layer());
+ EXPECT_EQ(root->layer()->children()[3], transient1->layer());
+
+ // Close the transient. Remaining windows are stable.
+ transient1.reset();
+
+ ASSERT_EQ(3u, root->children().size());
+ EXPECT_EQ(root->children()[0], window1.get());
+ EXPECT_EQ(root->children()[1], window2.get());
+ EXPECT_EQ(root->children()[2], transient2.get());
+ ASSERT_EQ(3u, root->layer()->children().size());
+ EXPECT_EQ(root->layer()->children()[0], window1->layer());
+ EXPECT_EQ(root->layer()->children()[1], window2->layer());
+ EXPECT_EQ(root->layer()->children()[2], transient2->layer());
+
+ // Open another window on top.
+ scoped_ptr<Window> window3(CreateTestWindowWithId(3, root_window()));
+
+ ASSERT_EQ(4u, root->children().size());
+ EXPECT_EQ(root->children()[0], window1.get());
+ EXPECT_EQ(root->children()[1], window2.get());
+ EXPECT_EQ(root->children()[2], transient2.get());
+ EXPECT_EQ(root->children()[3], window3.get());
+ ASSERT_EQ(4u, root->layer()->children().size());
+ EXPECT_EQ(root->layer()->children()[0], window1->layer());
+ EXPECT_EQ(root->layer()->children()[1], window2->layer());
+ EXPECT_EQ(root->layer()->children()[2], transient2->layer());
+ EXPECT_EQ(root->layer()->children()[3], window3->layer());
+
+ // Pretend we're closing the topmost non-transient window, then bring
+ // window2 to the top. It should not move.
+ window3->layer()->set_delegate(NULL);
+ root->StackChildAtTop(window2.get());
+
+ ASSERT_EQ(4u, root->children().size());
+ EXPECT_EQ(root->children()[0], window1.get());
+ EXPECT_EQ(root->children()[1], window2.get());
+ EXPECT_EQ(root->children()[2], transient2.get());
+ EXPECT_EQ(root->children()[3], window3.get());
+ ASSERT_EQ(4u, root->layer()->children().size());
+ EXPECT_EQ(root->layer()->children()[0], window1->layer());
+ EXPECT_EQ(root->layer()->children()[1], window2->layer());
+ EXPECT_EQ(root->layer()->children()[2], transient2->layer());
+ EXPECT_EQ(root->layer()->children()[3], window3->layer());
+
+ // Bring window1 to the top. It should move ahead of window2, but not
+ // ahead of window3 (with NULL delegate).
+ root->StackChildAtTop(window1.get());
+
+ ASSERT_EQ(4u, root->children().size());
+ EXPECT_EQ(root->children()[0], window2.get());
+ EXPECT_EQ(root->children()[1], transient2.get());
+ EXPECT_EQ(root->children()[2], window1.get());
+ EXPECT_EQ(root->children()[3], window3.get());
+ ASSERT_EQ(4u, root->layer()->children().size());
+ EXPECT_EQ(root->layer()->children()[0], window2->layer());
+ EXPECT_EQ(root->layer()->children()[1], transient2->layer());
+ EXPECT_EQ(root->layer()->children()[2], window1->layer());
+ EXPECT_EQ(root->layer()->children()[3], window3->layer());
+}
+
+class RootWindowAttachmentObserver : public WindowObserver {
+ public:
+ RootWindowAttachmentObserver() : added_count_(0), removed_count_(0) {}
+ virtual ~RootWindowAttachmentObserver() {}
+
+ int added_count() const { return added_count_; }
+ int removed_count() const { return removed_count_; }
+
+ void Clear() {
+ added_count_ = 0;
+ removed_count_ = 0;
+ }
+
+ // Overridden from WindowObserver:
+ virtual void OnWindowAddedToRootWindow(Window* window) OVERRIDE {
+ ++added_count_;
+ }
+ virtual void OnWindowRemovingFromRootWindow(Window* window) OVERRIDE {
+ ++removed_count_;
+ }
+
+ private:
+ int added_count_;
+ int removed_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(RootWindowAttachmentObserver);
+};
+
+TEST_F(WindowTest, RootWindowAttachment) {
+ RootWindowAttachmentObserver observer;
+
+ // Test a direct add/remove from the RootWindow.
+ scoped_ptr<Window> w1(new Window(NULL));
+ w1->Init(ui::LAYER_NOT_DRAWN);
+ w1->AddObserver(&observer);
+
+ SetDefaultParentByPrimaryRootWindow(w1.get());
+ EXPECT_EQ(1, observer.added_count());
+ EXPECT_EQ(0, observer.removed_count());
+
+ w1.reset();
+ EXPECT_EQ(1, observer.added_count());
+ EXPECT_EQ(1, observer.removed_count());
+
+ observer.Clear();
+
+ // Test an indirect add/remove from the RootWindow.
+ w1.reset(new Window(NULL));
+ w1->Init(ui::LAYER_NOT_DRAWN);
+ Window* w11 = new Window(NULL);
+ w11->Init(ui::LAYER_NOT_DRAWN);
+ w11->AddObserver(&observer);
+ w1->AddChild(w11);
+ EXPECT_EQ(0, observer.added_count());
+ EXPECT_EQ(0, observer.removed_count());
+
+ SetDefaultParentByPrimaryRootWindow(w1.get());
+ EXPECT_EQ(1, observer.added_count());
+ EXPECT_EQ(0, observer.removed_count());
+
+ w1.reset(); // Deletes w11.
+ w11 = NULL;
+ EXPECT_EQ(1, observer.added_count());
+ EXPECT_EQ(1, observer.removed_count());
+
+ observer.Clear();
+
+ // Test an indirect add/remove with nested observers.
+ w1.reset(new Window(NULL));
+ w1->Init(ui::LAYER_NOT_DRAWN);
+ w11 = new Window(NULL);
+ w11->Init(ui::LAYER_NOT_DRAWN);
+ w11->AddObserver(&observer);
+ w1->AddChild(w11);
+ Window* w111 = new Window(NULL);
+ w111->Init(ui::LAYER_NOT_DRAWN);
+ w111->AddObserver(&observer);
+ w11->AddChild(w111);
+
+ EXPECT_EQ(0, observer.added_count());
+ EXPECT_EQ(0, observer.removed_count());
+
+ SetDefaultParentByPrimaryRootWindow(w1.get());
+ EXPECT_EQ(2, observer.added_count());
+ EXPECT_EQ(0, observer.removed_count());
+
+ w1.reset(); // Deletes w11 and w111.
+ w11 = NULL;
+ w111 = NULL;
+ EXPECT_EQ(2, observer.added_count());
+ EXPECT_EQ(2, observer.removed_count());
+}
+
+TEST_F(WindowTest, OwnedByParentFalse) {
+ // By default, a window is owned by its parent. If this is set to false, the
+ // window will not be destroyed when its parent is.
+
+ scoped_ptr<Window> w1(new Window(NULL));
+ w1->Init(ui::LAYER_NOT_DRAWN);
+ scoped_ptr<Window> w2(new Window(NULL));
+ w2->set_owned_by_parent(false);
+ w2->Init(ui::LAYER_NOT_DRAWN);
+ w1->AddChild(w2.get());
+
+ w1.reset();
+
+ // We should be able to deref w2 still, but its parent should now be NULL.
+ EXPECT_EQ(NULL, w2->parent());
+}
+
+namespace {
+
+// Used By DeleteWindowFromOnWindowDestroyed. Destroys a Window from
+// OnWindowDestroyed().
+class OwningWindowDelegate : public TestWindowDelegate {
+ public:
+ OwningWindowDelegate() {}
+
+ void SetOwnedWindow(Window* window) {
+ owned_window_.reset(window);
+ }
+
+ virtual void OnWindowDestroyed() OVERRIDE {
+ owned_window_.reset(NULL);
+ }
+
+ private:
+ scoped_ptr<Window> owned_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(OwningWindowDelegate);
+};
+
+} // namespace
+
+// Creates a window with two child windows. When the first child window is
+// destroyed (WindowDelegate::OnWindowDestroyed) it deletes the second child.
+// This synthesizes BrowserView and the status bubble. Both are children of the
+// same parent and destroying BrowserView triggers it destroying the status
+// bubble.
+TEST_F(WindowTest, DeleteWindowFromOnWindowDestroyed) {
+ scoped_ptr<Window> parent(new Window(NULL));
+ parent->Init(ui::LAYER_NOT_DRAWN);
+ OwningWindowDelegate delegate;
+ Window* c1 = new Window(&delegate);
+ c1->Init(ui::LAYER_NOT_DRAWN);
+ parent->AddChild(c1);
+ Window* c2 = new Window(NULL);
+ c2->Init(ui::LAYER_NOT_DRAWN);
+ parent->AddChild(c2);
+ delegate.SetOwnedWindow(c2);
+ parent.reset();
+}
+
+namespace {
+
+// Used by DelegateNotifiedAsBoundsChange to verify OnBoundsChanged() is
+// invoked.
+class BoundsChangeDelegate : public TestWindowDelegate {
+ public:
+ BoundsChangeDelegate() : bounds_changed_(false) {}
+
+ void clear_bounds_changed() { bounds_changed_ = false; }
+ bool bounds_changed() const {
+ return bounds_changed_;
+ }
+
+ // Window
+ virtual void OnBoundsChanged(const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) OVERRIDE {
+ bounds_changed_ = true;
+ }
+
+ private:
+ // Was OnBoundsChanged() invoked?
+ bool bounds_changed_;
+
+ DISALLOW_COPY_AND_ASSIGN(BoundsChangeDelegate);
+};
+
+} // namespace
+
+// Verifies the delegate is notified when the actual bounds of the layer
+// change.
+TEST_F(WindowTest, DelegateNotifiedAsBoundsChange) {
+ BoundsChangeDelegate delegate;
+
+ // We cannot short-circuit animations in this test.
+ ui::ScopedAnimationDurationScaleMode normal_duration_mode(
+ ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
+
+ scoped_ptr<Window> window(
+ CreateTestWindowWithDelegate(&delegate, 1,
+ gfx::Rect(0, 0, 100, 100), root_window()));
+ window->layer()->GetAnimator()->set_disable_timer_for_test(true);
+
+ delegate.clear_bounds_changed();
+
+ // Animate to a different position.
+ {
+ ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
+ window->SetBounds(gfx::Rect(100, 100, 100, 100));
+ }
+
+ // Bounds shouldn't immediately have changed.
+ EXPECT_EQ("0,0 100x100", window->bounds().ToString());
+ EXPECT_FALSE(delegate.bounds_changed());
+
+ // Animate to the end, which should notify of the change.
+ base::TimeTicks start_time =
+ window->layer()->GetAnimator()->last_step_time();
+ ui::AnimationContainerElement* element = window->layer()->GetAnimator();
+ element->Step(start_time + base::TimeDelta::FromMilliseconds(1000));
+ EXPECT_TRUE(delegate.bounds_changed());
+ EXPECT_NE("0,0 100x100", window->bounds().ToString());
+}
+
+// Verifies the delegate is notified when the actual bounds of the layer
+// change even when the window is not the layer's delegate
+TEST_F(WindowTest, DelegateNotifiedAsBoundsChangeInHiddenLayer) {
+ BoundsChangeDelegate delegate;
+
+ // We cannot short-circuit animations in this test.
+ ui::ScopedAnimationDurationScaleMode normal_duration_mode(
+ ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
+
+ scoped_ptr<Window> window(
+ CreateTestWindowWithDelegate(&delegate, 1,
+ gfx::Rect(0, 0, 100, 100), root_window()));
+ window->layer()->GetAnimator()->set_disable_timer_for_test(true);
+
+ delegate.clear_bounds_changed();
+
+ // Suppress paint on the window since it is hidden (should reset the layer's
+ // delegate to NULL)
+ window->SuppressPaint();
+ EXPECT_EQ(NULL, window->layer()->delegate());
+
+ // Animate to a different position.
+ {
+ ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
+ window->SetBounds(gfx::Rect(100, 100, 110, 100));
+ }
+
+ // Layer delegate is NULL but we should still get bounds changed notification.
+ EXPECT_EQ("100,100 110x100", window->GetTargetBounds().ToString());
+ EXPECT_TRUE(delegate.bounds_changed());
+
+ delegate.clear_bounds_changed();
+
+ // Animate to the end: will *not* notify of the change since we are hidden.
+ base::TimeTicks start_time =
+ window->layer()->GetAnimator()->last_step_time();
+ ui::AnimationContainerElement* element = window->layer()->GetAnimator();
+ element->Step(start_time + base::TimeDelta::FromMilliseconds(1000));
+
+ // No bounds changed notification at the end of animation since layer
+ // delegate is NULL.
+ EXPECT_FALSE(delegate.bounds_changed());
+ EXPECT_NE("0,0 100x100", window->bounds().ToString());
+}
+
+namespace {
+
+// Used by AddChildNotifications to track notification counts.
+class AddChildNotificationsObserver : public WindowObserver {
+ public:
+ AddChildNotificationsObserver() : added_count_(0), removed_count_(0) {}
+
+ std::string CountStringAndReset() {
+ std::string result = base::IntToString(added_count_) + " " +
+ base::IntToString(removed_count_);
+ added_count_ = removed_count_ = 0;
+ return result;
+ }
+
+ // WindowObserver overrides:
+ virtual void OnWindowAddedToRootWindow(Window* window) OVERRIDE {
+ added_count_++;
+ }
+ virtual void OnWindowRemovingFromRootWindow(Window* window) OVERRIDE {
+ removed_count_++;
+ }
+
+ private:
+ int added_count_;
+ int removed_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(AddChildNotificationsObserver);
+};
+
+} // namespace
+
+// Assertions around when root window notifications are sent.
+TEST_F(WindowTest, AddChildNotifications) {
+ AddChildNotificationsObserver observer;
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, root_window()));
+ scoped_ptr<Window> w2(CreateTestWindowWithId(1, root_window()));
+ w2->AddObserver(&observer);
+ w2->Focus();
+ EXPECT_TRUE(w2->HasFocus());
+
+ // Move |w2| to be a child of |w1|.
+ w1->AddChild(w2.get());
+ // Sine we moved in the same root, observer shouldn't be notified.
+ EXPECT_EQ("0 0", observer.CountStringAndReset());
+ // |w2| should still have focus after moving.
+ EXPECT_TRUE(w2->HasFocus());
+}
+
+// Tests that a delegate that destroys itself when the window is destroyed does
+// not break.
+TEST_F(WindowTest, DelegateDestroysSelfOnWindowDestroy) {
+ scoped_ptr<Window> w1(CreateTestWindowWithDelegate(
+ new DestroyWindowDelegate(),
+ 0,
+ gfx::Rect(10, 20, 30, 40),
+ root_window()));
+}
+
+class HierarchyObserver : public WindowObserver {
+ public:
+ HierarchyObserver(Window* target) : target_(target) {
+ target_->AddObserver(this);
+ }
+ virtual ~HierarchyObserver() {
+ target_->RemoveObserver(this);
+ }
+
+ void ValidateState(
+ int index,
+ const WindowObserver::HierarchyChangeParams& params) const {
+ ParamsMatch(params_[index], params);
+ }
+
+ void Reset() {
+ params_.clear();
+ }
+
+ private:
+ // Overridden from WindowObserver:
+ virtual void OnWindowHierarchyChanging(
+ const HierarchyChangeParams& params) OVERRIDE {
+ params_.push_back(params);
+ }
+ virtual void OnWindowHierarchyChanged(
+ const HierarchyChangeParams& params) OVERRIDE {
+ params_.push_back(params);
+ }
+
+ void ParamsMatch(const WindowObserver::HierarchyChangeParams& p1,
+ const WindowObserver::HierarchyChangeParams& p2) const {
+ EXPECT_EQ(p1.phase, p2.phase);
+ EXPECT_EQ(p1.target, p2.target);
+ EXPECT_EQ(p1.new_parent, p2.new_parent);
+ EXPECT_EQ(p1.old_parent, p2.old_parent);
+ EXPECT_EQ(p1.receiver, p2.receiver);
+ }
+
+ Window* target_;
+ std::vector<WindowObserver::HierarchyChangeParams> params_;
+
+ DISALLOW_COPY_AND_ASSIGN(HierarchyObserver);
+};
+
+// Tests hierarchy change notifications.
+TEST_F(WindowTest, OnWindowHierarchyChange) {
+ {
+ // Simple add & remove.
+ HierarchyObserver oroot(root_window());
+
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, NULL));
+ HierarchyObserver o1(w1.get());
+
+ // Add.
+ root_window()->AddChild(w1.get());
+
+ WindowObserver::HierarchyChangeParams params;
+ params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGING;
+ params.target = w1.get();
+ params.old_parent = NULL;
+ params.new_parent = root_window();
+ params.receiver = w1.get();
+ o1.ValidateState(0, params);
+
+ params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGED;
+ params.receiver = w1.get();
+ o1.ValidateState(1, params);
+
+ params.receiver = root_window();
+ oroot.ValidateState(0, params);
+
+ // Remove.
+ o1.Reset();
+ oroot.Reset();
+
+ root_window()->RemoveChild(w1.get());
+
+ params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGING;
+ params.old_parent = root_window();
+ params.new_parent = NULL;
+ params.receiver = w1.get();
+
+ o1.ValidateState(0, params);
+
+ params.receiver = root_window();
+ oroot.ValidateState(0, params);
+
+ params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGED;
+ params.receiver = w1.get();
+ o1.ValidateState(1, params);
+ }
+
+ {
+ // Add & remove of hierarchy. Tests notification order per documentation in
+ // WindowObserver.
+ HierarchyObserver o(root_window());
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, NULL));
+ Window* w11 = CreateTestWindowWithId(11, w1.get());
+ w1->AddObserver(&o);
+ w11->AddObserver(&o);
+
+ // Add.
+ root_window()->AddChild(w1.get());
+
+ // Dispatched to target first.
+ int index = 0;
+ WindowObserver::HierarchyChangeParams params;
+ params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGING;
+ params.target = w1.get();
+ params.old_parent = NULL;
+ params.new_parent = root_window();
+ params.receiver = w1.get();
+ o.ValidateState(index++, params);
+
+ // Dispatched to target's children.
+ params.receiver = w11;
+ o.ValidateState(index++, params);
+
+ params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGED;
+
+ // Now process the "changed" phase.
+ params.receiver = w1.get();
+ o.ValidateState(index++, params);
+ params.receiver = w11;
+ o.ValidateState(index++, params);
+ params.receiver = root_window();
+ o.ValidateState(index++, params);
+
+ // Remove.
+ root_window()->RemoveChild(w1.get());
+ params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGING;
+ params.old_parent = root_window();
+ params.new_parent = NULL;
+ params.receiver = w1.get();
+ o.ValidateState(index++, params);
+ params.receiver = w11;
+ o.ValidateState(index++, params);
+ params.receiver = root_window();
+ o.ValidateState(index++, params);
+ params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGED;
+ params.receiver = w1.get();
+ o.ValidateState(index++, params);
+ params.receiver = w11;
+ o.ValidateState(index++, params);
+
+ w1.reset();
+ }
+
+ {
+ // Reparent. Tests notification order per documentation in WindowObserver.
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, root_window()));
+ Window* w11 = CreateTestWindowWithId(11, w1.get());
+ Window* w111 = CreateTestWindowWithId(111, w11);
+ scoped_ptr<Window> w2(CreateTestWindowWithId(2, root_window()));
+
+ HierarchyObserver o(root_window());
+ w1->AddObserver(&o);
+ w11->AddObserver(&o);
+ w111->AddObserver(&o);
+ w2->AddObserver(&o);
+
+ w2->AddChild(w11);
+
+ // Dispatched to target first.
+ int index = 0;
+ WindowObserver::HierarchyChangeParams params;
+ params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGING;
+ params.target = w11;
+ params.old_parent = w1.get();
+ params.new_parent = w2.get();
+ params.receiver = w11;
+ o.ValidateState(index++, params);
+
+ // Then to target's children.
+ params.receiver = w111;
+ o.ValidateState(index++, params);
+
+ // Then to target's old parent chain.
+ params.receiver = w1.get();
+ o.ValidateState(index++, params);
+ params.receiver = root_window();
+ o.ValidateState(index++, params);
+
+ // "Changed" phase.
+ params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGED;
+ params.receiver = w11;
+ o.ValidateState(index++, params);
+ params.receiver = w111;
+ o.ValidateState(index++, params);
+ params.receiver = w2.get();
+ o.ValidateState(index++, params);
+ params.receiver = root_window();
+ o.ValidateState(index++, params);
+
+ w1.reset();
+ w2.reset();
+ }
+
+}
+
+} // namespace test
+} // namespace aura