summaryrefslogtreecommitdiffstats
path: root/chromium/ui/wm/core
diff options
context:
space:
mode:
authorJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-08 14:30:41 +0200
committerJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-12 13:49:54 +0200
commitab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch)
tree498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/ui/wm/core
parent4ce69f7403811819800e7c5ae1318b2647e778d1 (diff)
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/ui/wm/core')
-rw-r--r--chromium/ui/wm/core/DEPS16
-rw-r--r--chromium/ui/wm/core/accelerator_delegate.h33
-rw-r--r--chromium/ui/wm/core/accelerator_filter.cc80
-rw-r--r--chromium/ui/wm/core/accelerator_filter.h40
-rw-r--r--chromium/ui/wm/core/base_focus_rules.cc195
-rw-r--r--chromium/ui/wm/core/base_focus_rules.h45
-rw-r--r--chromium/ui/wm/core/capture_controller.cc145
-rw-r--r--chromium/ui/wm/core/capture_controller.h86
-rw-r--r--chromium/ui/wm/core/compound_event_filter.cc263
-rw-r--r--chromium/ui/wm/core/compound_event_filter.h93
-rw-r--r--chromium/ui/wm/core/compound_event_filter_unittest.cc250
-rw-r--r--chromium/ui/wm/core/cursor_manager.cc217
-rw-r--r--chromium/ui/wm/core/cursor_manager.h93
-rw-r--r--chromium/ui/wm/core/cursor_manager_unittest.cc330
-rw-r--r--chromium/ui/wm/core/default_activation_client.cc157
-rw-r--r--chromium/ui/wm/core/default_activation_client.h73
-rw-r--r--chromium/ui/wm/core/easy_resize_window_targeter.cc62
-rw-r--r--chromium/ui/wm/core/easy_resize_window_targeter.h53
-rw-r--r--chromium/ui/wm/core/focus_controller.cc368
-rw-r--r--chromium/ui/wm/core/focus_controller.h128
-rw-r--r--chromium/ui/wm/core/focus_controller_unittest.cc1285
-rw-r--r--chromium/ui/wm/core/focus_rules.h66
-rw-r--r--chromium/ui/wm/core/image_grid.cc338
-rw-r--r--chromium/ui/wm/core/image_grid.h221
-rw-r--r--chromium/ui/wm/core/image_grid_unittest.cc340
-rw-r--r--chromium/ui/wm/core/input_method_event_filter.cc105
-rw-r--r--chromium/ui/wm/core/input_method_event_filter.h50
-rw-r--r--chromium/ui/wm/core/input_method_event_filter_unittest.cc143
-rw-r--r--chromium/ui/wm/core/masked_window_targeter.cc43
-rw-r--r--chromium/ui/wm/core/masked_window_targeter.h40
-rw-r--r--chromium/ui/wm/core/native_cursor_manager.h61
-rw-r--r--chromium/ui/wm/core/native_cursor_manager_delegate.h33
-rw-r--r--chromium/ui/wm/core/nested_accelerator_controller.cc57
-rw-r--r--chromium/ui/wm/core/nested_accelerator_controller.h47
-rw-r--r--chromium/ui/wm/core/nested_accelerator_controller_unittest.cc205
-rw-r--r--chromium/ui/wm/core/nested_accelerator_delegate.h34
-rw-r--r--chromium/ui/wm/core/nested_accelerator_dispatcher.cc21
-rw-r--r--chromium/ui/wm/core/nested_accelerator_dispatcher.h55
-rw-r--r--chromium/ui/wm/core/nested_accelerator_dispatcher_linux.cc104
-rw-r--r--chromium/ui/wm/core/nested_accelerator_dispatcher_win.cc74
-rw-r--r--chromium/ui/wm/core/shadow.cc193
-rw-r--r--chromium/ui/wm/core/shadow.h86
-rw-r--r--chromium/ui/wm/core/shadow_controller.cc272
-rw-r--r--chromium/ui/wm/core/shadow_controller.h69
-rw-r--r--chromium/ui/wm/core/shadow_controller_unittest.cc219
-rw-r--r--chromium/ui/wm/core/shadow_types.cc23
-rw-r--r--chromium/ui/wm/core/shadow_types.h34
-rw-r--r--chromium/ui/wm/core/transient_window_controller.cc40
-rw-r--r--chromium/ui/wm/core/transient_window_controller.h36
-rw-r--r--chromium/ui/wm/core/transient_window_manager.cc182
-rw-r--r--chromium/ui/wm/core/transient_window_manager.h91
-rw-r--r--chromium/ui/wm/core/transient_window_manager_unittest.cc666
-rw-r--r--chromium/ui/wm/core/transient_window_observer.h32
-rw-r--r--chromium/ui/wm/core/transient_window_stacking_client.cc131
-rw-r--r--chromium/ui/wm/core/transient_window_stacking_client.h37
-rw-r--r--chromium/ui/wm/core/transient_window_stacking_client_unittest.cc215
-rw-r--r--chromium/ui/wm/core/user_activity_detector.cc112
-rw-r--r--chromium/ui/wm/core/user_activity_detector.h81
-rw-r--r--chromium/ui/wm/core/user_activity_detector_unittest.cc200
-rw-r--r--chromium/ui/wm/core/user_activity_observer.h35
-rw-r--r--chromium/ui/wm/core/visibility_controller.cc89
-rw-r--r--chromium/ui/wm/core/visibility_controller.h74
-rw-r--r--chromium/ui/wm/core/visibility_controller_unittest.cc56
-rw-r--r--chromium/ui/wm/core/window_animations.cc658
-rw-r--r--chromium/ui/wm/core/window_animations.h117
-rw-r--r--chromium/ui/wm/core/window_animations_unittest.cc291
-rw-r--r--chromium/ui/wm/core/window_modality_controller.cc201
-rw-r--r--chromium/ui/wm/core/window_modality_controller.h71
-rw-r--r--chromium/ui/wm/core/window_util.cc129
-rw-r--r--chromium/ui/wm/core/window_util.h67
-rw-r--r--chromium/ui/wm/core/window_util_unittest.cc52
-rw-r--r--chromium/ui/wm/core/wm_core_switches.cc16
-rw-r--r--chromium/ui/wm/core/wm_core_switches.h24
-rw-r--r--chromium/ui/wm/core/wm_state.cc30
-rw-r--r--chromium/ui/wm/core/wm_state.h32
75 files changed, 10710 insertions, 0 deletions
diff --git a/chromium/ui/wm/core/DEPS b/chromium/ui/wm/core/DEPS
new file mode 100644
index 00000000000..ddcff7e29ff
--- /dev/null
+++ b/chromium/ui/wm/core/DEPS
@@ -0,0 +1,16 @@
+include_rules = [
+ "+grit/ui_resources.h",
+ "+ui/aura",
+ "+ui/base/accelerators",
+ "+ui/base/cursor",
+ "+ui/base/hit_test.h",
+ "+ui/base/ime",
+ "+ui/base/resource",
+ "+ui/base/ui_base_switches_util.h",
+ "+ui/base/ui_base_types.h",
+ "+ui/compositor",
+ "+ui/events",
+ "+ui/gfx",
+ "+ui/views/views_export.h",
+ "+third_party/skia",
+]
diff --git a/chromium/ui/wm/core/accelerator_delegate.h b/chromium/ui/wm/core/accelerator_delegate.h
new file mode 100644
index 00000000000..886d7dba445
--- /dev/null
+++ b/chromium/ui/wm/core/accelerator_delegate.h
@@ -0,0 +1,33 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_WM_CORE_ACCELERATOR_DELEGATE_H_
+#define UI_WM_CORE_ACCELERATOR_DELEGATE_H_
+
+namespace ui {
+class Accelerator;
+class KeyEvent;
+}
+
+namespace wm {
+
+class AcceleratorDelegate {
+ public:
+ virtual ~AcceleratorDelegate() {}
+
+ // Type of keys that triggers accelerators.
+ enum KeyType {
+ KEY_TYPE_SYSTEM,
+ KEY_TYPE_OTHER,
+ };
+
+ // Return true if the |accelerator| has been processed.
+ virtual bool ProcessAccelerator(const ui::KeyEvent& event,
+ const ui::Accelerator& accelerator,
+ KeyType key_type) = 0;
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_ACCELERATOR_DELEGATE_H_
diff --git a/chromium/ui/wm/core/accelerator_filter.cc b/chromium/ui/wm/core/accelerator_filter.cc
new file mode 100644
index 00000000000..51c6106d91e
--- /dev/null
+++ b/chromium/ui/wm/core/accelerator_filter.cc
@@ -0,0 +1,80 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/wm/core/accelerator_filter.h"
+
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/events/event.h"
+#include "ui/wm/core/accelerator_delegate.h"
+
+namespace wm {
+namespace {
+
+// Returns true if |key_code| is a key usually handled directly by the shell.
+bool IsSystemKey(ui::KeyboardCode key_code) {
+#if defined(OS_CHROMEOS)
+ switch (key_code) {
+ case ui::VKEY_MEDIA_LAUNCH_APP2: // Fullscreen button.
+ case ui::VKEY_MEDIA_LAUNCH_APP1: // Overview button.
+ case ui::VKEY_BRIGHTNESS_DOWN:
+ case ui::VKEY_BRIGHTNESS_UP:
+ case ui::VKEY_KBD_BRIGHTNESS_DOWN:
+ case ui::VKEY_KBD_BRIGHTNESS_UP:
+ case ui::VKEY_VOLUME_MUTE:
+ case ui::VKEY_VOLUME_DOWN:
+ case ui::VKEY_VOLUME_UP:
+ return true;
+ default:
+ return false;
+ }
+#endif // defined(OS_CHROMEOS)
+ return false;
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// AcceleratorFilter, public:
+
+AcceleratorFilter::AcceleratorFilter(scoped_ptr<AcceleratorDelegate> delegate)
+ : delegate_(delegate.Pass()) {
+}
+
+AcceleratorFilter::~AcceleratorFilter() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AcceleratorFilter, EventFilter implementation:
+
+void AcceleratorFilter::OnKeyEvent(ui::KeyEvent* event) {
+ const ui::EventType type = event->type();
+ DCHECK(event->target());
+ if ((type != ui::ET_KEY_PRESSED && type != ui::ET_KEY_RELEASED) ||
+ event->is_char() || !event->target()) {
+ return;
+ }
+
+ ui::Accelerator accelerator = CreateAcceleratorFromKeyEvent(*event);
+
+ AcceleratorDelegate::KeyType key_type =
+ IsSystemKey(event->key_code()) ? AcceleratorDelegate::KEY_TYPE_SYSTEM
+ : AcceleratorDelegate::KEY_TYPE_OTHER;
+
+ if (delegate_->ProcessAccelerator(*event, accelerator, key_type))
+ event->StopPropagation();
+}
+
+ui::Accelerator CreateAcceleratorFromKeyEvent(const ui::KeyEvent& key_event) {
+ const int kModifierFlagMask =
+ (ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN);
+
+ ui::Accelerator accelerator(key_event.key_code(),
+ key_event.flags() & kModifierFlagMask);
+ if (key_event.type() == ui::ET_KEY_RELEASED)
+ accelerator.set_type(ui::ET_KEY_RELEASED);
+ accelerator.set_is_repeat(key_event.IsRepeat());
+ return accelerator;
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/accelerator_filter.h b/chromium/ui/wm/core/accelerator_filter.h
new file mode 100644
index 00000000000..4e5295e098b
--- /dev/null
+++ b/chromium/ui/wm/core/accelerator_filter.h
@@ -0,0 +1,40 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_WM_CORE_ACCELERATOR_FILTER_H_
+#define UI_WM_CORE_ACCELERATOR_FILTER_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/events/event_handler.h"
+#include "ui/wm/wm_export.h"
+
+namespace ui {
+class Accelerator;
+}
+
+namespace wm {
+class AcceleratorDelegate;
+
+// AcceleratorFilter filters key events for AcceleratorControler handling global
+// keyboard accelerators.
+class WM_EXPORT AcceleratorFilter : public ui::EventHandler {
+ public:
+ AcceleratorFilter(scoped_ptr<AcceleratorDelegate> delegate);
+ virtual ~AcceleratorFilter();
+
+ // Overridden from ui::EventHandler:
+ virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
+
+ private:
+ scoped_ptr<AcceleratorDelegate> delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(AcceleratorFilter);
+};
+
+ui::Accelerator CreateAcceleratorFromKeyEvent(const ui::KeyEvent& key_event);
+
+} // namespace wm
+
+#endif // UI_WM_CORE_ACCELERATOR_FILTER_H_
diff --git a/chromium/ui/wm/core/base_focus_rules.cc b/chromium/ui/wm/core/base_focus_rules.cc
new file mode 100644
index 00000000000..e7160b7b3fe
--- /dev/null
+++ b/chromium/ui/wm/core/base_focus_rules.cc
@@ -0,0 +1,195 @@
+// 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/wm/core/base_focus_rules.h"
+
+#include "ui/aura/client/focus_client.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_event_dispatcher.h"
+#include "ui/wm/core/window_modality_controller.h"
+#include "ui/wm/core/window_util.h"
+#include "ui/wm/public/activation_delegate.h"
+
+namespace wm {
+namespace {
+
+aura::Window* GetFocusedWindow(aura::Window* context) {
+ aura::client::FocusClient* focus_client =
+ aura::client::GetFocusClient(context);
+ return focus_client ? focus_client->GetFocusedWindow() : NULL;
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// BaseFocusRules, protected:
+
+BaseFocusRules::BaseFocusRules() {
+}
+
+BaseFocusRules::~BaseFocusRules() {
+}
+
+bool BaseFocusRules::IsWindowConsideredVisibleForActivation(
+ aura::Window* window) const {
+ return window->IsVisible();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// BaseFocusRules, FocusRules implementation:
+
+bool BaseFocusRules::IsToplevelWindow(aura::Window* window) const {
+ // The window must in a valid hierarchy.
+ if (!window->GetRootWindow())
+ return false;
+
+ // The window must exist within a container that supports activation.
+ // The window cannot be blocked by a modal transient.
+ return SupportsChildActivation(window->parent());
+}
+
+bool BaseFocusRules::CanActivateWindow(aura::Window* window) const {
+ // It is possible to activate a NULL window, it is equivalent to clearing
+ // activation.
+ if (!window)
+ return true;
+
+ // Only toplevel windows can be activated.
+ if (!IsToplevelWindow(window))
+ return false;
+
+ // The window must be visible.
+ if (!IsWindowConsideredVisibleForActivation(window))
+ return false;
+
+ // The window's activation delegate must allow this window to be activated.
+ if (aura::client::GetActivationDelegate(window) &&
+ !aura::client::GetActivationDelegate(window)->ShouldActivate()) {
+ return false;
+ }
+
+ // A window must be focusable to be activatable. We don't call
+ // CanFocusWindow() from here because it will call back to us via
+ // GetActivatableWindow().
+ if (!window->CanFocus())
+ return false;
+
+ // The window cannot be blocked by a modal transient.
+ return !GetModalTransient(window);
+}
+
+bool BaseFocusRules::CanFocusWindow(aura::Window* window) const {
+ // It is possible to focus a NULL window, it is equivalent to clearing focus.
+ if (!window)
+ return true;
+
+ // The focused window is always inside the active window, so windows that
+ // aren't activatable can't contain the focused window.
+ aura::Window* activatable = GetActivatableWindow(window);
+ if (!activatable || !activatable->Contains(window))
+ return false;
+ return window->CanFocus();
+}
+
+aura::Window* BaseFocusRules::GetToplevelWindow(aura::Window* window) const {
+ aura::Window* parent = window->parent();
+ aura::Window* child = window;
+ while (parent) {
+ if (IsToplevelWindow(child))
+ return child;
+
+ parent = parent->parent();
+ child = child->parent();
+ }
+ return NULL;
+}
+
+aura::Window* BaseFocusRules::GetActivatableWindow(aura::Window* window) const {
+ aura::Window* parent = window->parent();
+ aura::Window* child = window;
+ while (parent) {
+ if (CanActivateWindow(child))
+ return child;
+
+ // CanActivateWindow() above will return false if |child| is blocked by a
+ // modal transient. In this case the modal is or contains the activatable
+ // window. We recurse because the modal may itself be blocked by a modal
+ // transient.
+ aura::Window* modal_transient = GetModalTransient(child);
+ if (modal_transient)
+ return GetActivatableWindow(modal_transient);
+
+ if (wm::GetTransientParent(child)) {
+ // To avoid infinite recursion, if |child| has a transient parent
+ // whose own modal transient is |child| itself, just return |child|.
+ aura::Window* parent_modal_transient =
+ GetModalTransient(wm::GetTransientParent(child));
+ if (parent_modal_transient == child)
+ return child;
+
+ return GetActivatableWindow(wm::GetTransientParent(child));
+ }
+
+ parent = parent->parent();
+ child = child->parent();
+ }
+ return NULL;
+}
+
+aura::Window* BaseFocusRules::GetFocusableWindow(aura::Window* window) const {
+ if (CanFocusWindow(window))
+ return window;
+
+ // |window| may be in a hierarchy that is non-activatable, in which case we
+ // need to cut over to the activatable hierarchy.
+ aura::Window* activatable = GetActivatableWindow(window);
+ if (!activatable) {
+ // There may not be a related activatable hierarchy to cut over to, in which
+ // case we try an unrelated one.
+ aura::Window* toplevel = GetToplevelWindow(window);
+ if (toplevel)
+ activatable = GetNextActivatableWindow(toplevel);
+ if (!activatable)
+ return NULL;
+ }
+
+ if (!activatable->Contains(window)) {
+ // If there's already a child window focused in the activatable hierarchy,
+ // just use that (i.e. don't shift focus), otherwise we need to at least cut
+ // over to the activatable hierarchy.
+ aura::Window* focused = GetFocusedWindow(activatable);
+ return activatable->Contains(focused) ? focused : activatable;
+ }
+
+ while (window && !CanFocusWindow(window))
+ window = window->parent();
+ return window;
+}
+
+aura::Window* BaseFocusRules::GetNextActivatableWindow(
+ aura::Window* ignore) const {
+ DCHECK(ignore);
+
+ // Can be called from the RootWindow's destruction, which has a NULL parent.
+ if (!ignore->parent())
+ return NULL;
+
+ // In the basic scenarios handled by BasicFocusRules, the pool of activatable
+ // windows is limited to the |ignore|'s siblings.
+ const aura::Window::Windows& siblings = ignore->parent()->children();
+ DCHECK(!siblings.empty());
+
+ for (aura::Window::Windows::const_reverse_iterator rit = siblings.rbegin();
+ rit != siblings.rend();
+ ++rit) {
+ aura::Window* cur = *rit;
+ if (cur == ignore)
+ continue;
+ if (CanActivateWindow(cur))
+ return cur;
+ }
+ return NULL;
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/base_focus_rules.h b/chromium/ui/wm/core/base_focus_rules.h
new file mode 100644
index 00000000000..07dfb65fbc9
--- /dev/null
+++ b/chromium/ui/wm/core/base_focus_rules.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_WM_CORE_BASE_FOCUS_RULES_H_
+#define UI_WM_CORE_BASE_FOCUS_RULES_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "ui/wm/core/focus_rules.h"
+
+namespace wm {
+
+// A set of basic focus and activation rules. Specializations should most likely
+// subclass this and call up to these methods rather than reimplementing them.
+class WM_EXPORT BaseFocusRules : public FocusRules {
+ protected:
+ BaseFocusRules();
+ virtual ~BaseFocusRules();
+
+ // Returns true if the children of |window| can be activated.
+ virtual bool SupportsChildActivation(aura::Window* window) const = 0;
+
+ // Returns true if |window| is considered visible for activation purposes.
+ virtual bool IsWindowConsideredVisibleForActivation(
+ aura::Window* window) const;
+
+ // Overridden from FocusRules:
+ virtual bool IsToplevelWindow(aura::Window* window) const OVERRIDE;
+ virtual bool CanActivateWindow(aura::Window* window) const OVERRIDE;
+ virtual bool CanFocusWindow(aura::Window* window) const OVERRIDE;
+ virtual aura::Window* GetToplevelWindow(aura::Window* window) const OVERRIDE;
+ virtual aura::Window* GetActivatableWindow(
+ aura::Window* window) const OVERRIDE;
+ virtual aura::Window* GetFocusableWindow(aura::Window* window) const OVERRIDE;
+ virtual aura::Window* GetNextActivatableWindow(
+ aura::Window* ignore) const OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BaseFocusRules);
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_BASE_FOCUS_RULES_H_
diff --git a/chromium/ui/wm/core/capture_controller.cc b/chromium/ui/wm/core/capture_controller.cc
new file mode 100644
index 00000000000..b3d8620c05e
--- /dev/null
+++ b/chromium/ui/wm/core/capture_controller.cc
@@ -0,0 +1,145 @@
+// 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/wm/core/capture_controller.h"
+
+#include "ui/aura/window.h"
+#include "ui/aura/window_event_dispatcher.h"
+#include "ui/aura/window_tree_host.h"
+
+namespace wm {
+
+////////////////////////////////////////////////////////////////////////////////
+// CaptureController, public:
+
+void CaptureController::Attach(aura::Window* root) {
+ DCHECK_EQ(0u, root_windows_.count(root));
+ root_windows_.insert(root);
+ aura::client::SetCaptureClient(root, this);
+}
+
+void CaptureController::Detach(aura::Window* root) {
+ root_windows_.erase(root);
+ aura::client::SetCaptureClient(root, NULL);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CaptureController, aura::client::CaptureClient implementation:
+
+void CaptureController::SetCapture(aura::Window* new_capture_window) {
+ if (capture_window_ == new_capture_window)
+ return;
+
+ // Make sure window has a root window.
+ DCHECK(!new_capture_window || new_capture_window->GetRootWindow());
+ DCHECK(!capture_window_ || capture_window_->GetRootWindow());
+
+ aura::Window* old_capture_window = capture_window_;
+ aura::Window* old_capture_root = old_capture_window ?
+ old_capture_window->GetRootWindow() : NULL;
+
+ // Copy the list in case it's modified out from under us.
+ RootWindows root_windows(root_windows_);
+
+ // If we're actually starting capture, then cancel any touches/gestures
+ // that aren't already locked to the new window, and transfer any on the
+ // old capture window to the new one. When capture is released we have no
+ // distinction between the touches/gestures that were in the window all
+ // along (and so shouldn't be canceled) and those that got moved, so
+ // just leave them all where they are.
+ if (new_capture_window) {
+ ui::GestureRecognizer::Get()->TransferEventsTo(old_capture_window,
+ new_capture_window);
+ }
+
+ capture_window_ = new_capture_window;
+
+ for (RootWindows::const_iterator i = root_windows.begin();
+ i != root_windows.end(); ++i) {
+ aura::client::CaptureDelegate* delegate = (*i)->GetHost()->dispatcher();
+ delegate->UpdateCapture(old_capture_window, new_capture_window);
+ }
+
+ aura::Window* capture_root =
+ capture_window_ ? capture_window_->GetRootWindow() : NULL;
+ if (capture_root != old_capture_root) {
+ if (old_capture_root) {
+ aura::client::CaptureDelegate* delegate =
+ old_capture_root->GetHost()->dispatcher();
+ delegate->ReleaseNativeCapture();
+ }
+ if (capture_root) {
+ aura::client::CaptureDelegate* delegate =
+ capture_root->GetHost()->dispatcher();
+ delegate->SetNativeCapture();
+ }
+ }
+}
+
+void CaptureController::ReleaseCapture(aura::Window* window) {
+ if (capture_window_ != window)
+ return;
+ SetCapture(NULL);
+}
+
+aura::Window* CaptureController::GetCaptureWindow() {
+ return capture_window_;
+}
+
+aura::Window* CaptureController::GetGlobalCaptureWindow() {
+ return capture_window_;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CaptureController, private:
+
+CaptureController::CaptureController()
+ : capture_window_(NULL) {
+}
+
+CaptureController::~CaptureController() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ScopedCaptureClient:
+
+// static
+CaptureController* ScopedCaptureClient::capture_controller_ = NULL;
+
+ScopedCaptureClient::ScopedCaptureClient(aura::Window* root)
+ : root_window_(root) {
+ root->AddObserver(this);
+ if (!capture_controller_)
+ capture_controller_ = new CaptureController;
+ capture_controller_->Attach(root);
+}
+
+ScopedCaptureClient::~ScopedCaptureClient() {
+ Shutdown();
+}
+
+// static
+bool ScopedCaptureClient::IsActive() {
+ return capture_controller_ && capture_controller_->is_active();
+}
+
+void ScopedCaptureClient::OnWindowDestroyed(aura::Window* window) {
+ DCHECK_EQ(window, root_window_);
+ Shutdown();
+}
+
+void ScopedCaptureClient::Shutdown() {
+ if (!root_window_)
+ return;
+
+ root_window_->RemoveObserver(this);
+ capture_controller_->Detach(root_window_);
+ if (!capture_controller_->is_active()) {
+ delete capture_controller_;
+ capture_controller_ = NULL;
+ }
+ root_window_ = NULL;
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/capture_controller.h b/chromium/ui/wm/core/capture_controller.h
new file mode 100644
index 00000000000..d8028183e29
--- /dev/null
+++ b/chromium/ui/wm/core/capture_controller.h
@@ -0,0 +1,86 @@
+// 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_WM_CORE_CAPTURE_CONTROLLER_H_
+#define UI_WM_CORE_CAPTURE_CONTROLLER_H_
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "ui/aura/client/capture_client.h"
+#include "ui/aura/window_observer.h"
+#include "ui/wm/wm_export.h"
+
+namespace wm {
+
+// Internal CaptureClient implementation. See ScopedCaptureClient for details.
+class WM_EXPORT CaptureController : public aura::client::CaptureClient {
+ public:
+ // Adds |root| to the list of RootWindows notified when capture changes.
+ void Attach(aura::Window* root);
+
+ // Removes |root| from the list of RootWindows notified when capture changes.
+ void Detach(aura::Window* root);
+
+ // Returns true if this CaptureController is installed on at least one
+ // RootWindow.
+ bool is_active() const { return !root_windows_.empty(); }
+
+ // Overridden from aura::client::CaptureClient:
+ virtual void SetCapture(aura::Window* window) OVERRIDE;
+ virtual void ReleaseCapture(aura::Window* window) OVERRIDE;
+ virtual aura::Window* GetCaptureWindow() OVERRIDE;
+ virtual aura::Window* GetGlobalCaptureWindow() OVERRIDE;
+
+ private:
+ friend class ScopedCaptureClient;
+ typedef std::set<aura::Window*> RootWindows;
+
+ CaptureController();
+ virtual ~CaptureController();
+
+ // The current capture window. NULL if there is no capture window.
+ aura::Window* capture_window_;
+
+ // Set of RootWindows notified when capture changes.
+ RootWindows root_windows_;
+
+ DISALLOW_COPY_AND_ASSIGN(CaptureController);
+};
+
+// ScopedCaptureClient is responsible for creating a CaptureClient for a
+// RootWindow. Specifically it creates a single CaptureController that is shared
+// among all ScopedCaptureClients and adds the RootWindow to it.
+class WM_EXPORT ScopedCaptureClient : public aura::WindowObserver {
+ public:
+ explicit ScopedCaptureClient(aura::Window* root);
+ virtual ~ScopedCaptureClient();
+
+ // Returns true if there is a CaptureController with at least one RootWindow.
+ static bool IsActive();
+
+ aura::client::CaptureClient* capture_client() {
+ return capture_controller_;
+ }
+
+ // Overridden from aura::WindowObserver:
+ virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
+
+ private:
+ // Invoked from destructor and OnWindowDestroyed() to cleanup.
+ void Shutdown();
+
+ // The single CaptureController instance.
+ static CaptureController* capture_controller_;
+
+ // RootWindow this ScopedCaptureClient was create for.
+ aura::Window* root_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedCaptureClient);
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_CAPTURE_CONTROLLER_H_
diff --git a/chromium/ui/wm/core/compound_event_filter.cc b/chromium/ui/wm/core/compound_event_filter.cc
new file mode 100644
index 00000000000..d1f652c02f6
--- /dev/null
+++ b/chromium/ui/wm/core/compound_event_filter.cc
@@ -0,0 +1,263 @@
+// 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/wm/core/compound_event_filter.h"
+
+#include "base/containers/hash_tables.h"
+#include "base/logging.h"
+#include "ui/aura/client/cursor_client.h"
+#include "ui/aura/env.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_delegate.h"
+#include "ui/aura/window_event_dispatcher.h"
+#include "ui/aura/window_tracker.h"
+#include "ui/base/hit_test.h"
+#include "ui/events/event.h"
+#include "ui/wm/public/activation_client.h"
+#include "ui/wm/public/drag_drop_client.h"
+
+#if defined(OS_CHROMEOS) && defined(USE_X11)
+#include "ui/events/x/touch_factory_x11.h"
+#endif
+
+namespace wm {
+
+namespace {
+
+// Returns true if the cursor should be hidden on touch events.
+// TODO(tdanderson|rsadam): Move this function into CursorClient.
+bool ShouldHideCursorOnTouch(const ui::TouchEvent& event) {
+#if defined(OS_WIN)
+ return true;
+#elif defined(OS_CHROMEOS)
+#if defined(USE_X11)
+ int device_id = event.source_device_id();
+ if (device_id >= 0 &&
+ !ui::TouchFactory::GetInstance()->IsMultiTouchDevice(device_id)) {
+ // If the touch event is coming from a mouse-device (i.e. not a real
+ // touch-device), then do not hide the cursor.
+ return false;
+ }
+#endif // defined(USE_X11)
+ return true;
+#else
+ // Linux Aura does not hide the cursor on touch by default.
+ // TODO(tdanderson): Change this if having consistency across
+ // all platforms which use Aura is desired.
+ return false;
+#endif
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// CompoundEventFilter, public:
+
+CompoundEventFilter::CompoundEventFilter() {
+}
+
+CompoundEventFilter::~CompoundEventFilter() {
+ // Additional filters are not owned by CompoundEventFilter and they
+ // should all be removed when running here. |handlers_| has
+ // check_empty == true and will DCHECK failure if it is not empty.
+}
+
+// static
+gfx::NativeCursor CompoundEventFilter::CursorForWindowComponent(
+ int window_component) {
+ switch (window_component) {
+ case HTBOTTOM:
+ return ui::kCursorSouthResize;
+ case HTBOTTOMLEFT:
+ return ui::kCursorSouthWestResize;
+ case HTBOTTOMRIGHT:
+ return ui::kCursorSouthEastResize;
+ case HTLEFT:
+ return ui::kCursorWestResize;
+ case HTRIGHT:
+ return ui::kCursorEastResize;
+ case HTTOP:
+ return ui::kCursorNorthResize;
+ case HTTOPLEFT:
+ return ui::kCursorNorthWestResize;
+ case HTTOPRIGHT:
+ return ui::kCursorNorthEastResize;
+ default:
+ return ui::kCursorNull;
+ }
+}
+
+void CompoundEventFilter::AddHandler(ui::EventHandler* handler) {
+ handlers_.AddObserver(handler);
+}
+
+void CompoundEventFilter::RemoveHandler(ui::EventHandler* handler) {
+ handlers_.RemoveObserver(handler);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CompoundEventFilter, private:
+
+void CompoundEventFilter::UpdateCursor(aura::Window* target,
+ ui::MouseEvent* event) {
+ // If drag and drop is in progress, let the drag drop client set the cursor
+ // instead of setting the cursor here.
+ aura::Window* root_window = target->GetRootWindow();
+ aura::client::DragDropClient* drag_drop_client =
+ aura::client::GetDragDropClient(root_window);
+ if (drag_drop_client && drag_drop_client->IsDragDropInProgress())
+ return;
+
+ aura::client::CursorClient* cursor_client =
+ aura::client::GetCursorClient(root_window);
+ if (cursor_client) {
+ gfx::NativeCursor cursor = target->GetCursor(event->location());
+ if ((event->flags() & ui::EF_IS_NON_CLIENT)) {
+ if (target->delegate()) {
+ int window_component =
+ target->delegate()->GetNonClientComponent(event->location());
+ cursor = CursorForWindowComponent(window_component);
+ } else {
+ // Allow the OS to handle non client cursors if we don't have a
+ // a delegate to handle the non client hittest.
+ return;
+ }
+ }
+ cursor_client->SetCursor(cursor);
+ }
+}
+
+void CompoundEventFilter::FilterKeyEvent(ui::KeyEvent* event) {
+ if (handlers_.might_have_observers()) {
+ ObserverListBase<ui::EventHandler>::Iterator it(handlers_);
+ ui::EventHandler* handler;
+ while (!event->stopped_propagation() && (handler = it.GetNext()) != NULL)
+ handler->OnKeyEvent(event);
+ }
+}
+
+void CompoundEventFilter::FilterMouseEvent(ui::MouseEvent* event) {
+ if (handlers_.might_have_observers()) {
+ ObserverListBase<ui::EventHandler>::Iterator it(handlers_);
+ ui::EventHandler* handler;
+ while (!event->stopped_propagation() && (handler = it.GetNext()) != NULL)
+ handler->OnMouseEvent(event);
+ }
+}
+
+void CompoundEventFilter::FilterTouchEvent(ui::TouchEvent* event) {
+ if (handlers_.might_have_observers()) {
+ ObserverListBase<ui::EventHandler>::Iterator it(handlers_);
+ ui::EventHandler* handler;
+ while (!event->stopped_propagation() && (handler = it.GetNext()) != NULL)
+ handler->OnTouchEvent(event);
+ }
+}
+
+void CompoundEventFilter::SetCursorVisibilityOnEvent(aura::Window* target,
+ ui::Event* event,
+ bool show) {
+ if (event->flags() & ui::EF_IS_SYNTHESIZED)
+ return;
+
+ aura::client::CursorClient* client =
+ aura::client::GetCursorClient(target->GetRootWindow());
+ if (!client)
+ return;
+
+ if (show)
+ client->ShowCursor();
+ else
+ client->HideCursor();
+}
+
+void CompoundEventFilter::SetMouseEventsEnableStateOnEvent(aura::Window* target,
+ ui::Event* event,
+ bool enable) {
+ if (event->flags() & ui::EF_IS_SYNTHESIZED)
+ return;
+ aura::client::CursorClient* client =
+ aura::client::GetCursorClient(target->GetRootWindow());
+ if (!client)
+ return;
+
+ if (enable)
+ client->EnableMouseEvents();
+ else
+ client->DisableMouseEvents();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CompoundEventFilter, ui::EventHandler implementation:
+
+void CompoundEventFilter::OnKeyEvent(ui::KeyEvent* event) {
+ aura::Window* target = static_cast<aura::Window*>(event->target());
+ aura::client::CursorClient* client =
+ aura::client::GetCursorClient(target->GetRootWindow());
+ if (client && client->ShouldHideCursorOnKeyEvent(*event))
+ SetCursorVisibilityOnEvent(target, event, false);
+
+ FilterKeyEvent(event);
+}
+
+void CompoundEventFilter::OnMouseEvent(ui::MouseEvent* event) {
+ aura::Window* window = static_cast<aura::Window*>(event->target());
+ aura::WindowTracker window_tracker;
+ window_tracker.Add(window);
+
+ // We must always update the cursor, otherwise the cursor can get stuck if an
+ // event filter registered with us consumes the event.
+ // It should also update the cursor for clicking and wheels for ChromeOS boot.
+ // When ChromeOS is booted, it hides the mouse cursor but immediate mouse
+ // operation will show the cursor.
+ // We also update the cursor for mouse enter in case a mouse cursor is sent to
+ // outside of the root window and moved back for some reasons (e.g. running on
+ // on Desktop for testing, or a bug in pointer barrier).
+ if (!(event->flags() & ui::EF_FROM_TOUCH) &&
+ (event->type() == ui::ET_MOUSE_ENTERED ||
+ event->type() == ui::ET_MOUSE_MOVED ||
+ event->type() == ui::ET_MOUSE_PRESSED ||
+ event->type() == ui::ET_MOUSEWHEEL)) {
+ SetMouseEventsEnableStateOnEvent(window, event, true);
+ SetCursorVisibilityOnEvent(window, event, true);
+ UpdateCursor(window, event);
+ }
+
+ FilterMouseEvent(event);
+}
+
+void CompoundEventFilter::OnScrollEvent(ui::ScrollEvent* event) {
+}
+
+void CompoundEventFilter::OnTouchEvent(ui::TouchEvent* event) {
+ FilterTouchEvent(event);
+ if (!event->handled() && event->type() == ui::ET_TOUCH_PRESSED &&
+ ShouldHideCursorOnTouch(*event) &&
+ !aura::Env::GetInstance()->IsMouseButtonDown()) {
+ SetMouseEventsEnableStateOnEvent(
+ static_cast<aura::Window*>(event->target()), event, false);
+ }
+}
+
+void CompoundEventFilter::OnGestureEvent(ui::GestureEvent* event) {
+ if (handlers_.might_have_observers()) {
+ ObserverListBase<ui::EventHandler>::Iterator it(handlers_);
+ ui::EventHandler* handler;
+ while (!event->stopped_propagation() && (handler = it.GetNext()) != NULL)
+ handler->OnGestureEvent(event);
+ }
+
+#if defined(OS_WIN)
+ // A Win8 edge swipe event is a special event that does not have location
+ // information associated with it, and is not preceeded by an ET_TOUCH_PRESSED
+ // event. So we treat it specially here.
+ if (!event->handled() && event->type() == ui::ET_GESTURE_WIN8_EDGE_SWIPE &&
+ !aura::Env::GetInstance()->IsMouseButtonDown()) {
+ SetMouseEventsEnableStateOnEvent(
+ static_cast<aura::Window*>(event->target()), event, false);
+ }
+#endif
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/compound_event_filter.h b/chromium/ui/wm/core/compound_event_filter.h
new file mode 100644
index 00000000000..ab4fdf83d06
--- /dev/null
+++ b/chromium/ui/wm/core/compound_event_filter.h
@@ -0,0 +1,93 @@
+// 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_WM_CORE_COMPOUND_EVENT_FILTER_H_
+#define UI_WM_CORE_COMPOUND_EVENT_FILTER_H_
+
+#include "base/compiler_specific.h"
+#include "base/observer_list.h"
+#include "ui/events/event.h"
+#include "ui/events/event_handler.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/wm/wm_export.h"
+
+namespace aura {
+class CursorManager;
+class RootWindow;
+}
+
+namespace ui {
+class GestureEvent;
+class KeyEvent;
+class LocatedEvent;
+class MouseEvent;
+class TouchEvent;
+}
+
+namespace wm {
+
+// TODO(beng): This class should die. AddEventHandler() on the root Window
+// should be used instead.
+// CompoundEventFilter gets all events first and can provide actions to those
+// events. It implements global features such as click to activate a window and
+// cursor change when moving mouse.
+// Additional event filters can be added to CompoundEventFilter. Events will
+// pass through those additional filters in their addition order and could be
+// consumed by any of those filters. If an event is consumed by a filter, the
+// rest of the filter(s) and CompoundEventFilter will not see the consumed
+// event.
+class WM_EXPORT CompoundEventFilter : public ui::EventHandler {
+ public:
+ CompoundEventFilter();
+ virtual ~CompoundEventFilter();
+
+ // Returns the cursor for the specified component.
+ static gfx::NativeCursor CursorForWindowComponent(int window_component);
+
+ // Adds/removes additional event filters. This does not take ownership of
+ // the EventHandler.
+ // NOTE: These handlers are deprecated. Use env::AddPreTargetEventHandler etc.
+ // instead.
+ void AddHandler(ui::EventHandler* filter);
+ void RemoveHandler(ui::EventHandler* filter);
+
+ private:
+ // Updates the cursor if the target provides a custom one, and provides
+ // default resize cursors for window edges.
+ void UpdateCursor(aura::Window* target, ui::MouseEvent* event);
+
+ // Dispatches event to additional filters.
+ void FilterKeyEvent(ui::KeyEvent* event);
+ void FilterMouseEvent(ui::MouseEvent* event);
+ void FilterTouchEvent(ui::TouchEvent* event);
+
+ // Sets the visibility of the cursor if the event is not synthesized.
+ void SetCursorVisibilityOnEvent(aura::Window* target,
+ ui::Event* event,
+ bool show);
+
+ // Enables or disables mouse events if the event is not synthesized.
+ void SetMouseEventsEnableStateOnEvent(aura::Window* target,
+ ui::Event* event,
+ bool enable);
+
+ // Overridden from ui::EventHandler:
+ virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
+ virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
+ virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE;
+ virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE;
+ virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
+
+ // Additional pre-target event handlers.
+ ObserverList<ui::EventHandler, true> handlers_;
+
+ // True if the cursur was hidden by the filter.
+ bool cursor_hidden_by_filter_;
+
+ DISALLOW_COPY_AND_ASSIGN(CompoundEventFilter);
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_COMPOUND_EVENT_FILTER_H_
diff --git a/chromium/ui/wm/core/compound_event_filter_unittest.cc b/chromium/ui/wm/core/compound_event_filter_unittest.cc
new file mode 100644
index 00000000000..afd3bcb5bd8
--- /dev/null
+++ b/chromium/ui/wm/core/compound_event_filter_unittest.cc
@@ -0,0 +1,250 @@
+// 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/wm/core/compound_event_filter.h"
+
+#include "ui/aura/client/cursor_client.h"
+#include "ui/aura/env.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_windows.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_event_dispatcher.h"
+#include "ui/events/event.h"
+#include "ui/events/event_utils.h"
+#include "ui/wm/core/default_activation_client.h"
+#include "ui/wm/public/activation_client.h"
+
+namespace {
+
+#if defined(OS_CHROMEOS) || defined(OS_WIN)
+base::TimeDelta GetTime() {
+ return ui::EventTimeForNow();
+}
+#endif // defined(OS_CHROMEOS) || defined(OS_WIN)
+
+}
+
+namespace wm {
+
+namespace {
+
+// An event filter that consumes all gesture events.
+class ConsumeGestureEventFilter : public ui::EventHandler {
+ public:
+ ConsumeGestureEventFilter() {}
+ virtual ~ConsumeGestureEventFilter() {}
+
+ private:
+ // Overridden from ui::EventHandler:
+ virtual void OnGestureEvent(ui::GestureEvent* e) OVERRIDE {
+ e->StopPropagation();
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(ConsumeGestureEventFilter);
+};
+
+} // namespace
+
+typedef aura::test::AuraTestBase CompoundEventFilterTest;
+
+#if defined(OS_CHROMEOS)
+// A keypress only hides the cursor on ChromeOS (crbug.com/304296).
+TEST_F(CompoundEventFilterTest, CursorVisibilityChange) {
+ scoped_ptr<CompoundEventFilter> compound_filter(new CompoundEventFilter);
+ aura::Env::GetInstance()->AddPreTargetHandler(compound_filter.get());
+ aura::test::TestWindowDelegate delegate;
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(&delegate, 1234,
+ gfx::Rect(5, 5, 100, 100), root_window()));
+ window->Show();
+ window->SetCapture();
+
+ aura::test::TestCursorClient cursor_client(root_window());
+
+ // Send key event to hide the cursor.
+ ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_A, 0, true);
+ DispatchEventUsingWindowDispatcher(&key);
+ EXPECT_FALSE(cursor_client.IsCursorVisible());
+
+ // Synthesized mouse event should not show the cursor.
+ ui::MouseEvent enter(ui::ET_MOUSE_ENTERED, gfx::Point(10, 10),
+ gfx::Point(10, 10), 0, 0);
+ enter.set_flags(enter.flags() | ui::EF_IS_SYNTHESIZED);
+ DispatchEventUsingWindowDispatcher(&enter);
+ EXPECT_FALSE(cursor_client.IsCursorVisible());
+
+ ui::MouseEvent move(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
+ gfx::Point(10, 10), 0, 0);
+ move.set_flags(enter.flags() | ui::EF_IS_SYNTHESIZED);
+ DispatchEventUsingWindowDispatcher(&move);
+ EXPECT_FALSE(cursor_client.IsCursorVisible());
+
+ // A real mouse event should show the cursor.
+ ui::MouseEvent real_move(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
+ gfx::Point(10, 10), 0, 0);
+ DispatchEventUsingWindowDispatcher(&real_move);
+ EXPECT_TRUE(cursor_client.IsCursorVisible());
+
+ // Disallow hiding the cursor on keypress.
+ cursor_client.set_should_hide_cursor_on_key_event(false);
+ key = ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, 0, true);
+ DispatchEventUsingWindowDispatcher(&key);
+ EXPECT_TRUE(cursor_client.IsCursorVisible());
+
+ // Allow hiding the cursor on keypress.
+ cursor_client.set_should_hide_cursor_on_key_event(true);
+ key = ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, 0, true);
+ DispatchEventUsingWindowDispatcher(&key);
+ EXPECT_FALSE(cursor_client.IsCursorVisible());
+
+ // Mouse synthesized exit event should not show the cursor.
+ ui::MouseEvent exit(ui::ET_MOUSE_EXITED, gfx::Point(10, 10),
+ gfx::Point(10, 10), 0, 0);
+ exit.set_flags(enter.flags() | ui::EF_IS_SYNTHESIZED);
+ DispatchEventUsingWindowDispatcher(&exit);
+ EXPECT_FALSE(cursor_client.IsCursorVisible());
+
+ aura::Env::GetInstance()->RemovePreTargetHandler(compound_filter.get());
+}
+#endif // defined(OS_CHROMEOS)
+
+#if defined(OS_CHROMEOS) || defined(OS_WIN)
+// Touch visually hides the cursor on ChromeOS and Windows.
+TEST_F(CompoundEventFilterTest, TouchHidesCursor) {
+ new wm::DefaultActivationClient(root_window());
+ scoped_ptr<CompoundEventFilter> compound_filter(new CompoundEventFilter);
+ aura::Env::GetInstance()->AddPreTargetHandler(compound_filter.get());
+ aura::test::TestWindowDelegate delegate;
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(&delegate, 1234,
+ gfx::Rect(5, 5, 100, 100), root_window()));
+ window->Show();
+ window->SetCapture();
+
+ aura::test::TestCursorClient cursor_client(root_window());
+
+ ui::MouseEvent mouse0(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
+ gfx::Point(10, 10), 0, 0);
+ DispatchEventUsingWindowDispatcher(&mouse0);
+ EXPECT_TRUE(cursor_client.IsMouseEventsEnabled());
+
+ // This press is required for the GestureRecognizer to associate a target
+ // with kTouchId
+ ui::TouchEvent press0(
+ ui::ET_TOUCH_PRESSED, gfx::Point(90, 90), 1, GetTime());
+ DispatchEventUsingWindowDispatcher(&press0);
+ EXPECT_FALSE(cursor_client.IsMouseEventsEnabled());
+
+ ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(10, 10), 1, GetTime());
+ DispatchEventUsingWindowDispatcher(&move);
+ EXPECT_FALSE(cursor_client.IsMouseEventsEnabled());
+
+ ui::TouchEvent release(
+ ui::ET_TOUCH_RELEASED, gfx::Point(10, 10), 1, GetTime());
+ DispatchEventUsingWindowDispatcher(&release);
+ EXPECT_FALSE(cursor_client.IsMouseEventsEnabled());
+
+ ui::MouseEvent mouse1(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
+ gfx::Point(10, 10), 0, 0);
+ // Move the cursor again. The cursor should be visible.
+ DispatchEventUsingWindowDispatcher(&mouse1);
+ EXPECT_TRUE(cursor_client.IsMouseEventsEnabled());
+
+ // Now activate the window and press on it again.
+ ui::TouchEvent press1(
+ ui::ET_TOUCH_PRESSED, gfx::Point(90, 90), 1, GetTime());
+ aura::client::GetActivationClient(
+ root_window())->ActivateWindow(window.get());
+ DispatchEventUsingWindowDispatcher(&press1);
+ EXPECT_FALSE(cursor_client.IsMouseEventsEnabled());
+ aura::Env::GetInstance()->RemovePreTargetHandler(compound_filter.get());
+}
+#endif // defined(OS_CHROMEOS) || defined(OS_WIN)
+
+// Tests that if an event filter consumes a gesture, then it doesn't focus the
+// window.
+TEST_F(CompoundEventFilterTest, FilterConsumedGesture) {
+ scoped_ptr<CompoundEventFilter> compound_filter(new CompoundEventFilter);
+ scoped_ptr<ui::EventHandler> gesure_handler(new ConsumeGestureEventFilter);
+ compound_filter->AddHandler(gesure_handler.get());
+ aura::Env::GetInstance()->AddPreTargetHandler(compound_filter.get());
+ aura::test::TestWindowDelegate delegate;
+ DCHECK(root_window());
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(&delegate, 1234,
+ gfx::Rect(5, 5, 100, 100), root_window()));
+ window->Show();
+
+ EXPECT_TRUE(window->CanFocus());
+ EXPECT_FALSE(window->HasFocus());
+
+ // Tap on the window should not focus it since the filter will be consuming
+ // the gestures.
+ aura::test::EventGenerator generator(root_window(), gfx::Point(50, 50));
+ generator.PressTouch();
+ EXPECT_FALSE(window->HasFocus());
+
+ compound_filter->RemoveHandler(gesure_handler.get());
+ aura::Env::GetInstance()->RemovePreTargetHandler(compound_filter.get());
+}
+
+// Verifies we don't attempt to hide the mouse when the mouse is down and a
+// touch event comes in.
+TEST_F(CompoundEventFilterTest, DontHideWhenMouseDown) {
+ aura::test::EventGenerator event_generator(root_window());
+
+ scoped_ptr<CompoundEventFilter> compound_filter(new CompoundEventFilter);
+ aura::Env::GetInstance()->AddPreTargetHandler(compound_filter.get());
+ aura::test::TestWindowDelegate delegate;
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(&delegate, 1234,
+ gfx::Rect(5, 5, 100, 100), root_window()));
+ window->Show();
+
+ aura::test::TestCursorClient cursor_client(root_window());
+
+ // Move and press the mouse over the window.
+ event_generator.MoveMouseTo(10, 10);
+ EXPECT_TRUE(cursor_client.IsMouseEventsEnabled());
+ event_generator.PressLeftButton();
+ EXPECT_TRUE(cursor_client.IsMouseEventsEnabled());
+ EXPECT_TRUE(aura::Env::GetInstance()->IsMouseButtonDown());
+
+ // Do a touch event. As the mouse button is down this should not disable mouse
+ // events.
+ event_generator.PressTouch();
+ EXPECT_TRUE(cursor_client.IsMouseEventsEnabled());
+ aura::Env::GetInstance()->RemovePreTargetHandler(compound_filter.get());
+}
+
+#if defined(OS_WIN)
+// Windows synthesizes mouse messages for touch events. We should not be
+// showing the cursor when we receive such messages.
+TEST_F(CompoundEventFilterTest, DontShowCursorOnMouseMovesFromTouch) {
+ scoped_ptr<CompoundEventFilter> compound_filter(new CompoundEventFilter);
+ aura::Env::GetInstance()->AddPreTargetHandler(compound_filter.get());
+ aura::test::TestWindowDelegate delegate;
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(&delegate, 1234,
+ gfx::Rect(5, 5, 100, 100), root_window()));
+ window->Show();
+ window->SetCapture();
+
+ aura::test::TestCursorClient cursor_client(root_window());
+ cursor_client.DisableMouseEvents();
+ EXPECT_FALSE(cursor_client.IsMouseEventsEnabled());
+
+ ui::MouseEvent mouse0(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
+ gfx::Point(10, 10), 0, 0);
+ mouse0.set_flags(mouse0.flags() | ui::EF_FROM_TOUCH);
+
+ DispatchEventUsingWindowDispatcher(&mouse0);
+ EXPECT_FALSE(cursor_client.IsMouseEventsEnabled());
+
+ mouse0.set_flags(mouse0.flags() & ~ui::EF_FROM_TOUCH);
+ DispatchEventUsingWindowDispatcher(&mouse0);
+ EXPECT_TRUE(cursor_client.IsMouseEventsEnabled());
+
+ aura::Env::GetInstance()->RemovePreTargetHandler(compound_filter.get());
+}
+#endif
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/cursor_manager.cc b/chromium/ui/wm/core/cursor_manager.cc
new file mode 100644
index 00000000000..e9f988566a3
--- /dev/null
+++ b/chromium/ui/wm/core/cursor_manager.cc
@@ -0,0 +1,217 @@
+// 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.
+
+#include "ui/wm/core/cursor_manager.h"
+
+#include "base/logging.h"
+#include "ui/aura/client/cursor_client_observer.h"
+#include "ui/wm/core/native_cursor_manager.h"
+#include "ui/wm/core/native_cursor_manager_delegate.h"
+
+namespace wm {
+
+namespace internal {
+
+// Represents the cursor state which is composed of cursor type, visibility, and
+// mouse events enable state. When mouse events are disabled, the cursor is
+// always invisible.
+class CursorState {
+ public:
+ CursorState()
+ : cursor_(ui::kCursorNone),
+ visible_(true),
+ cursor_set_(ui::CURSOR_SET_NORMAL),
+ mouse_events_enabled_(true),
+ visible_on_mouse_events_enabled_(true) {
+ }
+
+ gfx::NativeCursor cursor() const { return cursor_; }
+ void set_cursor(gfx::NativeCursor cursor) { cursor_ = cursor; }
+
+ bool visible() const { return visible_; }
+ void SetVisible(bool visible) {
+ if (mouse_events_enabled_)
+ visible_ = visible;
+ // Ignores the call when mouse events disabled.
+ }
+
+ ui::CursorSetType cursor_set() const { return cursor_set_; }
+ void set_cursor_set(ui::CursorSetType cursor_set) {
+ cursor_set_ = cursor_set;
+ }
+
+ bool mouse_events_enabled() const { return mouse_events_enabled_; }
+ void SetMouseEventsEnabled(bool enabled) {
+ if (mouse_events_enabled_ == enabled)
+ return;
+ mouse_events_enabled_ = enabled;
+
+ // Restores the visibility when mouse events are enabled.
+ if (enabled) {
+ visible_ = visible_on_mouse_events_enabled_;
+ } else {
+ visible_on_mouse_events_enabled_ = visible_;
+ visible_ = false;
+ }
+ }
+
+ private:
+ gfx::NativeCursor cursor_;
+ bool visible_;
+ ui::CursorSetType cursor_set_;
+ bool mouse_events_enabled_;
+
+ // The visibility to set when mouse events are enabled.
+ bool visible_on_mouse_events_enabled_;
+
+ DISALLOW_COPY_AND_ASSIGN(CursorState);
+};
+
+} // namespace internal
+
+CursorManager::CursorManager(scoped_ptr<NativeCursorManager> delegate)
+ : delegate_(delegate.Pass()),
+ cursor_lock_count_(0),
+ current_state_(new internal::CursorState),
+ state_on_unlock_(new internal::CursorState) {
+}
+
+CursorManager::~CursorManager() {
+}
+
+void CursorManager::SetCursor(gfx::NativeCursor cursor) {
+ state_on_unlock_->set_cursor(cursor);
+ if (cursor_lock_count_ == 0 &&
+ GetCursor() != state_on_unlock_->cursor()) {
+ delegate_->SetCursor(state_on_unlock_->cursor(), this);
+ }
+}
+
+gfx::NativeCursor CursorManager::GetCursor() const {
+ return current_state_->cursor();
+}
+
+void CursorManager::ShowCursor() {
+ state_on_unlock_->SetVisible(true);
+ if (cursor_lock_count_ == 0 &&
+ IsCursorVisible() != state_on_unlock_->visible()) {
+ delegate_->SetVisibility(state_on_unlock_->visible(), this);
+ FOR_EACH_OBSERVER(aura::client::CursorClientObserver, observers_,
+ OnCursorVisibilityChanged(true));
+ }
+}
+
+void CursorManager::HideCursor() {
+ state_on_unlock_->SetVisible(false);
+ if (cursor_lock_count_ == 0 &&
+ IsCursorVisible() != state_on_unlock_->visible()) {
+ delegate_->SetVisibility(state_on_unlock_->visible(), this);
+ FOR_EACH_OBSERVER(aura::client::CursorClientObserver, observers_,
+ OnCursorVisibilityChanged(false));
+ }
+}
+
+bool CursorManager::IsCursorVisible() const {
+ return current_state_->visible();
+}
+
+void CursorManager::SetCursorSet(ui::CursorSetType cursor_set) {
+ state_on_unlock_->set_cursor_set(cursor_set);
+ if (GetCursorSet() != state_on_unlock_->cursor_set())
+ delegate_->SetCursorSet(state_on_unlock_->cursor_set(), this);
+}
+
+ui::CursorSetType CursorManager::GetCursorSet() const {
+ return current_state_->cursor_set();
+}
+
+void CursorManager::EnableMouseEvents() {
+ state_on_unlock_->SetMouseEventsEnabled(true);
+ if (cursor_lock_count_ == 0 &&
+ IsMouseEventsEnabled() != state_on_unlock_->mouse_events_enabled()) {
+ delegate_->SetMouseEventsEnabled(state_on_unlock_->mouse_events_enabled(),
+ this);
+ }
+}
+
+void CursorManager::DisableMouseEvents() {
+ state_on_unlock_->SetMouseEventsEnabled(false);
+ if (cursor_lock_count_ == 0 &&
+ IsMouseEventsEnabled() != state_on_unlock_->mouse_events_enabled()) {
+ delegate_->SetMouseEventsEnabled(state_on_unlock_->mouse_events_enabled(),
+ this);
+ }
+}
+
+bool CursorManager::IsMouseEventsEnabled() const {
+ return current_state_->mouse_events_enabled();
+}
+
+void CursorManager::SetDisplay(const gfx::Display& display) {
+ delegate_->SetDisplay(display, this);
+}
+
+void CursorManager::LockCursor() {
+ cursor_lock_count_++;
+}
+
+void CursorManager::UnlockCursor() {
+ cursor_lock_count_--;
+ DCHECK_GE(cursor_lock_count_, 0);
+ if (cursor_lock_count_ > 0)
+ return;
+
+ if (GetCursor() != state_on_unlock_->cursor()) {
+ delegate_->SetCursor(state_on_unlock_->cursor(), this);
+ }
+ if (IsMouseEventsEnabled() != state_on_unlock_->mouse_events_enabled()) {
+ delegate_->SetMouseEventsEnabled(state_on_unlock_->mouse_events_enabled(),
+ this);
+ }
+ if (IsCursorVisible() != state_on_unlock_->visible()) {
+ delegate_->SetVisibility(state_on_unlock_->visible(),
+ this);
+ }
+}
+
+bool CursorManager::IsCursorLocked() const {
+ return cursor_lock_count_ > 0;
+}
+
+void CursorManager::AddObserver(
+ aura::client::CursorClientObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void CursorManager::RemoveObserver(
+ aura::client::CursorClientObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+bool CursorManager::ShouldHideCursorOnKeyEvent(
+ const ui::KeyEvent& event) const {
+ return false;
+}
+
+void CursorManager::CommitCursor(gfx::NativeCursor cursor) {
+ current_state_->set_cursor(cursor);
+}
+
+void CursorManager::CommitVisibility(bool visible) {
+ // TODO(tdanderson): Find a better place for this so we don't
+ // notify the observers more than is necessary.
+ FOR_EACH_OBSERVER(aura::client::CursorClientObserver, observers_,
+ OnCursorVisibilityChanged(visible));
+ current_state_->SetVisible(visible);
+}
+
+void CursorManager::CommitCursorSet(ui::CursorSetType cursor_set) {
+ current_state_->set_cursor_set(cursor_set);
+}
+
+void CursorManager::CommitMouseEventsEnabled(bool enabled) {
+ current_state_->SetMouseEventsEnabled(enabled);
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/cursor_manager.h b/chromium/ui/wm/core/cursor_manager.h
new file mode 100644
index 00000000000..f60ef14174c
--- /dev/null
+++ b/chromium/ui/wm/core/cursor_manager.h
@@ -0,0 +1,93 @@
+// 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_WM_CORE_CURSOR_MANAGER_H_
+#define UI_WM_CORE_CURSOR_MANAGER_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "ui/aura/client/cursor_client.h"
+#include "ui/base/cursor/cursor.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/point.h"
+#include "ui/wm/core/native_cursor_manager_delegate.h"
+#include "ui/wm/wm_export.h"
+
+namespace gfx {
+class Display;
+}
+
+namespace ui {
+class KeyEvent;
+}
+
+namespace wm {
+
+namespace internal {
+class CursorState;
+}
+
+class NativeCursorManager;
+
+// This class receives requests to change cursor properties, as well as
+// requests to queue any further changes until a later time. It sends changes
+// to the NativeCursorManager, which communicates back to us when these changes
+// were made through the NativeCursorManagerDelegate interface.
+class WM_EXPORT CursorManager : public aura::client::CursorClient,
+ public NativeCursorManagerDelegate {
+ public:
+ explicit CursorManager(scoped_ptr<NativeCursorManager> delegate);
+ virtual ~CursorManager();
+
+ // Overridden from aura::client::CursorClient:
+ virtual void SetCursor(gfx::NativeCursor) OVERRIDE;
+ virtual gfx::NativeCursor GetCursor() const OVERRIDE;
+ virtual void ShowCursor() OVERRIDE;
+ virtual void HideCursor() OVERRIDE;
+ virtual bool IsCursorVisible() const OVERRIDE;
+ virtual void SetCursorSet(ui::CursorSetType cursor_set) OVERRIDE;
+ virtual ui::CursorSetType GetCursorSet() const OVERRIDE;
+ virtual void EnableMouseEvents() OVERRIDE;
+ virtual void DisableMouseEvents() OVERRIDE;
+ virtual bool IsMouseEventsEnabled() const OVERRIDE;
+ virtual void SetDisplay(const gfx::Display& display) OVERRIDE;
+ virtual void LockCursor() OVERRIDE;
+ virtual void UnlockCursor() OVERRIDE;
+ virtual bool IsCursorLocked() const OVERRIDE;
+ virtual void AddObserver(
+ aura::client::CursorClientObserver* observer) OVERRIDE;
+ virtual void RemoveObserver(
+ aura::client::CursorClientObserver* observer) OVERRIDE;
+ virtual bool ShouldHideCursorOnKeyEvent(
+ const ui::KeyEvent& event) const OVERRIDE;
+
+ private:
+ // Overridden from NativeCursorManagerDelegate:
+ virtual void CommitCursor(gfx::NativeCursor cursor) OVERRIDE;
+ virtual void CommitVisibility(bool visible) OVERRIDE;
+ virtual void CommitCursorSet(ui::CursorSetType cursor_set) OVERRIDE;
+ virtual void CommitMouseEventsEnabled(bool enabled) OVERRIDE;
+
+ scoped_ptr<NativeCursorManager> delegate_;
+
+ // Number of times LockCursor() has been invoked without a corresponding
+ // UnlockCursor().
+ int cursor_lock_count_;
+
+ // The current state of the cursor.
+ scoped_ptr<internal::CursorState> current_state_;
+
+ // The cursor state to restore when the cursor is unlocked.
+ scoped_ptr<internal::CursorState> state_on_unlock_;
+
+ ObserverList<aura::client::CursorClientObserver> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(CursorManager);
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_CURSOR_MANAGER_H_
diff --git a/chromium/ui/wm/core/cursor_manager_unittest.cc b/chromium/ui/wm/core/cursor_manager_unittest.cc
new file mode 100644
index 00000000000..58c3b174d80
--- /dev/null
+++ b/chromium/ui/wm/core/cursor_manager_unittest.cc
@@ -0,0 +1,330 @@
+// 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.
+
+#include "ui/wm/core/cursor_manager.h"
+
+#include "ui/aura/client/cursor_client_observer.h"
+#include "ui/aura/test/aura_test_base.h"
+#include "ui/wm/core/native_cursor_manager.h"
+
+namespace {
+
+class TestingCursorManager : public wm::NativeCursorManager {
+ public:
+ // Overridden from wm::NativeCursorManager:
+ virtual void SetDisplay(
+ const gfx::Display& display,
+ wm::NativeCursorManagerDelegate* delegate) OVERRIDE {}
+
+ virtual void SetCursor(
+ gfx::NativeCursor cursor,
+ wm::NativeCursorManagerDelegate* delegate) OVERRIDE {
+ delegate->CommitCursor(cursor);
+ }
+
+ virtual void SetVisibility(
+ bool visible,
+ wm::NativeCursorManagerDelegate* delegate) OVERRIDE {
+ delegate->CommitVisibility(visible);
+ }
+
+ virtual void SetMouseEventsEnabled(
+ bool enabled,
+ wm::NativeCursorManagerDelegate* delegate) OVERRIDE {
+ delegate->CommitMouseEventsEnabled(enabled);
+ }
+
+ virtual void SetCursorSet(
+ ui::CursorSetType cursor_set,
+ wm::NativeCursorManagerDelegate* delegate) OVERRIDE {
+ delegate->CommitCursorSet(cursor_set);
+ }
+};
+
+} // namespace
+
+class CursorManagerTest : public aura::test::AuraTestBase {
+ protected:
+ CursorManagerTest()
+ : delegate_(new TestingCursorManager),
+ cursor_manager_(scoped_ptr<wm::NativeCursorManager>(
+ delegate_)) {
+ }
+
+ TestingCursorManager* delegate_;
+ wm::CursorManager cursor_manager_;
+};
+
+class TestingCursorClientObserver : public aura::client::CursorClientObserver {
+ public:
+ TestingCursorClientObserver()
+ : cursor_visibility_(false),
+ did_visibility_change_(false) {}
+ void reset() { cursor_visibility_ = did_visibility_change_ = false; }
+ bool is_cursor_visible() const { return cursor_visibility_; }
+ bool did_visibility_change() const { return did_visibility_change_; }
+
+ // Overridden from aura::client::CursorClientObserver:
+ virtual void OnCursorVisibilityChanged(bool is_visible) OVERRIDE {
+ cursor_visibility_ = is_visible;
+ did_visibility_change_ = true;
+ }
+
+ private:
+ bool cursor_visibility_;
+ bool did_visibility_change_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestingCursorClientObserver);
+};
+
+TEST_F(CursorManagerTest, ShowHideCursor) {
+ cursor_manager_.SetCursor(ui::kCursorCopy);
+ EXPECT_EQ(ui::kCursorCopy, cursor_manager_.GetCursor().native_type());
+
+ cursor_manager_.ShowCursor();
+ EXPECT_TRUE(cursor_manager_.IsCursorVisible());
+ cursor_manager_.HideCursor();
+ EXPECT_FALSE(cursor_manager_.IsCursorVisible());
+ // The current cursor does not change even when the cursor is not shown.
+ EXPECT_EQ(ui::kCursorCopy, cursor_manager_.GetCursor().native_type());
+
+ // Check if cursor visibility is locked.
+ cursor_manager_.LockCursor();
+ EXPECT_FALSE(cursor_manager_.IsCursorVisible());
+ cursor_manager_.ShowCursor();
+ EXPECT_FALSE(cursor_manager_.IsCursorVisible());
+ cursor_manager_.UnlockCursor();
+ EXPECT_TRUE(cursor_manager_.IsCursorVisible());
+
+ cursor_manager_.LockCursor();
+ EXPECT_TRUE(cursor_manager_.IsCursorVisible());
+ cursor_manager_.HideCursor();
+ EXPECT_TRUE(cursor_manager_.IsCursorVisible());
+ cursor_manager_.UnlockCursor();
+ EXPECT_FALSE(cursor_manager_.IsCursorVisible());
+
+ // Checks setting visiblity while cursor is locked does not affect the
+ // subsequent uses of UnlockCursor.
+ cursor_manager_.LockCursor();
+ cursor_manager_.HideCursor();
+ cursor_manager_.UnlockCursor();
+ EXPECT_FALSE(cursor_manager_.IsCursorVisible());
+
+ cursor_manager_.ShowCursor();
+ cursor_manager_.LockCursor();
+ cursor_manager_.UnlockCursor();
+ EXPECT_TRUE(cursor_manager_.IsCursorVisible());
+
+ cursor_manager_.LockCursor();
+ cursor_manager_.ShowCursor();
+ cursor_manager_.UnlockCursor();
+ EXPECT_TRUE(cursor_manager_.IsCursorVisible());
+
+ cursor_manager_.HideCursor();
+ cursor_manager_.LockCursor();
+ cursor_manager_.UnlockCursor();
+ EXPECT_FALSE(cursor_manager_.IsCursorVisible());
+}
+
+// Verifies that LockCursor/UnlockCursor work correctly with
+// EnableMouseEvents and DisableMouseEvents
+TEST_F(CursorManagerTest, EnableDisableMouseEvents) {
+ cursor_manager_.SetCursor(ui::kCursorCopy);
+ EXPECT_EQ(ui::kCursorCopy, cursor_manager_.GetCursor().native_type());
+
+ cursor_manager_.EnableMouseEvents();
+ EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled());
+ cursor_manager_.DisableMouseEvents();
+ EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled());
+ // The current cursor does not change even when the cursor is not shown.
+ EXPECT_EQ(ui::kCursorCopy, cursor_manager_.GetCursor().native_type());
+
+ // Check if cursor enable state is locked.
+ cursor_manager_.LockCursor();
+ EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled());
+ cursor_manager_.EnableMouseEvents();
+ EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled());
+ cursor_manager_.UnlockCursor();
+ EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled());
+
+ cursor_manager_.LockCursor();
+ EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled());
+ cursor_manager_.DisableMouseEvents();
+ EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled());
+ cursor_manager_.UnlockCursor();
+ EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled());
+
+ // Checks enabling cursor while cursor is locked does not affect the
+ // subsequent uses of UnlockCursor.
+ cursor_manager_.LockCursor();
+ cursor_manager_.DisableMouseEvents();
+ cursor_manager_.UnlockCursor();
+ EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled());
+
+ cursor_manager_.EnableMouseEvents();
+ cursor_manager_.LockCursor();
+ cursor_manager_.UnlockCursor();
+ EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled());
+
+ cursor_manager_.LockCursor();
+ cursor_manager_.EnableMouseEvents();
+ cursor_manager_.UnlockCursor();
+ EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled());
+
+ cursor_manager_.DisableMouseEvents();
+ cursor_manager_.LockCursor();
+ cursor_manager_.UnlockCursor();
+ EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled());
+}
+
+TEST_F(CursorManagerTest, SetCursorSet) {
+ EXPECT_EQ(ui::CURSOR_SET_NORMAL, cursor_manager_.GetCursorSet());
+
+ cursor_manager_.SetCursorSet(ui::CURSOR_SET_NORMAL);
+ EXPECT_EQ(ui::CURSOR_SET_NORMAL, cursor_manager_.GetCursorSet());
+
+ cursor_manager_.SetCursorSet(ui::CURSOR_SET_LARGE);
+ EXPECT_EQ(ui::CURSOR_SET_LARGE, cursor_manager_.GetCursorSet());
+
+ cursor_manager_.SetCursorSet(ui::CURSOR_SET_NORMAL);
+ EXPECT_EQ(ui::CURSOR_SET_NORMAL, cursor_manager_.GetCursorSet());
+}
+
+TEST_F(CursorManagerTest, IsMouseEventsEnabled) {
+ cursor_manager_.EnableMouseEvents();
+ EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled());
+ cursor_manager_.DisableMouseEvents();
+ EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled());
+}
+
+// Verifies that the mouse events enable state changes correctly when
+// ShowCursor/HideCursor and EnableMouseEvents/DisableMouseEvents are used
+// together.
+TEST_F(CursorManagerTest, ShowAndEnable) {
+ // Changing the visibility of the cursor does not affect the enable state.
+ cursor_manager_.EnableMouseEvents();
+ cursor_manager_.ShowCursor();
+ EXPECT_TRUE(cursor_manager_.IsCursorVisible());
+ EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled());
+ cursor_manager_.HideCursor();
+ EXPECT_FALSE(cursor_manager_.IsCursorVisible());
+ EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled());
+ cursor_manager_.ShowCursor();
+ EXPECT_TRUE(cursor_manager_.IsCursorVisible());
+ EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled());
+
+ // When mouse events are disabled, it also gets invisible.
+ EXPECT_TRUE(cursor_manager_.IsCursorVisible());
+ cursor_manager_.DisableMouseEvents();
+ EXPECT_FALSE(cursor_manager_.IsCursorVisible());
+ EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled());
+
+ // When mouse events are enabled, it restores the visibility state.
+ cursor_manager_.EnableMouseEvents();
+ EXPECT_TRUE(cursor_manager_.IsCursorVisible());
+ EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled());
+
+ cursor_manager_.ShowCursor();
+ cursor_manager_.DisableMouseEvents();
+ EXPECT_FALSE(cursor_manager_.IsCursorVisible());
+ EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled());
+ cursor_manager_.EnableMouseEvents();
+ EXPECT_TRUE(cursor_manager_.IsCursorVisible());
+ EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled());
+
+ cursor_manager_.HideCursor();
+ cursor_manager_.DisableMouseEvents();
+ EXPECT_FALSE(cursor_manager_.IsCursorVisible());
+ EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled());
+ cursor_manager_.EnableMouseEvents();
+ EXPECT_FALSE(cursor_manager_.IsCursorVisible());
+ EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled());
+
+ // When mouse events are disabled, ShowCursor is ignored.
+ cursor_manager_.DisableMouseEvents();
+ EXPECT_FALSE(cursor_manager_.IsCursorVisible());
+ EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled());
+ cursor_manager_.ShowCursor();
+ EXPECT_FALSE(cursor_manager_.IsCursorVisible());
+ EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled());
+ cursor_manager_.DisableMouseEvents();
+ EXPECT_FALSE(cursor_manager_.IsCursorVisible());
+ EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled());
+}
+
+// Verifies that calling DisableMouseEvents multiple times in a row makes no
+// difference compared with calling it once.
+// This is a regression test for http://crbug.com/169404.
+TEST_F(CursorManagerTest, MultipleDisableMouseEvents) {
+ cursor_manager_.DisableMouseEvents();
+ cursor_manager_.DisableMouseEvents();
+ cursor_manager_.EnableMouseEvents();
+ cursor_manager_.LockCursor();
+ cursor_manager_.UnlockCursor();
+ EXPECT_TRUE(cursor_manager_.IsCursorVisible());
+}
+
+// Verifies that calling EnableMouseEvents multiple times in a row makes no
+// difference compared with calling it once.
+TEST_F(CursorManagerTest, MultipleEnableMouseEvents) {
+ cursor_manager_.DisableMouseEvents();
+ cursor_manager_.EnableMouseEvents();
+ cursor_manager_.EnableMouseEvents();
+ cursor_manager_.LockCursor();
+ cursor_manager_.UnlockCursor();
+ EXPECT_TRUE(cursor_manager_.IsCursorVisible());
+}
+
+TEST_F(CursorManagerTest, TestCursorClientObserver) {
+ // Add two observers. Both should have OnCursorVisibilityChanged()
+ // invoked when the visibility of the cursor changes.
+ TestingCursorClientObserver observer_a;
+ TestingCursorClientObserver observer_b;
+ cursor_manager_.AddObserver(&observer_a);
+ cursor_manager_.AddObserver(&observer_b);
+
+ // Initial state before any events have been sent.
+ observer_a.reset();
+ observer_b.reset();
+ EXPECT_FALSE(observer_a.did_visibility_change());
+ EXPECT_FALSE(observer_b.did_visibility_change());
+ EXPECT_FALSE(observer_a.is_cursor_visible());
+ EXPECT_FALSE(observer_b.is_cursor_visible());
+
+ // Hide the cursor using HideCursor().
+ cursor_manager_.HideCursor();
+ EXPECT_TRUE(observer_a.did_visibility_change());
+ EXPECT_TRUE(observer_b.did_visibility_change());
+ EXPECT_FALSE(observer_a.is_cursor_visible());
+ EXPECT_FALSE(observer_b.is_cursor_visible());
+
+ // Show the cursor using ShowCursor().
+ observer_a.reset();
+ observer_b.reset();
+ cursor_manager_.ShowCursor();
+ EXPECT_TRUE(observer_a.did_visibility_change());
+ EXPECT_TRUE(observer_b.did_visibility_change());
+ EXPECT_TRUE(observer_a.is_cursor_visible());
+ EXPECT_TRUE(observer_b.is_cursor_visible());
+
+ // Remove observer_b. Its OnCursorVisibilityChanged() should
+ // not be invoked past this point.
+ cursor_manager_.RemoveObserver(&observer_b);
+
+ // Hide the cursor using HideCursor().
+ observer_a.reset();
+ observer_b.reset();
+ cursor_manager_.HideCursor();
+ EXPECT_TRUE(observer_a.did_visibility_change());
+ EXPECT_FALSE(observer_b.did_visibility_change());
+ EXPECT_FALSE(observer_a.is_cursor_visible());
+
+ // Show the cursor using ShowCursor().
+ observer_a.reset();
+ observer_b.reset();
+ cursor_manager_.ShowCursor();
+ EXPECT_TRUE(observer_a.did_visibility_change());
+ EXPECT_FALSE(observer_b.did_visibility_change());
+ EXPECT_TRUE(observer_a.is_cursor_visible());
+}
diff --git a/chromium/ui/wm/core/default_activation_client.cc b/chromium/ui/wm/core/default_activation_client.cc
new file mode 100644
index 00000000000..222c9acb4cb
--- /dev/null
+++ b/chromium/ui/wm/core/default_activation_client.cc
@@ -0,0 +1,157 @@
+// 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/wm/core/default_activation_client.h"
+
+#include "ui/aura/window.h"
+#include "ui/wm/public/activation_change_observer.h"
+#include "ui/wm/public/activation_delegate.h"
+
+namespace wm {
+
+// Takes care of observing root window destruction & destroying the client.
+class DefaultActivationClient::Deleter : public aura::WindowObserver {
+ public:
+ Deleter(DefaultActivationClient* client, aura::Window* root_window)
+ : client_(client),
+ root_window_(root_window) {
+ root_window_->AddObserver(this);
+ }
+
+ private:
+ virtual ~Deleter() {}
+
+ // Overridden from WindowObserver:
+ virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE {
+ DCHECK_EQ(window, root_window_);
+ root_window_->RemoveObserver(this);
+ delete client_;
+ delete this;
+ }
+
+ DefaultActivationClient* client_;
+ aura::Window* root_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(Deleter);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// DefaultActivationClient, public:
+
+DefaultActivationClient::DefaultActivationClient(aura::Window* root_window)
+ : last_active_(NULL) {
+ aura::client::SetActivationClient(root_window, this);
+ new Deleter(this, root_window);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// DefaultActivationClient, client::ActivationClient implementation:
+
+void DefaultActivationClient::AddObserver(
+ aura::client::ActivationChangeObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void DefaultActivationClient::RemoveObserver(
+ aura::client::ActivationChangeObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void DefaultActivationClient::ActivateWindow(aura::Window* window) {
+ aura::Window* last_active = GetActiveWindow();
+ if (last_active == window)
+ return;
+
+ last_active_ = last_active;
+ RemoveActiveWindow(window);
+ active_windows_.push_back(window);
+ window->parent()->StackChildAtTop(window);
+ window->AddObserver(this);
+
+ FOR_EACH_OBSERVER(aura::client::ActivationChangeObserver,
+ observers_,
+ OnWindowActivated(window, last_active));
+
+ aura::client::ActivationChangeObserver* observer =
+ aura::client::GetActivationChangeObserver(last_active);
+ if (observer)
+ observer->OnWindowActivated(window, last_active);
+ observer = aura::client::GetActivationChangeObserver(window);
+ if (observer)
+ observer->OnWindowActivated(window, last_active);
+}
+
+void DefaultActivationClient::DeactivateWindow(aura::Window* window) {
+ aura::client::ActivationChangeObserver* observer =
+ aura::client::GetActivationChangeObserver(window);
+ if (observer)
+ observer->OnWindowActivated(NULL, window);
+ if (last_active_)
+ ActivateWindow(last_active_);
+}
+
+aura::Window* DefaultActivationClient::GetActiveWindow() {
+ if (active_windows_.empty())
+ return NULL;
+ return active_windows_.back();
+}
+
+aura::Window* DefaultActivationClient::GetActivatableWindow(
+ aura::Window* window) {
+ return NULL;
+}
+
+aura::Window* DefaultActivationClient::GetToplevelWindow(aura::Window* window) {
+ return NULL;
+}
+
+bool DefaultActivationClient::OnWillFocusWindow(aura::Window* window,
+ const ui::Event* event) {
+ return true;
+}
+
+bool DefaultActivationClient::CanActivateWindow(aura::Window* window) const {
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// DefaultActivationClient, aura::WindowObserver implementation:
+
+void DefaultActivationClient::OnWindowDestroyed(aura::Window* window) {
+ if (window == last_active_)
+ last_active_ = NULL;
+
+ if (window == GetActiveWindow()) {
+ active_windows_.pop_back();
+ aura::Window* next_active = GetActiveWindow();
+ if (next_active && aura::client::GetActivationChangeObserver(next_active)) {
+ aura::client::GetActivationChangeObserver(next_active)->OnWindowActivated(
+ next_active, NULL);
+ }
+ return;
+ }
+
+ RemoveActiveWindow(window);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// DefaultActivationClient, private:
+
+DefaultActivationClient::~DefaultActivationClient() {
+ for (unsigned int i = 0; i < active_windows_.size(); ++i) {
+ active_windows_[i]->RemoveObserver(this);
+ }
+}
+
+void DefaultActivationClient::RemoveActiveWindow(aura::Window* window) {
+ for (unsigned int i = 0; i < active_windows_.size(); ++i) {
+ if (active_windows_[i] == window) {
+ active_windows_.erase(active_windows_.begin() + i);
+ window->RemoveObserver(this);
+ return;
+ }
+ }
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/default_activation_client.h b/chromium/ui/wm/core/default_activation_client.h
new file mode 100644
index 00000000000..3b6bffc3196
--- /dev/null
+++ b/chromium/ui/wm/core/default_activation_client.h
@@ -0,0 +1,73 @@
+// 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_WM_CORE_DEFAULT_ACTIVATION_CLIENT_H_
+#define UI_WM_CORE_DEFAULT_ACTIVATION_CLIENT_H_
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/observer_list.h"
+#include "ui/aura/window_observer.h"
+#include "ui/wm/public/activation_client.h"
+#include "ui/wm/wm_export.h"
+
+namespace aura {
+namespace client {
+class ActivationChangeObserver;
+}
+}
+
+namespace wm {
+
+// Simple ActivationClient implementation for use by tests and other targets
+// that just need basic behavior (e.g. activate windows whenever requested,
+// restack windows at the top when they're activated, etc.). This object deletes
+// itself when the root window it is associated with is destroyed.
+class WM_EXPORT DefaultActivationClient : public aura::client::ActivationClient,
+ public aura::WindowObserver {
+ public:
+ explicit DefaultActivationClient(aura::Window* root_window);
+
+ // Overridden from aura::client::ActivationClient:
+ virtual void AddObserver(
+ aura::client::ActivationChangeObserver* observer) OVERRIDE;
+ virtual void RemoveObserver(
+ aura::client::ActivationChangeObserver* observer) OVERRIDE;
+ virtual void ActivateWindow(aura::Window* window) OVERRIDE;
+ virtual void DeactivateWindow(aura::Window* window) OVERRIDE;
+ virtual aura::Window* GetActiveWindow() OVERRIDE;
+ virtual aura::Window* GetActivatableWindow(aura::Window* window) OVERRIDE;
+ virtual aura::Window* GetToplevelWindow(aura::Window* window) OVERRIDE;
+ virtual bool OnWillFocusWindow(aura::Window* window,
+ const ui::Event* event) OVERRIDE;
+ virtual bool CanActivateWindow(aura::Window* window) const OVERRIDE;
+
+ // Overridden from WindowObserver:
+ virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
+
+ private:
+ class Deleter;
+
+ virtual ~DefaultActivationClient();
+ void RemoveActiveWindow(aura::Window* window);
+
+ // This class explicitly does NOT store the active window in a window property
+ // to make sure that ActivationChangeObserver is not treated as part of the
+ // aura API. Assumptions to that end will cause tests that use this client to
+ // fail.
+ std::vector<aura::Window*> active_windows_;
+
+ // The window which was active before the currently active one.
+ aura::Window* last_active_;
+
+ ObserverList<aura::client::ActivationChangeObserver> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(DefaultActivationClient);
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_DEFAULT_ACTIVATION_CLIENT_H_
diff --git a/chromium/ui/wm/core/easy_resize_window_targeter.cc b/chromium/ui/wm/core/easy_resize_window_targeter.cc
new file mode 100644
index 00000000000..445028cbf92
--- /dev/null
+++ b/chromium/ui/wm/core/easy_resize_window_targeter.cc
@@ -0,0 +1,62 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/wm/core/easy_resize_window_targeter.h"
+
+#include "ui/aura/window.h"
+#include "ui/gfx/geometry/insets_f.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/wm/public/transient_window_client.h"
+
+namespace wm {
+
+EasyResizeWindowTargeter::EasyResizeWindowTargeter(
+ aura::Window* container,
+ const gfx::Insets& mouse_extend,
+ const gfx::Insets& touch_extend)
+ : container_(container),
+ mouse_extend_(mouse_extend),
+ touch_extend_(touch_extend) {
+}
+
+EasyResizeWindowTargeter::~EasyResizeWindowTargeter() {
+}
+
+bool EasyResizeWindowTargeter::EventLocationInsideBounds(
+ ui::EventTarget* target,
+ const ui::LocatedEvent& event) const {
+ aura::Window* window = static_cast<aura::Window*>(target);
+ if (ShouldUseExtendedBounds(window)) {
+ // Note that |event|'s location is in |window|'s parent's coordinate system,
+ // so convert it to |window|'s coordinate system first.
+ gfx::Point point = event.location();
+ if (window->parent())
+ aura::Window::ConvertPointToTarget(window->parent(), window, &point);
+
+ gfx::Rect bounds(window->bounds().size());
+ if (event.IsTouchEvent() || event.IsGestureEvent())
+ bounds.Inset(touch_extend_);
+ else
+ bounds.Inset(mouse_extend_);
+
+ return bounds.Contains(point);
+ }
+ return WindowTargeter::EventLocationInsideBounds(window, event);
+}
+
+bool EasyResizeWindowTargeter::ShouldUseExtendedBounds(
+ const aura::Window* window) const {
+ // Use the extended bounds only for immediate child windows of |container_|.
+ // Use the default targetter otherwise.
+ if (window->parent() != container_)
+ return false;
+
+ aura::client::TransientWindowClient* transient_window_client =
+ aura::client::GetTransientWindowClient();
+ return !transient_window_client ||
+ !transient_window_client->GetTransientParent(window) ||
+ transient_window_client->GetTransientParent(window) == container_;
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/easy_resize_window_targeter.h b/chromium/ui/wm/core/easy_resize_window_targeter.h
new file mode 100644
index 00000000000..35b9eb2b865
--- /dev/null
+++ b/chromium/ui/wm/core/easy_resize_window_targeter.h
@@ -0,0 +1,53 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_WM_CORE_EASY_RESIZE_WINDOW_TARGETER_H_
+#define UI_WM_CORE_EASY_RESIZE_WINDOW_TARGETER_H_
+
+#include "ui/aura/window_targeter.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/wm/wm_export.h"
+
+namespace wm {
+
+// An EventTargeter for a container window that uses a slightly larger
+// hit-target region for easier resize.
+class WM_EXPORT EasyResizeWindowTargeter : public aura::WindowTargeter {
+ public:
+ // |container| window is the owner of this targeter.
+ EasyResizeWindowTargeter(aura::Window* container,
+ const gfx::Insets& mouse_extend,
+ const gfx::Insets& touch_extend);
+
+ virtual ~EasyResizeWindowTargeter();
+
+ protected:
+ void set_mouse_extend(const gfx::Insets& mouse_extend) {
+ mouse_extend_ = mouse_extend;
+ }
+
+ void set_touch_extend(const gfx::Insets& touch_extend) {
+ touch_extend_ = touch_extend;
+ }
+
+ // ui::EventTargeter:
+ virtual bool EventLocationInsideBounds(
+ ui::EventTarget* target,
+ const ui::LocatedEvent& event) const OVERRIDE;
+
+ private:
+ // Returns true if the hit testing (EventLocationInsideBounds()) should use
+ // the extended bounds.
+ bool ShouldUseExtendedBounds(const aura::Window* window) const;
+
+ aura::Window* container_;
+ gfx::Insets mouse_extend_;
+ gfx::Insets touch_extend_;
+
+ DISALLOW_COPY_AND_ASSIGN(EasyResizeWindowTargeter);
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_EASY_RESIZE_WINDOW_TARGETER_H_
diff --git a/chromium/ui/wm/core/focus_controller.cc b/chromium/ui/wm/core/focus_controller.cc
new file mode 100644
index 00000000000..1534e402e19
--- /dev/null
+++ b/chromium/ui/wm/core/focus_controller.cc
@@ -0,0 +1,368 @@
+// 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/wm/core/focus_controller.h"
+
+#include "base/auto_reset.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/client/capture_client.h"
+#include "ui/aura/client/focus_change_observer.h"
+#include "ui/aura/env.h"
+#include "ui/aura/window_tracker.h"
+#include "ui/base/ime/text_input_focus_manager.h"
+#include "ui/events/event.h"
+#include "ui/wm/core/focus_rules.h"
+#include "ui/wm/core/window_util.h"
+#include "ui/wm/public/activation_change_observer.h"
+
+namespace wm {
+namespace {
+
+// When a modal window is activated, we bring its entire transient parent chain
+// to the front. This function must be called before the modal transient is
+// stacked at the top to ensure correct stacking order.
+void StackTransientParentsBelowModalWindow(aura::Window* window) {
+ if (window->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_WINDOW)
+ return;
+
+ aura::Window* transient_parent = wm::GetTransientParent(window);
+ while (transient_parent) {
+ transient_parent->parent()->StackChildAtTop(transient_parent);
+ transient_parent = wm::GetTransientParent(transient_parent);
+ }
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// FocusController, public:
+
+FocusController::FocusController(FocusRules* rules)
+ : active_window_(NULL),
+ focused_window_(NULL),
+ updating_focus_(false),
+ updating_activation_(false),
+ rules_(rules),
+ observer_manager_(this) {
+ DCHECK(rules);
+}
+
+FocusController::~FocusController() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FocusController, aura::client::ActivationClient implementation:
+
+void FocusController::AddObserver(
+ aura::client::ActivationChangeObserver* observer) {
+ activation_observers_.AddObserver(observer);
+}
+
+void FocusController::RemoveObserver(
+ aura::client::ActivationChangeObserver* observer) {
+ activation_observers_.RemoveObserver(observer);
+}
+
+void FocusController::ActivateWindow(aura::Window* window) {
+ FocusWindow(window);
+}
+
+void FocusController::DeactivateWindow(aura::Window* window) {
+ if (window)
+ FocusWindow(rules_->GetNextActivatableWindow(window));
+}
+
+aura::Window* FocusController::GetActiveWindow() {
+ return active_window_;
+}
+
+aura::Window* FocusController::GetActivatableWindow(aura::Window* window) {
+ return rules_->GetActivatableWindow(window);
+}
+
+aura::Window* FocusController::GetToplevelWindow(aura::Window* window) {
+ return rules_->GetToplevelWindow(window);
+}
+
+bool FocusController::OnWillFocusWindow(aura::Window* window,
+ const ui::Event* event) {
+ NOTREACHED();
+ return false;
+}
+
+bool FocusController::CanActivateWindow(aura::Window* window) const {
+ return rules_->CanActivateWindow(window);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FocusController, aura::client::FocusClient implementation:
+
+void FocusController::AddObserver(
+ aura::client::FocusChangeObserver* observer) {
+ focus_observers_.AddObserver(observer);
+}
+
+void FocusController::RemoveObserver(
+ aura::client::FocusChangeObserver* observer) {
+ focus_observers_.RemoveObserver(observer);
+}
+
+void FocusController::FocusWindow(aura::Window* window) {
+ if (window &&
+ (window->Contains(focused_window_) || window->Contains(active_window_))) {
+ return;
+ }
+
+ // We should not be messing with the focus if the window has capture, unless
+ // no has focus.
+ if (window && (aura::client::GetCaptureWindow(window) == window) &&
+ focused_window_) {
+ return;
+ }
+
+ // Focusing a window also activates its containing activatable window. Note
+ // that the rules could redirect activation activation and/or focus.
+ aura::Window* focusable = rules_->GetFocusableWindow(window);
+ aura::Window* activatable =
+ focusable ? rules_->GetActivatableWindow(focusable) : NULL;
+
+ // We need valid focusable/activatable windows in the event we're not clearing
+ // focus. "Clearing focus" is inferred by whether or not |window| passed to
+ // this function is non-NULL.
+ if (window && (!focusable || !activatable))
+ return;
+ DCHECK((focusable && activatable) || !window);
+
+ // Activation change observers may change the focused window. If this happens
+ // we must not adjust the focus below since this will clobber that change.
+ aura::Window* last_focused_window = focused_window_;
+ if (!updating_activation_)
+ SetActiveWindow(window, activatable);
+
+ // If the window's ActivationChangeObserver shifted focus to a valid window,
+ // we don't want to focus the window we thought would be focused by default.
+ bool activation_changed_focus = last_focused_window != focused_window_;
+ if (!updating_focus_ && (!activation_changed_focus || !focused_window_)) {
+ if (active_window_ && focusable)
+ DCHECK(active_window_->Contains(focusable));
+ SetFocusedWindow(focusable);
+ }
+}
+
+void FocusController::ResetFocusWithinActiveWindow(aura::Window* window) {
+ DCHECK(window);
+ if (!active_window_)
+ return;
+ if (!active_window_->Contains(window))
+ return;
+ SetFocusedWindow(window);
+}
+
+aura::Window* FocusController::GetFocusedWindow() {
+ return focused_window_;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FocusController, ui::EventHandler implementation:
+void FocusController::OnKeyEvent(ui::KeyEvent* event) {
+}
+
+void FocusController::OnMouseEvent(ui::MouseEvent* event) {
+ if (event->type() == ui::ET_MOUSE_PRESSED && !event->handled())
+ WindowFocusedFromInputEvent(static_cast<aura::Window*>(event->target()));
+}
+
+void FocusController::OnScrollEvent(ui::ScrollEvent* event) {
+}
+
+void FocusController::OnTouchEvent(ui::TouchEvent* event) {
+}
+
+void FocusController::OnGestureEvent(ui::GestureEvent* event) {
+ if (event->type() == ui::ET_GESTURE_BEGIN &&
+ event->details().touch_points() == 1 &&
+ !event->handled()) {
+ WindowFocusedFromInputEvent(static_cast<aura::Window*>(event->target()));
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FocusController, aura::WindowObserver implementation:
+
+void FocusController::OnWindowVisibilityChanged(aura::Window* window,
+ bool visible) {
+ if (!visible)
+ WindowLostFocusFromDispositionChange(window, window->parent());
+}
+
+void FocusController::OnWindowDestroying(aura::Window* window) {
+ WindowLostFocusFromDispositionChange(window, window->parent());
+}
+
+void FocusController::OnWindowHierarchyChanging(
+ const HierarchyChangeParams& params) {
+ if (params.receiver == active_window_ &&
+ params.target->Contains(params.receiver) && (!params.new_parent ||
+ aura::client::GetFocusClient(params.new_parent) !=
+ aura::client::GetFocusClient(params.receiver))) {
+ WindowLostFocusFromDispositionChange(params.receiver, params.old_parent);
+ }
+}
+
+void FocusController::OnWindowHierarchyChanged(
+ const HierarchyChangeParams& params) {
+ if (params.receiver == focused_window_ &&
+ params.target->Contains(params.receiver) && (!params.new_parent ||
+ aura::client::GetFocusClient(params.new_parent) !=
+ aura::client::GetFocusClient(params.receiver))) {
+ WindowLostFocusFromDispositionChange(params.receiver, params.old_parent);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FocusController, private:
+
+void FocusController::SetFocusedWindow(aura::Window* window) {
+ if (updating_focus_ || window == focused_window_)
+ return;
+ DCHECK(rules_->CanFocusWindow(window));
+ if (window)
+ DCHECK_EQ(window, rules_->GetFocusableWindow(window));
+
+ base::AutoReset<bool> updating_focus(&updating_focus_, true);
+ aura::Window* lost_focus = focused_window_;
+
+ // |window| is going to get the focus, so reset the text input client.
+ // OnWindowFocused() may set a proper text input client if the implementation
+ // supports text input.
+ ui::TextInputFocusManager* text_input_focus_manager =
+ ui::TextInputFocusManager::GetInstance();
+ if (window)
+ text_input_focus_manager->FocusTextInputClient(NULL);
+
+ // Allow for the window losing focus to be deleted during dispatch. If it is
+ // deleted pass NULL to observers instead of a deleted window.
+ aura::WindowTracker window_tracker;
+ if (lost_focus)
+ window_tracker.Add(lost_focus);
+ if (focused_window_ && observer_manager_.IsObserving(focused_window_) &&
+ focused_window_ != active_window_) {
+ observer_manager_.Remove(focused_window_);
+ }
+ focused_window_ = window;
+ if (focused_window_ && !observer_manager_.IsObserving(focused_window_))
+ observer_manager_.Add(focused_window_);
+
+ FOR_EACH_OBSERVER(aura::client::FocusChangeObserver,
+ focus_observers_,
+ OnWindowFocused(focused_window_,
+ window_tracker.Contains(lost_focus) ?
+ lost_focus : NULL));
+ if (window_tracker.Contains(lost_focus)) {
+ aura::client::FocusChangeObserver* observer =
+ aura::client::GetFocusChangeObserver(lost_focus);
+ if (observer)
+ observer->OnWindowFocused(focused_window_, lost_focus);
+ }
+ aura::client::FocusChangeObserver* observer =
+ aura::client::GetFocusChangeObserver(focused_window_);
+ if (observer) {
+ observer->OnWindowFocused(
+ focused_window_,
+ window_tracker.Contains(lost_focus) ? lost_focus : NULL);
+ }
+
+ // Ensure that the text input client is reset when the window loses the focus.
+ if (!window)
+ text_input_focus_manager->FocusTextInputClient(NULL);
+}
+
+void FocusController::SetActiveWindow(aura::Window* requested_window,
+ aura::Window* window) {
+ if (updating_activation_)
+ return;
+
+ if (window == active_window_) {
+ if (requested_window) {
+ FOR_EACH_OBSERVER(aura::client::ActivationChangeObserver,
+ activation_observers_,
+ OnAttemptToReactivateWindow(requested_window,
+ active_window_));
+ }
+ return;
+ }
+
+ DCHECK(rules_->CanActivateWindow(window));
+ if (window)
+ DCHECK_EQ(window, rules_->GetActivatableWindow(window));
+
+ base::AutoReset<bool> updating_activation(&updating_activation_, true);
+ aura::Window* lost_activation = active_window_;
+ // Allow for the window losing activation to be deleted during dispatch. If
+ // it is deleted pass NULL to observers instead of a deleted window.
+ aura::WindowTracker window_tracker;
+ if (lost_activation)
+ window_tracker.Add(lost_activation);
+ if (active_window_ && observer_manager_.IsObserving(active_window_) &&
+ focused_window_ != active_window_) {
+ observer_manager_.Remove(active_window_);
+ }
+ active_window_ = window;
+ if (active_window_ && !observer_manager_.IsObserving(active_window_))
+ observer_manager_.Add(active_window_);
+ if (active_window_) {
+ StackTransientParentsBelowModalWindow(active_window_);
+ active_window_->parent()->StackChildAtTop(active_window_);
+ }
+
+ aura::client::ActivationChangeObserver* observer = NULL;
+ if (window_tracker.Contains(lost_activation)) {
+ observer = aura::client::GetActivationChangeObserver(lost_activation);
+ if (observer)
+ observer->OnWindowActivated(active_window_, lost_activation);
+ }
+ observer = aura::client::GetActivationChangeObserver(active_window_);
+ if (observer) {
+ observer->OnWindowActivated(
+ active_window_,
+ window_tracker.Contains(lost_activation) ? lost_activation : NULL);
+ }
+ FOR_EACH_OBSERVER(aura::client::ActivationChangeObserver,
+ activation_observers_,
+ OnWindowActivated(active_window_,
+ window_tracker.Contains(lost_activation) ?
+ lost_activation : NULL));
+}
+
+void FocusController::WindowLostFocusFromDispositionChange(
+ aura::Window* window,
+ aura::Window* next) {
+ // A window's modality state will interfere with focus restoration during its
+ // destruction.
+ window->ClearProperty(aura::client::kModalKey);
+ // TODO(beng): See if this function can be replaced by a call to
+ // FocusWindow().
+ // Activation adjustments are handled first in the event of a disposition
+ // changed. If an activation change is necessary, focus is reset as part of
+ // that process so there's no point in updating focus independently.
+ if (window == active_window_) {
+ aura::Window* next_activatable = rules_->GetNextActivatableWindow(window);
+ SetActiveWindow(NULL, next_activatable);
+ if (!(active_window_ && active_window_->Contains(focused_window_)))
+ SetFocusedWindow(next_activatable);
+ } else if (window->Contains(focused_window_)) {
+ // Active window isn't changing, but focused window might be.
+ SetFocusedWindow(rules_->GetFocusableWindow(next));
+ }
+}
+
+void FocusController::WindowFocusedFromInputEvent(aura::Window* window) {
+ // Only focus |window| if it or any of its parents can be focused. Otherwise
+ // FocusWindow() will focus the topmost window, which may not be the
+ // currently focused one.
+ if (rules_->CanFocusWindow(GetToplevelWindow(window)))
+ FocusWindow(window);
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/focus_controller.h b/chromium/ui/wm/core/focus_controller.h
new file mode 100644
index 00000000000..3366ea0c18f
--- /dev/null
+++ b/chromium/ui/wm/core/focus_controller.h
@@ -0,0 +1,128 @@
+// 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_WM_CORE_FOCUS_CONTROLLER_H_
+#define UI_WM_CORE_FOCUS_CONTROLLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "base/scoped_observer.h"
+#include "ui/aura/client/focus_client.h"
+#include "ui/aura/window_observer.h"
+#include "ui/events/event_handler.h"
+#include "ui/wm/public/activation_client.h"
+#include "ui/wm/wm_export.h"
+
+namespace wm {
+
+class FocusRules;
+
+// FocusController handles focus and activation changes for an environment
+// encompassing one or more RootWindows. Within an environment there can be
+// only one focused and one active window at a time. When focus or activation
+// changes notifications are sent using the
+// aura::client::Focus/ActivationChangeObserver interfaces.
+// Changes to focus and activation can be from three sources:
+// . the Aura Client API (implemented here in aura::client::ActivationClient).
+// (The FocusController must be set as the ActivationClient implementation
+// for all RootWindows).
+// . input events (implemented here in ui::EventHandler).
+// (The FocusController must be registered as a pre-target handler for
+// the applicable environment owner, either a RootWindow or another type).
+// . Window disposition changes (implemented here in aura::WindowObserver).
+// (The FocusController registers itself as an observer of the active and
+// focused windows).
+class WM_EXPORT FocusController : public aura::client::ActivationClient,
+ public aura::client::FocusClient,
+ public ui::EventHandler,
+ public aura::WindowObserver {
+ public:
+ // |rules| cannot be NULL.
+ explicit FocusController(FocusRules* rules);
+ virtual ~FocusController();
+
+ private:
+ // Overridden from aura::client::ActivationClient:
+ virtual void AddObserver(
+ aura::client::ActivationChangeObserver* observer) OVERRIDE;
+ virtual void RemoveObserver(
+ aura::client::ActivationChangeObserver* observer) OVERRIDE;
+ virtual void ActivateWindow(aura::Window* window) OVERRIDE;
+ virtual void DeactivateWindow(aura::Window* window) OVERRIDE;
+ virtual aura::Window* GetActiveWindow() OVERRIDE;
+ virtual aura::Window* GetActivatableWindow(aura::Window* window) OVERRIDE;
+ virtual aura::Window* GetToplevelWindow(aura::Window* window) OVERRIDE;
+ virtual bool OnWillFocusWindow(aura::Window* window,
+ const ui::Event* event) OVERRIDE;
+ virtual bool CanActivateWindow(aura::Window* window) const OVERRIDE;
+
+ // Overridden from aura::client::FocusClient:
+ virtual void AddObserver(
+ aura::client::FocusChangeObserver* observer) OVERRIDE;
+ virtual void RemoveObserver(
+ aura::client::FocusChangeObserver* observer) OVERRIDE;
+ virtual void FocusWindow(aura::Window* window) OVERRIDE;
+ virtual void ResetFocusWithinActiveWindow(aura::Window* window) OVERRIDE;
+ virtual aura::Window* GetFocusedWindow() OVERRIDE;
+
+ // Overridden from ui::EventHandler:
+ virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
+ virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
+ virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE;
+ virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE;
+ virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
+
+ // Overridden from aura::WindowObserver:
+ virtual void OnWindowVisibilityChanged(aura::Window* window,
+ bool visible) OVERRIDE;
+ virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
+ virtual void OnWindowHierarchyChanging(
+ const HierarchyChangeParams& params) OVERRIDE;
+ virtual void OnWindowHierarchyChanged(
+ const HierarchyChangeParams& params) OVERRIDE;
+
+ // Internal implementation that sets the focused window, fires events etc.
+ // This function must be called with a valid focusable window.
+ void SetFocusedWindow(aura::Window* window);
+
+ // Internal implementation that sets the active window, fires events etc.
+ // This function must be called with a valid |activatable_window|.
+ // |requested window| refers to the window that was passed in to an external
+ // request (e.g. FocusWindow or ActivateWindow). It may be NULL, e.g. if
+ // SetActiveWindow was not called by an external request. |activatable_window|
+ // refers to the actual window to be activated, which may be different.
+ void SetActiveWindow(aura::Window* requested_window,
+ aura::Window* activatable_window);
+
+ // Called when a window's disposition changed such that it and its hierarchy
+ // are no longer focusable/activatable. |next| is a valid window that is used
+ // as a starting point for finding a window to focus next based on rules.
+ void WindowLostFocusFromDispositionChange(aura::Window* window,
+ aura::Window* next);
+
+ // Called when an attempt is made to focus or activate a window via an input
+ // event targeted at that window. Rules determine the best focusable window
+ // for the input window.
+ void WindowFocusedFromInputEvent(aura::Window* window);
+
+ aura::Window* active_window_;
+ aura::Window* focused_window_;
+
+ bool updating_focus_;
+ bool updating_activation_;
+
+ scoped_ptr<FocusRules> rules_;
+
+ ObserverList<aura::client::ActivationChangeObserver> activation_observers_;
+ ObserverList<aura::client::FocusChangeObserver> focus_observers_;
+
+ ScopedObserver<aura::Window, aura::WindowObserver> observer_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(FocusController);
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_FOCUS_CONTROLLER_H_
diff --git a/chromium/ui/wm/core/focus_controller_unittest.cc b/chromium/ui/wm/core/focus_controller_unittest.cc
new file mode 100644
index 00000000000..5d65943d1b4
--- /dev/null
+++ b/chromium/ui/wm/core/focus_controller_unittest.cc
@@ -0,0 +1,1285 @@
+// 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/wm/core/focus_controller.h"
+
+#include <map>
+
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/client/default_capture_client.h"
+#include "ui/aura/client/focus_change_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/window.h"
+#include "ui/aura/window_event_dispatcher.h"
+#include "ui/aura/window_tracker.h"
+#include "ui/base/ime/dummy_text_input_client.h"
+#include "ui/base/ime/text_input_focus_manager.h"
+#include "ui/events/event.h"
+#include "ui/events/event_constants.h"
+#include "ui/events/event_handler.h"
+#include "ui/wm/core/base_focus_rules.h"
+#include "ui/wm/core/wm_state.h"
+#include "ui/wm/public/activation_change_observer.h"
+#include "ui/wm/public/activation_client.h"
+
+namespace wm {
+
+class FocusNotificationObserver : public aura::client::ActivationChangeObserver,
+ public aura::client::FocusChangeObserver {
+ public:
+ FocusNotificationObserver()
+ : activation_changed_count_(0),
+ focus_changed_count_(0),
+ reactivation_count_(0),
+ reactivation_requested_window_(NULL),
+ reactivation_actual_window_(NULL) {}
+ virtual ~FocusNotificationObserver() {}
+
+ void ExpectCounts(int activation_changed_count, int focus_changed_count) {
+ EXPECT_EQ(activation_changed_count, activation_changed_count_);
+ EXPECT_EQ(focus_changed_count, focus_changed_count_);
+ }
+ int reactivation_count() const {
+ return reactivation_count_;
+ }
+ aura::Window* reactivation_requested_window() const {
+ return reactivation_requested_window_;
+ }
+ aura::Window* reactivation_actual_window() const {
+ return reactivation_actual_window_;
+ }
+
+ private:
+ // Overridden from aura::client::ActivationChangeObserver:
+ virtual void OnWindowActivated(aura::Window* gained_active,
+ aura::Window* lost_active) OVERRIDE {
+ ++activation_changed_count_;
+ }
+ virtual void OnAttemptToReactivateWindow(
+ aura::Window* request_active,
+ aura::Window* actual_active) OVERRIDE {
+ ++reactivation_count_;
+ reactivation_requested_window_ = request_active;
+ reactivation_actual_window_ = actual_active;
+ }
+
+ // Overridden from aura::client::FocusChangeObserver:
+ virtual void OnWindowFocused(aura::Window* gained_focus,
+ aura::Window* lost_focus) OVERRIDE {
+ ++focus_changed_count_;
+ }
+
+ int activation_changed_count_;
+ int focus_changed_count_;
+ int reactivation_count_;
+ aura::Window* reactivation_requested_window_;
+ aura::Window* reactivation_actual_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(FocusNotificationObserver);
+};
+
+class WindowDeleter {
+ public:
+ virtual aura::Window* GetDeletedWindow() = 0;
+
+ protected:
+ virtual ~WindowDeleter() {}
+};
+
+// ActivationChangeObserver and FocusChangeObserver that keeps track of whether
+// it was notified about activation changes or focus changes with a deleted
+// window.
+class RecordingActivationAndFocusChangeObserver
+ : public aura::client::ActivationChangeObserver,
+ public aura::client::FocusChangeObserver {
+ public:
+ RecordingActivationAndFocusChangeObserver(aura::Window* root,
+ WindowDeleter* deleter)
+ : root_(root),
+ deleter_(deleter),
+ was_notified_with_deleted_window_(false) {
+ aura::client::GetActivationClient(root_)->AddObserver(this);
+ aura::client::GetFocusClient(root_)->AddObserver(this);
+ }
+ virtual ~RecordingActivationAndFocusChangeObserver() {
+ aura::client::GetActivationClient(root_)->RemoveObserver(this);
+ aura::client::GetFocusClient(root_)->RemoveObserver(this);
+ }
+
+ bool was_notified_with_deleted_window() const {
+ return was_notified_with_deleted_window_;
+ }
+
+ // Overridden from aura::client::ActivationChangeObserver:
+ virtual void OnWindowActivated(aura::Window* gained_active,
+ aura::Window* lost_active) OVERRIDE {
+ if (lost_active && lost_active == deleter_->GetDeletedWindow())
+ was_notified_with_deleted_window_ = true;
+ }
+
+ // Overridden from aura::client::FocusChangeObserver:
+ virtual void OnWindowFocused(aura::Window* gained_focus,
+ aura::Window* lost_focus) OVERRIDE {
+ if (lost_focus && lost_focus == deleter_->GetDeletedWindow())
+ was_notified_with_deleted_window_ = true;
+ }
+
+ private:
+ aura::Window* root_;
+
+ // Not owned.
+ WindowDeleter* deleter_;
+
+ // Whether the observer was notified about the loss of activation or the
+ // loss of focus with a window already deleted by |deleter_| as the
+ // |lost_active| or |lost_focus| parameter.
+ bool was_notified_with_deleted_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(RecordingActivationAndFocusChangeObserver);
+};
+
+// ActivationChangeObserver that deletes the window losing activation.
+class DeleteOnLoseActivationChangeObserver :
+ public aura::client::ActivationChangeObserver,
+ public WindowDeleter {
+ public:
+ explicit DeleteOnLoseActivationChangeObserver(aura::Window* window)
+ : root_(window->GetRootWindow()),
+ window_(window),
+ did_delete_(false) {
+ aura::client::GetActivationClient(root_)->AddObserver(this);
+ }
+ virtual ~DeleteOnLoseActivationChangeObserver() {
+ aura::client::GetActivationClient(root_)->RemoveObserver(this);
+ }
+
+ // Overridden from aura::client::ActivationChangeObserver:
+ virtual void OnWindowActivated(aura::Window* gained_active,
+ aura::Window* lost_active) OVERRIDE {
+ if (window_ && lost_active == window_) {
+ delete lost_active;
+ did_delete_ = true;
+ }
+ }
+
+ // Overridden from WindowDeleter:
+ virtual aura::Window* GetDeletedWindow() OVERRIDE {
+ return did_delete_ ? window_ : NULL;
+ }
+
+ private:
+ aura::Window* root_;
+ aura::Window* window_;
+ bool did_delete_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeleteOnLoseActivationChangeObserver);
+};
+
+// FocusChangeObserver that deletes the window losing focus.
+class DeleteOnLoseFocusChangeObserver
+ : public aura::client::FocusChangeObserver,
+ public WindowDeleter {
+ public:
+ explicit DeleteOnLoseFocusChangeObserver(aura::Window* window)
+ : root_(window->GetRootWindow()),
+ window_(window),
+ did_delete_(false) {
+ aura::client::GetFocusClient(root_)->AddObserver(this);
+ }
+ virtual ~DeleteOnLoseFocusChangeObserver() {
+ aura::client::GetFocusClient(root_)->RemoveObserver(this);
+ }
+
+ // Overridden from aura::client::FocusChangeObserver:
+ virtual void OnWindowFocused(aura::Window* gained_focus,
+ aura::Window* lost_focus) OVERRIDE {
+ if (window_ && lost_focus == window_) {
+ delete lost_focus;
+ did_delete_ = true;
+ }
+ }
+
+ // Overridden from WindowDeleter:
+ virtual aura::Window* GetDeletedWindow() OVERRIDE {
+ return did_delete_ ? window_ : NULL;
+ }
+
+ private:
+ aura::Window* root_;
+ aura::Window* window_;
+ bool did_delete_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeleteOnLoseFocusChangeObserver);
+};
+
+class ScopedFocusNotificationObserver : public FocusNotificationObserver {
+ public:
+ ScopedFocusNotificationObserver(aura::Window* root_window)
+ : root_window_(root_window) {
+ aura::client::GetActivationClient(root_window_)->AddObserver(this);
+ aura::client::GetFocusClient(root_window_)->AddObserver(this);
+ }
+ virtual ~ScopedFocusNotificationObserver() {
+ aura::client::GetActivationClient(root_window_)->RemoveObserver(this);
+ aura::client::GetFocusClient(root_window_)->RemoveObserver(this);
+ }
+
+ private:
+ aura::Window* root_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedFocusNotificationObserver);
+};
+
+class ScopedTargetFocusNotificationObserver : public FocusNotificationObserver {
+ public:
+ ScopedTargetFocusNotificationObserver(aura::Window* root_window, int id)
+ : target_(root_window->GetChildById(id)) {
+ aura::client::SetActivationChangeObserver(target_, this);
+ aura::client::SetFocusChangeObserver(target_, this);
+ tracker_.Add(target_);
+ }
+ virtual ~ScopedTargetFocusNotificationObserver() {
+ if (tracker_.Contains(target_)) {
+ aura::client::SetActivationChangeObserver(target_, NULL);
+ aura::client::SetFocusChangeObserver(target_, NULL);
+ }
+ }
+
+ private:
+ aura::Window* target_;
+ aura::WindowTracker tracker_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedTargetFocusNotificationObserver);
+};
+
+class ScopedFocusedTextInputClientChanger
+ : public ScopedFocusNotificationObserver {
+ public:
+ ScopedFocusedTextInputClientChanger(aura::Window* root_window,
+ ui::TextInputClient* text_input_client)
+ : ScopedFocusNotificationObserver(root_window),
+ text_input_client_(text_input_client) {}
+
+ private:
+ // Overridden from aura::client::FocusChangeObserver:
+ virtual void OnWindowFocused(aura::Window* gained_focus,
+ aura::Window* lost_focus) OVERRIDE {
+ ui::TextInputFocusManager::GetInstance()->FocusTextInputClient(
+ text_input_client_);
+ }
+
+ ui::TextInputClient* text_input_client_;
+};
+
+// Used to fake the handling of events in the pre-target phase.
+class SimpleEventHandler : public ui::EventHandler {
+ public:
+ SimpleEventHandler() {}
+ virtual ~SimpleEventHandler() {}
+
+ // Overridden from ui::EventHandler:
+ virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
+ event->SetHandled();
+ }
+ virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
+ event->SetHandled();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SimpleEventHandler);
+};
+
+class FocusShiftingActivationObserver
+ : public aura::client::ActivationChangeObserver {
+ public:
+ explicit FocusShiftingActivationObserver(aura::Window* activated_window)
+ : activated_window_(activated_window),
+ shift_focus_to_(NULL) {}
+ virtual ~FocusShiftingActivationObserver() {}
+
+ void set_shift_focus_to(aura::Window* shift_focus_to) {
+ shift_focus_to_ = shift_focus_to;
+ }
+
+ private:
+ // Overridden from aura::client::ActivationChangeObserver:
+ virtual void OnWindowActivated(aura::Window* gained_active,
+ aura::Window* lost_active) OVERRIDE {
+ // Shift focus to a child. This should prevent the default focusing from
+ // occurring in FocusController::FocusWindow().
+ if (gained_active == activated_window_) {
+ aura::client::FocusClient* client =
+ aura::client::GetFocusClient(gained_active);
+ client->FocusWindow(shift_focus_to_);
+ }
+ }
+
+ aura::Window* activated_window_;
+ aura::Window* shift_focus_to_;
+
+ DISALLOW_COPY_AND_ASSIGN(FocusShiftingActivationObserver);
+};
+
+// BaseFocusRules subclass that allows basic overrides of focus/activation to
+// be tested. This is intended more as a test that the override system works at
+// all, rather than as an exhaustive set of use cases, those should be covered
+// in tests for those FocusRules implementations.
+class TestFocusRules : public BaseFocusRules {
+ public:
+ TestFocusRules() : focus_restriction_(NULL) {}
+
+ // Restricts focus and activation to this window and its child hierarchy.
+ void set_focus_restriction(aura::Window* focus_restriction) {
+ focus_restriction_ = focus_restriction;
+ }
+
+ // Overridden from BaseFocusRules:
+ virtual bool SupportsChildActivation(aura::Window* window) const OVERRIDE {
+ // In FocusControllerTests, only the RootWindow has activatable children.
+ return window->GetRootWindow() == window;
+ }
+ virtual bool CanActivateWindow(aura::Window* window) const OVERRIDE {
+ // Restricting focus to a non-activatable child window means the activatable
+ // parent outside the focus restriction is activatable.
+ bool can_activate =
+ CanFocusOrActivate(window) || window->Contains(focus_restriction_);
+ return can_activate ? BaseFocusRules::CanActivateWindow(window) : false;
+ }
+ virtual bool CanFocusWindow(aura::Window* window) const OVERRIDE {
+ return CanFocusOrActivate(window) ?
+ BaseFocusRules::CanFocusWindow(window) : false;
+ }
+ virtual aura::Window* GetActivatableWindow(
+ aura::Window* window) const OVERRIDE {
+ return BaseFocusRules::GetActivatableWindow(
+ CanFocusOrActivate(window) ? window : focus_restriction_);
+ }
+ virtual aura::Window* GetFocusableWindow(
+ aura::Window* window) const OVERRIDE {
+ return BaseFocusRules::GetFocusableWindow(
+ CanFocusOrActivate(window) ? window : focus_restriction_);
+ }
+ virtual aura::Window* GetNextActivatableWindow(
+ aura::Window* ignore) const OVERRIDE {
+ aura::Window* next_activatable =
+ BaseFocusRules::GetNextActivatableWindow(ignore);
+ return CanFocusOrActivate(next_activatable) ?
+ next_activatable : GetActivatableWindow(focus_restriction_);
+ }
+
+ private:
+ bool CanFocusOrActivate(aura::Window* window) const {
+ return !focus_restriction_ || focus_restriction_->Contains(window);
+ }
+
+ aura::Window* focus_restriction_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestFocusRules);
+};
+
+// Common infrastructure shared by all FocusController test types.
+class FocusControllerTestBase : public aura::test::AuraTestBase {
+ protected:
+ FocusControllerTestBase() {}
+
+ // Overridden from aura::test::AuraTestBase:
+ virtual void SetUp() OVERRIDE {
+ wm_state_.reset(new wm::WMState);
+ // FocusController registers itself as an Env observer so it can catch all
+ // window initializations, including the root_window()'s, so we create it
+ // before allowing the base setup.
+ test_focus_rules_ = new TestFocusRules;
+ focus_controller_.reset(new FocusController(test_focus_rules_));
+ aura::test::AuraTestBase::SetUp();
+ root_window()->AddPreTargetHandler(focus_controller_.get());
+ aura::client::SetFocusClient(root_window(), focus_controller_.get());
+ aura::client::SetActivationClient(root_window(), focus_controller_.get());
+
+ // Hierarchy used by all tests:
+ // root_window
+ // +-- w1
+ // | +-- w11
+ // | +-- w12
+ // +-- w2
+ // | +-- w21
+ // | +-- w211
+ // +-- w3
+ aura::Window* w1 = aura::test::CreateTestWindowWithDelegate(
+ aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 1,
+ gfx::Rect(0, 0, 50, 50), root_window());
+ aura::test::CreateTestWindowWithDelegate(
+ aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 11,
+ gfx::Rect(5, 5, 10, 10), w1);
+ aura::test::CreateTestWindowWithDelegate(
+ aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 12,
+ gfx::Rect(15, 15, 10, 10), w1);
+ aura::Window* w2 = aura::test::CreateTestWindowWithDelegate(
+ aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 2,
+ gfx::Rect(75, 75, 50, 50), root_window());
+ aura::Window* w21 = aura::test::CreateTestWindowWithDelegate(
+ aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 21,
+ gfx::Rect(5, 5, 10, 10), w2);
+ aura::test::CreateTestWindowWithDelegate(
+ aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 211,
+ gfx::Rect(1, 1, 5, 5), w21);
+ aura::test::CreateTestWindowWithDelegate(
+ aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 3,
+ gfx::Rect(125, 125, 50, 50), root_window());
+ }
+ virtual void TearDown() OVERRIDE {
+ root_window()->RemovePreTargetHandler(focus_controller_.get());
+ aura::test::AuraTestBase::TearDown();
+ test_focus_rules_ = NULL; // Owned by FocusController.
+ focus_controller_.reset();
+ wm_state_.reset();
+ }
+
+ void FocusWindow(aura::Window* window) {
+ aura::client::GetFocusClient(root_window())->FocusWindow(window);
+ }
+ aura::Window* GetFocusedWindow() {
+ return aura::client::GetFocusClient(root_window())->GetFocusedWindow();
+ }
+ int GetFocusedWindowId() {
+ aura::Window* focused_window = GetFocusedWindow();
+ return focused_window ? focused_window->id() : -1;
+ }
+ void ActivateWindow(aura::Window* window) {
+ aura::client::GetActivationClient(root_window())->ActivateWindow(window);
+ }
+ void DeactivateWindow(aura::Window* window) {
+ aura::client::GetActivationClient(root_window())->DeactivateWindow(window);
+ }
+ aura::Window* GetActiveWindow() {
+ return aura::client::GetActivationClient(root_window())->GetActiveWindow();
+ }
+ int GetActiveWindowId() {
+ aura::Window* active_window = GetActiveWindow();
+ return active_window ? active_window->id() : -1;
+ }
+
+ TestFocusRules* test_focus_rules() { return test_focus_rules_; }
+
+ // Test functions.
+ virtual void BasicFocus() = 0;
+ virtual void BasicActivation() = 0;
+ virtual void FocusEvents() = 0;
+ virtual void DuplicateFocusEvents() {}
+ virtual void ActivationEvents() = 0;
+ virtual void ReactivationEvents() {}
+ virtual void DuplicateActivationEvents() {}
+ virtual void ShiftFocusWithinActiveWindow() {}
+ virtual void ShiftFocusToChildOfInactiveWindow() {}
+ virtual void ShiftFocusToParentOfFocusedWindow() {}
+ virtual void FocusRulesOverride() = 0;
+ virtual void ActivationRulesOverride() = 0;
+ virtual void ShiftFocusOnActivation() {}
+ virtual void ShiftFocusOnActivationDueToHide() {}
+ virtual void NoShiftActiveOnActivation() {}
+ virtual void NoFocusChangeOnClickOnCaptureWindow() {}
+ virtual void ChangeFocusWhenNothingFocusedAndCaptured() {}
+ virtual void DontPassDeletedWindow() {}
+ virtual void FocusedTextInputClient() {}
+
+ private:
+ scoped_ptr<FocusController> focus_controller_;
+ TestFocusRules* test_focus_rules_;
+ scoped_ptr<wm::WMState> wm_state_;
+
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerTestBase);
+};
+
+// Test base for tests where focus is directly set to a target window.
+class FocusControllerDirectTestBase : public FocusControllerTestBase {
+ protected:
+ FocusControllerDirectTestBase() {}
+
+ // Different test types shift focus in different ways.
+ virtual void FocusWindowDirect(aura::Window* window) = 0;
+ virtual void ActivateWindowDirect(aura::Window* window) = 0;
+ virtual void DeactivateWindowDirect(aura::Window* window) = 0;
+
+ // Input events do not change focus if the window can not be focused.
+ virtual bool IsInputEvent() = 0;
+
+ void FocusWindowById(int id) {
+ aura::Window* window = root_window()->GetChildById(id);
+ DCHECK(window);
+ FocusWindowDirect(window);
+ }
+ void ActivateWindowById(int id) {
+ aura::Window* window = root_window()->GetChildById(id);
+ DCHECK(window);
+ ActivateWindowDirect(window);
+ }
+
+ // Overridden from FocusControllerTestBase:
+ virtual void BasicFocus() OVERRIDE {
+ EXPECT_EQ(NULL, GetFocusedWindow());
+ FocusWindowById(1);
+ EXPECT_EQ(1, GetFocusedWindowId());
+ FocusWindowById(2);
+ EXPECT_EQ(2, GetFocusedWindowId());
+ }
+ virtual void BasicActivation() OVERRIDE {
+ EXPECT_EQ(NULL, GetActiveWindow());
+ ActivateWindowById(1);
+ EXPECT_EQ(1, GetActiveWindowId());
+ ActivateWindowById(2);
+ EXPECT_EQ(2, GetActiveWindowId());
+ // Verify that attempting to deactivate NULL does not crash and does not
+ // change activation.
+ DeactivateWindow(NULL);
+ EXPECT_EQ(2, GetActiveWindowId());
+ DeactivateWindow(GetActiveWindow());
+ EXPECT_EQ(1, GetActiveWindowId());
+ }
+ virtual void FocusEvents() OVERRIDE {
+ ScopedFocusNotificationObserver root_observer(root_window());
+ ScopedTargetFocusNotificationObserver observer1(root_window(), 1);
+ ScopedTargetFocusNotificationObserver observer2(root_window(), 2);
+
+ root_observer.ExpectCounts(0, 0);
+ observer1.ExpectCounts(0, 0);
+ observer2.ExpectCounts(0, 0);
+
+ FocusWindowById(1);
+ root_observer.ExpectCounts(1, 1);
+ observer1.ExpectCounts(1, 1);
+ observer2.ExpectCounts(0, 0);
+
+ FocusWindowById(2);
+ root_observer.ExpectCounts(2, 2);
+ observer1.ExpectCounts(2, 2);
+ observer2.ExpectCounts(1, 1);
+ }
+ virtual void DuplicateFocusEvents() OVERRIDE {
+ // Focusing an existing focused window should not resend focus events.
+ ScopedFocusNotificationObserver root_observer(root_window());
+ ScopedTargetFocusNotificationObserver observer1(root_window(), 1);
+
+ root_observer.ExpectCounts(0, 0);
+ observer1.ExpectCounts(0, 0);
+
+ FocusWindowById(1);
+ root_observer.ExpectCounts(1, 1);
+ observer1.ExpectCounts(1, 1);
+
+ FocusWindowById(1);
+ root_observer.ExpectCounts(1, 1);
+ observer1.ExpectCounts(1, 1);
+ }
+ virtual void ActivationEvents() OVERRIDE {
+ ActivateWindowById(1);
+
+ ScopedFocusNotificationObserver root_observer(root_window());
+ ScopedTargetFocusNotificationObserver observer1(root_window(), 1);
+ ScopedTargetFocusNotificationObserver observer2(root_window(), 2);
+
+ root_observer.ExpectCounts(0, 0);
+ observer1.ExpectCounts(0, 0);
+ observer2.ExpectCounts(0, 0);
+
+ ActivateWindowById(2);
+ root_observer.ExpectCounts(1, 1);
+ observer1.ExpectCounts(1, 1);
+ observer2.ExpectCounts(1, 1);
+ }
+ virtual void ReactivationEvents() OVERRIDE {
+ ActivateWindowById(1);
+ ScopedFocusNotificationObserver root_observer(root_window());
+ EXPECT_EQ(0, root_observer.reactivation_count());
+ root_window()->GetChildById(2)->Hide();
+ // When we attempt to activate "2", which cannot be activated because it
+ // is not visible, "1" will be reactivated.
+ ActivateWindowById(2);
+ EXPECT_EQ(1, root_observer.reactivation_count());
+ EXPECT_EQ(root_window()->GetChildById(2),
+ root_observer.reactivation_requested_window());
+ EXPECT_EQ(root_window()->GetChildById(1),
+ root_observer.reactivation_actual_window());
+ }
+ virtual void DuplicateActivationEvents() OVERRIDE {
+ // Activating an existing active window should not resend activation events.
+ ActivateWindowById(1);
+
+ ScopedFocusNotificationObserver root_observer(root_window());
+ ScopedTargetFocusNotificationObserver observer1(root_window(), 1);
+ ScopedTargetFocusNotificationObserver observer2(root_window(), 2);
+
+ root_observer.ExpectCounts(0, 0);
+ observer1.ExpectCounts(0, 0);
+ observer2.ExpectCounts(0, 0);
+
+ ActivateWindowById(2);
+ root_observer.ExpectCounts(1, 1);
+ observer1.ExpectCounts(1, 1);
+ observer2.ExpectCounts(1, 1);
+
+ ActivateWindowById(2);
+ root_observer.ExpectCounts(1, 1);
+ observer1.ExpectCounts(1, 1);
+ observer2.ExpectCounts(1, 1);
+ }
+ virtual void ShiftFocusWithinActiveWindow() OVERRIDE {
+ ActivateWindowById(1);
+ EXPECT_EQ(1, GetActiveWindowId());
+ EXPECT_EQ(1, GetFocusedWindowId());
+ FocusWindowById(11);
+ EXPECT_EQ(11, GetFocusedWindowId());
+ FocusWindowById(12);
+ EXPECT_EQ(12, GetFocusedWindowId());
+ }
+ virtual void ShiftFocusToChildOfInactiveWindow() OVERRIDE {
+ ActivateWindowById(2);
+ EXPECT_EQ(2, GetActiveWindowId());
+ EXPECT_EQ(2, GetFocusedWindowId());
+ FocusWindowById(11);
+ EXPECT_EQ(1, GetActiveWindowId());
+ EXPECT_EQ(11, GetFocusedWindowId());
+ }
+ virtual void ShiftFocusToParentOfFocusedWindow() OVERRIDE {
+ ActivateWindowById(1);
+ EXPECT_EQ(1, GetFocusedWindowId());
+ FocusWindowById(11);
+ EXPECT_EQ(11, GetFocusedWindowId());
+ FocusWindowById(1);
+ // Focus should _not_ shift to the parent of the already-focused window.
+ EXPECT_EQ(11, GetFocusedWindowId());
+ }
+ virtual void FocusRulesOverride() OVERRIDE {
+ EXPECT_EQ(NULL, GetFocusedWindow());
+ FocusWindowById(11);
+ EXPECT_EQ(11, GetFocusedWindowId());
+
+ test_focus_rules()->set_focus_restriction(root_window()->GetChildById(211));
+ FocusWindowById(12);
+ // Input events leave focus unchanged; direct API calls will change focus
+ // to the restricted window.
+ int focused_window = IsInputEvent() ? 11 : 211;
+ EXPECT_EQ(focused_window, GetFocusedWindowId());
+
+ test_focus_rules()->set_focus_restriction(NULL);
+ FocusWindowById(12);
+ EXPECT_EQ(12, GetFocusedWindowId());
+ }
+ virtual void ActivationRulesOverride() OVERRIDE {
+ ActivateWindowById(1);
+ EXPECT_EQ(1, GetActiveWindowId());
+ EXPECT_EQ(1, GetFocusedWindowId());
+
+ aura::Window* w3 = root_window()->GetChildById(3);
+ test_focus_rules()->set_focus_restriction(w3);
+
+ ActivateWindowById(2);
+ // Input events leave activation unchanged; direct API calls will activate
+ // the restricted window.
+ int active_window = IsInputEvent() ? 1 : 3;
+ EXPECT_EQ(active_window, GetActiveWindowId());
+ EXPECT_EQ(active_window, GetFocusedWindowId());
+
+ test_focus_rules()->set_focus_restriction(NULL);
+ ActivateWindowById(2);
+ EXPECT_EQ(2, GetActiveWindowId());
+ EXPECT_EQ(2, GetFocusedWindowId());
+ }
+ virtual void ShiftFocusOnActivation() OVERRIDE {
+ // When a window is activated, by default that window is also focused.
+ // An ActivationChangeObserver may shift focus to another window within the
+ // same activatable window.
+ ActivateWindowById(2);
+ EXPECT_EQ(2, GetFocusedWindowId());
+ ActivateWindowById(1);
+ EXPECT_EQ(1, GetFocusedWindowId());
+
+ ActivateWindowById(2);
+
+ aura::Window* target = root_window()->GetChildById(1);
+ aura::client::ActivationClient* client =
+ aura::client::GetActivationClient(root_window());
+
+ scoped_ptr<FocusShiftingActivationObserver> observer(
+ new FocusShiftingActivationObserver(target));
+ observer->set_shift_focus_to(target->GetChildById(11));
+ client->AddObserver(observer.get());
+
+ ActivateWindowById(1);
+
+ // w1's ActivationChangeObserver shifted focus to this child, pre-empting
+ // FocusController's default setting.
+ EXPECT_EQ(11, GetFocusedWindowId());
+
+ ActivateWindowById(2);
+ EXPECT_EQ(2, GetFocusedWindowId());
+
+ // Simulate a focus reset by the ActivationChangeObserver. This should
+ // trigger the default setting in FocusController.
+ observer->set_shift_focus_to(NULL);
+ ActivateWindowById(1);
+ EXPECT_EQ(1, GetFocusedWindowId());
+
+ client->RemoveObserver(observer.get());
+
+ ActivateWindowById(2);
+ EXPECT_EQ(2, GetFocusedWindowId());
+ ActivateWindowById(1);
+ EXPECT_EQ(1, GetFocusedWindowId());
+ }
+ virtual void ShiftFocusOnActivationDueToHide() OVERRIDE {
+ // Similar to ShiftFocusOnActivation except the activation change is
+ // triggered by hiding the active window.
+ ActivateWindowById(1);
+ EXPECT_EQ(1, GetFocusedWindowId());
+
+ // Removes window 3 as candidate for next activatable window.
+ root_window()->GetChildById(3)->Hide();
+ EXPECT_EQ(1, GetFocusedWindowId());
+
+ aura::Window* target = root_window()->GetChildById(2);
+ aura::client::ActivationClient* client =
+ aura::client::GetActivationClient(root_window());
+
+ scoped_ptr<FocusShiftingActivationObserver> observer(
+ new FocusShiftingActivationObserver(target));
+ observer->set_shift_focus_to(target->GetChildById(21));
+ client->AddObserver(observer.get());
+
+ // Hide the active window.
+ root_window()->GetChildById(1)->Hide();
+
+ EXPECT_EQ(21, GetFocusedWindowId());
+
+ client->RemoveObserver(observer.get());
+ }
+ virtual void NoShiftActiveOnActivation() OVERRIDE {
+ // When a window is activated, we need to prevent any change to activation
+ // from being made in response to an activation change notification.
+ }
+
+ virtual void NoFocusChangeOnClickOnCaptureWindow() OVERRIDE {
+ scoped_ptr<aura::client::DefaultCaptureClient> capture_client(
+ new aura::client::DefaultCaptureClient(root_window()));
+ // Clicking on a window which has capture should not cause a focus change
+ // to the window. This test verifies whether that is indeed the case.
+ ActivateWindowById(1);
+
+ EXPECT_EQ(1, GetActiveWindowId());
+ EXPECT_EQ(1, GetFocusedWindowId());
+
+ aura::Window* w2 = root_window()->GetChildById(2);
+ aura::client::GetCaptureClient(root_window())->SetCapture(w2);
+ aura::test::EventGenerator generator(root_window(), w2);
+ generator.ClickLeftButton();
+
+ EXPECT_EQ(1, GetActiveWindowId());
+ EXPECT_EQ(1, GetFocusedWindowId());
+ aura::client::GetCaptureClient(root_window())->ReleaseCapture(w2);
+ }
+
+ // Verifies focus change is honored while capture held.
+ virtual void ChangeFocusWhenNothingFocusedAndCaptured() OVERRIDE {
+ scoped_ptr<aura::client::DefaultCaptureClient> capture_client(
+ new aura::client::DefaultCaptureClient(root_window()));
+ aura::Window* w1 = root_window()->GetChildById(1);
+ aura::client::GetCaptureClient(root_window())->SetCapture(w1);
+
+ EXPECT_EQ(-1, GetActiveWindowId());
+ EXPECT_EQ(-1, GetFocusedWindowId());
+
+ FocusWindowById(1);
+
+ EXPECT_EQ(1, GetActiveWindowId());
+ EXPECT_EQ(1, GetFocusedWindowId());
+
+ aura::client::GetCaptureClient(root_window())->ReleaseCapture(w1);
+ }
+
+ // Verifies if a window that loses activation or focus is deleted during
+ // observer notification we don't pass the deleted window to other observers.
+ virtual void DontPassDeletedWindow() OVERRIDE {
+ FocusWindowById(1);
+
+ EXPECT_EQ(1, GetActiveWindowId());
+ EXPECT_EQ(1, GetFocusedWindowId());
+
+ {
+ aura::Window* to_delete = root_window()->GetChildById(1);
+ DeleteOnLoseActivationChangeObserver observer1(to_delete);
+ RecordingActivationAndFocusChangeObserver observer2(root_window(),
+ &observer1);
+
+ FocusWindowById(2);
+
+ EXPECT_EQ(2, GetActiveWindowId());
+ EXPECT_EQ(2, GetFocusedWindowId());
+
+ EXPECT_EQ(to_delete, observer1.GetDeletedWindow());
+ EXPECT_FALSE(observer2.was_notified_with_deleted_window());
+ }
+
+ {
+ aura::Window* to_delete = root_window()->GetChildById(2);
+ DeleteOnLoseFocusChangeObserver observer1(to_delete);
+ RecordingActivationAndFocusChangeObserver observer2(root_window(),
+ &observer1);
+
+ FocusWindowById(3);
+
+ EXPECT_EQ(3, GetActiveWindowId());
+ EXPECT_EQ(3, GetFocusedWindowId());
+
+ EXPECT_EQ(to_delete, observer1.GetDeletedWindow());
+ EXPECT_FALSE(observer2.was_notified_with_deleted_window());
+ }
+ }
+
+ // Verifies if the focused text input client is cleared when a window gains
+ // or loses the focus.
+ virtual void FocusedTextInputClient() OVERRIDE {
+ ui::TextInputFocusManager* text_input_focus_manager =
+ ui::TextInputFocusManager::GetInstance();
+ ui::DummyTextInputClient text_input_client;
+ ui::TextInputClient* null_text_input_client = NULL;
+
+ EXPECT_EQ(null_text_input_client,
+ text_input_focus_manager->GetFocusedTextInputClient());
+
+ text_input_focus_manager->FocusTextInputClient(&text_input_client);
+ EXPECT_EQ(&text_input_client,
+ text_input_focus_manager->GetFocusedTextInputClient());
+ FocusWindowById(1);
+ // The focused text input client gets cleared when a window gets focused
+ // unless any of observers sets the focused text input client.
+ EXPECT_EQ(null_text_input_client,
+ text_input_focus_manager->GetFocusedTextInputClient());
+
+ ScopedFocusedTextInputClientChanger text_input_focus_changer(
+ root_window(), &text_input_client);
+ EXPECT_EQ(null_text_input_client,
+ text_input_focus_manager->GetFocusedTextInputClient());
+ FocusWindowById(2);
+ // |text_input_focus_changer| sets the focused text input client.
+ EXPECT_EQ(&text_input_client,
+ text_input_focus_manager->GetFocusedTextInputClient());
+
+ FocusWindow(NULL);
+ // The focused text input client gets cleared when a window loses the focus.
+ EXPECT_EQ(null_text_input_client,
+ text_input_focus_manager->GetFocusedTextInputClient());
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerDirectTestBase);
+};
+
+// Focus and Activation changes via aura::client::ActivationClient API.
+class FocusControllerApiTest : public FocusControllerDirectTestBase {
+ public:
+ FocusControllerApiTest() {}
+
+ private:
+ // Overridden from FocusControllerTestBase:
+ virtual void FocusWindowDirect(aura::Window* window) OVERRIDE {
+ FocusWindow(window);
+ }
+ virtual void ActivateWindowDirect(aura::Window* window) OVERRIDE {
+ ActivateWindow(window);
+ }
+ virtual void DeactivateWindowDirect(aura::Window* window) OVERRIDE {
+ DeactivateWindow(window);
+ }
+ virtual bool IsInputEvent() OVERRIDE { return false; }
+
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerApiTest);
+};
+
+// Focus and Activation changes via input events.
+class FocusControllerMouseEventTest : public FocusControllerDirectTestBase {
+ public:
+ FocusControllerMouseEventTest() {}
+
+ // Tests that a handled mouse or gesture event does not trigger a window
+ // activation.
+ void IgnoreHandledEvent() {
+ EXPECT_EQ(NULL, GetActiveWindow());
+ aura::Window* w1 = root_window()->GetChildById(1);
+ SimpleEventHandler handler;
+ root_window()->PrependPreTargetHandler(&handler);
+ aura::test::EventGenerator generator(root_window(), w1);
+ generator.ClickLeftButton();
+ EXPECT_EQ(NULL, GetActiveWindow());
+ generator.GestureTapAt(w1->bounds().CenterPoint());
+ EXPECT_EQ(NULL, GetActiveWindow());
+ root_window()->RemovePreTargetHandler(&handler);
+ generator.ClickLeftButton();
+ EXPECT_EQ(1, GetActiveWindowId());
+ }
+
+ private:
+ // Overridden from FocusControllerTestBase:
+ virtual void FocusWindowDirect(aura::Window* window) OVERRIDE {
+ aura::test::EventGenerator generator(root_window(), window);
+ generator.ClickLeftButton();
+ }
+ virtual void ActivateWindowDirect(aura::Window* window) OVERRIDE {
+ aura::test::EventGenerator generator(root_window(), window);
+ generator.ClickLeftButton();
+ }
+ virtual void DeactivateWindowDirect(aura::Window* window) OVERRIDE {
+ aura::Window* next_activatable =
+ test_focus_rules()->GetNextActivatableWindow(window);
+ aura::test::EventGenerator generator(root_window(), next_activatable);
+ generator.ClickLeftButton();
+ }
+ virtual bool IsInputEvent() OVERRIDE { return true; }
+
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerMouseEventTest);
+};
+
+class FocusControllerGestureEventTest : public FocusControllerDirectTestBase {
+ public:
+ FocusControllerGestureEventTest() {}
+
+ private:
+ // Overridden from FocusControllerTestBase:
+ virtual void FocusWindowDirect(aura::Window* window) OVERRIDE {
+ aura::test::EventGenerator generator(root_window(), window);
+ generator.GestureTapAt(window->bounds().CenterPoint());
+ }
+ virtual void ActivateWindowDirect(aura::Window* window) OVERRIDE {
+ aura::test::EventGenerator generator(root_window(), window);
+ generator.GestureTapAt(window->bounds().CenterPoint());
+ }
+ virtual void DeactivateWindowDirect(aura::Window* window) OVERRIDE {
+ aura::Window* next_activatable =
+ test_focus_rules()->GetNextActivatableWindow(window);
+ aura::test::EventGenerator generator(root_window(), next_activatable);
+ generator.GestureTapAt(window->bounds().CenterPoint());
+ }
+ virtual bool IsInputEvent() OVERRIDE { return true; }
+
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerGestureEventTest);
+};
+
+// Test base for tests where focus is implicitly set to a window as the result
+// of a disposition change to the focused window or the hierarchy that contains
+// it.
+class FocusControllerImplicitTestBase : public FocusControllerTestBase {
+ protected:
+ explicit FocusControllerImplicitTestBase(bool parent) : parent_(parent) {}
+
+ aura::Window* GetDispositionWindow(aura::Window* window) {
+ return parent_ ? window->parent() : window;
+ }
+
+ // Change the disposition of |window| in such a way as it will lose focus.
+ virtual void ChangeWindowDisposition(aura::Window* window) = 0;
+
+ // Allow each disposition change test to add additional post-disposition
+ // change expectations.
+ virtual void PostDispostionChangeExpectations() {}
+
+ // Overridden from FocusControllerTestBase:
+ virtual void BasicFocus() OVERRIDE {
+ EXPECT_EQ(NULL, GetFocusedWindow());
+
+ aura::Window* w211 = root_window()->GetChildById(211);
+ FocusWindow(w211);
+ EXPECT_EQ(211, GetFocusedWindowId());
+
+ ChangeWindowDisposition(w211);
+ // BasicFocusRules passes focus to the parent.
+ EXPECT_EQ(parent_ ? 2 : 21, GetFocusedWindowId());
+ }
+ virtual void BasicActivation() OVERRIDE {
+ DCHECK(!parent_) << "Activation tests don't support parent changes.";
+
+ EXPECT_EQ(NULL, GetActiveWindow());
+
+ aura::Window* w2 = root_window()->GetChildById(2);
+ ActivateWindow(w2);
+ EXPECT_EQ(2, GetActiveWindowId());
+
+ ChangeWindowDisposition(w2);
+ EXPECT_EQ(3, GetActiveWindowId());
+ PostDispostionChangeExpectations();
+ }
+ virtual void FocusEvents() OVERRIDE {
+ aura::Window* w211 = root_window()->GetChildById(211);
+ FocusWindow(w211);
+
+ ScopedFocusNotificationObserver root_observer(root_window());
+ ScopedTargetFocusNotificationObserver observer211(root_window(), 211);
+ root_observer.ExpectCounts(0, 0);
+ observer211.ExpectCounts(0, 0);
+
+ ChangeWindowDisposition(w211);
+ root_observer.ExpectCounts(0, 1);
+ observer211.ExpectCounts(0, 1);
+ }
+ virtual void ActivationEvents() OVERRIDE {
+ DCHECK(!parent_) << "Activation tests don't support parent changes.";
+
+ aura::Window* w2 = root_window()->GetChildById(2);
+ ActivateWindow(w2);
+
+ ScopedFocusNotificationObserver root_observer(root_window());
+ ScopedTargetFocusNotificationObserver observer2(root_window(), 2);
+ ScopedTargetFocusNotificationObserver observer3(root_window(), 3);
+ root_observer.ExpectCounts(0, 0);
+ observer2.ExpectCounts(0, 0);
+ observer3.ExpectCounts(0, 0);
+
+ ChangeWindowDisposition(w2);
+ root_observer.ExpectCounts(1, 1);
+ observer2.ExpectCounts(1, 1);
+ observer3.ExpectCounts(1, 1);
+ }
+ virtual void FocusRulesOverride() OVERRIDE {
+ EXPECT_EQ(NULL, GetFocusedWindow());
+ aura::Window* w211 = root_window()->GetChildById(211);
+ FocusWindow(w211);
+ EXPECT_EQ(211, GetFocusedWindowId());
+
+ test_focus_rules()->set_focus_restriction(root_window()->GetChildById(11));
+ ChangeWindowDisposition(w211);
+ // Normally, focus would shift to the parent (w21) but the override shifts
+ // it to 11.
+ EXPECT_EQ(11, GetFocusedWindowId());
+
+ test_focus_rules()->set_focus_restriction(NULL);
+ }
+ virtual void ActivationRulesOverride() OVERRIDE {
+ DCHECK(!parent_) << "Activation tests don't support parent changes.";
+
+ aura::Window* w1 = root_window()->GetChildById(1);
+ ActivateWindow(w1);
+
+ EXPECT_EQ(1, GetActiveWindowId());
+ EXPECT_EQ(1, GetFocusedWindowId());
+
+ aura::Window* w3 = root_window()->GetChildById(3);
+ test_focus_rules()->set_focus_restriction(w3);
+
+ // Normally, activation/focus would move to w2, but since we have a focus
+ // restriction, it should move to w3 instead.
+ ChangeWindowDisposition(w1);
+ EXPECT_EQ(3, GetActiveWindowId());
+ EXPECT_EQ(3, GetFocusedWindowId());
+
+ test_focus_rules()->set_focus_restriction(NULL);
+ ActivateWindow(root_window()->GetChildById(2));
+ EXPECT_EQ(2, GetActiveWindowId());
+ EXPECT_EQ(2, GetFocusedWindowId());
+ }
+
+ private:
+ // When true, the disposition change occurs to the parent of the window
+ // instead of to the window. This verifies that changes occurring in the
+ // hierarchy that contains the window affect the window's focus.
+ bool parent_;
+
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerImplicitTestBase);
+};
+
+// Focus and Activation changes in response to window visibility changes.
+class FocusControllerHideTest : public FocusControllerImplicitTestBase {
+ public:
+ FocusControllerHideTest() : FocusControllerImplicitTestBase(false) {}
+
+ protected:
+ FocusControllerHideTest(bool parent)
+ : FocusControllerImplicitTestBase(parent) {}
+
+ // Overridden from FocusControllerImplicitTestBase:
+ virtual void ChangeWindowDisposition(aura::Window* window) OVERRIDE {
+ GetDispositionWindow(window)->Hide();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerHideTest);
+};
+
+// Focus and Activation changes in response to window parent visibility
+// changes.
+class FocusControllerParentHideTest : public FocusControllerHideTest {
+ public:
+ FocusControllerParentHideTest() : FocusControllerHideTest(true) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerParentHideTest);
+};
+
+// Focus and Activation changes in response to window destruction.
+class FocusControllerDestructionTest : public FocusControllerImplicitTestBase {
+ public:
+ FocusControllerDestructionTest() : FocusControllerImplicitTestBase(false) {}
+
+ protected:
+ FocusControllerDestructionTest(bool parent)
+ : FocusControllerImplicitTestBase(parent) {}
+
+ // Overridden from FocusControllerImplicitTestBase:
+ virtual void ChangeWindowDisposition(aura::Window* window) OVERRIDE {
+ delete GetDispositionWindow(window);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerDestructionTest);
+};
+
+// Focus and Activation changes in response to window parent destruction.
+class FocusControllerParentDestructionTest
+ : public FocusControllerDestructionTest {
+ public:
+ FocusControllerParentDestructionTest()
+ : FocusControllerDestructionTest(true) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerParentDestructionTest);
+};
+
+// Focus and Activation changes in response to window removal.
+class FocusControllerRemovalTest : public FocusControllerImplicitTestBase {
+ public:
+ FocusControllerRemovalTest() : FocusControllerImplicitTestBase(false) {}
+
+ protected:
+ FocusControllerRemovalTest(bool parent)
+ : FocusControllerImplicitTestBase(parent) {}
+
+ // Overridden from FocusControllerImplicitTestBase:
+ virtual void ChangeWindowDisposition(aura::Window* window) OVERRIDE {
+ aura::Window* disposition_window = GetDispositionWindow(window);
+ disposition_window->parent()->RemoveChild(disposition_window);
+ window_owner_.reset(disposition_window);
+ }
+ virtual void TearDown() OVERRIDE {
+ window_owner_.reset();
+ FocusControllerImplicitTestBase::TearDown();
+ }
+
+ private:
+ scoped_ptr<aura::Window> window_owner_;
+
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerRemovalTest);
+};
+
+// Focus and Activation changes in response to window parent removal.
+class FocusControllerParentRemovalTest : public FocusControllerRemovalTest {
+ public:
+ FocusControllerParentRemovalTest() : FocusControllerRemovalTest(true) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerParentRemovalTest);
+};
+
+
+#define FOCUS_CONTROLLER_TEST(TESTCLASS, TESTNAME) \
+ TEST_F(TESTCLASS, TESTNAME) { TESTNAME(); }
+
+// Runs direct focus change tests (input events and API calls).
+#define DIRECT_FOCUS_CHANGE_TESTS(TESTNAME) \
+ FOCUS_CONTROLLER_TEST(FocusControllerApiTest, TESTNAME) \
+ FOCUS_CONTROLLER_TEST(FocusControllerMouseEventTest, TESTNAME) \
+ FOCUS_CONTROLLER_TEST(FocusControllerGestureEventTest, TESTNAME)
+
+// Runs implicit focus change tests for disposition changes to target.
+#define IMPLICIT_FOCUS_CHANGE_TARGET_TESTS(TESTNAME) \
+ FOCUS_CONTROLLER_TEST(FocusControllerHideTest, TESTNAME) \
+ FOCUS_CONTROLLER_TEST(FocusControllerDestructionTest, TESTNAME) \
+ FOCUS_CONTROLLER_TEST(FocusControllerRemovalTest, TESTNAME)
+
+// Runs implicit focus change tests for disposition changes to target's parent
+// hierarchy.
+#define IMPLICIT_FOCUS_CHANGE_PARENT_TESTS(TESTNAME) \
+ /* TODO(beng): parent destruction tests are not supported at
+ present due to workspace manager issues. \
+ FOCUS_CONTROLLER_TEST(FocusControllerParentDestructionTest, TESTNAME) */ \
+ FOCUS_CONTROLLER_TEST(FocusControllerParentHideTest, TESTNAME) \
+ FOCUS_CONTROLLER_TEST(FocusControllerParentRemovalTest, TESTNAME)
+
+// Runs all implicit focus change tests (changes to the target and target's
+// parent hierarchy)
+#define IMPLICIT_FOCUS_CHANGE_TESTS(TESTNAME) \
+ IMPLICIT_FOCUS_CHANGE_TARGET_TESTS(TESTNAME) \
+ IMPLICIT_FOCUS_CHANGE_PARENT_TESTS(TESTNAME)
+
+// Runs all possible focus change tests.
+#define ALL_FOCUS_TESTS(TESTNAME) \
+ DIRECT_FOCUS_CHANGE_TESTS(TESTNAME) \
+ IMPLICIT_FOCUS_CHANGE_TESTS(TESTNAME)
+
+// Runs focus change tests that apply only to the target. For example,
+// implicit activation changes caused by window disposition changes do not
+// occur when changes to the containing hierarchy happen.
+#define TARGET_FOCUS_TESTS(TESTNAME) \
+ DIRECT_FOCUS_CHANGE_TESTS(TESTNAME) \
+ IMPLICIT_FOCUS_CHANGE_TARGET_TESTS(TESTNAME)
+
+// - Focuses a window, verifies that focus changed.
+ALL_FOCUS_TESTS(BasicFocus);
+
+// - Activates a window, verifies that activation changed.
+TARGET_FOCUS_TESTS(BasicActivation);
+
+// - Focuses a window, verifies that focus events were dispatched.
+ALL_FOCUS_TESTS(FocusEvents);
+
+// - Focuses or activates a window multiple times, verifies that events are only
+// dispatched when focus/activation actually changes.
+DIRECT_FOCUS_CHANGE_TESTS(DuplicateFocusEvents);
+DIRECT_FOCUS_CHANGE_TESTS(DuplicateActivationEvents);
+
+// - Activates a window, verifies that activation events were dispatched.
+TARGET_FOCUS_TESTS(ActivationEvents);
+
+// - Attempts to active a hidden window, verifies that current window is
+// attempted to be reactivated and the appropriate event dispatched.
+FOCUS_CONTROLLER_TEST(FocusControllerApiTest, ReactivationEvents);
+
+// - Input events/API calls shift focus between focusable windows within the
+// active window.
+DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusWithinActiveWindow);
+
+// - Input events/API calls to a child window of an inactive window shifts
+// activation to the activatable parent and focuses the child.
+DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusToChildOfInactiveWindow);
+
+// - Input events/API calls to focus the parent of the focused window do not
+// shift focus away from the child.
+DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusToParentOfFocusedWindow);
+
+// - Verifies that FocusRules determine what can be focused.
+ALL_FOCUS_TESTS(FocusRulesOverride);
+
+// - Verifies that FocusRules determine what can be activated.
+TARGET_FOCUS_TESTS(ActivationRulesOverride);
+
+// - Verifies that attempts to change focus or activation from a focus or
+// activation change observer are ignored.
+DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusOnActivation);
+DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusOnActivationDueToHide);
+DIRECT_FOCUS_CHANGE_TESTS(NoShiftActiveOnActivation);
+
+// Clicking on a window which has capture should not result in a focus change.
+DIRECT_FOCUS_CHANGE_TESTS(NoFocusChangeOnClickOnCaptureWindow);
+
+FOCUS_CONTROLLER_TEST(FocusControllerApiTest,
+ ChangeFocusWhenNothingFocusedAndCaptured);
+
+// See description above DontPassDeletedWindow() for details.
+FOCUS_CONTROLLER_TEST(FocusControllerApiTest, DontPassDeletedWindow);
+
+// - Verifies that the focused text input client is cleard when the window focus
+// changes.
+ALL_FOCUS_TESTS(FocusedTextInputClient);
+
+// If a mouse event was handled, it should not activate a window.
+FOCUS_CONTROLLER_TEST(FocusControllerMouseEventTest, IgnoreHandledEvent);
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/focus_rules.h b/chromium/ui/wm/core/focus_rules.h
new file mode 100644
index 00000000000..cb9a4ddbd8f
--- /dev/null
+++ b/chromium/ui/wm/core/focus_rules.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_WM_CORE_FOCUS_RULES_H_
+#define UI_WM_CORE_FOCUS_RULES_H_
+
+#include "ui/wm/wm_export.h"
+
+namespace aura {
+class Window;
+}
+
+namespace wm {
+
+// Implemented by an object that establishes the rules about what can be
+// focused or activated.
+class WM_EXPORT FocusRules {
+ public:
+ virtual ~FocusRules() {}
+
+ // Returns true if |window| is a toplevel window. Whether or not a window
+ // is considered toplevel is determined by a similar set of rules that
+ // govern activation and focus. Not all toplevel windows are activatable,
+ // call CanActivateWindow() to determine if a window can be activated.
+ virtual bool IsToplevelWindow(aura::Window* window) const = 0;
+ // Returns true if |window| can be activated or focused.
+ virtual bool CanActivateWindow(aura::Window* window) const = 0;
+ // For CanFocusWindow(), NULL is supported, because NULL is a valid focusable
+ // window (in the case of clearing focus).
+ virtual bool CanFocusWindow(aura::Window* window) const = 0;
+
+ // Returns the toplevel window containing |window|. Not all toplevel windows
+ // are activatable, call GetActivatableWindow() instead to return the
+ // activatable window, which might be in a different hierarchy.
+ // Will return NULL if |window| is not contained by a window considered to be
+ // a toplevel window.
+ virtual aura::Window* GetToplevelWindow(aura::Window* window) const = 0;
+ // Returns the activatable or focusable window given an attempt to activate or
+ // focus |window|. Some possible scenarios (not intended to be exhaustive):
+ // - |window| is a child of a non-focusable window and so focus must be set
+ // according to rules defined by the delegate, e.g. to a parent.
+ // - |window| is an activatable window that is the transient parent of a modal
+ // window, so attempts to activate |window| should result in the modal
+ // transient being activated instead.
+ // These methods may return NULL if they are unable to find an activatable
+ // or focusable window given |window|.
+ virtual aura::Window* GetActivatableWindow(aura::Window* window) const = 0;
+ virtual aura::Window* GetFocusableWindow(aura::Window* window) const = 0;
+
+ // Returns the next window to activate in the event that |ignore| is no longer
+ // activatable. This function is called when something is happening to
+ // |ignore| that means it can no longer have focus or activation, including
+ // but not limited to:
+ // - it or its parent hierarchy is being hidden, or removed from the
+ // RootWindow.
+ // - it is being destroyed.
+ // - it is being explicitly deactivated.
+ // |ignore| cannot be NULL.
+ virtual aura::Window* GetNextActivatableWindow(
+ aura::Window* ignore) const = 0;
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_FOCUS_RULES_H_
diff --git a/chromium/ui/wm/core/image_grid.cc b/chromium/ui/wm/core/image_grid.cc
new file mode 100644
index 00000000000..db41628ea38
--- /dev/null
+++ b/chromium/ui/wm/core/image_grid.cc
@@ -0,0 +1,338 @@
+// 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/wm/core/image_grid.h"
+
+#include <algorithm>
+
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkXfermode.h"
+#include "ui/compositor/dip_util.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/image/image_skia_operations.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/size.h"
+#include "ui/gfx/size_conversions.h"
+#include "ui/gfx/transform.h"
+
+using std::max;
+using std::min;
+
+namespace wm {
+namespace {
+
+// Sets the scaling for the transform applied to a layer. The left, top,
+// right and bottom layers are stretched to the height or width of the
+// center image.
+
+void ScaleWidth(gfx::Size center, ui::Layer* layer, gfx::Transform& transform) {
+ float layer_width = layer->bounds().width() * layer->device_scale_factor();
+ float scale = static_cast<float>(center.width()) / layer_width;
+ transform.Scale(scale, 1.0);
+}
+
+void ScaleHeight(gfx::Size center,
+ ui::Layer* layer,
+ gfx::Transform& transform) {
+ float layer_height = layer->bounds().height() * layer->device_scale_factor();
+ float scale = static_cast<float>(center.height()) / layer_height;
+ transform.Scale(1.0, scale);
+}
+
+// Returns the dimensions of |image| if non-NULL or gfx::Size(0, 0) otherwise.
+gfx::Size GetImageSize(const gfx::Image* image) {
+ return image ? gfx::Size(image->ToImageSkia()->width(),
+ image->ToImageSkia()->height())
+ : gfx::Size();
+}
+
+// Returns true if |layer|'s bounds don't fit within |size|.
+bool LayerExceedsSize(const ui::Layer* layer, const gfx::Size& size) {
+ return layer->bounds().width() > size.width() ||
+ layer->bounds().height() > size.height();
+}
+
+} // namespace
+
+gfx::RectF ImageGrid::TestAPI::GetTransformedLayerBounds(
+ const ui::Layer& layer) {
+ gfx::RectF bounds = layer.bounds();
+ layer.transform().TransformRect(&bounds);
+ return bounds;
+}
+
+ImageGrid::ImageGrid()
+ : layer_(new ui::Layer(ui::LAYER_NOT_DRAWN)),
+ top_image_height_(0),
+ bottom_image_height_(0),
+ left_image_width_(0),
+ right_image_width_(0),
+ base_top_row_height_(0),
+ base_bottom_row_height_(0),
+ base_left_column_width_(0),
+ base_right_column_width_(0) {
+}
+
+ImageGrid::~ImageGrid() {
+}
+
+void ImageGrid::SetImages(const gfx::Image* top_left_image,
+ const gfx::Image* top_image,
+ const gfx::Image* top_right_image,
+ const gfx::Image* left_image,
+ const gfx::Image* center_image,
+ const gfx::Image* right_image,
+ const gfx::Image* bottom_left_image,
+ const gfx::Image* bottom_image,
+ const gfx::Image* bottom_right_image) {
+ SetImage(top_left_image, &top_left_layer_, &top_left_painter_, NONE);
+ SetImage(top_image, &top_layer_, &top_painter_, HORIZONTAL);
+ SetImage(top_right_image, &top_right_layer_, &top_right_painter_, NONE);
+ SetImage(left_image, &left_layer_, &left_painter_, VERTICAL);
+ SetImage(center_image, &center_layer_, &center_painter_, NONE);
+ SetImage(right_image, &right_layer_, &right_painter_, VERTICAL);
+ SetImage(bottom_left_image, &bottom_left_layer_, &bottom_left_painter_, NONE);
+ SetImage(bottom_image, &bottom_layer_, &bottom_painter_, HORIZONTAL);
+ SetImage(
+ bottom_right_image, &bottom_right_layer_, &bottom_right_painter_, NONE);
+
+ top_image_height_ = GetImageSize(top_image).height();
+ bottom_image_height_ = GetImageSize(bottom_image).height();
+ left_image_width_ = GetImageSize(left_image).width();
+ right_image_width_ = GetImageSize(right_image).width();
+
+ base_top_row_height_ = max(GetImageSize(top_left_image).height(),
+ max(GetImageSize(top_image).height(),
+ GetImageSize(top_right_image).height()));
+ base_bottom_row_height_ = max(GetImageSize(bottom_left_image).height(),
+ max(GetImageSize(bottom_image).height(),
+ GetImageSize(bottom_right_image).height()));
+ base_left_column_width_ = max(GetImageSize(top_left_image).width(),
+ max(GetImageSize(left_image).width(),
+ GetImageSize(bottom_left_image).width()));
+ base_right_column_width_ = max(GetImageSize(top_right_image).width(),
+ max(GetImageSize(right_image).width(),
+ GetImageSize(bottom_right_image).width()));
+
+ // Invalidate previous |size_| so calls to SetSize() will recompute it.
+ size_.SetSize(0, 0);
+}
+
+void ImageGrid::SetSize(const gfx::Size& size) {
+ if (size_ == size)
+ return;
+
+ size_ = size;
+
+ gfx::Rect updated_bounds = layer_->bounds();
+ updated_bounds.set_size(size);
+ layer_->SetBounds(updated_bounds);
+
+ // Calculate the available amount of space for corner images on all sides of
+ // the grid. If the images don't fit, we need to clip them.
+ const int left = min(base_left_column_width_, size_.width() / 2);
+ const int right = min(base_right_column_width_, size_.width() - left);
+ const int top = min(base_top_row_height_, size_.height() / 2);
+ const int bottom = min(base_bottom_row_height_, size_.height() - top);
+
+ // The remaining space goes to the center image.
+ int center_width = std::max(size.width() - left - right, 0);
+ int center_height = std::max(size.height() - top - bottom, 0);
+
+ // At non-integer scale factors, the ratio of dimensions in DIP is not
+ // necessarily the same as the ratio in physical pixels due to rounding. Set
+ // the transform on each of the layers based on dimensions in pixels.
+ gfx::Size center_size_in_pixels = gfx::ToFlooredSize(gfx::ScaleSize(
+ gfx::Size(center_width, center_height), layer_->device_scale_factor()));
+
+ if (top_layer_.get()) {
+ if (center_width > 0) {
+ gfx::Transform transform;
+ transform.Translate(left, 0);
+ ScaleWidth(center_size_in_pixels, top_layer_.get(), transform);
+ top_layer_->SetTransform(transform);
+ }
+ top_layer_->SetVisible(center_width > 0);
+ }
+ if (bottom_layer_.get()) {
+ if (center_width > 0) {
+ gfx::Transform transform;
+ transform.Translate(
+ left, size.height() - bottom_layer_->bounds().height());
+ ScaleWidth(center_size_in_pixels, bottom_layer_.get(), transform);
+ bottom_layer_->SetTransform(transform);
+ }
+ bottom_layer_->SetVisible(center_width > 0);
+ }
+ if (left_layer_.get()) {
+ if (center_height > 0) {
+ gfx::Transform transform;
+ transform.Translate(0, top);
+ ScaleHeight(center_size_in_pixels, left_layer_.get(), transform);
+ left_layer_->SetTransform(transform);
+ }
+ left_layer_->SetVisible(center_height > 0);
+ }
+ if (right_layer_.get()) {
+ if (center_height > 0) {
+ gfx::Transform transform;
+ transform.Translate(
+ size.width() - right_layer_->bounds().width(), top);
+ ScaleHeight(center_size_in_pixels, right_layer_.get(), transform);
+ right_layer_->SetTransform(transform);
+ }
+ right_layer_->SetVisible(center_height > 0);
+ }
+
+ if (top_left_layer_.get()) {
+ // No transformation needed; it should be at (0, 0) and unscaled.
+ top_left_painter_->SetClipRect(
+ LayerExceedsSize(top_left_layer_.get(), gfx::Size(left, top)) ?
+ gfx::Rect(gfx::Rect(0, 0, left, top)) :
+ gfx::Rect(),
+ top_left_layer_.get());
+ }
+ if (top_right_layer_.get()) {
+ gfx::Transform transform;
+ transform.Translate(size.width() - top_right_layer_->bounds().width(), 0.0);
+ top_right_layer_->SetTransform(transform);
+ top_right_painter_->SetClipRect(
+ LayerExceedsSize(top_right_layer_.get(), gfx::Size(right, top)) ?
+ gfx::Rect(top_right_layer_->bounds().width() - right, 0,
+ right, top) :
+ gfx::Rect(),
+ top_right_layer_.get());
+ }
+ if (bottom_left_layer_.get()) {
+ gfx::Transform transform;
+ transform.Translate(
+ 0.0, size.height() - bottom_left_layer_->bounds().height());
+ bottom_left_layer_->SetTransform(transform);
+ bottom_left_painter_->SetClipRect(
+ LayerExceedsSize(bottom_left_layer_.get(), gfx::Size(left, bottom)) ?
+ gfx::Rect(0, bottom_left_layer_->bounds().height() - bottom,
+ left, bottom) :
+ gfx::Rect(),
+ bottom_left_layer_.get());
+ }
+ if (bottom_right_layer_.get()) {
+ gfx::Transform transform;
+ transform.Translate(
+ size.width() - bottom_right_layer_->bounds().width(),
+ size.height() - bottom_right_layer_->bounds().height());
+ bottom_right_layer_->SetTransform(transform);
+ bottom_right_painter_->SetClipRect(
+ LayerExceedsSize(bottom_right_layer_.get(), gfx::Size(right, bottom)) ?
+ gfx::Rect(bottom_right_layer_->bounds().width() - right,
+ bottom_right_layer_->bounds().height() - bottom,
+ right, bottom) :
+ gfx::Rect(),
+ bottom_right_layer_.get());
+ }
+
+ if (center_layer_.get()) {
+ if (center_width > 0 && center_height > 0) {
+ gfx::Transform transform;
+ transform.Translate(left, top);
+ transform.Scale(center_width / center_layer_->bounds().width(),
+ center_height / center_layer_->bounds().height());
+ center_layer_->SetTransform(transform);
+ }
+ center_layer_->SetVisible(center_width > 0 && center_height > 0);
+ }
+}
+
+void ImageGrid::SetContentBounds(const gfx::Rect& content_bounds) {
+
+ SetSize(gfx::Size(
+ content_bounds.width() + left_image_width_ + right_image_width_,
+ content_bounds.height() + top_image_height_ +
+ bottom_image_height_));
+ layer_->SetBounds(
+ gfx::Rect(content_bounds.x() - left_image_width_,
+ content_bounds.y() - top_image_height_,
+ layer_->bounds().width(),
+ layer_->bounds().height()));
+}
+
+void ImageGrid::ImagePainter::SetClipRect(const gfx::Rect& clip_rect,
+ ui::Layer* layer) {
+ if (clip_rect != clip_rect_) {
+ clip_rect_ = clip_rect;
+ layer->SchedulePaint(layer->bounds());
+ }
+}
+
+void ImageGrid::ImagePainter::OnPaintLayer(gfx::Canvas* canvas) {
+ if (!clip_rect_.IsEmpty())
+ canvas->ClipRect(clip_rect_);
+ canvas->DrawImageInt(image_, 0, 0);
+}
+
+void ImageGrid::ImagePainter::OnDeviceScaleFactorChanged(
+ float device_scale_factor) {
+ // Redrawing will take care of scale factor change.
+}
+
+base::Closure ImageGrid::ImagePainter::PrepareForLayerBoundsChange() {
+ return base::Closure();
+}
+
+void ImageGrid::SetImage(const gfx::Image* image,
+ scoped_ptr<ui::Layer>* layer_ptr,
+ scoped_ptr<ImagePainter>* painter_ptr,
+ ImageType type) {
+ // Minimum width (for HORIZONTAL) or height (for VERTICAL) of the
+ // |image| so that layers are scaled property if the device scale
+ // factor is non integral.
+ const int kMinimumSize = 20;
+
+ // Clean out old layers and painters.
+ if (layer_ptr->get())
+ layer_->Remove(layer_ptr->get());
+ layer_ptr->reset();
+ painter_ptr->reset();
+
+ // If we're not using an image, we're done.
+ if (!image)
+ return;
+
+ gfx::ImageSkia image_skia = image->AsImageSkia();
+ switch (type) {
+ case HORIZONTAL:
+ if (image_skia.width() < kMinimumSize) {
+ image_skia = gfx::ImageSkiaOperations::CreateResizedImage(
+ image_skia,
+ skia::ImageOperations::RESIZE_GOOD,
+ gfx::Size(kMinimumSize, image_skia.height()));
+ }
+ break;
+ case VERTICAL:
+ if (image_skia.height() < kMinimumSize) {
+ image_skia = gfx::ImageSkiaOperations::CreateResizedImage(
+ image_skia,
+ skia::ImageOperations::RESIZE_GOOD,
+ gfx::Size(image_skia.width(), kMinimumSize));
+ }
+ break;
+ case NONE:
+ break;
+ }
+
+ // Set up the new layer and painter.
+ layer_ptr->reset(new ui::Layer(ui::LAYER_TEXTURED));
+
+ const gfx::Size size = image_skia.size();
+ layer_ptr->get()->SetBounds(gfx::Rect(0, 0, size.width(), size.height()));
+
+ painter_ptr->reset(new ImagePainter(image_skia));
+ layer_ptr->get()->set_delegate(painter_ptr->get());
+ layer_ptr->get()->SetFillsBoundsOpaquely(false);
+ layer_ptr->get()->SetVisible(true);
+ layer_->Add(layer_ptr->get());
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/image_grid.h b/chromium/ui/wm/core/image_grid.h
new file mode 100644
index 00000000000..987ee26c726
--- /dev/null
+++ b/chromium/ui/wm/core/image_grid.h
@@ -0,0 +1,221 @@
+// 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_WM_CORE_IMAGE_GRID_H_
+#define UI_WM_CORE_IMAGE_GRID_H_
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_delegate.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/size.h"
+#include "ui/wm/wm_export.h"
+
+namespace gfx {
+class Image;
+} // namespace gfx
+
+namespace wm {
+
+// An ImageGrid is a 3x3 array of ui::Layers, each containing an image.
+//
+// As the grid is resized, its images fill the requested space:
+// - corner images are not scaled
+// - top and bottom images are scaled horizontally
+// - left and right images are scaled vertically
+// - the center image is scaled in both directions
+//
+// If one of the non-center images is smaller than the largest images in its
+// row or column, it will be aligned with the outside of the grid. For
+// example, given 4x4 top-left and top-right images and a 1x2 top images:
+//
+// +--------+---------------------+--------+
+// | | top | |
+// | top- +---------------------+ top- +
+// | left | | right |
+// +----+---+ +---+----+
+// | | | |
+// ...
+//
+// This may seem odd at first, but it lets ImageGrid be used to draw shadows
+// with curved corners that extend inwards beyond a window's borders. In the
+// below example, the top-left corner image is overlaid on top of the window's
+// top-left corner:
+//
+// +---------+-----------------------
+// | ..xxx|XXXXXXXXXXXXXXXXXX
+// | .xXXXXX|XXXXXXXXXXXXXXXXXX_____
+// | .xXX | ^ window's top edge
+// | .xXX |
+// +---------+
+// | xXX|
+// | xXX|< window's left edge
+// | xXX|
+// ...
+//
+class WM_EXPORT ImageGrid {
+ public:
+ // Helper class for use by tests.
+ class WM_EXPORT TestAPI {
+ public:
+ TestAPI(ImageGrid* grid) : grid_(grid) {}
+
+ gfx::Rect top_left_clip_rect() const {
+ return grid_->top_left_painter_->clip_rect_;
+ }
+ gfx::Rect top_right_clip_rect() const {
+ return grid_->top_right_painter_->clip_rect_;
+ }
+ gfx::Rect bottom_left_clip_rect() const {
+ return grid_->bottom_left_painter_->clip_rect_;
+ }
+ gfx::Rect bottom_right_clip_rect() const {
+ return grid_->bottom_right_painter_->clip_rect_;
+ }
+
+ // Returns |layer|'s bounds after applying the layer's current transform.
+ gfx::RectF GetTransformedLayerBounds(const ui::Layer& layer);
+
+ private:
+ ImageGrid* grid_; // not owned
+
+ DISALLOW_COPY_AND_ASSIGN(TestAPI);
+ };
+
+ ImageGrid();
+ ~ImageGrid();
+
+ ui::Layer* layer() { return layer_.get(); }
+ int top_image_height() const { return top_image_height_; }
+ int bottom_image_height() const { return bottom_image_height_; }
+ int left_image_width() const { return left_image_width_; }
+ int right_image_width() const { return right_image_width_; }
+
+ // Visible to allow independent layer animations and for testing.
+ ui::Layer* top_left_layer() const { return top_left_layer_.get(); }
+ ui::Layer* top_layer() const { return top_layer_.get(); }
+ ui::Layer* top_right_layer() const { return top_right_layer_.get(); }
+ ui::Layer* left_layer() const { return left_layer_.get(); }
+ ui::Layer* center_layer() const { return center_layer_.get(); }
+ ui::Layer* right_layer() const { return right_layer_.get(); }
+ ui::Layer* bottom_left_layer() const { return bottom_left_layer_.get(); }
+ ui::Layer* bottom_layer() const { return bottom_layer_.get(); }
+ ui::Layer* bottom_right_layer() const { return bottom_right_layer_.get(); }
+
+ // Sets the grid to display the passed-in images (any of which can be NULL).
+ // Ownership of the images remains with the caller. May be called more than
+ // once to switch images.
+ void SetImages(const gfx::Image* top_left_image,
+ const gfx::Image* top_image,
+ const gfx::Image* top_right_image,
+ const gfx::Image* left_image,
+ const gfx::Image* center_image,
+ const gfx::Image* right_image,
+ const gfx::Image* bottom_left_image,
+ const gfx::Image* bottom_image,
+ const gfx::Image* bottom_right_image);
+
+ void SetSize(const gfx::Size& size);
+
+ // Sets the grid to a position and size such that the inner edges of the top,
+ // bottom, left and right images will be flush with |content_bounds_in_dip|.
+ void SetContentBounds(const gfx::Rect& content_bounds_in_dip);
+
+ private:
+ // Delegate responsible for painting a specific image on a layer.
+ class ImagePainter : public ui::LayerDelegate {
+ public:
+ ImagePainter(const gfx::ImageSkia& image) : image_(image) {}
+ virtual ~ImagePainter() {}
+
+ // Clips |layer| to |clip_rect|. Triggers a repaint if the clipping
+ // rectangle has changed. An empty rectangle disables clipping.
+ void SetClipRect(const gfx::Rect& clip_rect, ui::Layer* layer);
+
+ // ui::LayerDelegate implementation:
+ virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE;
+ virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE;
+ virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE;
+
+ private:
+ friend class TestAPI;
+
+ const gfx::ImageSkia image_;
+
+ gfx::Rect clip_rect_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImagePainter);
+ };
+
+ enum ImageType {
+ HORIZONTAL,
+ VERTICAL,
+ NONE,
+ };
+
+ // Sets |layer_ptr| and |painter_ptr| to display |image| and adds the
+ // passed-in layer to |layer_|. If image is NULL resets |layer_ptr| and
+ // |painter_ptr| and removes any existing layer from |layer_|.
+ // If |type| is either HORIZONTAL or VERTICAL, it may resize the image to
+ // guarantee that it has minimum size in order for scaling work properly
+ // with fractional device scale factors. crbug.com/376519.
+ void SetImage(const gfx::Image* image,
+ scoped_ptr<ui::Layer>* layer_ptr,
+ scoped_ptr<ImagePainter>* painter_ptr,
+ ImageType type);
+
+ // Layer that contains all of the image layers.
+ scoped_ptr<ui::Layer> layer_;
+
+ // The grid's dimensions.
+ gfx::Size size_;
+
+ // Heights and widths of the images displayed by |top_layer_|,
+ // |bottom_layer_|, |left_layer_|, and |right_layer_|.
+ int top_image_height_;
+ int bottom_image_height_;
+ int left_image_width_;
+ int right_image_width_;
+
+ // Heights of the tallest images in the top and bottom rows and the widest
+ // images in the left and right columns. Note that we may have less actual
+ // space than this available if the images are large and |size_| is small.
+ int base_top_row_height_;
+ int base_bottom_row_height_;
+ int base_left_column_width_;
+ int base_right_column_width_;
+
+ // Layers used to display the various images. Children of |layer_|.
+ // Positions for which no images were supplied are NULL.
+ scoped_ptr<ui::Layer> top_left_layer_;
+ scoped_ptr<ui::Layer> top_layer_;
+ scoped_ptr<ui::Layer> top_right_layer_;
+ scoped_ptr<ui::Layer> left_layer_;
+ scoped_ptr<ui::Layer> center_layer_;
+ scoped_ptr<ui::Layer> right_layer_;
+ scoped_ptr<ui::Layer> bottom_left_layer_;
+ scoped_ptr<ui::Layer> bottom_layer_;
+ scoped_ptr<ui::Layer> bottom_right_layer_;
+
+ // Delegates responsible for painting the above layers.
+ // Positions for which no images were supplied are NULL.
+ scoped_ptr<ImagePainter> top_left_painter_;
+ scoped_ptr<ImagePainter> top_painter_;
+ scoped_ptr<ImagePainter> top_right_painter_;
+ scoped_ptr<ImagePainter> left_painter_;
+ scoped_ptr<ImagePainter> center_painter_;
+ scoped_ptr<ImagePainter> right_painter_;
+ scoped_ptr<ImagePainter> bottom_left_painter_;
+ scoped_ptr<ImagePainter> bottom_painter_;
+ scoped_ptr<ImagePainter> bottom_right_painter_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImageGrid);
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_IMAGE_GRID_H_
diff --git a/chromium/ui/wm/core/image_grid_unittest.cc b/chromium/ui/wm/core/image_grid_unittest.cc
new file mode 100644
index 00000000000..4b413e44af0
--- /dev/null
+++ b/chromium/ui/wm/core/image_grid_unittest.cc
@@ -0,0 +1,340 @@
+// 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/wm/core/image_grid.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/aura/test/aura_test_base.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/image/image_skia.h"
+
+namespace wm {
+
+namespace {
+
+// Creates a gfx::Image with the requested dimensions.
+gfx::Image* CreateImage(const gfx::Size& size) {
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height());
+ return new gfx::Image(gfx::ImageSkia::CreateFrom1xBitmap(bitmap));
+}
+
+} // namespace
+
+typedef aura::test::AuraTestBase ImageGridTest;
+
+// Test that an ImageGrid's layers are transformed correctly when SetSize() is
+// called.
+TEST_F(ImageGridTest, Basic) {
+ // Size of the images around the grid's border.
+ const int kBorder = 2;
+
+ scoped_ptr<gfx::Image> image_1x1(CreateImage(gfx::Size(1, 1)));
+ scoped_ptr<gfx::Image> image_1xB(CreateImage(gfx::Size(1, kBorder)));
+ scoped_ptr<gfx::Image> image_Bx1(CreateImage(gfx::Size(kBorder, 1)));
+ scoped_ptr<gfx::Image> image_BxB(CreateImage(gfx::Size(kBorder, kBorder)));
+
+ ImageGrid grid;
+ grid.SetImages(image_BxB.get(), image_1xB.get(), image_BxB.get(),
+ image_Bx1.get(), image_1x1.get(), image_Bx1.get(),
+ image_BxB.get(), image_1xB.get(), image_BxB.get());
+
+ ImageGrid::TestAPI test_api(&grid);
+ ASSERT_TRUE(grid.top_left_layer() != NULL);
+ ASSERT_TRUE(grid.top_layer() != NULL);
+ ASSERT_TRUE(grid.top_right_layer() != NULL);
+ ASSERT_TRUE(grid.left_layer() != NULL);
+ ASSERT_TRUE(grid.center_layer() != NULL);
+ ASSERT_TRUE(grid.right_layer() != NULL);
+ ASSERT_TRUE(grid.bottom_left_layer() != NULL);
+ ASSERT_TRUE(grid.bottom_layer() != NULL);
+ ASSERT_TRUE(grid.bottom_right_layer() != NULL);
+
+ const gfx::Size size(20, 30);
+ grid.SetSize(size);
+
+ // The top-left layer should be flush with the top-left corner and unscaled.
+ EXPECT_EQ(gfx::RectF(0, 0, kBorder, kBorder).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *grid.top_left_layer()).ToString());
+
+ // The top layer should be flush with the top edge and stretched horizontally
+ // between the two top corners.
+ EXPECT_EQ(gfx::RectF(
+ kBorder, 0, size.width() - 2 * kBorder, kBorder).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *grid.top_layer()).ToString());
+
+ // The top-right layer should be flush with the top-right corner and unscaled.
+ EXPECT_EQ(gfx::RectF(size.width() - kBorder, 0, kBorder, kBorder).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *grid.top_right_layer()).ToString());
+
+ // The left layer should be flush with the left edge and stretched vertically
+ // between the two left corners.
+ EXPECT_EQ(gfx::RectF(
+ 0, kBorder, kBorder, size.height() - 2 * kBorder).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *grid.left_layer()).ToString());
+
+ // The center layer should fill the space in the middle of the grid.
+ EXPECT_EQ(gfx::RectF(
+ kBorder, kBorder, size.width() - 2 * kBorder,
+ size.height() - 2 * kBorder).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *grid.center_layer()).ToString());
+
+ // The right layer should be flush with the right edge and stretched
+ // vertically between the two right corners.
+ EXPECT_EQ(gfx::RectF(
+ size.width() - kBorder, kBorder,
+ kBorder, size.height() - 2 * kBorder).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *grid.right_layer()).ToString());
+
+ // The bottom-left layer should be flush with the bottom-left corner and
+ // unscaled.
+ EXPECT_EQ(gfx::RectF(0, size.height() - kBorder, kBorder, kBorder).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *grid.bottom_left_layer()).ToString());
+
+ // The bottom layer should be flush with the bottom edge and stretched
+ // horizontally between the two bottom corners.
+ EXPECT_EQ(gfx::RectF(
+ kBorder, size.height() - kBorder,
+ size.width() - 2 * kBorder, kBorder).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *grid.bottom_layer()).ToString());
+
+ // The bottom-right layer should be flush with the bottom-right corner and
+ // unscaled.
+ EXPECT_EQ(gfx::RectF(
+ size.width() - kBorder, size.height() - kBorder,
+ kBorder, kBorder).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *grid.bottom_right_layer()).ToString());
+}
+
+// Test that an ImageGrid's layers are transformed correctly when
+// SetContentBounds() is called.
+TEST_F(ImageGridTest, SetContentBounds) {
+ // Size of the images around the grid's border.
+ const int kBorder = 2;
+
+ scoped_ptr<gfx::Image> image_1x1(CreateImage(gfx::Size(1, 1)));
+ scoped_ptr<gfx::Image> image_1xB(CreateImage(gfx::Size(1, kBorder)));
+ scoped_ptr<gfx::Image> image_Bx1(CreateImage(gfx::Size(kBorder, 1)));
+ scoped_ptr<gfx::Image> image_BxB(CreateImage(gfx::Size(kBorder, kBorder)));
+
+ ImageGrid grid;
+ grid.SetImages(image_BxB.get(), image_1xB.get(), image_BxB.get(),
+ image_Bx1.get(), image_1x1.get(), image_Bx1.get(),
+ image_BxB.get(), image_1xB.get(), image_BxB.get());
+
+ ImageGrid::TestAPI test_api(&grid);
+
+ const gfx::Point origin(5, 10);
+ const gfx::Size size(20, 30);
+ grid.SetContentBounds(gfx::Rect(origin, size));
+
+ // The master layer is positioned above the top-left corner of the content
+ // bounds and has height/width that contain the grid and bounds.
+ EXPECT_EQ(gfx::RectF(origin.x() - kBorder,
+ origin.y() - kBorder,
+ size.width() + 2 * kBorder,
+ size.height() + 2 * kBorder).ToString(),
+ test_api.GetTransformedLayerBounds(*grid.layer()).ToString());
+}
+
+// Check that we don't crash if only a single image is supplied.
+TEST_F(ImageGridTest, SingleImage) {
+ const int kBorder = 1;
+ scoped_ptr<gfx::Image> image(CreateImage(gfx::Size(kBorder, kBorder)));
+
+ ImageGrid grid;
+ grid.SetImages(NULL, image.get(), NULL,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL);
+
+ ImageGrid::TestAPI test_api(&grid);
+ EXPECT_TRUE(grid.top_left_layer() == NULL);
+ ASSERT_TRUE(grid.top_layer() != NULL);
+ EXPECT_TRUE(grid.top_right_layer() == NULL);
+ EXPECT_TRUE(grid.left_layer() == NULL);
+ EXPECT_TRUE(grid.center_layer() == NULL);
+ EXPECT_TRUE(grid.right_layer() == NULL);
+ EXPECT_TRUE(grid.bottom_left_layer() == NULL);
+ EXPECT_TRUE(grid.bottom_layer() == NULL);
+ EXPECT_TRUE(grid.bottom_right_layer() == NULL);
+
+ const gfx::Size kSize(10, 10);
+ grid.SetSize(kSize);
+
+ // The top layer should be scaled horizontally across the entire width, but it
+ // shouldn't be scaled vertically.
+ EXPECT_EQ(gfx::RectF(0, 0, kSize.width(), kBorder).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *grid.top_layer()).ToString());
+}
+
+// Check that we don't crash when we reset existing images to NULL and
+// reset NULL images to new ones.
+TEST_F(ImageGridTest, ResetImages) {
+ const int kBorder = 1;
+ scoped_ptr<gfx::Image> image(CreateImage(gfx::Size(kBorder, kBorder)));
+
+ ImageGrid grid;
+ grid.SetImages(NULL, image.get(), NULL,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL);
+
+ // Only the top edge has a layer.
+ ImageGrid::TestAPI test_api(&grid);
+ ASSERT_TRUE(grid.top_left_layer() == NULL);
+ ASSERT_FALSE(grid.top_layer() == NULL);
+ ASSERT_TRUE(grid.top_right_layer() == NULL);
+ ASSERT_TRUE(grid.left_layer() == NULL);
+ ASSERT_TRUE(grid.center_layer() == NULL);
+ ASSERT_TRUE(grid.right_layer() == NULL);
+ ASSERT_TRUE(grid.bottom_left_layer() == NULL);
+ ASSERT_TRUE(grid.bottom_layer() == NULL);
+ ASSERT_TRUE(grid.bottom_right_layer() == NULL);
+
+ grid.SetImages(NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ NULL, image.get(), NULL);
+
+ // Now only the bottom edge has a layer.
+ ASSERT_TRUE(grid.top_left_layer() == NULL);
+ ASSERT_TRUE(grid.top_layer() == NULL);
+ ASSERT_TRUE(grid.top_right_layer() == NULL);
+ ASSERT_TRUE(grid.left_layer() == NULL);
+ ASSERT_TRUE(grid.center_layer() == NULL);
+ ASSERT_TRUE(grid.right_layer() == NULL);
+ ASSERT_TRUE(grid.bottom_left_layer() == NULL);
+ ASSERT_FALSE(grid.bottom_layer() == NULL);
+ ASSERT_TRUE(grid.bottom_right_layer() == NULL);
+}
+
+// Test that side (top, left, right, bottom) layers that are narrower than their
+// adjacent corner layers stay pinned to the outside edges instead of getting
+// moved inwards or scaled. This exercises the scenario used for shadows.
+TEST_F(ImageGridTest, SmallerSides) {
+ const int kCorner = 2;
+ const int kEdge = 1;
+
+ scoped_ptr<gfx::Image> top_left_image(
+ CreateImage(gfx::Size(kCorner, kCorner)));
+ scoped_ptr<gfx::Image> top_image(CreateImage(gfx::Size(kEdge, kEdge)));
+ scoped_ptr<gfx::Image> top_right_image(
+ CreateImage(gfx::Size(kCorner, kCorner)));
+ scoped_ptr<gfx::Image> left_image(CreateImage(gfx::Size(kEdge, kEdge)));
+ scoped_ptr<gfx::Image> right_image(CreateImage(gfx::Size(kEdge, kEdge)));
+
+ ImageGrid grid;
+ grid.SetImages(top_left_image.get(), top_image.get(), top_right_image.get(),
+ left_image.get(), NULL, right_image.get(),
+ NULL, NULL, NULL);
+ ImageGrid::TestAPI test_api(&grid);
+
+ const gfx::Size kSize(20, 30);
+ grid.SetSize(kSize);
+
+ // The top layer should be flush with the top edge and stretched horizontally
+ // between the two top corners.
+ EXPECT_EQ(gfx::RectF(
+ kCorner, 0, kSize.width() - 2 * kCorner, kEdge).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *grid.top_layer()).ToString());
+
+ // The left layer should be flush with the left edge and stretched vertically
+ // between the top left corner and the bottom.
+ EXPECT_EQ(gfx::RectF(
+ 0, kCorner, kEdge, kSize.height() - kCorner).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *grid.left_layer()).ToString());
+
+ // The right layer should be flush with the right edge and stretched
+ // vertically between the top right corner and the bottom.
+ EXPECT_EQ(gfx::RectF(
+ kSize.width() - kEdge, kCorner,
+ kEdge, kSize.height() - kCorner).ToString(),
+ test_api.GetTransformedLayerBounds(
+ *grid.right_layer()).ToString());
+}
+
+// Test that we hide or clip layers as needed when the grid is assigned a small
+// size.
+TEST_F(ImageGridTest, TooSmall) {
+ const int kCorner = 5;
+ const int kCenter = 3;
+ const int kEdge = 3;
+
+ scoped_ptr<gfx::Image> top_left_image(
+ CreateImage(gfx::Size(kCorner, kCorner)));
+ scoped_ptr<gfx::Image> top_image(CreateImage(gfx::Size(kEdge, kEdge)));
+ scoped_ptr<gfx::Image> top_right_image(
+ CreateImage(gfx::Size(kCorner, kCorner)));
+ scoped_ptr<gfx::Image> left_image(CreateImage(gfx::Size(kEdge, kEdge)));
+ scoped_ptr<gfx::Image> center_image(CreateImage(gfx::Size(kCenter, kCenter)));
+ scoped_ptr<gfx::Image> right_image(CreateImage(gfx::Size(kEdge, kEdge)));
+ scoped_ptr<gfx::Image> bottom_left_image(
+ CreateImage(gfx::Size(kCorner, kCorner)));
+ scoped_ptr<gfx::Image> bottom_image(CreateImage(gfx::Size(kEdge, kEdge)));
+ scoped_ptr<gfx::Image> bottom_right_image(
+ CreateImage(gfx::Size(kCorner, kCorner)));
+
+ ImageGrid grid;
+ grid.SetImages(
+ top_left_image.get(), top_image.get(), top_right_image.get(),
+ left_image.get(), center_image.get(), right_image.get(),
+ bottom_left_image.get(), bottom_image.get(), bottom_right_image.get());
+ ImageGrid::TestAPI test_api(&grid);
+
+ // Set a size that's smaller than the combined (unscaled) corner images.
+ const gfx::Size kSmallSize(kCorner + kCorner - 3, kCorner + kCorner - 5);
+ grid.SetSize(kSmallSize);
+
+ // The scalable images around the sides and in the center should be hidden.
+ EXPECT_FALSE(grid.top_layer()->visible());
+ EXPECT_FALSE(grid.bottom_layer()->visible());
+ EXPECT_FALSE(grid.left_layer()->visible());
+ EXPECT_FALSE(grid.right_layer()->visible());
+ EXPECT_FALSE(grid.center_layer()->visible());
+
+ // The corner images' clip rects should sum to the expected size.
+ EXPECT_EQ(kSmallSize.width(),
+ test_api.top_left_clip_rect().width() +
+ test_api.top_right_clip_rect().width());
+ EXPECT_EQ(kSmallSize.width(),
+ test_api.bottom_left_clip_rect().width() +
+ test_api.bottom_right_clip_rect().width());
+ EXPECT_EQ(kSmallSize.height(),
+ test_api.top_left_clip_rect().height() +
+ test_api.bottom_left_clip_rect().height());
+ EXPECT_EQ(kSmallSize.height(),
+ test_api.top_right_clip_rect().height() +
+ test_api.bottom_right_clip_rect().height());
+
+ // Resize the grid to be large enough to show all images.
+ const gfx::Size kLargeSize(kCorner + kCorner + kCenter,
+ kCorner + kCorner + kCenter);
+ grid.SetSize(kLargeSize);
+
+ // The scalable images should be visible now.
+ EXPECT_TRUE(grid.top_layer()->visible());
+ EXPECT_TRUE(grid.bottom_layer()->visible());
+ EXPECT_TRUE(grid.left_layer()->visible());
+ EXPECT_TRUE(grid.right_layer()->visible());
+ EXPECT_TRUE(grid.center_layer()->visible());
+
+ // We shouldn't be clipping the corner images anymore.
+ EXPECT_TRUE(test_api.top_left_clip_rect().IsEmpty());
+ EXPECT_TRUE(test_api.top_right_clip_rect().IsEmpty());
+ EXPECT_TRUE(test_api.bottom_left_clip_rect().IsEmpty());
+ EXPECT_TRUE(test_api.bottom_right_clip_rect().IsEmpty());
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/input_method_event_filter.cc b/chromium/ui/wm/core/input_method_event_filter.cc
new file mode 100644
index 00000000000..3488e4adb8c
--- /dev/null
+++ b/chromium/ui/wm/core/input_method_event_filter.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/wm/core/input_method_event_filter.h"
+
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/base/ime/input_method.h"
+#include "ui/base/ime/input_method_factory.h"
+#include "ui/base/ime/text_input_client.h"
+#include "ui/events/event.h"
+#include "ui/events/event_processor.h"
+
+namespace wm {
+
+////////////////////////////////////////////////////////////////////////////////
+// InputMethodEventFilter, public:
+
+InputMethodEventFilter::InputMethodEventFilter(gfx::AcceleratedWidget widget)
+ : input_method_(ui::CreateInputMethod(this, widget)) {
+ // TODO(yusukes): Check if the root window is currently focused and pass the
+ // result to Init().
+ input_method_->Init(true);
+}
+
+InputMethodEventFilter::~InputMethodEventFilter() {
+}
+
+void InputMethodEventFilter::SetInputMethodPropertyInRootWindow(
+ aura::Window* root_window) {
+ root_window->SetProperty(aura::client::kRootWindowInputMethodKey,
+ input_method_.get());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// InputMethodEventFilter, EventFilter implementation:
+
+void InputMethodEventFilter::OnKeyEvent(ui::KeyEvent* event) {
+ // We're processing key events as follows (details are simplified).
+ //
+ // At the beginning, key events have a ET_KEY_{PRESSED,RELEASED} event type,
+ // and they're passed from step 1 through step 3.
+ // 1. EventProcessor::OnEventFromSource()
+ // 2. InputMethodEventFilter::OnKeyEvent()
+ // 3. InputMethod::DispatchKeyEvent()
+ // where InputMethod may call DispatchKeyEventPostIME() if IME didn't consume
+ // the key event. Otherwise, step 4 through step 6 are skipped and we fall
+ // down to step 7 directly.
+ // 4. InputMethodEventFilter::DispatchKeyEventPostIME()
+ // where the key event is marked as TRANSLATED and the event type becomes
+ // ET_TRANSLATED_KEY_{PRESS,RELEASE}. Then, we dispatch the event again from
+ // the beginning.
+ // 5. EventProcessor::OnEventFromSource() [second time]
+ // 6. InputMethodEventFilter::OnKeyEvent() [second time]
+ // where we know that the event was already processed once by IME and
+ // re-dispatched, we don't pass the event to IME again. Instead we unmark the
+ // event as not translated (as same as the original state), and let the event
+ // dispatcher continue to dispatch the event to the rest event handlers.
+ // 7. EventHandler::OnKeyEvent()
+ if (event->IsTranslated()) {
+ // The |event| was already processed by IME, so we don't pass the event to
+ // IME again. Just let the event dispatcher continue to dispatch the event.
+ event->SetTranslated(false);
+ } else {
+ if (input_method_->DispatchKeyEvent(*event))
+ event->StopPropagation();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// InputMethodEventFilter, ui::InputMethodDelegate implementation:
+
+bool InputMethodEventFilter::DispatchKeyEventPostIME(
+ const ui::KeyEvent& event) {
+#if defined(OS_WIN)
+ DCHECK(!event.HasNativeEvent() || event.native_event().message != WM_CHAR);
+#endif
+ // Since the underlying IME didn't consume the key event, we're going to
+ // dispatch the event again from the beginning of the tree of event targets.
+ // This time we have to skip dispatching the event to the IME, we mark the
+ // event as TRANSLATED so we can distinguish this event as a second time
+ // dispatched event.
+ // For the target where to dispatch the event, always tries the current
+ // focused text input client's attached window. And fallback to the target
+ // carried by event.
+ aura::Window* target_window = NULL;
+ ui::TextInputClient* input = input_method_->GetTextInputClient();
+ if (input)
+ target_window = input->GetAttachedWindow();
+ if (!target_window)
+ target_window = static_cast<aura::Window*>(event.target());
+ if (!target_window)
+ return false;
+ ui::EventProcessor* target_dispatcher =
+ target_window->GetRootWindow()->GetHost()->event_processor();
+ ui::KeyEvent aura_event(event);
+ aura_event.SetTranslated(true);
+ ui::EventDispatchDetails details =
+ target_dispatcher->OnEventFromSource(&aura_event);
+ CHECK(!details.dispatcher_destroyed);
+ return aura_event.handled();
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/input_method_event_filter.h b/chromium/ui/wm/core/input_method_event_filter.h
new file mode 100644
index 00000000000..af823089b13
--- /dev/null
+++ b/chromium/ui/wm/core/input_method_event_filter.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_WM_CORE_INPUT_METHOD_EVENT_FILTER_H_
+#define UI_WM_CORE_INPUT_METHOD_EVENT_FILTER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/base/ime/input_method_delegate.h"
+#include "ui/events/event_handler.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/wm/wm_export.h"
+
+namespace ui {
+class EventProcessor;
+class InputMethod;
+}
+
+namespace wm {
+
+// An event filter that forwards a KeyEvent to a system IME, and dispatches a
+// TranslatedKeyEvent to the root window as needed.
+class WM_EXPORT InputMethodEventFilter
+ : public ui::EventHandler,
+ public ui::internal::InputMethodDelegate {
+ public:
+ explicit InputMethodEventFilter(gfx::AcceleratedWidget widget);
+ virtual ~InputMethodEventFilter();
+
+ void SetInputMethodPropertyInRootWindow(aura::Window* root_window);
+
+ ui::InputMethod* input_method() const { return input_method_.get(); }
+
+ private:
+ // Overridden from ui::EventHandler:
+ virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
+
+ // Overridden from ui::internal::InputMethodDelegate:
+ virtual bool DispatchKeyEventPostIME(const ui::KeyEvent& event) OVERRIDE;
+
+ scoped_ptr<ui::InputMethod> input_method_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputMethodEventFilter);
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_INPUT_METHOD_EVENT_FILTER_H_
diff --git a/chromium/ui/wm/core/input_method_event_filter_unittest.cc b/chromium/ui/wm/core/input_method_event_filter_unittest.cc
new file mode 100644
index 00000000000..50c1fbc0c55
--- /dev/null
+++ b/chromium/ui/wm/core/input_method_event_filter_unittest.cc
@@ -0,0 +1,143 @@
+// 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/wm/core/input_method_event_filter.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/test/aura_test_base.h"
+#include "ui/aura/test/event_generator.h"
+#include "ui/aura/test/test_windows.h"
+#include "ui/aura/window_event_dispatcher.h"
+#include "ui/base/ime/dummy_text_input_client.h"
+#include "ui/base/ime/input_method.h"
+#include "ui/base/ime/text_input_focus_manager.h"
+#include "ui/base/ui_base_switches_util.h"
+#include "ui/events/test/test_event_handler.h"
+#include "ui/wm/core/compound_event_filter.h"
+#include "ui/wm/core/default_activation_client.h"
+#include "ui/wm/public/activation_client.h"
+
+#if !defined(OS_WIN) && !defined(USE_X11)
+// On platforms except Windows and X11, aura::test::EventGenerator::PressKey
+// generates a key event without native_event(), which is not supported by
+// ui::MockInputMethod.
+#define TestInputMethodKeyEventPropagation \
+DISABLED_TestInputMethodKeyEventPropagation
+#endif
+
+namespace wm {
+
+class TestTextInputClient : public ui::DummyTextInputClient {
+ public:
+ explicit TestTextInputClient(aura::Window* window) : window_(window) {}
+
+ virtual aura::Window* GetAttachedWindow() const OVERRIDE { return window_; }
+
+ private:
+ aura::Window* window_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestTextInputClient);
+};
+
+class InputMethodEventFilterTest : public aura::test::AuraTestBase {
+ public:
+ InputMethodEventFilterTest() {}
+ virtual ~InputMethodEventFilterTest() {}
+
+ // testing::Test overrides:
+ virtual void SetUp() OVERRIDE {
+ aura::test::AuraTestBase::SetUp();
+
+ root_window()->AddPreTargetHandler(&root_filter_);
+ input_method_event_filter_.reset(
+ new InputMethodEventFilter(host()->GetAcceleratedWidget()));
+ input_method_event_filter_->SetInputMethodPropertyInRootWindow(
+ root_window());
+ root_filter_.AddHandler(input_method_event_filter_.get());
+ root_filter_.AddHandler(&test_filter_);
+
+ test_window_.reset(aura::test::CreateTestWindowWithDelegate(
+ &test_window_delegate_, -1, gfx::Rect(), root_window()));
+ test_input_client_.reset(new TestTextInputClient(test_window_.get()));
+
+ input_method_event_filter_->input_method()->SetFocusedTextInputClient(
+ test_input_client_.get());
+ }
+
+ virtual void TearDown() OVERRIDE {
+ test_window_.reset();
+ root_filter_.RemoveHandler(&test_filter_);
+ root_filter_.RemoveHandler(input_method_event_filter_.get());
+ root_window()->RemovePreTargetHandler(&root_filter_);
+
+ input_method_event_filter_.reset();
+ test_input_client_.reset();
+ aura::test::AuraTestBase::TearDown();
+ }
+
+ protected:
+ CompoundEventFilter root_filter_;
+ ui::test::TestEventHandler test_filter_;
+ scoped_ptr<InputMethodEventFilter> input_method_event_filter_;
+ aura::test::TestWindowDelegate test_window_delegate_;
+ scoped_ptr<aura::Window> test_window_;
+ scoped_ptr<TestTextInputClient> test_input_client_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(InputMethodEventFilterTest);
+};
+
+TEST_F(InputMethodEventFilterTest, TestInputMethodProperty) {
+ // Tests if InputMethodEventFilter adds a window property on its
+ // construction.
+ EXPECT_TRUE(root_window()->GetProperty(
+ aura::client::kRootWindowInputMethodKey));
+}
+
+// Tests if InputMethodEventFilter dispatches a ui::ET_TRANSLATED_KEY_* event to
+// the root window.
+TEST_F(InputMethodEventFilterTest, TestInputMethodKeyEventPropagation) {
+ // Send a fake key event to the root window. InputMethodEventFilter, which is
+ // automatically set up by AshTestBase, consumes it and sends a new
+ // ui::ET_TRANSLATED_KEY_* event to the root window, which will be consumed by
+ // the test event filter.
+ aura::test::EventGenerator generator(root_window());
+ EXPECT_EQ(0, test_filter_.num_key_events());
+ generator.PressKey(ui::VKEY_SPACE, 0);
+ EXPECT_EQ(1, test_filter_.num_key_events());
+ generator.ReleaseKey(ui::VKEY_SPACE, 0);
+ EXPECT_EQ(2, test_filter_.num_key_events());
+}
+
+TEST_F(InputMethodEventFilterTest, TestEventDispatching) {
+ ui::KeyEvent evt(ui::ET_KEY_PRESSED,
+ ui::VKEY_PROCESSKEY,
+ ui::EF_IME_FABRICATED_KEY,
+ false);
+ // Calls DispatchKeyEventPostIME() without a focused text input client.
+ if (switches::IsTextInputFocusManagerEnabled())
+ ui::TextInputFocusManager::GetInstance()->FocusTextInputClient(NULL);
+ else
+ input_method_event_filter_->input_method()->SetFocusedTextInputClient(NULL);
+ input_method_event_filter_->input_method()->DispatchKeyEvent(evt);
+ // Verifies 0 key event happened because InputMethodEventFilter::
+ // DispatchKeyEventPostIME() returns false.
+ EXPECT_EQ(0, test_filter_.num_key_events());
+
+ // Calls DispatchKeyEventPostIME() with a focused text input client.
+ if (switches::IsTextInputFocusManagerEnabled()) {
+ ui::TextInputFocusManager::GetInstance()->FocusTextInputClient(
+ test_input_client_.get());
+ } else {
+ input_method_event_filter_->input_method()->SetFocusedTextInputClient(
+ test_input_client_.get());
+ }
+ input_method_event_filter_->input_method()->DispatchKeyEvent(evt);
+ // Verifies 1 key event happened because InputMethodEventFilter::
+ // DispatchKeyEventPostIME() returns true.
+ EXPECT_EQ(1, test_filter_.num_key_events());
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/masked_window_targeter.cc b/chromium/ui/wm/core/masked_window_targeter.cc
new file mode 100644
index 00000000000..5cbdb1993b1
--- /dev/null
+++ b/chromium/ui/wm/core/masked_window_targeter.cc
@@ -0,0 +1,43 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/wm/core/masked_window_targeter.h"
+
+#include "ui/aura/window.h"
+#include "ui/gfx/path.h"
+
+namespace wm {
+
+MaskedWindowTargeter::MaskedWindowTargeter(aura::Window* masked_window)
+ : masked_window_(masked_window) {
+}
+
+MaskedWindowTargeter::~MaskedWindowTargeter() {}
+
+bool MaskedWindowTargeter::EventLocationInsideBounds(
+ ui::EventTarget* target,
+ const ui::LocatedEvent& event) const {
+ aura::Window* window = static_cast<aura::Window*>(target);
+ if (window == masked_window_) {
+ gfx::Path mask;
+ if (!GetHitTestMask(window, &mask))
+ return WindowTargeter::EventLocationInsideBounds(window, event);
+
+ gfx::Size size = window->bounds().size();
+ SkRegion clip_region;
+ clip_region.setRect(0, 0, size.width(), size.height());
+
+ gfx::Point point = event.location();
+ if (window->parent())
+ aura::Window::ConvertPointToTarget(window->parent(), window, &point);
+
+ SkRegion mask_region;
+ return mask_region.setPath(mask, clip_region) &&
+ mask_region.contains(point.x(), point.y());
+ }
+
+ return WindowTargeter::EventLocationInsideBounds(window, event);
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/masked_window_targeter.h b/chromium/ui/wm/core/masked_window_targeter.h
new file mode 100644
index 00000000000..61bb3b1eeb4
--- /dev/null
+++ b/chromium/ui/wm/core/masked_window_targeter.h
@@ -0,0 +1,40 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_WM_CORE_MASKED_WINDOW_TARGETER_H_
+#define UI_WM_CORE_MASKED_WINDOW_TARGETER_H_
+
+#include "ui/aura/window_targeter.h"
+#include "ui/wm/wm_export.h"
+
+namespace gfx {
+class Path;
+}
+
+namespace wm {
+
+class WM_EXPORT MaskedWindowTargeter : public aura::WindowTargeter {
+ public:
+ explicit MaskedWindowTargeter(aura::Window* masked_window);
+ virtual ~MaskedWindowTargeter();
+
+ protected:
+ // Sets the hit-test mask for |window| in |mask| (in |window|'s local
+ // coordinate system). Returns whether a valid mask has been set in |mask|.
+ virtual bool GetHitTestMask(aura::Window* window, gfx::Path* mask) const = 0;
+
+ // ui::EventTargeter:
+ virtual bool EventLocationInsideBounds(
+ ui::EventTarget* target,
+ const ui::LocatedEvent& event) const OVERRIDE;
+
+ private:
+ aura::Window* masked_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(MaskedWindowTargeter);
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_MASKED_WINDOW_TARGETER_H_
diff --git a/chromium/ui/wm/core/native_cursor_manager.h b/chromium/ui/wm/core/native_cursor_manager.h
new file mode 100644
index 00000000000..de16ef8ce2d
--- /dev/null
+++ b/chromium/ui/wm/core/native_cursor_manager.h
@@ -0,0 +1,61 @@
+// 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_WM_CORE_NATIVE_CURSOR_MANAGER_H_
+#define UI_WM_CORE_NATIVE_CURSOR_MANAGER_H_
+
+#include "base/strings/string16.h"
+#include "ui/base/cursor/cursor.h"
+#include "ui/wm/core/native_cursor_manager_delegate.h"
+#include "ui/wm/wm_export.h"
+
+namespace gfx {
+class Display;
+}
+
+namespace wm {
+
+// Interface where platforms such as Ash or Desktop aura are notified of
+// requested changes to cursor state. When requested, implementer should tell
+// the CursorManager of any actual state changes performed through the
+// delegate.
+class WM_EXPORT NativeCursorManager {
+ public:
+ virtual ~NativeCursorManager() {}
+
+ // A request to set the screen DPI. Can cause changes in the current cursor.
+ virtual void SetDisplay(
+ const gfx::Display& display,
+ NativeCursorManagerDelegate* delegate) = 0;
+
+ // A request to set the cursor to |cursor|. At minimum, implementer should
+ // call NativeCursorManagerDelegate::CommitCursor() with whatever cursor is
+ // actually used.
+ virtual void SetCursor(
+ gfx::NativeCursor cursor,
+ NativeCursorManagerDelegate* delegate) = 0;
+
+ // A request to set the visibility of the cursor. At minimum, implementer
+ // should call NativeCursorManagerDelegate::CommitVisibility() with whatever
+ // the visibility is.
+ virtual void SetVisibility(
+ bool visible,
+ NativeCursorManagerDelegate* delegate) = 0;
+
+ // A request to set the cursor set.
+ virtual void SetCursorSet(
+ ui::CursorSetType cursor_set,
+ NativeCursorManagerDelegate* delegate) = 0;
+
+ // A request to set whether mouse events are disabled. At minimum,
+ // implementer should call NativeCursorManagerDelegate::
+ // CommitMouseEventsEnabled() with whether mouse events are actually enabled.
+ virtual void SetMouseEventsEnabled(
+ bool enabled,
+ NativeCursorManagerDelegate* delegate) = 0;
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_NATIVE_CURSOR_MANAGER_H_
diff --git a/chromium/ui/wm/core/native_cursor_manager_delegate.h b/chromium/ui/wm/core/native_cursor_manager_delegate.h
new file mode 100644
index 00000000000..1393b2cd4b4
--- /dev/null
+++ b/chromium/ui/wm/core/native_cursor_manager_delegate.h
@@ -0,0 +1,33 @@
+// 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_WM_CORE_NATIVE_CURSOR_MANAGER_DELEGATE_H_
+#define UI_WM_CORE_NATIVE_CURSOR_MANAGER_DELEGATE_H_
+
+#include "ui/base/cursor/cursor.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/wm/wm_export.h"
+
+namespace wm {
+
+// The non-public interface that CursorManager exposes to its users. This
+// gives accessors to all the current state, and mutators to all the current
+// state.
+class WM_EXPORT NativeCursorManagerDelegate {
+ public:
+ virtual ~NativeCursorManagerDelegate() {}
+
+ // TODO(tdanderson): Possibly remove this interface.
+ virtual gfx::NativeCursor GetCursor() const = 0;
+ virtual bool IsCursorVisible() const = 0;
+
+ virtual void CommitCursor(gfx::NativeCursor cursor) = 0;
+ virtual void CommitVisibility(bool visible) = 0;
+ virtual void CommitCursorSet(ui::CursorSetType cursor_set) = 0;
+ virtual void CommitMouseEventsEnabled(bool enabled) = 0;
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_NATIVE_CURSOR_MANAGER_DELEGATE_H_
diff --git a/chromium/ui/wm/core/nested_accelerator_controller.cc b/chromium/ui/wm/core/nested_accelerator_controller.cc
new file mode 100644
index 00000000000..e4ace4f777a
--- /dev/null
+++ b/chromium/ui/wm/core/nested_accelerator_controller.cc
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/wm/core/nested_accelerator_controller.h"
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "ui/wm/core/nested_accelerator_delegate.h"
+#include "ui/wm/core/nested_accelerator_dispatcher.h"
+
+namespace wm {
+
+NestedAcceleratorController::NestedAcceleratorController(
+ NestedAcceleratorDelegate* delegate)
+ : dispatcher_delegate_(delegate) {
+ DCHECK(delegate);
+}
+
+NestedAcceleratorController::~NestedAcceleratorController() {
+}
+
+void NestedAcceleratorController::PrepareNestedLoopClosures(
+ base::MessagePumpDispatcher* nested_dispatcher,
+ base::Closure* run_closure,
+ base::Closure* quit_closure) {
+ scoped_ptr<NestedAcceleratorDispatcher> old_accelerator_dispatcher =
+ accelerator_dispatcher_.Pass();
+ accelerator_dispatcher_ = NestedAcceleratorDispatcher::Create(
+ dispatcher_delegate_.get(), nested_dispatcher);
+
+ scoped_ptr<base::RunLoop> run_loop = accelerator_dispatcher_->CreateRunLoop();
+ *quit_closure =
+ base::Bind(&NestedAcceleratorController::QuitNestedMessageLoop,
+ base::Unretained(this),
+ run_loop->QuitClosure());
+ *run_closure = base::Bind(&NestedAcceleratorController::RunNestedMessageLoop,
+ base::Unretained(this),
+ base::Passed(&run_loop),
+ base::Passed(&old_accelerator_dispatcher));
+}
+
+void NestedAcceleratorController::RunNestedMessageLoop(
+ scoped_ptr<base::RunLoop> run_loop,
+ scoped_ptr<NestedAcceleratorDispatcher> old_accelerator_dispatcher) {
+ run_loop->Run();
+ accelerator_dispatcher_ = old_accelerator_dispatcher.Pass();
+}
+
+void NestedAcceleratorController::QuitNestedMessageLoop(
+ const base::Closure& quit_runloop) {
+ quit_runloop.Run();
+ accelerator_dispatcher_.reset();
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/nested_accelerator_controller.h b/chromium/ui/wm/core/nested_accelerator_controller.h
new file mode 100644
index 00000000000..2826d72ea27
--- /dev/null
+++ b/chromium/ui/wm/core/nested_accelerator_controller.h
@@ -0,0 +1,47 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_WM_CORE_NESTED_ACCELERATOR_CONTROLLER_H_
+#define UI_WM_CORE_NESTED_ACCELERATOR_CONTROLLER_H_
+
+#include "base/callback.h"
+#include "base/message_loop/message_loop.h"
+#include "ui/wm/public/dispatcher_client.h"
+#include "ui/wm/wm_export.h"
+
+namespace wm {
+
+class NestedAcceleratorDelegate;
+class NestedAcceleratorDispatcher;
+
+// Creates a dispatcher which wraps another dispatcher.
+// The outer dispatcher runs first and performs ash specific handling.
+// If it does not consume the event it forwards the event to the nested
+// dispatcher.
+class WM_EXPORT NestedAcceleratorController
+ : public aura::client::DispatcherClient {
+ public:
+ explicit NestedAcceleratorController(NestedAcceleratorDelegate* delegate);
+ virtual ~NestedAcceleratorController();
+
+ // aura::client::DispatcherClient:
+ virtual void PrepareNestedLoopClosures(
+ base::MessagePumpDispatcher* dispatcher,
+ base::Closure* run_closure,
+ base::Closure* quit_closure) OVERRIDE;
+
+ private:
+ void RunNestedMessageLoop(scoped_ptr<base::RunLoop> run_loop,
+ scoped_ptr<NestedAcceleratorDispatcher> dispatcher);
+ void QuitNestedMessageLoop(const base::Closure& quit_runloop);
+
+ scoped_ptr<NestedAcceleratorDispatcher> accelerator_dispatcher_;
+ scoped_ptr<NestedAcceleratorDelegate> dispatcher_delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(NestedAcceleratorController);
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_NESTED_ACCELERATOR_CONTROLLER_H_
diff --git a/chromium/ui/wm/core/nested_accelerator_controller_unittest.cc b/chromium/ui/wm/core/nested_accelerator_controller_unittest.cc
new file mode 100644
index 00000000000..9fa394ef5f6
--- /dev/null
+++ b/chromium/ui/wm/core/nested_accelerator_controller_unittest.cc
@@ -0,0 +1,205 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/wm/core/nested_accelerator_controller.h"
+
+#include "base/bind.h"
+#include "base/event_types.h"
+#include "base/message_loop/message_loop.h"
+#include "ui/aura/test/aura_test_base.h"
+#include "ui/aura/test/test_windows.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_event_dispatcher.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/base/accelerators/accelerator_manager.h"
+#include "ui/events/event_constants.h"
+#include "ui/events/event_utils.h"
+#include "ui/events/platform/platform_event_dispatcher.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/events/platform/scoped_event_dispatcher.h"
+#include "ui/wm/core/nested_accelerator_delegate.h"
+#include "ui/wm/public/dispatcher_client.h"
+
+#if defined(USE_X11)
+#include <X11/Xlib.h>
+#include "ui/events/test/events_test_utils_x11.h"
+#endif // USE_X11
+
+namespace wm {
+namespace test {
+
+namespace {
+
+class MockDispatcher : public ui::PlatformEventDispatcher {
+ public:
+ MockDispatcher() : num_key_events_dispatched_(0) {}
+
+ int num_key_events_dispatched() { return num_key_events_dispatched_; }
+
+ private:
+ // ui::PlatformEventDispatcher:
+ virtual bool CanDispatchEvent(const ui::PlatformEvent& event) OVERRIDE {
+ return true;
+ }
+ virtual uint32_t DispatchEvent(const ui::PlatformEvent& event) OVERRIDE {
+ if (ui::EventTypeFromNative(event) == ui::ET_KEY_RELEASED)
+ num_key_events_dispatched_++;
+ return ui::POST_DISPATCH_NONE;
+ }
+
+ int num_key_events_dispatched_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockDispatcher);
+};
+
+class TestTarget : public ui::AcceleratorTarget {
+ public:
+ TestTarget() : accelerator_pressed_count_(0) {}
+ virtual ~TestTarget() {}
+
+ int accelerator_pressed_count() const { return accelerator_pressed_count_; }
+
+ // Overridden from ui::AcceleratorTarget:
+ virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE {
+ accelerator_pressed_count_++;
+ return true;
+ }
+ virtual bool CanHandleAccelerators() const OVERRIDE { return true; }
+
+ private:
+ int accelerator_pressed_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestTarget);
+};
+
+void DispatchKeyReleaseA(aura::Window* root_window) {
+// Sending both keydown and keyup is necessary here because the accelerator
+// manager only checks a keyup event following a keydown event. See
+// ShouldHandle() in ui/base/accelerators/accelerator_manager.cc for details.
+#if defined(OS_WIN)
+ MSG native_event_down = {NULL, WM_KEYDOWN, ui::VKEY_A, 0};
+ aura::WindowTreeHost* host = root_window->GetHost();
+ host->PostNativeEvent(native_event_down);
+ MSG native_event_up = {NULL, WM_KEYUP, ui::VKEY_A, 0};
+ host->PostNativeEvent(native_event_up);
+#elif defined(USE_X11)
+ ui::ScopedXI2Event native_event;
+ native_event.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, 0);
+ aura::WindowTreeHost* host = root_window->GetHost();
+ host->PostNativeEvent(native_event);
+ native_event.InitKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_A, 0);
+ host->PostNativeEvent(native_event);
+#endif
+ // Make sure the inner message-loop terminates after dispatching the events.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::MessageLoop::current()->QuitClosure());
+}
+
+class MockNestedAcceleratorDelegate : public NestedAcceleratorDelegate {
+ public:
+ MockNestedAcceleratorDelegate()
+ : accelerator_manager_(new ui::AcceleratorManager) {}
+ virtual ~MockNestedAcceleratorDelegate() {}
+
+ // NestedAcceleratorDelegate:
+ virtual Result ProcessAccelerator(
+ const ui::Accelerator& accelerator) OVERRIDE {
+ return accelerator_manager_->Process(accelerator) ?
+ RESULT_PROCESSED : RESULT_NOT_PROCESSED;
+ }
+
+ void Register(const ui::Accelerator& accelerator,
+ ui::AcceleratorTarget* target) {
+ accelerator_manager_->Register(
+ accelerator, ui::AcceleratorManager::kNormalPriority, target);
+ }
+
+ private:
+ scoped_ptr<ui::AcceleratorManager> accelerator_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockNestedAcceleratorDelegate);
+};
+
+class NestedAcceleratorTest : public aura::test::AuraTestBase {
+ public:
+ NestedAcceleratorTest() {}
+ virtual ~NestedAcceleratorTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ AuraTestBase::SetUp();
+ delegate_ = new MockNestedAcceleratorDelegate();
+ nested_accelerator_controller_.reset(
+ new NestedAcceleratorController(delegate_));
+ aura::client::SetDispatcherClient(root_window(),
+ nested_accelerator_controller_.get());
+ }
+
+ virtual void TearDown() OVERRIDE {
+ aura::client::SetDispatcherClient(root_window(), NULL);
+ AuraTestBase::TearDown();
+ delegate_ = NULL;
+ nested_accelerator_controller_.reset();
+ }
+
+ MockNestedAcceleratorDelegate* delegate() { return delegate_; }
+
+ private:
+ scoped_ptr<NestedAcceleratorController> nested_accelerator_controller_;
+ MockNestedAcceleratorDelegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(NestedAcceleratorTest);
+};
+
+} // namespace
+
+// Aura window above lock screen in z order.
+TEST_F(NestedAcceleratorTest, AssociatedWindowAboveLockScreen) {
+ // TODO(oshima|sadrul): remove when Win implements PES.
+ if (!ui::PlatformEventSource::GetInstance())
+ return;
+ MockDispatcher inner_dispatcher;
+ scoped_ptr<aura::Window> mock_lock_container(
+ CreateNormalWindow(0, root_window(), NULL));
+ aura::test::CreateTestWindowWithId(1, mock_lock_container.get());
+
+ scoped_ptr<aura::Window> associated_window(
+ CreateNormalWindow(2, root_window(), NULL));
+ EXPECT_TRUE(aura::test::WindowIsAbove(associated_window.get(),
+ mock_lock_container.get()));
+
+ DispatchKeyReleaseA(root_window());
+ scoped_ptr<ui::ScopedEventDispatcher> override_dispatcher =
+ ui::PlatformEventSource::GetInstance()->OverrideDispatcher(
+ &inner_dispatcher);
+ aura::client::DispatcherRunLoop run_loop(
+ aura::client::GetDispatcherClient(root_window()), NULL);
+ run_loop.Run();
+ EXPECT_EQ(1, inner_dispatcher.num_key_events_dispatched());
+}
+
+// Test that the nested dispatcher handles accelerators.
+TEST_F(NestedAcceleratorTest, AcceleratorsHandled) {
+ // TODO(oshima|sadrul): remove when Win implements PES.
+ if (!ui::PlatformEventSource::GetInstance())
+ return;
+ MockDispatcher inner_dispatcher;
+ ui::Accelerator accelerator(ui::VKEY_A, ui::EF_NONE);
+ accelerator.set_type(ui::ET_KEY_RELEASED);
+ TestTarget target;
+ delegate()->Register(accelerator, &target);
+
+ DispatchKeyReleaseA(root_window());
+ scoped_ptr<ui::ScopedEventDispatcher> override_dispatcher =
+ ui::PlatformEventSource::GetInstance()->OverrideDispatcher(
+ &inner_dispatcher);
+ aura::client::DispatcherRunLoop run_loop(
+ aura::client::GetDispatcherClient(root_window()), NULL);
+ run_loop.Run();
+ EXPECT_EQ(0, inner_dispatcher.num_key_events_dispatched());
+ EXPECT_EQ(1, target.accelerator_pressed_count());
+}
+
+} // namespace test
+} // namespace wm
diff --git a/chromium/ui/wm/core/nested_accelerator_delegate.h b/chromium/ui/wm/core/nested_accelerator_delegate.h
new file mode 100644
index 00000000000..4b13c0d882b
--- /dev/null
+++ b/chromium/ui/wm/core/nested_accelerator_delegate.h
@@ -0,0 +1,34 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_WM_CORE_NESTED_ACCELERATOR_DELEGATE_H_
+#define UI_WM_CORE_NESTED_ACCELERATOR_DELEGATE_H_
+
+namespace ui {
+class Accelerator;
+}
+
+namespace wm {
+
+// A delegate interface that implements the behavior of nested accelerator
+// handling.
+class NestedAcceleratorDelegate {
+ public:
+ enum Result {
+ RESULT_PROCESSED,
+ RESULT_NOT_PROCESSED,
+ // The key event should be ignored now and instead be reposted so that
+ // next event loop.
+ RESULT_PROCESS_LATER,
+ };
+
+ virtual ~NestedAcceleratorDelegate() {}
+
+ // Attempts to process the |accelerator|.
+ virtual Result ProcessAccelerator(const ui::Accelerator& accelerator) = 0;
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_NESTED_ACCELERATOR_DELEGATE_H_
diff --git a/chromium/ui/wm/core/nested_accelerator_dispatcher.cc b/chromium/ui/wm/core/nested_accelerator_dispatcher.cc
new file mode 100644
index 00000000000..d37c93c796f
--- /dev/null
+++ b/chromium/ui/wm/core/nested_accelerator_dispatcher.cc
@@ -0,0 +1,21 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/wm/core/nested_accelerator_dispatcher.h"
+
+#include "base/logging.h"
+#include "ui/wm/core/nested_accelerator_delegate.h"
+
+namespace wm {
+
+NestedAcceleratorDispatcher::NestedAcceleratorDispatcher(
+ NestedAcceleratorDelegate* delegate)
+ : delegate_(delegate) {
+ DCHECK(delegate);
+}
+
+NestedAcceleratorDispatcher::~NestedAcceleratorDispatcher() {
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/nested_accelerator_dispatcher.h b/chromium/ui/wm/core/nested_accelerator_dispatcher.h
new file mode 100644
index 00000000000..df5dd0862d9
--- /dev/null
+++ b/chromium/ui/wm/core/nested_accelerator_dispatcher.h
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_WM_CORE_NESTED_ACCELERATOR_DISPATCHER_H_
+#define UI_WM_CORE_NESTED_ACCELERATOR_DISPATCHER_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/wm/wm_export.h"
+
+namespace base {
+class MessagePumpDispatcher;
+class RunLoop;
+}
+
+namespace ui {
+class KeyEvent;
+}
+
+namespace wm {
+
+class NestedAcceleratorDelegate;
+
+// Dispatcher for handling accelerators from menu.
+//
+// Wraps a nested dispatcher to which control is passed if no accelerator key
+// has been pressed. If the nested dispatcher is NULL, then the control is
+// passed back to the default dispatcher.
+// TODO(pkotwicz): Add support for a |nested_dispatcher| which sends
+// events to a system IME.
+class WM_EXPORT NestedAcceleratorDispatcher {
+ public:
+ virtual ~NestedAcceleratorDispatcher();
+
+ static scoped_ptr<NestedAcceleratorDispatcher> Create(
+ NestedAcceleratorDelegate* dispatcher_delegate,
+ base::MessagePumpDispatcher* nested_dispatcher);
+
+ // Creates a base::RunLoop object to run a nested message loop.
+ virtual scoped_ptr<base::RunLoop> CreateRunLoop() = 0;
+
+ protected:
+ explicit NestedAcceleratorDispatcher(NestedAcceleratorDelegate* delegate);
+
+ NestedAcceleratorDelegate*
+ delegate_; // Owned by NestedAcceleratorController.
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NestedAcceleratorDispatcher);
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_NESTED_ACCELERATOR_DISPATCHER_H_
diff --git a/chromium/ui/wm/core/nested_accelerator_dispatcher_linux.cc b/chromium/ui/wm/core/nested_accelerator_dispatcher_linux.cc
new file mode 100644
index 00000000000..12340b66a7b
--- /dev/null
+++ b/chromium/ui/wm/core/nested_accelerator_dispatcher_linux.cc
@@ -0,0 +1,104 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/wm/core/nested_accelerator_dispatcher.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/run_loop.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/events/event.h"
+#include "ui/events/platform/platform_event_dispatcher.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/events/platform/scoped_event_dispatcher.h"
+#include "ui/wm/core/accelerator_filter.h"
+#include "ui/wm/core/nested_accelerator_delegate.h"
+
+#if defined(USE_X11)
+#include <X11/Xlib.h>
+#endif
+
+namespace wm {
+
+namespace {
+
+#if defined(USE_OZONE)
+bool IsKeyEvent(const base::NativeEvent& native_event) {
+ const ui::KeyEvent* event = static_cast<const ui::KeyEvent*>(native_event);
+ return event->IsKeyEvent();
+}
+#elif defined(USE_X11)
+bool IsKeyEvent(const XEvent* xev) {
+ return xev->type == KeyPress || xev->type == KeyRelease;
+}
+#else
+#error Unknown build platform: you should have either use_ozone or use_x11.
+#endif
+
+scoped_ptr<ui::ScopedEventDispatcher> OverrideDispatcher(
+ ui::PlatformEventDispatcher* dispatcher) {
+ ui::PlatformEventSource* source = ui::PlatformEventSource::GetInstance();
+ return source ? source->OverrideDispatcher(dispatcher)
+ : scoped_ptr<ui::ScopedEventDispatcher>();
+}
+
+} // namespace
+
+class NestedAcceleratorDispatcherLinux : public NestedAcceleratorDispatcher,
+ public ui::PlatformEventDispatcher {
+ public:
+ explicit NestedAcceleratorDispatcherLinux(NestedAcceleratorDelegate* delegate)
+ : NestedAcceleratorDispatcher(delegate),
+ restore_dispatcher_(OverrideDispatcher(this)) {}
+
+ virtual ~NestedAcceleratorDispatcherLinux() {}
+
+ private:
+ // AcceleratorDispatcher:
+ virtual scoped_ptr<base::RunLoop> CreateRunLoop() OVERRIDE {
+ return scoped_ptr<base::RunLoop>(new base::RunLoop());
+ }
+
+ // ui::PlatformEventDispatcher:
+ virtual bool CanDispatchEvent(const ui::PlatformEvent& event) OVERRIDE {
+ return true;
+ }
+
+ virtual uint32_t DispatchEvent(const ui::PlatformEvent& event) OVERRIDE {
+ if (IsKeyEvent(event)) {
+ ui::KeyEvent key_event(event, false);
+ ui::Accelerator accelerator = CreateAcceleratorFromKeyEvent(key_event);
+
+ switch (delegate_->ProcessAccelerator(accelerator)) {
+ case NestedAcceleratorDelegate::RESULT_PROCESS_LATER:
+#if defined(USE_X11)
+ XPutBackEvent(event->xany.display, event);
+#else
+ NOTIMPLEMENTED();
+#endif
+ return ui::POST_DISPATCH_NONE;
+ case NestedAcceleratorDelegate::RESULT_PROCESSED:
+ return ui::POST_DISPATCH_NONE;
+ case NestedAcceleratorDelegate::RESULT_NOT_PROCESSED:
+ break;
+ }
+ }
+ ui::PlatformEventDispatcher* prev = *restore_dispatcher_;
+
+ return prev ? prev->DispatchEvent(event)
+ : ui::POST_DISPATCH_PERFORM_DEFAULT;
+ }
+
+ scoped_ptr<ui::ScopedEventDispatcher> restore_dispatcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(NestedAcceleratorDispatcherLinux);
+};
+
+scoped_ptr<NestedAcceleratorDispatcher> NestedAcceleratorDispatcher::Create(
+ NestedAcceleratorDelegate* delegate,
+ base::MessagePumpDispatcher* nested_dispatcher) {
+ return scoped_ptr<NestedAcceleratorDispatcher>(
+ new NestedAcceleratorDispatcherLinux(delegate));
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/nested_accelerator_dispatcher_win.cc b/chromium/ui/wm/core/nested_accelerator_dispatcher_win.cc
new file mode 100644
index 00000000000..a810bb9d88e
--- /dev/null
+++ b/chromium/ui/wm/core/nested_accelerator_dispatcher_win.cc
@@ -0,0 +1,74 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/wm/core/nested_accelerator_dispatcher.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_pump_dispatcher.h"
+#include "base/run_loop.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/events/event.h"
+#include "ui/wm/core/accelerator_filter.h"
+#include "ui/wm/core/nested_accelerator_delegate.h"
+
+using base::MessagePumpDispatcher;
+
+namespace wm {
+
+namespace {
+
+bool IsKeyEvent(const MSG& msg) {
+ return msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN ||
+ msg.message == WM_KEYUP || msg.message == WM_SYSKEYUP;
+}
+
+} // namespace
+
+class NestedAcceleratorDispatcherWin : public NestedAcceleratorDispatcher,
+ public MessagePumpDispatcher {
+ public:
+ NestedAcceleratorDispatcherWin(NestedAcceleratorDelegate* delegate,
+ MessagePumpDispatcher* nested)
+ : NestedAcceleratorDispatcher(delegate), nested_dispatcher_(nested) {}
+ virtual ~NestedAcceleratorDispatcherWin() {}
+
+ private:
+ // NestedAcceleratorDispatcher:
+ virtual scoped_ptr<base::RunLoop> CreateRunLoop() OVERRIDE {
+ return scoped_ptr<base::RunLoop>(new base::RunLoop(this));
+ }
+
+ // MessagePumpDispatcher:
+ virtual uint32_t Dispatch(const MSG& event) OVERRIDE {
+ if (IsKeyEvent(event)) {
+ ui::KeyEvent key_event(event, false);
+ ui::Accelerator accelerator = CreateAcceleratorFromKeyEvent(key_event);
+
+ switch (delegate_->ProcessAccelerator(accelerator)) {
+ case NestedAcceleratorDelegate::RESULT_PROCESS_LATER:
+ return POST_DISPATCH_QUIT_LOOP;
+ case NestedAcceleratorDelegate::RESULT_PROCESSED:
+ return POST_DISPATCH_NONE;
+ case NestedAcceleratorDelegate::RESULT_NOT_PROCESSED:
+ break;
+ }
+ }
+
+ return nested_dispatcher_ ? nested_dispatcher_->Dispatch(event)
+ : POST_DISPATCH_PERFORM_DEFAULT;
+ }
+
+ MessagePumpDispatcher* nested_dispatcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(NestedAcceleratorDispatcherWin);
+};
+
+scoped_ptr<NestedAcceleratorDispatcher> NestedAcceleratorDispatcher::Create(
+ NestedAcceleratorDelegate* delegate,
+ MessagePumpDispatcher* nested_dispatcher) {
+ return scoped_ptr<NestedAcceleratorDispatcher>(
+ new NestedAcceleratorDispatcherWin(delegate, nested_dispatcher));
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/shadow.cc b/chromium/ui/wm/core/shadow.cc
new file mode 100644
index 00000000000..1eb0ac6b4e3
--- /dev/null
+++ b/chromium/ui/wm/core/shadow.cc
@@ -0,0 +1,193 @@
+// 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/wm/core/shadow.h"
+
+#include "grit/ui_resources.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/wm/core/image_grid.h"
+
+namespace {
+
+// Shadow opacity for different styles.
+const float kActiveShadowOpacity = 1.0f;
+const float kInactiveShadowOpacity = 0.2f;
+const float kSmallShadowOpacity = 1.0f;
+
+// Interior inset for different styles.
+const int kActiveInteriorInset = 0;
+const int kInactiveInteriorInset = 0;
+const int kSmallInteriorInset = 5;
+
+// Duration for opacity animation in milliseconds.
+const int kShadowAnimationDurationMs = 100;
+
+float GetOpacityForStyle(wm::Shadow::Style style) {
+ switch (style) {
+ case wm::Shadow::STYLE_ACTIVE:
+ return kActiveShadowOpacity;
+ case wm::Shadow::STYLE_INACTIVE:
+ return kInactiveShadowOpacity;
+ case wm::Shadow::STYLE_SMALL:
+ return kSmallShadowOpacity;
+ }
+ return 1.0f;
+}
+
+int GetInteriorInsetForStyle(wm::Shadow::Style style) {
+ switch (style) {
+ case wm::Shadow::STYLE_ACTIVE:
+ return kActiveInteriorInset;
+ case wm::Shadow::STYLE_INACTIVE:
+ return kInactiveInteriorInset;
+ case wm::Shadow::STYLE_SMALL:
+ return kSmallInteriorInset;
+ }
+ return 0;
+}
+
+} // namespace
+
+namespace wm {
+
+Shadow::Shadow() : style_(STYLE_ACTIVE), interior_inset_(0) {
+}
+
+Shadow::~Shadow() {
+}
+
+void Shadow::Init(Style style) {
+ style_ = style;
+ image_grid_.reset(new ImageGrid);
+ UpdateImagesForStyle();
+ image_grid_->layer()->set_name("Shadow");
+ image_grid_->layer()->SetOpacity(GetOpacityForStyle(style_));
+}
+
+void Shadow::SetContentBounds(const gfx::Rect& content_bounds) {
+ content_bounds_ = content_bounds;
+ UpdateImageGridBounds();
+}
+
+ui::Layer* Shadow::layer() const {
+ return image_grid_->layer();
+}
+
+void Shadow::SetStyle(Style style) {
+ if (style_ == style)
+ return;
+
+ Style old_style = style_;
+ style_ = style;
+
+ // Stop waiting for any as yet unfinished implicit animations.
+ StopObservingImplicitAnimations();
+
+ // If we're switching to or from the small style, don't bother with
+ // animations.
+ if (style == STYLE_SMALL || old_style == STYLE_SMALL) {
+ UpdateImagesForStyle();
+ image_grid_->layer()->SetOpacity(GetOpacityForStyle(style));
+ return;
+ }
+
+ // If we're becoming active, switch images now. Because the inactive image
+ // has a very low opacity the switch isn't noticeable and this approach
+ // allows us to use only a single set of shadow images at a time.
+ if (style == STYLE_ACTIVE) {
+ UpdateImagesForStyle();
+ // Opacity was baked into inactive image, start opacity low to match.
+ image_grid_->layer()->SetOpacity(kInactiveShadowOpacity);
+ }
+
+ {
+ // Property sets within this scope will be implicitly animated.
+ ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator());
+ settings.AddObserver(this);
+ settings.SetTransitionDuration(
+ base::TimeDelta::FromMilliseconds(kShadowAnimationDurationMs));
+ switch (style_) {
+ case STYLE_ACTIVE:
+ image_grid_->layer()->SetOpacity(kActiveShadowOpacity);
+ break;
+ case STYLE_INACTIVE:
+ image_grid_->layer()->SetOpacity(kInactiveShadowOpacity);
+ break;
+ default:
+ NOTREACHED() << "Unhandled style " << style_;
+ break;
+ }
+ }
+}
+
+void Shadow::OnImplicitAnimationsCompleted() {
+ // If we just finished going inactive, switch images. This doesn't cause
+ // a visual pop because the inactive image opacity is so low.
+ if (style_ == STYLE_INACTIVE) {
+ UpdateImagesForStyle();
+ // Opacity is baked into inactive image, so set fully opaque.
+ image_grid_->layer()->SetOpacity(1.0f);
+ }
+}
+
+void Shadow::UpdateImagesForStyle() {
+ ResourceBundle& res = ResourceBundle::GetSharedInstance();
+ switch (style_) {
+ case STYLE_ACTIVE:
+ image_grid_->SetImages(
+ &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_TOP_LEFT),
+ &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_TOP),
+ &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_TOP_RIGHT),
+ &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_LEFT),
+ NULL,
+ &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_RIGHT),
+ &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_BOTTOM_LEFT),
+ &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_BOTTOM),
+ &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_BOTTOM_RIGHT));
+ break;
+ case STYLE_INACTIVE:
+ image_grid_->SetImages(
+ &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_TOP_LEFT),
+ &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_TOP),
+ &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_TOP_RIGHT),
+ &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_LEFT),
+ NULL,
+ &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_RIGHT),
+ &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_BOTTOM_LEFT),
+ &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_BOTTOM),
+ &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_BOTTOM_RIGHT));
+ break;
+ case STYLE_SMALL:
+ image_grid_->SetImages(
+ &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_TOP_LEFT),
+ &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_TOP),
+ &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_TOP_RIGHT),
+ &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_LEFT),
+ NULL,
+ &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_RIGHT),
+ &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_BOTTOM_LEFT),
+ &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_BOTTOM),
+ &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_BOTTOM_RIGHT));
+ break;
+ default:
+ NOTREACHED() << "Unhandled style " << style_;
+ break;
+ }
+
+ // Update interior inset for style.
+ interior_inset_ = GetInteriorInsetForStyle(style_);
+
+ // Image sizes may have changed.
+ UpdateImageGridBounds();
+}
+
+void Shadow::UpdateImageGridBounds() {
+ // Update bounds based on content bounds and image sizes.
+ gfx::Rect image_grid_bounds = content_bounds_;
+ image_grid_bounds.Inset(interior_inset_, interior_inset_);
+ image_grid_->SetContentBounds(image_grid_bounds);
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/shadow.h b/chromium/ui/wm/core/shadow.h
new file mode 100644
index 00000000000..c153ecf158e
--- /dev/null
+++ b/chromium/ui/wm/core/shadow.h
@@ -0,0 +1,86 @@
+// 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_WM_CORE_SHADOW_H_
+#define UI_WM_CORE_SHADOW_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/compositor/layer_animation_observer.h"
+#include "ui/gfx/rect.h"
+#include "ui/wm/wm_export.h"
+
+namespace ui {
+class Layer;
+} // namespace ui
+
+namespace wm {
+
+class ImageGrid;
+
+// Simple class that draws a drop shadow around content at given bounds.
+class WM_EXPORT Shadow : public ui::ImplicitAnimationObserver {
+ public:
+ enum Style {
+ // Active windows have more opaque shadows, shifted down to make the window
+ // appear "higher".
+ STYLE_ACTIVE,
+
+ // Inactive windows have less opaque shadows.
+ STYLE_INACTIVE,
+
+ // Small windows like tooltips and context menus have lighter, smaller
+ // shadows.
+ STYLE_SMALL,
+ };
+
+ Shadow();
+ virtual ~Shadow();
+
+ void Init(Style style);
+
+ // Returns |image_grid_|'s ui::Layer. This is exposed so it can be added to
+ // the same layer as the content and stacked below it. SetContentBounds()
+ // should be used to adjust the shadow's size and position (rather than
+ // applying transformations to this layer).
+ ui::Layer* layer() const;
+
+ const gfx::Rect& content_bounds() const { return content_bounds_; }
+ Style style() const { return style_; }
+
+ // Moves and resizes |image_grid_| to frame |content_bounds|.
+ void SetContentBounds(const gfx::Rect& content_bounds);
+
+ // Sets the shadow's style, animating opacity as necessary.
+ void SetStyle(Style style);
+
+ // ui::ImplicitAnimationObserver overrides:
+ virtual void OnImplicitAnimationsCompleted() OVERRIDE;
+
+ private:
+ // Updates the |image_grid_| images to the current |style_|.
+ void UpdateImagesForStyle();
+
+ // Updates the |image_grid_| bounds based on its image sizes and the
+ // current |content_bounds_|.
+ void UpdateImageGridBounds();
+
+ // The current style, set when the transition animation starts.
+ Style style_;
+
+ scoped_ptr<ImageGrid> image_grid_;
+
+ // Bounds of the content that the shadow encloses.
+ gfx::Rect content_bounds_;
+
+ // The interior inset of the shadow images. The content bounds of the image
+ // grid should be set to |content_bounds_| inset by this amount.
+ int interior_inset_;
+
+ DISALLOW_COPY_AND_ASSIGN(Shadow);
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_SHADOW_H_
diff --git a/chromium/ui/wm/core/shadow_controller.cc b/chromium/ui/wm/core/shadow_controller.cc
new file mode 100644
index 00000000000..c809389fadb
--- /dev/null
+++ b/chromium/ui/wm/core/shadow_controller.cc
@@ -0,0 +1,272 @@
+// 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/wm/core/shadow_controller.h"
+
+#include <utility>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/memory/linked_ptr.h"
+#include "base/scoped_observer.h"
+#include "ui/aura/env.h"
+#include "ui/aura/env_observer.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_event_dispatcher.h"
+#include "ui/aura/window_observer.h"
+#include "ui/compositor/layer.h"
+#include "ui/wm/core/shadow.h"
+#include "ui/wm/core/shadow_types.h"
+#include "ui/wm/core/window_util.h"
+#include "ui/wm/public/activation_client.h"
+
+using std::make_pair;
+
+namespace wm {
+
+namespace {
+
+ShadowType GetShadowTypeFromWindow(aura::Window* window) {
+ switch (window->type()) {
+ case ui::wm::WINDOW_TYPE_NORMAL:
+ case ui::wm::WINDOW_TYPE_PANEL:
+ case ui::wm::WINDOW_TYPE_MENU:
+ case ui::wm::WINDOW_TYPE_TOOLTIP:
+ return SHADOW_TYPE_RECTANGULAR;
+ default:
+ break;
+ }
+ return SHADOW_TYPE_NONE;
+}
+
+bool ShouldUseSmallShadowForWindow(aura::Window* window) {
+ switch (window->type()) {
+ case ui::wm::WINDOW_TYPE_MENU:
+ case ui::wm::WINDOW_TYPE_TOOLTIP:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+// Returns the shadow style to be applied to |losing_active| when it is losing
+// active to |gaining_active|. |gaining_active| may be of a type that hides when
+// inactive, and as such we do not want to render |losing_active| as inactive.
+Shadow::Style GetShadowStyleForWindowLosingActive(
+ aura::Window* losing_active,
+ aura::Window* gaining_active) {
+ if (gaining_active && aura::client::GetHideOnDeactivate(gaining_active)) {
+ aura::Window::Windows::const_iterator it =
+ std::find(GetTransientChildren(losing_active).begin(),
+ GetTransientChildren(losing_active).end(),
+ gaining_active);
+ if (it != GetTransientChildren(losing_active).end())
+ return Shadow::STYLE_ACTIVE;
+ }
+ return Shadow::STYLE_INACTIVE;
+}
+
+} // namespace
+
+// ShadowController::Impl ------------------------------------------------------
+
+// Real implementation of the ShadowController. ShadowController observes
+// ActivationChangeObserver, which are per ActivationClient, where as there is
+// only a single Impl (as it observes all window creation by way of an
+// EnvObserver).
+class ShadowController::Impl :
+ public aura::EnvObserver,
+ public aura::WindowObserver,
+ public base::RefCounted<Impl> {
+ public:
+ // Returns the singleton instance, destroyed when there are no more refs.
+ static Impl* GetInstance();
+
+ // aura::EnvObserver override:
+ virtual void OnWindowInitialized(aura::Window* window) OVERRIDE;
+
+ // aura::WindowObserver overrides:
+ virtual void OnWindowPropertyChanged(
+ aura::Window* window, const void* key, intptr_t old) OVERRIDE;
+ virtual void OnWindowBoundsChanged(
+ aura::Window* window,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) OVERRIDE;
+ virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
+
+ private:
+ friend class base::RefCounted<Impl>;
+ friend class ShadowController;
+ friend class ShadowController::TestApi;
+
+ typedef std::map<aura::Window*, linked_ptr<Shadow> > WindowShadowMap;
+
+ Impl();
+ virtual ~Impl();
+
+ // Forwarded from ShadowController.
+ void OnWindowActivated(aura::Window* gained_active,
+ aura::Window* lost_active);
+
+ // Checks if |window| is visible and contains a property requesting a shadow.
+ bool ShouldShowShadowForWindow(aura::Window* window) const;
+
+ // Returns |window|'s shadow from |window_shadows_|, or NULL if no shadow
+ // exists.
+ Shadow* GetShadowForWindow(aura::Window* window);
+
+ // Updates the shadow styles for windows when activation changes.
+ void HandleWindowActivationChange(aura::Window* gaining_active,
+ aura::Window* losing_active);
+
+ // Shows or hides |window|'s shadow as needed (creating the shadow if
+ // necessary).
+ void HandlePossibleShadowVisibilityChange(aura::Window* window);
+
+ // Creates a new shadow for |window| and stores it in |window_shadows_|. The
+ // shadow's bounds are initialized and it is added to the window's layer.
+ void CreateShadowForWindow(aura::Window* window);
+
+ WindowShadowMap window_shadows_;
+
+ ScopedObserver<aura::Window, aura::WindowObserver> observer_manager_;
+
+ static Impl* instance_;
+
+ DISALLOW_COPY_AND_ASSIGN(Impl);
+};
+
+// static
+ShadowController::Impl* ShadowController::Impl::instance_ = NULL;
+
+// static
+ShadowController::Impl* ShadowController::Impl::GetInstance() {
+ if (!instance_)
+ instance_ = new Impl();
+ return instance_;
+}
+
+void ShadowController::Impl::OnWindowInitialized(aura::Window* window) {
+ observer_manager_.Add(window);
+ SetShadowType(window, GetShadowTypeFromWindow(window));
+ HandlePossibleShadowVisibilityChange(window);
+}
+
+void ShadowController::Impl::OnWindowPropertyChanged(aura::Window* window,
+ const void* key,
+ intptr_t old) {
+ if (key == kShadowTypeKey) {
+ HandlePossibleShadowVisibilityChange(window);
+ return;
+ }
+}
+
+void ShadowController::Impl::OnWindowBoundsChanged(
+ aura::Window* window,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {
+ Shadow* shadow = GetShadowForWindow(window);
+ if (shadow)
+ shadow->SetContentBounds(gfx::Rect(new_bounds.size()));
+}
+
+void ShadowController::Impl::OnWindowDestroyed(aura::Window* window) {
+ window_shadows_.erase(window);
+ observer_manager_.Remove(window);
+}
+
+void ShadowController::Impl::OnWindowActivated(aura::Window* gained_active,
+ aura::Window* lost_active) {
+ if (gained_active) {
+ Shadow* shadow = GetShadowForWindow(gained_active);
+ if (shadow && !ShouldUseSmallShadowForWindow(gained_active))
+ shadow->SetStyle(Shadow::STYLE_ACTIVE);
+ }
+ if (lost_active) {
+ Shadow* shadow = GetShadowForWindow(lost_active);
+ if (shadow && !ShouldUseSmallShadowForWindow(lost_active)) {
+ shadow->SetStyle(GetShadowStyleForWindowLosingActive(lost_active,
+ gained_active));
+ }
+ }
+}
+
+bool ShadowController::Impl::ShouldShowShadowForWindow(
+ aura::Window* window) const {
+ const ShadowType type = GetShadowType(window);
+ switch (type) {
+ case SHADOW_TYPE_NONE:
+ return false;
+ case SHADOW_TYPE_RECTANGULAR:
+ return true;
+ default:
+ NOTREACHED() << "Unknown shadow type " << type;
+ return false;
+ }
+}
+
+Shadow* ShadowController::Impl::GetShadowForWindow(aura::Window* window) {
+ WindowShadowMap::const_iterator it = window_shadows_.find(window);
+ return it != window_shadows_.end() ? it->second.get() : NULL;
+}
+
+void ShadowController::Impl::HandlePossibleShadowVisibilityChange(
+ aura::Window* window) {
+ const bool should_show = ShouldShowShadowForWindow(window);
+ Shadow* shadow = GetShadowForWindow(window);
+ if (shadow)
+ shadow->layer()->SetVisible(should_show);
+ else if (should_show && !shadow)
+ CreateShadowForWindow(window);
+}
+
+void ShadowController::Impl::CreateShadowForWindow(aura::Window* window) {
+ linked_ptr<Shadow> shadow(new Shadow());
+ window_shadows_.insert(make_pair(window, shadow));
+
+ shadow->Init(ShouldUseSmallShadowForWindow(window) ?
+ Shadow::STYLE_SMALL : Shadow::STYLE_ACTIVE);
+ shadow->SetContentBounds(gfx::Rect(window->bounds().size()));
+ shadow->layer()->SetVisible(ShouldShowShadowForWindow(window));
+ window->layer()->Add(shadow->layer());
+}
+
+ShadowController::Impl::Impl()
+ : observer_manager_(this) {
+ aura::Env::GetInstance()->AddObserver(this);
+}
+
+ShadowController::Impl::~Impl() {
+ DCHECK_EQ(instance_, this);
+ aura::Env::GetInstance()->RemoveObserver(this);
+ instance_ = NULL;
+}
+
+// ShadowController ------------------------------------------------------------
+
+ShadowController::ShadowController(
+ aura::client::ActivationClient* activation_client)
+ : activation_client_(activation_client),
+ impl_(Impl::GetInstance()) {
+ // Watch for window activation changes.
+ activation_client_->AddObserver(this);
+}
+
+ShadowController::~ShadowController() {
+ activation_client_->RemoveObserver(this);
+}
+
+void ShadowController::OnWindowActivated(aura::Window* gained_active,
+ aura::Window* lost_active) {
+ impl_->OnWindowActivated(gained_active, lost_active);
+}
+
+// ShadowController::TestApi ---------------------------------------------------
+
+Shadow* ShadowController::TestApi::GetShadowForWindow(aura::Window* window) {
+ return controller_->impl_->GetShadowForWindow(window);
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/shadow_controller.h b/chromium/ui/wm/core/shadow_controller.h
new file mode 100644
index 00000000000..c84689453f3
--- /dev/null
+++ b/chromium/ui/wm/core/shadow_controller.h
@@ -0,0 +1,69 @@
+// 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_WM_CORE_SHADOW_CONTROLLER_H_
+#define UI_WM_CORE_SHADOW_CONTROLLER_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "ui/wm/public/activation_change_observer.h"
+#include "ui/wm/wm_export.h"
+
+namespace aura {
+class Window;
+namespace client {
+class ActivationClient;
+}
+}
+namespace gfx {
+class Rect;
+}
+
+namespace wm {
+
+class Shadow;
+
+// ShadowController observes changes to windows and creates and updates drop
+// shadows as needed. ShadowController itself is light weight and per
+// ActivationClient. ShadowController delegates to its implementation class,
+// which observes all window creation.
+class WM_EXPORT ShadowController :
+ public aura::client::ActivationChangeObserver {
+ public:
+ class WM_EXPORT TestApi {
+ public:
+ explicit TestApi(ShadowController* controller) : controller_(controller) {}
+ ~TestApi() {}
+
+ Shadow* GetShadowForWindow(aura::Window* window);
+
+ private:
+ ShadowController* controller_; // not owned
+
+ DISALLOW_COPY_AND_ASSIGN(TestApi);
+ };
+
+ explicit ShadowController(aura::client::ActivationClient* activation_client);
+ virtual ~ShadowController();
+
+ // aura::client::ActivationChangeObserver overrides:
+ virtual void OnWindowActivated(aura::Window* gained_active,
+ aura::Window* lost_active) OVERRIDE;
+
+ private:
+ class Impl;
+
+ aura::client::ActivationClient* activation_client_;
+
+ scoped_refptr<Impl> impl_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShadowController);
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_SHADOW_CONTROLLER_H_
diff --git a/chromium/ui/wm/core/shadow_controller_unittest.cc b/chromium/ui/wm/core/shadow_controller_unittest.cc
new file mode 100644
index 00000000000..d82d015f1a0
--- /dev/null
+++ b/chromium/ui/wm/core/shadow_controller_unittest.cc
@@ -0,0 +1,219 @@
+// 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/wm/core/shadow_controller.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/aura/client/window_tree_client.h"
+#include "ui/aura/test/aura_test_base.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_event_dispatcher.h"
+#include "ui/compositor/layer.h"
+#include "ui/wm/core/default_activation_client.h"
+#include "ui/wm/core/shadow.h"
+#include "ui/wm/core/shadow_types.h"
+#include "ui/wm/core/window_util.h"
+#include "ui/wm/core/wm_state.h"
+#include "ui/wm/public/activation_client.h"
+
+namespace wm {
+
+class ShadowControllerTest : public aura::test::AuraTestBase {
+ public:
+ ShadowControllerTest() {}
+ virtual ~ShadowControllerTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ wm_state_.reset(new wm::WMState);
+ AuraTestBase::SetUp();
+ new wm::DefaultActivationClient(root_window());
+ aura::client::ActivationClient* activation_client =
+ aura::client::GetActivationClient(root_window());
+ shadow_controller_.reset(new ShadowController(activation_client));
+ }
+ virtual void TearDown() OVERRIDE {
+ shadow_controller_.reset();
+ AuraTestBase::TearDown();
+ wm_state_.reset();
+ }
+
+ protected:
+ ShadowController* shadow_controller() { return shadow_controller_.get(); }
+
+ void ActivateWindow(aura::Window* window) {
+ DCHECK(window);
+ DCHECK(window->GetRootWindow());
+ aura::client::GetActivationClient(window->GetRootWindow())->ActivateWindow(
+ window);
+ }
+
+ private:
+ scoped_ptr<ShadowController> shadow_controller_;
+ scoped_ptr<wm::WMState> wm_state_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShadowControllerTest);
+};
+
+// Tests that various methods in Window update the Shadow object as expected.
+TEST_F(ShadowControllerTest, Shadow) {
+ scoped_ptr<aura::Window> window(new aura::Window(NULL));
+ window->SetType(ui::wm::WINDOW_TYPE_NORMAL);
+ window->Init(aura::WINDOW_LAYER_TEXTURED);
+ ParentWindow(window.get());
+
+ // We should create the shadow before the window is visible (the shadow's
+ // layer won't get drawn yet since it's a child of the window's layer).
+ ShadowController::TestApi api(shadow_controller());
+ const Shadow* shadow = api.GetShadowForWindow(window.get());
+ ASSERT_TRUE(shadow != NULL);
+ EXPECT_TRUE(shadow->layer()->visible());
+
+ // The shadow should remain visible after window visibility changes.
+ window->Show();
+ EXPECT_TRUE(shadow->layer()->visible());
+ window->Hide();
+ EXPECT_TRUE(shadow->layer()->visible());
+
+ // If the shadow is disabled, it should be hidden.
+ SetShadowType(window.get(), SHADOW_TYPE_NONE);
+ window->Show();
+ EXPECT_FALSE(shadow->layer()->visible());
+ SetShadowType(window.get(), SHADOW_TYPE_RECTANGULAR);
+ EXPECT_TRUE(shadow->layer()->visible());
+
+ // The shadow's layer should be a child of the window's layer.
+ EXPECT_EQ(window->layer(), shadow->layer()->parent());
+
+ window->parent()->RemoveChild(window.get());
+ aura::Window* window_ptr = window.get();
+ window.reset();
+ EXPECT_TRUE(api.GetShadowForWindow(window_ptr) == NULL);
+}
+
+// Tests that the window's shadow's bounds are updated correctly.
+TEST_F(ShadowControllerTest, ShadowBounds) {
+ scoped_ptr<aura::Window> window(new aura::Window(NULL));
+ window->SetType(ui::wm::WINDOW_TYPE_NORMAL);
+ window->Init(aura::WINDOW_LAYER_TEXTURED);
+ ParentWindow(window.get());
+ window->Show();
+
+ const gfx::Rect kOldBounds(20, 30, 400, 300);
+ window->SetBounds(kOldBounds);
+
+ // When the shadow is first created, it should use the window's size (but
+ // remain at the origin, since it's a child of the window's layer).
+ SetShadowType(window.get(), SHADOW_TYPE_RECTANGULAR);
+ ShadowController::TestApi api(shadow_controller());
+ const Shadow* shadow = api.GetShadowForWindow(window.get());
+ ASSERT_TRUE(shadow != NULL);
+ EXPECT_EQ(gfx::Rect(kOldBounds.size()).ToString(),
+ shadow->content_bounds().ToString());
+
+ // When we change the window's bounds, the shadow's should be updated too.
+ gfx::Rect kNewBounds(50, 60, 500, 400);
+ window->SetBounds(kNewBounds);
+ EXPECT_EQ(gfx::Rect(kNewBounds.size()).ToString(),
+ shadow->content_bounds().ToString());
+}
+
+// Tests that activating a window changes the shadow style.
+TEST_F(ShadowControllerTest, ShadowStyle) {
+ ShadowController::TestApi api(shadow_controller());
+
+ scoped_ptr<aura::Window> window1(new aura::Window(NULL));
+ window1->SetType(ui::wm::WINDOW_TYPE_NORMAL);
+ window1->Init(aura::WINDOW_LAYER_TEXTURED);
+ ParentWindow(window1.get());
+ window1->SetBounds(gfx::Rect(10, 20, 300, 400));
+ window1->Show();
+ ActivateWindow(window1.get());
+
+ // window1 is active, so style should have active appearance.
+ Shadow* shadow1 = api.GetShadowForWindow(window1.get());
+ ASSERT_TRUE(shadow1 != NULL);
+ EXPECT_EQ(Shadow::STYLE_ACTIVE, shadow1->style());
+
+ // Create another window and activate it.
+ scoped_ptr<aura::Window> window2(new aura::Window(NULL));
+ window2->SetType(ui::wm::WINDOW_TYPE_NORMAL);
+ window2->Init(aura::WINDOW_LAYER_TEXTURED);
+ ParentWindow(window2.get());
+ window2->SetBounds(gfx::Rect(11, 21, 301, 401));
+ window2->Show();
+ ActivateWindow(window2.get());
+
+ // window1 is now inactive, so shadow should go inactive.
+ Shadow* shadow2 = api.GetShadowForWindow(window2.get());
+ ASSERT_TRUE(shadow2 != NULL);
+ EXPECT_EQ(Shadow::STYLE_INACTIVE, shadow1->style());
+ EXPECT_EQ(Shadow::STYLE_ACTIVE, shadow2->style());
+}
+
+// Tests that we use smaller shadows for tooltips and menus.
+TEST_F(ShadowControllerTest, SmallShadowsForTooltipsAndMenus) {
+ ShadowController::TestApi api(shadow_controller());
+
+ scoped_ptr<aura::Window> tooltip_window(new aura::Window(NULL));
+ tooltip_window->SetType(ui::wm::WINDOW_TYPE_TOOLTIP);
+ tooltip_window->Init(aura::WINDOW_LAYER_TEXTURED);
+ ParentWindow(tooltip_window.get());
+ tooltip_window->SetBounds(gfx::Rect(10, 20, 300, 400));
+ tooltip_window->Show();
+
+ Shadow* tooltip_shadow = api.GetShadowForWindow(tooltip_window.get());
+ ASSERT_TRUE(tooltip_shadow != NULL);
+ EXPECT_EQ(Shadow::STYLE_SMALL, tooltip_shadow->style());
+
+ scoped_ptr<aura::Window> menu_window(new aura::Window(NULL));
+ menu_window->SetType(ui::wm::WINDOW_TYPE_MENU);
+ menu_window->Init(aura::WINDOW_LAYER_TEXTURED);
+ ParentWindow(menu_window.get());
+ menu_window->SetBounds(gfx::Rect(10, 20, 300, 400));
+ menu_window->Show();
+
+ Shadow* menu_shadow = api.GetShadowForWindow(tooltip_window.get());
+ ASSERT_TRUE(menu_shadow != NULL);
+ EXPECT_EQ(Shadow::STYLE_SMALL, menu_shadow->style());
+}
+
+// http://crbug.com/120210 - transient parents of certain types of transients
+// should not lose their shadow when they lose activation to the transient.
+TEST_F(ShadowControllerTest, TransientParentKeepsActiveShadow) {
+ ShadowController::TestApi api(shadow_controller());
+
+ scoped_ptr<aura::Window> window1(new aura::Window(NULL));
+ window1->SetType(ui::wm::WINDOW_TYPE_NORMAL);
+ window1->Init(aura::WINDOW_LAYER_TEXTURED);
+ ParentWindow(window1.get());
+ window1->SetBounds(gfx::Rect(10, 20, 300, 400));
+ window1->Show();
+ ActivateWindow(window1.get());
+
+ // window1 is active, so style should have active appearance.
+ Shadow* shadow1 = api.GetShadowForWindow(window1.get());
+ ASSERT_TRUE(shadow1 != NULL);
+ EXPECT_EQ(Shadow::STYLE_ACTIVE, shadow1->style());
+
+ // Create a window that is transient to window1, and that has the 'hide on
+ // deactivate' property set. Upon activation, window1 should still have an
+ // active shadow.
+ scoped_ptr<aura::Window> window2(new aura::Window(NULL));
+ window2->SetType(ui::wm::WINDOW_TYPE_NORMAL);
+ window2->Init(aura::WINDOW_LAYER_TEXTURED);
+ ParentWindow(window2.get());
+ window2->SetBounds(gfx::Rect(11, 21, 301, 401));
+ AddTransientChild(window1.get(), window2.get());
+ aura::client::SetHideOnDeactivate(window2.get(), true);
+ window2->Show();
+ ActivateWindow(window2.get());
+
+ // window1 is now inactive, but its shadow should still appear active.
+ EXPECT_EQ(Shadow::STYLE_ACTIVE, shadow1->style());
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/shadow_types.cc b/chromium/ui/wm/core/shadow_types.cc
new file mode 100644
index 00000000000..a7afe89b50d
--- /dev/null
+++ b/chromium/ui/wm/core/shadow_types.cc
@@ -0,0 +1,23 @@
+// 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/wm/core/shadow_types.h"
+
+#include "ui/aura/window_property.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(wm::ShadowType);
+
+namespace wm {
+
+void SetShadowType(aura::Window* window, ShadowType shadow_type) {
+ window->SetProperty(kShadowTypeKey, shadow_type);
+}
+
+ShadowType GetShadowType(aura::Window* window) {
+ return window->GetProperty(kShadowTypeKey);
+}
+
+DEFINE_WINDOW_PROPERTY_KEY(ShadowType, kShadowTypeKey, SHADOW_TYPE_NONE);
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/shadow_types.h b/chromium/ui/wm/core/shadow_types.h
new file mode 100644
index 00000000000..1198a43b16c
--- /dev/null
+++ b/chromium/ui/wm/core/shadow_types.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_WM_CORE_SHADOW_TYPES_H_
+#define UI_WM_CORE_SHADOW_TYPES_H_
+
+#include "ui/aura/window.h"
+#include "ui/wm/wm_export.h"
+
+namespace aura {
+class Window;
+}
+
+namespace wm {
+
+// Different types of drop shadows that can be drawn under a window by the
+// shell. Used as a value for the kShadowTypeKey property.
+enum ShadowType {
+ // Starts at 0 due to the cast in GetShadowType().
+ SHADOW_TYPE_NONE = 0,
+ SHADOW_TYPE_RECTANGULAR,
+};
+
+WM_EXPORT void SetShadowType(aura::Window* window, ShadowType shadow_type);
+WM_EXPORT ShadowType GetShadowType(aura::Window* window);
+
+// A property key describing the drop shadow that should be displayed under the
+// window. If unset, no shadow is displayed.
+extern const aura::WindowProperty<ShadowType>* const kShadowTypeKey;
+
+} // namespace wm
+
+#endif // UI_WM_CORE_SHADOW_TYPES_H_
diff --git a/chromium/ui/wm/core/transient_window_controller.cc b/chromium/ui/wm/core/transient_window_controller.cc
new file mode 100644
index 00000000000..bb1945c09b2
--- /dev/null
+++ b/chromium/ui/wm/core/transient_window_controller.cc
@@ -0,0 +1,40 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/wm/core/transient_window_controller.h"
+
+#include "ui/wm/core/transient_window_manager.h"
+
+namespace wm {
+
+TransientWindowController::TransientWindowController() {
+}
+
+TransientWindowController::~TransientWindowController() {
+}
+
+void TransientWindowController::AddTransientChild(aura::Window* parent,
+ aura::Window* child) {
+ TransientWindowManager::Get(parent)->AddTransientChild(child);
+}
+
+void TransientWindowController::RemoveTransientChild(aura::Window* parent,
+ aura::Window* child) {
+ TransientWindowManager::Get(parent)->RemoveTransientChild(child);
+}
+
+aura::Window* TransientWindowController::GetTransientParent(
+ aura::Window* window) {
+ return const_cast<aura::Window*>(GetTransientParent(
+ const_cast<const aura::Window*>(window)));
+}
+
+const aura::Window* TransientWindowController::GetTransientParent(
+ const aura::Window* window) {
+ const TransientWindowManager* window_manager =
+ TransientWindowManager::Get(window);
+ return window_manager ? window_manager->transient_parent() : NULL;
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/transient_window_controller.h b/chromium/ui/wm/core/transient_window_controller.h
new file mode 100644
index 00000000000..8638824ae36
--- /dev/null
+++ b/chromium/ui/wm/core/transient_window_controller.h
@@ -0,0 +1,36 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_WM_CORE_TRANSIENT_WINDOW_CONTROLLER_H_
+#define UI_WM_CORE_TRANSIENT_WINDOW_CONTROLLER_H_
+
+#include "ui/wm/public/transient_window_client.h"
+#include "ui/wm/wm_export.h"
+
+namespace wm {
+
+// TransientWindowClient implementation. Uses TransientWindowManager to handle
+// tracking transient per window.
+class WM_EXPORT TransientWindowController
+ : public aura::client::TransientWindowClient {
+ public:
+ TransientWindowController();
+ virtual ~TransientWindowController();
+
+ // TransientWindowClient:
+ virtual void AddTransientChild(aura::Window* parent,
+ aura::Window* child) OVERRIDE;
+ virtual void RemoveTransientChild(aura::Window* parent,
+ aura::Window* child) OVERRIDE;
+ virtual aura::Window* GetTransientParent(aura::Window* window) OVERRIDE;
+ virtual const aura::Window* GetTransientParent(
+ const aura::Window* window) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TransientWindowController);
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_TRANSIENT_WINDOW_CONTROLLER_H_
diff --git a/chromium/ui/wm/core/transient_window_manager.cc b/chromium/ui/wm/core/transient_window_manager.cc
new file mode 100644
index 00000000000..120af84786c
--- /dev/null
+++ b/chromium/ui/wm/core/transient_window_manager.cc
@@ -0,0 +1,182 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/wm/core/transient_window_manager.h"
+
+#include <algorithm>
+#include <functional>
+
+#include "base/auto_reset.h"
+#include "base/stl_util.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_property.h"
+#include "ui/wm/core/transient_window_observer.h"
+#include "ui/wm/core/transient_window_stacking_client.h"
+#include "ui/wm/core/window_util.h"
+
+using aura::Window;
+
+namespace wm {
+
+DEFINE_OWNED_WINDOW_PROPERTY_KEY(TransientWindowManager, kPropertyKey, NULL);
+
+TransientWindowManager::~TransientWindowManager() {
+}
+
+// static
+TransientWindowManager* TransientWindowManager::Get(Window* window) {
+ TransientWindowManager* manager = window->GetProperty(kPropertyKey);
+ if (!manager) {
+ manager = new TransientWindowManager(window);
+ window->SetProperty(kPropertyKey, manager);
+ }
+ return manager;
+}
+
+// static
+const TransientWindowManager* TransientWindowManager::Get(
+ const Window* window) {
+ return window->GetProperty(kPropertyKey);
+}
+
+void TransientWindowManager::AddObserver(TransientWindowObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void TransientWindowManager::RemoveObserver(TransientWindowObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void TransientWindowManager::AddTransientChild(Window* child) {
+ // TransientWindowStackingClient does the stacking of transient windows. If it
+ // isn't installed stacking is going to be wrong.
+ DCHECK(TransientWindowStackingClient::instance_);
+
+ TransientWindowManager* child_manager = Get(child);
+ if (child_manager->transient_parent_)
+ Get(child_manager->transient_parent_)->RemoveTransientChild(child);
+ DCHECK(std::find(transient_children_.begin(), transient_children_.end(),
+ child) == transient_children_.end());
+ transient_children_.push_back(child);
+ child_manager->transient_parent_ = window_;
+
+ // Restack |child| properly above its transient parent, if they share the same
+ // parent.
+ if (child->parent() == window_->parent())
+ RestackTransientDescendants();
+
+ FOR_EACH_OBSERVER(TransientWindowObserver, observers_,
+ OnTransientChildAdded(window_, child));
+}
+
+void TransientWindowManager::RemoveTransientChild(Window* child) {
+ Windows::iterator i =
+ std::find(transient_children_.begin(), transient_children_.end(), child);
+ DCHECK(i != transient_children_.end());
+ transient_children_.erase(i);
+ TransientWindowManager* child_manager = Get(child);
+ DCHECK_EQ(window_, child_manager->transient_parent_);
+ child_manager->transient_parent_ = NULL;
+
+ // If |child| and its former transient parent share the same parent, |child|
+ // should be restacked properly so it is not among transient children of its
+ // former parent, anymore.
+ if (window_->parent() == child->parent())
+ RestackTransientDescendants();
+
+ FOR_EACH_OBSERVER(TransientWindowObserver, observers_,
+ OnTransientChildRemoved(window_, child));
+}
+
+bool TransientWindowManager::IsStackingTransient(
+ const aura::Window* target) const {
+ return stacking_target_ == target;
+}
+
+TransientWindowManager::TransientWindowManager(Window* window)
+ : window_(window),
+ transient_parent_(NULL),
+ stacking_target_(NULL) {
+ window_->AddObserver(this);
+}
+
+void TransientWindowManager::RestackTransientDescendants() {
+ Window* parent = window_->parent();
+ if (!parent)
+ return;
+
+ // Stack any transient children that share the same parent to be in front of
+ // |window_|. The existing stacking order is preserved by iterating backwards
+ // and always stacking on top.
+ Window::Windows children(parent->children());
+ for (Window::Windows::reverse_iterator it = children.rbegin();
+ it != children.rend(); ++it) {
+ if ((*it) != window_ && HasTransientAncestor(*it, window_)) {
+ TransientWindowManager* descendant_manager = Get(*it);
+ base::AutoReset<Window*> resetter(
+ &descendant_manager->stacking_target_,
+ window_);
+ parent->StackChildAbove((*it), window_);
+ }
+ }
+}
+
+void TransientWindowManager::OnWindowParentChanged(aura::Window* window,
+ aura::Window* parent) {
+ DCHECK_EQ(window_, window);
+ // Stack |window| properly if it is transient child of a sibling.
+ Window* transient_parent = wm::GetTransientParent(window);
+ if (transient_parent && transient_parent->parent() == parent) {
+ TransientWindowManager* transient_parent_manager =
+ Get(transient_parent);
+ transient_parent_manager->RestackTransientDescendants();
+ }
+}
+
+void TransientWindowManager::OnWindowVisibilityChanging(Window* window,
+ bool visible) {
+ // TODO(sky): move handling of becoming visible here.
+ if (!visible) {
+ std::for_each(transient_children_.begin(), transient_children_.end(),
+ std::mem_fun(&Window::Hide));
+ }
+}
+
+void TransientWindowManager::OnWindowStackingChanged(Window* window) {
+ DCHECK_EQ(window_, window);
+
+ // Do nothing if we initiated the stacking change.
+ const TransientWindowManager* transient_manager =
+ Get(static_cast<const Window*>(window));
+ if (transient_manager && transient_manager->stacking_target_) {
+ Windows::const_iterator window_i = std::find(
+ window->parent()->children().begin(),
+ window->parent()->children().end(),
+ window);
+ DCHECK(window_i != window->parent()->children().end());
+ if (window_i != window->parent()->children().begin() &&
+ (*(window_i - 1) == transient_manager->stacking_target_))
+ return;
+ }
+
+ RestackTransientDescendants();
+}
+
+void TransientWindowManager::OnWindowDestroying(Window* window) {
+ // Removes ourselves from our transient parent (if it hasn't been done by the
+ // RootWindow).
+ if (transient_parent_) {
+ TransientWindowManager::Get(transient_parent_)->RemoveTransientChild(
+ window_);
+ }
+
+ // 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());
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/transient_window_manager.h b/chromium/ui/wm/core/transient_window_manager.h
new file mode 100644
index 00000000000..7d37679f924
--- /dev/null
+++ b/chromium/ui/wm/core/transient_window_manager.h
@@ -0,0 +1,91 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_WM_CORE_TRANSIENT_WINDOW_MANAGER_H_
+#define UI_WM_CORE_TRANSIENT_WINDOW_MANAGER_H_
+
+#include <vector>
+
+#include "base/observer_list.h"
+#include "ui/aura/window_observer.h"
+#include "ui/wm/wm_export.h"
+
+namespace wm {
+
+class TransientWindowObserver;
+
+// TransientWindowManager manages the set of transient children for a window
+// along with the transient parent. 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.
+// TODO(sky): when we nuke TransientWindowClient rename this to
+// TransientWindowController.
+class WM_EXPORT TransientWindowManager : public aura::WindowObserver {
+ public:
+ typedef std::vector<aura::Window*> Windows;
+
+ virtual ~TransientWindowManager();
+
+ // Returns the TransientWindowManager for |window|. This never returns NULL.
+ static TransientWindowManager* Get(aura::Window* window);
+
+ // Returns the TransientWindowManager for |window| only if it already exists.
+ // WARNING: this may return NULL.
+ static const TransientWindowManager* Get(const aura::Window* window);
+
+ void AddObserver(TransientWindowObserver* observer);
+ void RemoveObserver(TransientWindowObserver* observer);
+
+ // Adds or removes a transient child.
+ void AddTransientChild(aura::Window* child);
+ void RemoveTransientChild(aura::Window* child);
+
+ const Windows& transient_children() const { return transient_children_; }
+
+ aura::Window* transient_parent() { return transient_parent_; }
+ const aura::Window* transient_parent() const { return transient_parent_; }
+
+ // Returns true if in the process of stacking |window_| on top of |target|.
+ // That is, when the stacking order of a window changes
+ // (OnWindowStackingChanged()) the transients may get restacked as well. This
+ // function can be used to detect if TransientWindowManager is in the process
+ // of stacking a transient as the result of window stacking changing.
+ bool IsStackingTransient(const aura::Window* target) const;
+
+ private:
+ explicit TransientWindowManager(aura::Window* window);
+
+ // Stacks transient descendants of this window that are its siblings just
+ // above it.
+ void RestackTransientDescendants();
+
+ // WindowObserver:
+ virtual void OnWindowParentChanged(aura::Window* window,
+ aura::Window* parent) OVERRIDE;
+ virtual void OnWindowVisibilityChanging(aura::Window* window,
+ bool visible) OVERRIDE;
+ virtual void OnWindowStackingChanged(aura::Window* window) OVERRIDE;
+ virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
+
+ aura::Window* window_;
+ aura::Window* transient_parent_;
+ Windows transient_children_;
+
+ // If non-null we're actively restacking transient as the result of a
+ // transient ancestor changing.
+ aura::Window* stacking_target_;
+
+ ObserverList<TransientWindowObserver> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(TransientWindowManager);
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_TRANSIENT_WINDOW_MANAGER_H_
diff --git a/chromium/ui/wm/core/transient_window_manager_unittest.cc b/chromium/ui/wm/core/transient_window_manager_unittest.cc
new file mode 100644
index 00000000000..05b8e88e4de
--- /dev/null
+++ b/chromium/ui/wm/core/transient_window_manager_unittest.cc
@@ -0,0 +1,666 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/wm/core/transient_window_manager.h"
+
+#include "ui/aura/client/visibility_client.h"
+#include "ui/aura/client/window_tree_client.h"
+#include "ui/aura/layout_manager.h"
+#include "ui/aura/test/aura_test_base.h"
+#include "ui/aura/test/test_windows.h"
+#include "ui/aura/window.h"
+#include "ui/wm/core/transient_window_observer.h"
+#include "ui/wm/core/window_util.h"
+#include "ui/wm/core/wm_state.h"
+
+using aura::Window;
+
+using aura::test::ChildWindowIDsAsString;
+using aura::test::CreateTestWindowWithId;
+
+namespace wm {
+
+class TestTransientWindowObserver : public TransientWindowObserver {
+ public:
+ TestTransientWindowObserver() : add_count_(0), remove_count_(0) {
+ }
+
+ virtual ~TestTransientWindowObserver() {
+ }
+
+ int add_count() const { return add_count_; }
+ int remove_count() const { return remove_count_; }
+
+ // TransientWindowObserver overrides:
+ virtual void OnTransientChildAdded(Window* window,
+ Window* transient) OVERRIDE {
+ add_count_++;
+ }
+ virtual void OnTransientChildRemoved(Window* window,
+ Window* transient) OVERRIDE {
+ remove_count_++;
+ }
+
+ private:
+ int add_count_;
+ int remove_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestTransientWindowObserver);
+};
+
+class TransientWindowManagerTest : public aura::test::AuraTestBase {
+ public:
+ TransientWindowManagerTest() {}
+ virtual ~TransientWindowManagerTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ AuraTestBase::SetUp();
+ wm_state_.reset(new wm::WMState);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ wm_state_.reset();
+ AuraTestBase::TearDown();
+ }
+
+ protected:
+ // Creates a transient window that is transient to |parent|.
+ Window* CreateTransientChild(int id, Window* parent) {
+ Window* window = new Window(NULL);
+ window->set_id(id);
+ window->SetType(ui::wm::WINDOW_TYPE_NORMAL);
+ window->Init(aura::WINDOW_LAYER_TEXTURED);
+ AddTransientChild(parent, window);
+ aura::client::ParentWindowWithContext(window, root_window(), gfx::Rect());
+ return window;
+ }
+
+ private:
+ scoped_ptr<wm::WMState> wm_state_;
+
+ DISALLOW_COPY_AND_ASSIGN(TransientWindowManagerTest);
+};
+
+// Various assertions for transient children.
+TEST_F(TransientWindowManagerTest, 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());
+ // w2 is now owned by w1.
+ AddTransientChild(w1.get(), w2);
+ // Stack w1 at the top (end), this should force w2 to be last (on top of w1).
+ parent->StackChildAtTop(w1.get());
+ ASSERT_EQ(3u, parent->children().size());
+ EXPECT_EQ(w2, parent->children().back());
+
+ // Destroy w1, which should also destroy w3 (since it's a transient child).
+ w1.reset();
+ w2 = 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());
+ AddTransientChild(w1.get(), 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 transient children are stacked as a unit when using stack above.
+TEST_F(TransientWindowManagerTest, TransientChildrenGroupAbove) {
+ scoped_ptr<Window> parent(CreateTestWindowWithId(0, root_window()));
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get()));
+ Window* w11 = CreateTestWindowWithId(11, parent.get());
+ scoped_ptr<Window> w2(CreateTestWindowWithId(2, parent.get()));
+ Window* w21 = CreateTestWindowWithId(21, parent.get());
+ Window* w211 = CreateTestWindowWithId(211, parent.get());
+ Window* w212 = CreateTestWindowWithId(212, parent.get());
+ Window* w213 = CreateTestWindowWithId(213, parent.get());
+ Window* w22 = CreateTestWindowWithId(22, parent.get());
+ ASSERT_EQ(8u, parent->children().size());
+
+ // w11 is now owned by w1.
+ AddTransientChild(w1.get(), w11);
+ // w21 is now owned by w2.
+ AddTransientChild(w2.get(), w21);
+ // w22 is now owned by w2.
+ AddTransientChild(w2.get(), w22);
+ // w211 is now owned by w21.
+ AddTransientChild(w21, w211);
+ // w212 is now owned by w21.
+ AddTransientChild(w21, w212);
+ // w213 is now owned by w21.
+ AddTransientChild(w21, w213);
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ // Stack w1 at the top (end), this should force w11 to be last (on top of w1).
+ parent->StackChildAtTop(w1.get());
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
+
+ // This tests that the order in children_ array rather than in
+ // transient_children_ array is used when reinserting transient children.
+ // If transient_children_ array was used '22' would be following '21'.
+ parent->StackChildAtTop(w2.get());
+ EXPECT_EQ(w22, parent->children().back());
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w11, w2.get());
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w21, w1.get());
+ EXPECT_EQ(w22, parent->children().back());
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w21, w22);
+ EXPECT_EQ(w213, parent->children().back());
+ EXPECT_EQ("1 11 2 22 21 211 212 213", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w11, w21);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 211 212 213 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w213, w21);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+
+ // No change when stacking a transient parent above its transient child.
+ parent->StackChildAbove(w21, w211);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+
+ // This tests that the order in children_ array rather than in
+ // transient_children_ array is used when reinserting transient children.
+ // If transient_children_ array was used '22' would be following '21'.
+ parent->StackChildAbove(w2.get(), w1.get());
+ EXPECT_EQ(w212, parent->children().back());
+ EXPECT_EQ("1 11 2 22 21 213 211 212", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w11, w213);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+}
+
+// Tests that transient children are stacked as a unit when using stack below.
+TEST_F(TransientWindowManagerTest, TransientChildrenGroupBelow) {
+ scoped_ptr<Window> parent(CreateTestWindowWithId(0, root_window()));
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get()));
+ Window* w11 = CreateTestWindowWithId(11, parent.get());
+ scoped_ptr<Window> w2(CreateTestWindowWithId(2, parent.get()));
+ Window* w21 = CreateTestWindowWithId(21, parent.get());
+ Window* w211 = CreateTestWindowWithId(211, parent.get());
+ Window* w212 = CreateTestWindowWithId(212, parent.get());
+ Window* w213 = CreateTestWindowWithId(213, parent.get());
+ Window* w22 = CreateTestWindowWithId(22, parent.get());
+ ASSERT_EQ(8u, parent->children().size());
+
+ // w11 is now owned by w1.
+ AddTransientChild(w1.get(), w11);
+ // w21 is now owned by w2.
+ AddTransientChild(w2.get(), w21);
+ // w22 is now owned by w2.
+ AddTransientChild(w2.get(), w22);
+ // w211 is now owned by w21.
+ AddTransientChild(w21, w211);
+ // w212 is now owned by w21.
+ AddTransientChild(w21, w212);
+ // w213 is now owned by w21.
+ AddTransientChild(w21, w213);
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ // Stack w2 at the bottom, this should force w11 to be last (on top of w1).
+ // This also tests that the order in children_ array rather than in
+ // transient_children_ array is used when reinserting transient children.
+ // If transient_children_ array was used '22' would be following '21'.
+ parent->StackChildAtBottom(w2.get());
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAtBottom(w1.get());
+ EXPECT_EQ(w22, parent->children().back());
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w21, w1.get());
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w11, w2.get());
+ EXPECT_EQ(w22, parent->children().back());
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w22, w21);
+ EXPECT_EQ(w213, parent->children().back());
+ EXPECT_EQ("1 11 2 22 21 211 212 213", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w21, w11);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 211 212 213 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w213, w211);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+
+ // No change when stacking a transient parent below its transient child.
+ parent->StackChildBelow(w21, w211);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w1.get(), w2.get());
+ EXPECT_EQ(w212, parent->children().back());
+ EXPECT_EQ("1 11 2 22 21 213 211 212", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w213, w11);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+}
+
+// Tests that transient windows are stacked properly when created.
+TEST_F(TransientWindowManagerTest, StackUponCreation) {
+ scoped_ptr<Window> window0(CreateTestWindowWithId(0, root_window()));
+ scoped_ptr<Window> window1(CreateTestWindowWithId(1, root_window()));
+
+ scoped_ptr<Window> window2(CreateTransientChild(2, window0.get()));
+ EXPECT_EQ("0 2 1", ChildWindowIDsAsString(root_window()));
+}
+
+// Tests that windows are restacked properly after a call to AddTransientChild()
+// or RemoveTransientChild().
+TEST_F(TransientWindowManagerTest, RestackUponAddOrRemoveTransientChild) {
+ scoped_ptr<Window> windows[4];
+ for (int i = 0; i < 4; i++)
+ windows[i].reset(CreateTestWindowWithId(i, root_window()));
+ EXPECT_EQ("0 1 2 3", ChildWindowIDsAsString(root_window()));
+
+ AddTransientChild(windows[0].get(), windows[2].get());
+ EXPECT_EQ("0 2 1 3", ChildWindowIDsAsString(root_window()));
+
+ AddTransientChild(windows[0].get(), windows[3].get());
+ EXPECT_EQ("0 2 3 1", ChildWindowIDsAsString(root_window()));
+
+ RemoveTransientChild(windows[0].get(), windows[2].get());
+ EXPECT_EQ("0 3 2 1", ChildWindowIDsAsString(root_window()));
+
+ RemoveTransientChild(windows[0].get(), windows[3].get());
+ EXPECT_EQ("0 3 2 1", ChildWindowIDsAsString(root_window()));
+}
+
+namespace {
+
+// Used by NotifyDelegateAfterDeletingTransients. Adds a string to a vector when
+// OnWindowDestroyed() is invoked so that destruction order can be verified.
+class DestroyedTrackingDelegate : public aura::test::TestWindowDelegate {
+ public:
+ explicit DestroyedTrackingDelegate(const std::string& name,
+ std::vector<std::string>* results)
+ : name_(name),
+ results_(results) {}
+
+ virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE {
+ results_->push_back(name_);
+ }
+
+ private:
+ const std::string name_;
+ std::vector<std::string>* results_;
+
+ DISALLOW_COPY_AND_ASSIGN(DestroyedTrackingDelegate);
+};
+
+} // namespace
+
+// Verifies the delegate is notified of destruction after transients are
+// destroyed.
+TEST_F(TransientWindowManagerTest, NotifyDelegateAfterDeletingTransients) {
+ std::vector<std::string> destruction_order;
+
+ DestroyedTrackingDelegate parent_delegate("parent", &destruction_order);
+ scoped_ptr<Window> parent(new Window(&parent_delegate));
+ parent->Init(aura::WINDOW_LAYER_NOT_DRAWN);
+
+ DestroyedTrackingDelegate transient_delegate("transient", &destruction_order);
+ Window* transient = new Window(&transient_delegate); // Owned by |parent|.
+ transient->Init(aura::WINDOW_LAYER_NOT_DRAWN);
+ AddTransientChild(parent.get(), transient);
+ parent.reset();
+
+ ASSERT_EQ(2u, destruction_order.size());
+ EXPECT_EQ("transient", destruction_order[0]);
+ EXPECT_EQ("parent", destruction_order[1]);
+}
+
+TEST_F(TransientWindowManagerTest, StackTransientsWhoseLayersHaveNoDelegate) {
+ // 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_window()));
+
+ // 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_window()->StackChildAtTop(window1.get());
+
+ EXPECT_EQ("2 3 1 11 12 13", ChildWindowIDsAsString(root_window()));
+}
+
+TEST_F(TransientWindowManagerTest,
+ StackTransientsLayersRelativeToOtherTransients) {
+ // 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()));
+
+ EXPECT_EQ("1 11 12 13", ChildWindowIDsAsString(root_window()));
+
+ // Stack 11 above 12.
+ root_window()->StackChildAbove(window11.get(), window12.get());
+ EXPECT_EQ("1 12 11 13", ChildWindowIDsAsString(root_window()));
+
+ // Stack 13 below 12.
+ root_window()->StackChildBelow(window13.get(), window12.get());
+ EXPECT_EQ("1 13 12 11", ChildWindowIDsAsString(root_window()));
+
+ // Stack 11 above 1.
+ root_window()->StackChildAbove(window11.get(), window1.get());
+ EXPECT_EQ("1 11 13 12", ChildWindowIDsAsString(root_window()));
+
+ // Stack 12 below 13.
+ root_window()->StackChildBelow(window12.get(), window13.get());
+ EXPECT_EQ("1 11 12 13", ChildWindowIDsAsString(root_window()));
+}
+
+TEST_F(TransientWindowManagerTest,
+ StackTransientsLayersRelativeToOtherTransientsNoLayerDelegate) {
+ // 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_window()));
+
+ window1->layer()->set_delegate(NULL);
+
+ // Stack 1 at top.
+ root_window()->StackChildAtTop(window1.get());
+ EXPECT_EQ("2 3 1 11 12 13", ChildWindowIDsAsString(root_window()));
+}
+
+class StackingMadrigalLayoutManager : public aura::LayoutManager {
+ public:
+ explicit StackingMadrigalLayoutManager(Window* 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);
+ }
+
+ Window* root_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(StackingMadrigalLayoutManager);
+};
+
+class StackingMadrigalVisibilityClient : public aura::client::VisibilityClient {
+ public:
+ explicit StackingMadrigalVisibilityClient(Window* root_window)
+ : ignored_window_(NULL) {
+ aura::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(TransientWindowManagerTest, 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(aura::test::WindowIsAbove(window11.get(), window1.get()));
+ EXPECT_TRUE(aura::test::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(aura::test::WindowIsAbove(window12.get(), window1.get()));
+ EXPECT_TRUE(aura::test::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(aura::test::WindowIsAbove(window12.get(), window1.get()));
+ EXPECT_TRUE(aura::test::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(TransientWindowManagerTest, 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.
+ Window* 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());
+ EXPECT_EQ("1 11 2 21", ChildWindowIDsAsString(root_window()));
+
+ // This brings window1 and its transient to the front.
+ root->StackChildAtTop(window1.get());
+ EXPECT_EQ("2 21 1 11", ChildWindowIDsAsString(root_window()));
+
+ 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());
+}
+
+// Verifies TransientWindowObserver is notified appropriately.
+TEST_F(TransientWindowManagerTest, TransientWindowObserverNotified) {
+ scoped_ptr<Window> parent(CreateTestWindowWithId(0, root_window()));
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get()));
+
+ TestTransientWindowObserver test_observer;
+ TransientWindowManager::Get(parent.get())->AddObserver(&test_observer);
+
+ AddTransientChild(parent.get(), w1.get());
+ EXPECT_EQ(1, test_observer.add_count());
+ EXPECT_EQ(0, test_observer.remove_count());
+
+ RemoveTransientChild(parent.get(), w1.get());
+ EXPECT_EQ(1, test_observer.add_count());
+ EXPECT_EQ(1, test_observer.remove_count());
+
+ TransientWindowManager::Get(parent.get())->RemoveObserver(&test_observer);
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/transient_window_observer.h b/chromium/ui/wm/core/transient_window_observer.h
new file mode 100644
index 00000000000..aff5af5e2ef
--- /dev/null
+++ b/chromium/ui/wm/core/transient_window_observer.h
@@ -0,0 +1,32 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_WM_CORE_TRANSIENT_WINDOW_OBSERVER_H_
+#define UI_WM_CORE_TRANSIENT_WINDOW_OBSERVER_H_
+
+#include "ui/wm/wm_export.h"
+
+namespace aura {
+class Window;
+}
+
+namespace wm {
+
+class WM_EXPORT TransientWindowObserver {
+ public:
+ // Called when a transient child is added to |window|.
+ virtual void OnTransientChildAdded(aura::Window* window,
+ aura::Window* transient) = 0;
+
+ // Called when a transient child is removed from |window|.
+ virtual void OnTransientChildRemoved(aura::Window* window,
+ aura::Window* transient) = 0;
+
+ protected:
+ virtual ~TransientWindowObserver() {}
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_TRANSIENT_WINDOW_OBSERVER_H_
diff --git a/chromium/ui/wm/core/transient_window_stacking_client.cc b/chromium/ui/wm/core/transient_window_stacking_client.cc
new file mode 100644
index 00000000000..f8dc42c51f0
--- /dev/null
+++ b/chromium/ui/wm/core/transient_window_stacking_client.cc
@@ -0,0 +1,131 @@
+// 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.
+
+#include "ui/wm/core/transient_window_stacking_client.h"
+
+#include <algorithm>
+
+#include "ui/wm/core/transient_window_manager.h"
+#include "ui/wm/core/window_util.h"
+
+using aura::Window;
+
+namespace wm {
+
+namespace {
+
+// Populates |ancestors| with all transient ancestors of |window| that are
+// siblings of |window|. Returns true if any ancestors were found, false if not.
+bool GetAllTransientAncestors(Window* window, Window::Windows* ancestors) {
+ Window* parent = window->parent();
+ for (; window; window = GetTransientParent(window)) {
+ if (window->parent() == parent)
+ ancestors->push_back(window);
+ }
+ return (!ancestors->empty());
+}
+
+// Replaces |window1| and |window2| with their possible transient ancestors that
+// are still siblings (have a common transient parent). |window1| and |window2|
+// are not modified if such ancestors cannot be found.
+void FindCommonTransientAncestor(Window** window1, Window** window2) {
+ DCHECK(window1);
+ DCHECK(window2);
+ DCHECK(*window1);
+ DCHECK(*window2);
+ // Assemble chains of ancestors of both windows.
+ Window::Windows ancestors1;
+ Window::Windows ancestors2;
+ if (!GetAllTransientAncestors(*window1, &ancestors1) ||
+ !GetAllTransientAncestors(*window2, &ancestors2)) {
+ return;
+ }
+ // Walk the two chains backwards and look for the first difference.
+ Window::Windows::reverse_iterator it1 = ancestors1.rbegin();
+ Window::Windows::reverse_iterator it2 = ancestors2.rbegin();
+ for (; it1 != ancestors1.rend() && it2 != ancestors2.rend(); ++it1, ++it2) {
+ if (*it1 != *it2) {
+ *window1 = *it1;
+ *window2 = *it2;
+ break;
+ }
+ }
+}
+
+// Adjusts |target| so that we don't attempt to stack on top of a window with a
+// NULL delegate.
+void SkipNullDelegates(Window::StackDirection direction, Window** target) {
+ const Window::Windows& children((*target)->parent()->children());
+ 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
+ // TransientWindowManagerTest.StackingMadrigal and StackOverClosingTransient
+ // for an explanation of this.
+ while (target_i > 0) {
+ const size_t index = direction == Window::STACK_ABOVE ?
+ target_i : target_i - 1;
+ if (!children[index]->layer() ||
+ children[index]->layer()->delegate() != NULL)
+ break;
+ --target_i;
+ }
+ *target = children[target_i];
+}
+
+} // namespace
+
+// static
+TransientWindowStackingClient* TransientWindowStackingClient::instance_ = NULL;
+
+TransientWindowStackingClient::TransientWindowStackingClient() {
+ instance_ = this;
+}
+
+TransientWindowStackingClient::~TransientWindowStackingClient() {
+ if (instance_ == this)
+ instance_ = NULL;
+}
+
+bool TransientWindowStackingClient::AdjustStacking(
+ Window** child,
+ Window** target,
+ Window::StackDirection* direction) {
+ const TransientWindowManager* transient_manager =
+ TransientWindowManager::Get(static_cast<const Window*>(*child));
+ if (transient_manager && transient_manager->IsStackingTransient(*target))
+ return true;
+
+ // For windows that have transient children stack the transient ancestors that
+ // are siblings. This prevents one transient group from being inserted in the
+ // middle of another.
+ FindCommonTransientAncestor(child, target);
+
+ // When stacking above skip to the topmost transient descendant of the target.
+ if (*direction == Window::STACK_ABOVE &&
+ !HasTransientAncestor(*child, *target)) {
+ const Window::Windows& siblings((*child)->parent()->children());
+ size_t target_i =
+ std::find(siblings.begin(), siblings.end(), *target) - siblings.begin();
+ while (target_i + 1 < siblings.size() &&
+ HasTransientAncestor(siblings[target_i + 1], *target)) {
+ ++target_i;
+ }
+ *target = siblings[target_i];
+ }
+
+ SkipNullDelegates(*direction, target);
+
+ // If we couldn't find a valid target position, don't move anything.
+ if (*direction == Window::STACK_ABOVE &&
+ ((*target)->layer() && (*target)->layer()->delegate() == NULL)) {
+ return false;
+ }
+
+ return *child != *target;
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/transient_window_stacking_client.h b/chromium/ui/wm/core/transient_window_stacking_client.h
new file mode 100644
index 00000000000..4ab4aea1742
--- /dev/null
+++ b/chromium/ui/wm/core/transient_window_stacking_client.h
@@ -0,0 +1,37 @@
+// 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_WM_CORE_TRANSIENT_WINDOW_STACKING_CLIENT_H_
+#define UI_WM_CORE_TRANSIENT_WINDOW_STACKING_CLIENT_H_
+
+#include "ui/aura/client/window_stacking_client.h"
+#include "ui/wm/wm_export.h"
+
+namespace wm {
+
+class TransientWindowManager;
+
+class WM_EXPORT TransientWindowStackingClient
+ : public aura::client::WindowStackingClient {
+ public:
+ TransientWindowStackingClient();
+ virtual ~TransientWindowStackingClient();
+
+ // WindowStackingClient:
+ virtual bool AdjustStacking(aura::Window** child,
+ aura::Window** target,
+ aura::Window::StackDirection* direction) OVERRIDE;
+
+ private:
+ // Purely for DCHECKs.
+ friend class TransientWindowManager;
+
+ static TransientWindowStackingClient* instance_;
+
+ DISALLOW_COPY_AND_ASSIGN(TransientWindowStackingClient);
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_TRANSIENT_WINDOW_STACKING_CLIENT_H_
diff --git a/chromium/ui/wm/core/transient_window_stacking_client_unittest.cc b/chromium/ui/wm/core/transient_window_stacking_client_unittest.cc
new file mode 100644
index 00000000000..c5d3bd05319
--- /dev/null
+++ b/chromium/ui/wm/core/transient_window_stacking_client_unittest.cc
@@ -0,0 +1,215 @@
+// 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.
+
+#include "ui/wm/core/transient_window_stacking_client.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/aura/test/aura_test_base.h"
+#include "ui/aura/test/test_windows.h"
+#include "ui/compositor/test/test_layers.h"
+#include "ui/wm/core/window_util.h"
+
+using aura::test::ChildWindowIDsAsString;
+using aura::test::CreateTestWindowWithId;
+using aura::Window;
+
+namespace wm {
+
+class TransientWindowStackingClientTest : public aura::test::AuraTestBase {
+ public:
+ TransientWindowStackingClientTest() {}
+ virtual ~TransientWindowStackingClientTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ AuraTestBase::SetUp();
+ client_.reset(new TransientWindowStackingClient);
+ aura::client::SetWindowStackingClient(client_.get());
+ }
+
+ virtual void TearDown() OVERRIDE {
+ aura::client::SetWindowStackingClient(NULL);
+ AuraTestBase::TearDown();
+ }
+
+ private:
+ scoped_ptr<TransientWindowStackingClient> client_;
+ DISALLOW_COPY_AND_ASSIGN(TransientWindowStackingClientTest);
+};
+
+// Tests that transient children are stacked as a unit when using stack above.
+TEST_F(TransientWindowStackingClientTest, TransientChildrenGroupAbove) {
+ scoped_ptr<Window> parent(CreateTestWindowWithId(0, root_window()));
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get()));
+ Window* w11 = CreateTestWindowWithId(11, parent.get());
+ scoped_ptr<Window> w2(CreateTestWindowWithId(2, parent.get()));
+ Window* w21 = CreateTestWindowWithId(21, parent.get());
+ Window* w211 = CreateTestWindowWithId(211, parent.get());
+ Window* w212 = CreateTestWindowWithId(212, parent.get());
+ Window* w213 = CreateTestWindowWithId(213, parent.get());
+ Window* w22 = CreateTestWindowWithId(22, parent.get());
+ ASSERT_EQ(8u, parent->children().size());
+
+ AddTransientChild(w1.get(), w11); // w11 is now owned by w1.
+ AddTransientChild(w2.get(), w21); // w21 is now owned by w2.
+ AddTransientChild(w2.get(), w22); // w22 is now owned by w2.
+ AddTransientChild(w21, w211); // w211 is now owned by w21.
+ AddTransientChild(w21, w212); // w212 is now owned by w21.
+ AddTransientChild(w21, w213); // w213 is now owned by w21.
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ // Stack w1 at the top (end), this should force w11 to be last (on top of w1).
+ parent->StackChildAtTop(w1.get());
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
+
+ // This tests that the order in children_ array rather than in
+ // transient_children_ array is used when reinserting transient children.
+ // If transient_children_ array was used '22' would be following '21'.
+ parent->StackChildAtTop(w2.get());
+ EXPECT_EQ(w22, parent->children().back());
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w11, w2.get());
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w21, w1.get());
+ EXPECT_EQ(w22, parent->children().back());
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w21, w22);
+ EXPECT_EQ(w213, parent->children().back());
+ EXPECT_EQ("1 11 2 22 21 211 212 213", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w11, w21);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 211 212 213 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w213, w21);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+
+ // No change when stacking a transient parent above its transient child.
+ parent->StackChildAbove(w21, w211);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+
+ // This tests that the order in children_ array rather than in
+ // transient_children_ array is used when reinserting transient children.
+ // If transient_children_ array was used '22' would be following '21'.
+ parent->StackChildAbove(w2.get(), w1.get());
+ EXPECT_EQ(w212, parent->children().back());
+ EXPECT_EQ("1 11 2 22 21 213 211 212", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w11, w213);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+}
+
+// Tests that transient children are stacked as a unit when using stack below.
+TEST_F(TransientWindowStackingClientTest, TransientChildrenGroupBelow) {
+ scoped_ptr<Window> parent(CreateTestWindowWithId(0, root_window()));
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get()));
+ Window* w11 = CreateTestWindowWithId(11, parent.get());
+ scoped_ptr<Window> w2(CreateTestWindowWithId(2, parent.get()));
+ Window* w21 = CreateTestWindowWithId(21, parent.get());
+ Window* w211 = CreateTestWindowWithId(211, parent.get());
+ Window* w212 = CreateTestWindowWithId(212, parent.get());
+ Window* w213 = CreateTestWindowWithId(213, parent.get());
+ Window* w22 = CreateTestWindowWithId(22, parent.get());
+ ASSERT_EQ(8u, parent->children().size());
+
+ AddTransientChild(w1.get(), w11); // w11 is now owned by w1.
+ AddTransientChild(w2.get(), w21); // w21 is now owned by w2.
+ AddTransientChild(w2.get(), w22); // w22 is now owned by w2.
+ AddTransientChild(w21, w211); // w211 is now owned by w21.
+ AddTransientChild(w21, w212); // w212 is now owned by w21.
+ AddTransientChild(w21, w213); // w213 is now owned by w21.
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ // Stack w2 at the bottom, this should force w11 to be last (on top of w1).
+ // This also tests that the order in children_ array rather than in
+ // transient_children_ array is used when reinserting transient children.
+ // If transient_children_ array was used '22' would be following '21'.
+ parent->StackChildAtBottom(w2.get());
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAtBottom(w1.get());
+ EXPECT_EQ(w22, parent->children().back());
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w21, w1.get());
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w11, w2.get());
+ EXPECT_EQ(w22, parent->children().back());
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w22, w21);
+ EXPECT_EQ(w213, parent->children().back());
+ EXPECT_EQ("1 11 2 22 21 211 212 213", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w21, w11);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 211 212 213 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w213, w211);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+
+ // No change when stacking a transient parent below its transient child.
+ parent->StackChildBelow(w21, w211);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w1.get(), w2.get());
+ EXPECT_EQ(w212, parent->children().back());
+ EXPECT_EQ("1 11 2 22 21 213 211 212", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w213, w11);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+}
+
+TEST_F(TransientWindowStackingClientTest,
+ 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()));
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/user_activity_detector.cc b/chromium/ui/wm/core/user_activity_detector.cc
new file mode 100644
index 00000000000..56bf563b3d0
--- /dev/null
+++ b/chromium/ui/wm/core/user_activity_detector.cc
@@ -0,0 +1,112 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/wm/core/user_activity_detector.h"
+
+#include "base/format_macros.h"
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "ui/events/event.h"
+#include "ui/wm/core/user_activity_observer.h"
+
+namespace wm {
+
+namespace {
+
+// Returns a string describing |event|.
+std::string GetEventDebugString(const ui::Event* event) {
+ std::string details = base::StringPrintf(
+ "type=%d name=%s flags=%d time=%" PRId64,
+ event->type(), event->name().c_str(), event->flags(),
+ event->time_stamp().InMilliseconds());
+
+ if (event->IsKeyEvent()) {
+ details += base::StringPrintf(" key_code=%d",
+ static_cast<const ui::KeyEvent*>(event)->key_code());
+ } else if (event->IsMouseEvent() || event->IsTouchEvent() ||
+ event->IsGestureEvent()) {
+ details += base::StringPrintf(" location=%s",
+ static_cast<const ui::LocatedEvent*>(
+ event)->location().ToString().c_str());
+ }
+
+ return details;
+}
+
+} // namespace
+
+const int UserActivityDetector::kNotifyIntervalMs = 200;
+
+// Too low and mouse events generated at the tail end of reconfiguration
+// will be reported as user activity and turn the screen back on; too high
+// and we'll ignore legitimate activity.
+const int UserActivityDetector::kDisplayPowerChangeIgnoreMouseMs = 1000;
+
+UserActivityDetector::UserActivityDetector() {
+}
+
+UserActivityDetector::~UserActivityDetector() {
+}
+
+bool UserActivityDetector::HasObserver(UserActivityObserver* observer) const {
+ return observers_.HasObserver(observer);
+}
+
+void UserActivityDetector::AddObserver(UserActivityObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void UserActivityDetector::RemoveObserver(UserActivityObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void UserActivityDetector::OnDisplayPowerChanging() {
+ honor_mouse_events_time_ = GetCurrentTime() +
+ base::TimeDelta::FromMilliseconds(kDisplayPowerChangeIgnoreMouseMs);
+}
+
+void UserActivityDetector::OnKeyEvent(ui::KeyEvent* event) {
+ HandleActivity(event);
+}
+
+void UserActivityDetector::OnMouseEvent(ui::MouseEvent* event) {
+ if (event->flags() & ui::EF_IS_SYNTHESIZED)
+ return;
+ if (!honor_mouse_events_time_.is_null() &&
+ GetCurrentTime() < honor_mouse_events_time_)
+ return;
+
+ HandleActivity(event);
+}
+
+void UserActivityDetector::OnScrollEvent(ui::ScrollEvent* event) {
+ HandleActivity(event);
+}
+
+void UserActivityDetector::OnTouchEvent(ui::TouchEvent* event) {
+ HandleActivity(event);
+}
+
+void UserActivityDetector::OnGestureEvent(ui::GestureEvent* event) {
+ HandleActivity(event);
+}
+
+base::TimeTicks UserActivityDetector::GetCurrentTime() const {
+ return !now_for_test_.is_null() ? now_for_test_ : base::TimeTicks::Now();
+}
+
+void UserActivityDetector::HandleActivity(const ui::Event* event) {
+ base::TimeTicks now = GetCurrentTime();
+ last_activity_time_ = now;
+ if (last_observer_notification_time_.is_null() ||
+ (now - last_observer_notification_time_).InMillisecondsF() >=
+ kNotifyIntervalMs) {
+ if (VLOG_IS_ON(1))
+ VLOG(1) << "Reporting user activity: " << GetEventDebugString(event);
+ FOR_EACH_OBSERVER(UserActivityObserver, observers_, OnUserActivity(event));
+ last_observer_notification_time_ = now;
+ }
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/user_activity_detector.h b/chromium/ui/wm/core/user_activity_detector.h
new file mode 100644
index 00000000000..851f07ac325
--- /dev/null
+++ b/chromium/ui/wm/core/user_activity_detector.h
@@ -0,0 +1,81 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_WM_CORE_USER_ACTIVITY_DETECTOR_H_
+#define UI_WM_CORE_USER_ACTIVITY_DETECTOR_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/observer_list.h"
+#include "base/time/time.h"
+#include "ui/events/event_handler.h"
+#include "ui/wm/wm_export.h"
+
+namespace wm {
+
+class UserActivityObserver;
+
+// Watches for input events and notifies observers that the user is active.
+class WM_EXPORT UserActivityDetector : public ui::EventHandler {
+ public:
+ // Minimum amount of time between notifications to observers.
+ static const int kNotifyIntervalMs;
+
+ // Amount of time that mouse events should be ignored after notification
+ // is received that displays' power states are being changed.
+ static const int kDisplayPowerChangeIgnoreMouseMs;
+
+ UserActivityDetector();
+ virtual ~UserActivityDetector();
+
+ base::TimeTicks last_activity_time() const { return last_activity_time_; }
+
+ void set_now_for_test(base::TimeTicks now) { now_for_test_ = now; }
+
+ bool HasObserver(UserActivityObserver* observer) const;
+ void AddObserver(UserActivityObserver* observer);
+ void RemoveObserver(UserActivityObserver* observer);
+
+ // Called when displays are about to be turned on or off.
+ void OnDisplayPowerChanging();
+
+ // ui::EventHandler implementation.
+ virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
+ virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
+ virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE;
+ virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE;
+ virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
+
+ private:
+ // Returns |now_for_test_| if set or base::TimeTicks::Now() otherwise.
+ base::TimeTicks GetCurrentTime() const;
+
+ // Updates |last_activity_time_|. Additionally notifies observers and
+ // updates |last_observer_notification_time_| if enough time has passed
+ // since the last notification.
+ void HandleActivity(const ui::Event* event);
+
+ ObserverList<UserActivityObserver> observers_;
+
+ // Last time at which user activity was observed.
+ base::TimeTicks last_activity_time_;
+
+ // Last time at which we notified observers that the user was active.
+ base::TimeTicks last_observer_notification_time_;
+
+ // If set, used when the current time is needed. This can be set by tests to
+ // simulate the passage of time.
+ base::TimeTicks now_for_test_;
+
+ // If set, mouse events will be ignored until this time is reached. This
+ // is to avoid reporting mouse events that occur when displays are turned
+ // on or off as user activity.
+ base::TimeTicks honor_mouse_events_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserActivityDetector);
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_USER_ACTIVITY_DETECTOR_H_
diff --git a/chromium/ui/wm/core/user_activity_detector_unittest.cc b/chromium/ui/wm/core/user_activity_detector_unittest.cc
new file mode 100644
index 00000000000..8ff14b4154f
--- /dev/null
+++ b/chromium/ui/wm/core/user_activity_detector_unittest.cc
@@ -0,0 +1,200 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/wm/core/user_activity_detector.h"
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
+#include "ui/aura/test/aura_test_base.h"
+#include "ui/events/event.h"
+#include "ui/events/event_constants.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/gfx/point.h"
+#include "ui/wm/core/user_activity_observer.h"
+
+namespace wm {
+
+// Implementation that just counts the number of times we've been told that the
+// user is active.
+class TestUserActivityObserver : public UserActivityObserver {
+ public:
+ TestUserActivityObserver() : num_invocations_(0) {}
+
+ int num_invocations() const { return num_invocations_; }
+ void reset_stats() { num_invocations_ = 0; }
+
+ // UserActivityObserver implementation.
+ virtual void OnUserActivity(const ui::Event* event) OVERRIDE {
+ num_invocations_++;
+ }
+
+ private:
+ // Number of times that OnUserActivity() has been called.
+ int num_invocations_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestUserActivityObserver);
+};
+
+class UserActivityDetectorTest : public aura::test::AuraTestBase {
+ public:
+ UserActivityDetectorTest() {}
+ virtual ~UserActivityDetectorTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ AuraTestBase::SetUp();
+ observer_.reset(new TestUserActivityObserver);
+ detector_.reset(new UserActivityDetector);
+ detector_->AddObserver(observer_.get());
+
+ now_ = base::TimeTicks::Now();
+ detector_->set_now_for_test(now_);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ detector_->RemoveObserver(observer_.get());
+ AuraTestBase::TearDown();
+ }
+
+ protected:
+ // Move |detector_|'s idea of the current time forward by |delta|.
+ void AdvanceTime(base::TimeDelta delta) {
+ now_ += delta;
+ detector_->set_now_for_test(now_);
+ }
+
+ scoped_ptr<UserActivityDetector> detector_;
+ scoped_ptr<TestUserActivityObserver> observer_;
+
+ base::TimeTicks now_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(UserActivityDetectorTest);
+};
+
+// Checks that the observer is notified in response to different types of input
+// events.
+TEST_F(UserActivityDetectorTest, Basic) {
+ ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE, false);
+ detector_->OnKeyEvent(&key_event);
+ EXPECT_FALSE(key_event.handled());
+ EXPECT_EQ(now_.ToInternalValue(),
+ detector_->last_activity_time().ToInternalValue());
+ EXPECT_EQ(1, observer_->num_invocations());
+ observer_->reset_stats();
+
+ base::TimeDelta advance_delta = base::TimeDelta::FromMilliseconds(
+ UserActivityDetector::kNotifyIntervalMs);
+ AdvanceTime(advance_delta);
+ ui::MouseEvent mouse_event(
+ ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), ui::EF_NONE, ui::EF_NONE);
+ detector_->OnMouseEvent(&mouse_event);
+ EXPECT_FALSE(mouse_event.handled());
+ EXPECT_EQ(now_.ToInternalValue(),
+ detector_->last_activity_time().ToInternalValue());
+ EXPECT_EQ(1, observer_->num_invocations());
+ observer_->reset_stats();
+
+ base::TimeTicks time_before_ignore = now_;
+
+ // Temporarily ignore mouse events when displays are turned on or off.
+ detector_->OnDisplayPowerChanging();
+ detector_->OnMouseEvent(&mouse_event);
+ EXPECT_FALSE(mouse_event.handled());
+ EXPECT_EQ(time_before_ignore.ToInternalValue(),
+ detector_->last_activity_time().ToInternalValue());
+ EXPECT_EQ(0, observer_->num_invocations());
+ observer_->reset_stats();
+
+ const base::TimeDelta kIgnoreMouseTime =
+ base::TimeDelta::FromMilliseconds(
+ UserActivityDetector::kDisplayPowerChangeIgnoreMouseMs);
+ AdvanceTime(kIgnoreMouseTime / 2);
+ detector_->OnMouseEvent(&mouse_event);
+ EXPECT_FALSE(mouse_event.handled());
+ EXPECT_EQ(time_before_ignore.ToInternalValue(),
+ detector_->last_activity_time().ToInternalValue());
+ EXPECT_EQ(0, observer_->num_invocations());
+ observer_->reset_stats();
+
+ // After enough time has passed, mouse events should be reported again.
+ AdvanceTime(std::max(kIgnoreMouseTime, advance_delta));
+ detector_->OnMouseEvent(&mouse_event);
+ EXPECT_FALSE(mouse_event.handled());
+ EXPECT_EQ(now_.ToInternalValue(),
+ detector_->last_activity_time().ToInternalValue());
+ EXPECT_EQ(1, observer_->num_invocations());
+ observer_->reset_stats();
+
+ AdvanceTime(advance_delta);
+ ui::TouchEvent touch_event(
+ ui::ET_TOUCH_PRESSED, gfx::Point(), 0, base::TimeDelta());
+ detector_->OnTouchEvent(&touch_event);
+ EXPECT_FALSE(touch_event.handled());
+ EXPECT_EQ(now_.ToInternalValue(),
+ detector_->last_activity_time().ToInternalValue());
+ EXPECT_EQ(1, observer_->num_invocations());
+ observer_->reset_stats();
+
+ AdvanceTime(advance_delta);
+ ui::GestureEvent gesture_event(
+ ui::ET_GESTURE_TAP, 0, 0, ui::EF_NONE,
+ base::TimeDelta::FromMilliseconds(base::Time::Now().ToDoubleT() * 1000),
+ ui::GestureEventDetails(ui::ET_GESTURE_TAP, 0, 0), 0U);
+ detector_->OnGestureEvent(&gesture_event);
+ EXPECT_FALSE(gesture_event.handled());
+ EXPECT_EQ(now_.ToInternalValue(),
+ detector_->last_activity_time().ToInternalValue());
+ EXPECT_EQ(1, observer_->num_invocations());
+ observer_->reset_stats();
+}
+
+// Checks that observers aren't notified too frequently.
+TEST_F(UserActivityDetectorTest, RateLimitNotifications) {
+ // The observer should be notified about a key event.
+ ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE, false);
+ detector_->OnKeyEvent(&event);
+ EXPECT_FALSE(event.handled());
+ EXPECT_EQ(1, observer_->num_invocations());
+ observer_->reset_stats();
+
+ // It shouldn't be notified if a second event occurs in the same instant in
+ // time.
+ detector_->OnKeyEvent(&event);
+ EXPECT_FALSE(event.handled());
+ EXPECT_EQ(0, observer_->num_invocations());
+ observer_->reset_stats();
+
+ // Advance the time, but not quite enough for another notification to be sent.
+ AdvanceTime(
+ base::TimeDelta::FromMilliseconds(
+ UserActivityDetector::kNotifyIntervalMs - 100));
+ detector_->OnKeyEvent(&event);
+ EXPECT_FALSE(event.handled());
+ EXPECT_EQ(0, observer_->num_invocations());
+ observer_->reset_stats();
+
+ // Advance time by the notification interval, definitely moving out of the
+ // rate limit. This should let us trigger another notification.
+ AdvanceTime(base::TimeDelta::FromMilliseconds(
+ UserActivityDetector::kNotifyIntervalMs));
+
+ detector_->OnKeyEvent(&event);
+ EXPECT_FALSE(event.handled());
+ EXPECT_EQ(1, observer_->num_invocations());
+}
+
+// Checks that the detector ignores synthetic mouse events.
+TEST_F(UserActivityDetectorTest, IgnoreSyntheticMouseEvents) {
+ ui::MouseEvent mouse_event(
+ ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), ui::EF_IS_SYNTHESIZED,
+ ui::EF_NONE);
+ detector_->OnMouseEvent(&mouse_event);
+ EXPECT_FALSE(mouse_event.handled());
+ EXPECT_EQ(base::TimeTicks().ToInternalValue(),
+ detector_->last_activity_time().ToInternalValue());
+ EXPECT_EQ(0, observer_->num_invocations());
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/user_activity_observer.h b/chromium/ui/wm/core/user_activity_observer.h
new file mode 100644
index 00000000000..0fb35a61f06
--- /dev/null
+++ b/chromium/ui/wm/core/user_activity_observer.h
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_WM_CORE_USER_ACTIVITY_OBSERVER_H_
+#define UI_WM_CORE_USER_ACTIVITY_OBSERVER_H_
+
+#include "base/basictypes.h"
+#include "ui/wm/wm_export.h"
+
+namespace ui {
+class Event;
+}
+
+namespace wm {
+
+// Interface for classes that want to be notified about user activity.
+// Implementations should register themselves with UserActivityDetector.
+class WM_EXPORT UserActivityObserver {
+ public:
+ // Invoked periodically while the user is active (i.e. generating input
+ // events). |event| is the event that triggered the notification; it may
+ // be NULL in some cases (e.g. testing or synthetic invocations).
+ virtual void OnUserActivity(const ui::Event* event) = 0;
+
+ protected:
+ UserActivityObserver() {}
+ virtual ~UserActivityObserver() {}
+
+ DISALLOW_COPY_AND_ASSIGN(UserActivityObserver);
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_USER_ACTIVITY_OBSERVER_H_
diff --git a/chromium/ui/wm/core/visibility_controller.cc b/chromium/ui/wm/core/visibility_controller.cc
new file mode 100644
index 00000000000..fc187a94902
--- /dev/null
+++ b/chromium/ui/wm/core/visibility_controller.cc
@@ -0,0 +1,89 @@
+// 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/wm/core/visibility_controller.h"
+
+#include "ui/aura/window.h"
+#include "ui/aura/window_property.h"
+#include "ui/compositor/layer.h"
+#include "ui/wm/core/window_animations.h"
+
+namespace wm {
+
+namespace {
+
+// Property set on all windows whose child windows' visibility changes are
+// animated.
+DEFINE_WINDOW_PROPERTY_KEY(
+ bool, kChildWindowVisibilityChangesAnimatedKey, false);
+
+bool ShouldAnimateWindow(aura::Window* window) {
+ return window->parent() && window->parent()->GetProperty(
+ kChildWindowVisibilityChangesAnimatedKey);
+}
+
+} // namespace
+
+VisibilityController::VisibilityController() {
+}
+
+VisibilityController::~VisibilityController() {
+}
+
+bool VisibilityController::CallAnimateOnChildWindowVisibilityChanged(
+ aura::Window* window,
+ bool visible) {
+ return AnimateOnChildWindowVisibilityChanged(window, visible);
+}
+
+void VisibilityController::UpdateLayerVisibility(aura::Window* window,
+ bool visible) {
+ bool animated = window->type() != ui::wm::WINDOW_TYPE_CONTROL &&
+ window->type() != ui::wm::WINDOW_TYPE_UNKNOWN &&
+ ShouldAnimateWindow(window);
+ animated = animated &&
+ CallAnimateOnChildWindowVisibilityChanged(window, visible);
+
+ // If we're already in the process of hiding don't do anything. Otherwise we
+ // may end up prematurely canceling the animation.
+ // This does not check opacity as when fading out a visibility change should
+ // also be scheduled (to do otherwise would mean the window can not be seen,
+ // opacity is 0, yet the window is marked as visible) (see CL 132903003).
+ // TODO(vollick): remove this.
+ if (!visible &&
+ window->layer()->GetAnimator()->IsAnimatingProperty(
+ ui::LayerAnimationElement::VISIBILITY) &&
+ !window->layer()->GetTargetVisibility()) {
+ return;
+ }
+
+ // When a window is made visible, we always make its layer visible
+ // immediately. When a window is hidden, the layer must be left visible and
+ // only made not visible once the animation is complete.
+ if (!animated || visible)
+ window->layer()->SetVisible(visible);
+}
+
+SuspendChildWindowVisibilityAnimations::SuspendChildWindowVisibilityAnimations(
+ aura::Window* window)
+ : window_(window),
+ original_enabled_(window->GetProperty(
+ kChildWindowVisibilityChangesAnimatedKey)) {
+ window_->ClearProperty(kChildWindowVisibilityChangesAnimatedKey);
+}
+
+SuspendChildWindowVisibilityAnimations::
+ ~SuspendChildWindowVisibilityAnimations() {
+ if (original_enabled_)
+ window_->SetProperty(kChildWindowVisibilityChangesAnimatedKey, true);
+ else
+ window_->ClearProperty(kChildWindowVisibilityChangesAnimatedKey);
+}
+
+void SetChildWindowVisibilityChangesAnimated(aura::Window* window) {
+ window->SetProperty(kChildWindowVisibilityChangesAnimatedKey, true);
+}
+
+} // namespace wm
+
diff --git a/chromium/ui/wm/core/visibility_controller.h b/chromium/ui/wm/core/visibility_controller.h
new file mode 100644
index 00000000000..47ae9910c7d
--- /dev/null
+++ b/chromium/ui/wm/core/visibility_controller.h
@@ -0,0 +1,74 @@
+// 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_WM_CORE_VISIBILITY_CONTROLLER_H_
+#define UI_WM_CORE_VISIBILITY_CONTROLLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "ui/aura/client/visibility_client.h"
+#include "ui/wm/wm_export.h"
+
+namespace wm {
+
+class WM_EXPORT VisibilityController
+ : public aura::client::VisibilityClient {
+ public:
+ VisibilityController();
+ virtual ~VisibilityController();
+
+ protected:
+ // Subclasses override if they want to call a different implementation of
+ // this function.
+ // TODO(beng): potentially replace by an actual window animator class in
+ // window_animations.h.
+ virtual bool CallAnimateOnChildWindowVisibilityChanged(aura::Window* window,
+ bool visible);
+
+ private:
+ // Overridden from aura::client::VisibilityClient:
+ virtual void UpdateLayerVisibility(aura::Window* window,
+ bool visible) OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(VisibilityController);
+};
+
+// Suspends the animations for visibility changes during the lifetime of an
+// instance of this class.
+//
+// Example:
+//
+// void ViewName::UnanimatedAction() {
+// SuspendChildWindowVisibilityAnimations suspend(parent);
+// // Perform unanimated action here.
+// // ...
+// // When the method finishes, visibility animations will return to their
+// // previous state.
+// }
+//
+class WM_EXPORT SuspendChildWindowVisibilityAnimations {
+ public:
+ // Suspend visibility animations of child windows.
+ explicit SuspendChildWindowVisibilityAnimations(aura::Window* window);
+
+ // Restore visibility animations to their original state.
+ ~SuspendChildWindowVisibilityAnimations();
+
+ private:
+ // The window to manage.
+ aura::Window* window_;
+
+ // Whether the visibility animations on child windows were originally enabled.
+ const bool original_enabled_;
+
+ DISALLOW_COPY_AND_ASSIGN(SuspendChildWindowVisibilityAnimations);
+};
+
+// Tells |window| to animate visibility changes to its children.
+void WM_EXPORT SetChildWindowVisibilityChangesAnimated(
+ aura::Window* window);
+
+} // namespace wm
+
+#endif // UI_WM_CORE_VISIBILITY_CONTROLLER_H_
diff --git a/chromium/ui/wm/core/visibility_controller_unittest.cc b/chromium/ui/wm/core/visibility_controller_unittest.cc
new file mode 100644
index 00000000000..5825d49530c
--- /dev/null
+++ b/chromium/ui/wm/core/visibility_controller_unittest.cc
@@ -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.
+
+#include "ui/wm/core/visibility_controller.h"
+
+#include "ui/aura/test/aura_test_base.h"
+#include "ui/aura/test/test_window_delegate.h"
+#include "ui/aura/test/test_windows.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_event_dispatcher.h"
+#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_animator.h"
+#include "ui/compositor/scoped_animation_duration_scale_mode.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
+
+namespace wm {
+
+typedef aura::test::AuraTestBase VisibilityControllerTest;
+
+// Check that a transparency change to 0 will not cause a hide call to be
+// ignored.
+TEST_F(VisibilityControllerTest, AnimateTransparencyToZeroAndHideHides) {
+ // We cannot disable animations for this test.
+ ui::ScopedAnimationDurationScaleMode normal_duration_mode(
+ ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
+
+ VisibilityController controller;
+ aura::client::SetVisibilityClient(root_window(), &controller);
+
+ SetChildWindowVisibilityChangesAnimated(root_window());
+
+ aura::test::TestWindowDelegate d;
+ scoped_ptr<aura::Window> window(aura::test::CreateTestWindowWithDelegate(
+ &d, -2, gfx::Rect(0, 0, 50, 50), root_window()));
+ ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
+ settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(5));
+
+ EXPECT_TRUE(window->layer()->visible());
+ EXPECT_TRUE(window->IsVisible());
+
+ window->layer()->SetOpacity(0.0);
+ EXPECT_TRUE(window->layer()->visible());
+ EXPECT_TRUE(window->IsVisible());
+ EXPECT_TRUE(window->layer()->GetAnimator()->
+ IsAnimatingProperty(ui::LayerAnimationElement::OPACITY));
+ EXPECT_EQ(0.0f, window->layer()->GetTargetOpacity());
+
+ // Check that the visibility is correct after the hide animation has finished.
+ window->Hide();
+ window->layer()->GetAnimator()->StopAnimating();
+ EXPECT_FALSE(window->layer()->visible());
+ EXPECT_FALSE(window->IsVisible());
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/window_animations.cc b/chromium/ui/wm/core/window_animations.cc
new file mode 100644
index 00000000000..a79db3a15d6
--- /dev/null
+++ b/chromium/ui/wm/core/window_animations.cc
@@ -0,0 +1,658 @@
+// 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/wm/core/window_animations.h"
+
+#include <math.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "base/time/time.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_delegate.h"
+#include "ui/aura/window_observer.h"
+#include "ui/aura/window_property.h"
+#include "ui/compositor/compositor_observer.h"
+#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_animation_observer.h"
+#include "ui/compositor/layer_animation_sequence.h"
+#include "ui/compositor/layer_animator.h"
+#include "ui/compositor/layer_tree_owner.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/gfx/animation/animation.h"
+#include "ui/gfx/interpolated_transform.h"
+#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/screen.h"
+#include "ui/gfx/vector2d.h"
+#include "ui/gfx/vector3d_f.h"
+#include "ui/wm/core/window_util.h"
+#include "ui/wm/core/wm_core_switches.h"
+#include "ui/wm/public/animation_host.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(int)
+DECLARE_WINDOW_PROPERTY_TYPE(wm::WindowVisibilityAnimationType)
+DECLARE_WINDOW_PROPERTY_TYPE(wm::WindowVisibilityAnimationTransition)
+DECLARE_WINDOW_PROPERTY_TYPE(float)
+DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(WM_EXPORT, bool)
+
+namespace wm {
+namespace {
+const float kWindowAnimation_Vertical_TranslateY = 15.f;
+
+// A base class for hiding animation observer which has two roles:
+// 1) Notifies AnimationHost at the end of hiding animation.
+// 2) Detaches the window's layers for hiding animation and deletes
+// them upon completion of the animation. This is necessary to a)
+// ensure that the animation continues in the event of the window being
+// deleted, and b) to ensure that the animation is visible even if the
+// window gets restacked below other windows when focus or activation
+// changes.
+// The subclass will determine when the animation is completed.
+class HidingWindowAnimationObserverBase : public aura::WindowObserver {
+ public:
+ HidingWindowAnimationObserverBase(aura::Window* window) : window_(window) {
+ window_->AddObserver(this);
+ }
+ virtual ~HidingWindowAnimationObserverBase() {
+ if (window_)
+ window_->RemoveObserver(this);
+ }
+
+ // aura::WindowObserver:
+ virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
+ DCHECK_EQ(window, window_);
+ WindowInvalid();
+ }
+
+ virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE {
+ DCHECK_EQ(window, window_);
+ WindowInvalid();
+ }
+
+ // Detach the current layers and create new layers for |window_|.
+ // Stack the original layers above |window_| and its transient
+ // children. If the window has transient children, the original
+ // layers will be moved above the top most transient child so that
+ // activation change does not put the window above the animating
+ // layer.
+ void DetachAndRecreateLayers() {
+ layer_owner_ = RecreateLayers(window_);
+ if (window_->parent()) {
+ const aura::Window::Windows& transient_children =
+ GetTransientChildren(window_);
+ aura::Window::Windows::const_iterator iter =
+ std::find(window_->parent()->children().begin(),
+ window_->parent()->children().end(),
+ window_);
+ DCHECK(iter != window_->parent()->children().end());
+ aura::Window* topmost_transient_child = NULL;
+ for (++iter; iter != window_->parent()->children().end(); ++iter) {
+ if (std::find(transient_children.begin(),
+ transient_children.end(),
+ *iter) != transient_children.end()) {
+ topmost_transient_child = *iter;
+ }
+ }
+ if (topmost_transient_child) {
+ window_->parent()->layer()->StackAbove(
+ layer_owner_->root(), topmost_transient_child->layer());
+ }
+ }
+ }
+
+ protected:
+ // Invoked when the hiding animation is completed. It will delete
+ // 'this', and no operation should be made on this object after this
+ // point.
+ void OnAnimationCompleted() {
+ // Window may have been destroyed by this point.
+ if (window_) {
+ aura::client::AnimationHost* animation_host =
+ aura::client::GetAnimationHost(window_);
+ if (animation_host)
+ animation_host->OnWindowHidingAnimationCompleted();
+ window_->RemoveObserver(this);
+ }
+ delete this;
+ }
+
+ private:
+ // Invoked when the window is destroyed (or destroying).
+ void WindowInvalid() {
+ layer_owner_->root()->SuppressPaint();
+
+ window_->RemoveObserver(this);
+ window_ = NULL;
+ }
+
+ aura::Window* window_;
+
+ // The owner of detached layers.
+ scoped_ptr<ui::LayerTreeOwner> layer_owner_;
+
+ DISALLOW_COPY_AND_ASSIGN(HidingWindowAnimationObserverBase);
+};
+
+} // namespace
+
+DEFINE_WINDOW_PROPERTY_KEY(int,
+ kWindowVisibilityAnimationTypeKey,
+ WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT);
+DEFINE_WINDOW_PROPERTY_KEY(int, kWindowVisibilityAnimationDurationKey, 0);
+DEFINE_WINDOW_PROPERTY_KEY(WindowVisibilityAnimationTransition,
+ kWindowVisibilityAnimationTransitionKey,
+ ANIMATE_BOTH);
+DEFINE_WINDOW_PROPERTY_KEY(float,
+ kWindowVisibilityAnimationVerticalPositionKey,
+ kWindowAnimation_Vertical_TranslateY);
+
+// A HidingWindowAnimationObserver that deletes observer and detached
+// layers upon the completion of the implicit animation.
+class ImplicitHidingWindowAnimationObserver
+ : public HidingWindowAnimationObserverBase,
+ public ui::ImplicitAnimationObserver {
+ public:
+ ImplicitHidingWindowAnimationObserver(
+ aura::Window* window,
+ ui::ScopedLayerAnimationSettings* settings);
+ virtual ~ImplicitHidingWindowAnimationObserver() {}
+
+ // ui::ImplicitAnimationObserver:
+ virtual void OnImplicitAnimationsCompleted() OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ImplicitHidingWindowAnimationObserver);
+};
+
+namespace {
+
+const int kDefaultAnimationDurationForMenuMS = 150;
+
+const float kWindowAnimation_HideOpacity = 0.f;
+const float kWindowAnimation_ShowOpacity = 1.f;
+const float kWindowAnimation_TranslateFactor = 0.5f;
+const float kWindowAnimation_ScaleFactor = .95f;
+
+const int kWindowAnimation_Rotate_DurationMS = 180;
+const int kWindowAnimation_Rotate_OpacityDurationPercent = 90;
+const float kWindowAnimation_Rotate_TranslateY = -20.f;
+const float kWindowAnimation_Rotate_PerspectiveDepth = 500.f;
+const float kWindowAnimation_Rotate_DegreesX = 5.f;
+const float kWindowAnimation_Rotate_ScaleFactor = .99f;
+
+const float kWindowAnimation_Bounce_Scale = 1.02f;
+const int kWindowAnimation_Bounce_DurationMS = 180;
+const int kWindowAnimation_Bounce_GrowShrinkDurationPercent = 40;
+
+base::TimeDelta GetWindowVisibilityAnimationDuration(
+ const aura::Window& window) {
+ int duration =
+ window.GetProperty(kWindowVisibilityAnimationDurationKey);
+ if (duration == 0 && window.type() == ui::wm::WINDOW_TYPE_MENU) {
+ return base::TimeDelta::FromMilliseconds(
+ kDefaultAnimationDurationForMenuMS);
+ }
+ return base::TimeDelta::FromInternalValue(duration);
+}
+
+// Gets/sets the WindowVisibilityAnimationType associated with a window.
+// TODO(beng): redundant/fold into method on public api?
+int GetWindowVisibilityAnimationType(aura::Window* window) {
+ int type = window->GetProperty(kWindowVisibilityAnimationTypeKey);
+ if (type == WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT) {
+ return (window->type() == ui::wm::WINDOW_TYPE_MENU ||
+ window->type() == ui::wm::WINDOW_TYPE_TOOLTIP)
+ ? WINDOW_VISIBILITY_ANIMATION_TYPE_FADE
+ : WINDOW_VISIBILITY_ANIMATION_TYPE_DROP;
+ }
+ return type;
+}
+
+void GetTransformRelativeToRoot(ui::Layer* layer, gfx::Transform* transform) {
+ const ui::Layer* root = layer;
+ while (root->parent())
+ root = root->parent();
+ layer->GetTargetTransformRelativeTo(root, transform);
+}
+
+gfx::Rect GetLayerWorldBoundsAfterTransform(ui::Layer* layer,
+ const gfx::Transform& transform) {
+ gfx::Transform in_world = transform;
+ GetTransformRelativeToRoot(layer, &in_world);
+
+ gfx::RectF transformed = layer->bounds();
+ in_world.TransformRect(&transformed);
+
+ return gfx::ToEnclosingRect(transformed);
+}
+
+// Augment the host window so that the enclosing bounds of the full
+// animation will fit inside of it.
+void AugmentWindowSize(aura::Window* window,
+ const gfx::Transform& end_transform) {
+ aura::client::AnimationHost* animation_host =
+ aura::client::GetAnimationHost(window);
+ if (!animation_host)
+ return;
+
+ const gfx::Rect& world_at_start = window->bounds();
+ gfx::Rect world_at_end =
+ GetLayerWorldBoundsAfterTransform(window->layer(), end_transform);
+ gfx::Rect union_in_window_space =
+ gfx::UnionRects(world_at_start, world_at_end);
+
+ // Calculate the top left and bottom right deltas to be added to the window
+ // bounds.
+ gfx::Vector2d top_left_delta(world_at_start.x() - union_in_window_space.x(),
+ world_at_start.y() - union_in_window_space.y());
+
+ gfx::Vector2d bottom_right_delta(
+ union_in_window_space.x() + union_in_window_space.width() -
+ (world_at_start.x() + world_at_start.width()),
+ union_in_window_space.y() + union_in_window_space.height() -
+ (world_at_start.y() + world_at_start.height()));
+
+ DCHECK(top_left_delta.x() >= 0 && top_left_delta.y() >= 0 &&
+ bottom_right_delta.x() >= 0 && bottom_right_delta.y() >= 0);
+
+ animation_host->SetHostTransitionOffsets(top_left_delta, bottom_right_delta);
+}
+
+// Shows a window using an animation, animating its opacity from 0.f to 1.f,
+// its visibility to true, and its transform from |start_transform| to
+// |end_transform|.
+void AnimateShowWindowCommon(aura::Window* window,
+ const gfx::Transform& start_transform,
+ const gfx::Transform& end_transform) {
+ AugmentWindowSize(window, end_transform);
+
+ window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
+ window->layer()->SetTransform(start_transform);
+ window->layer()->SetVisible(true);
+
+ {
+ // Property sets within this scope will be implicitly animated.
+ ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
+ base::TimeDelta duration = GetWindowVisibilityAnimationDuration(*window);
+ if (duration.ToInternalValue() > 0)
+ settings.SetTransitionDuration(duration);
+
+ window->layer()->SetTransform(end_transform);
+ window->layer()->SetOpacity(kWindowAnimation_ShowOpacity);
+ }
+}
+
+// Hides a window using an animation, animating its opacity from 1.f to 0.f,
+// its visibility to false, and its transform to |end_transform|.
+void AnimateHideWindowCommon(aura::Window* window,
+ const gfx::Transform& end_transform) {
+ AugmentWindowSize(window, end_transform);
+
+ // Property sets within this scope will be implicitly animated.
+ ScopedHidingAnimationSettings hiding_settings(window);
+ base::TimeDelta duration = GetWindowVisibilityAnimationDuration(*window);
+ if (duration.ToInternalValue() > 0)
+ hiding_settings.layer_animation_settings()->SetTransitionDuration(duration);
+
+ window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
+ window->layer()->SetTransform(end_transform);
+ window->layer()->SetVisible(false);
+}
+
+static gfx::Transform GetScaleForWindow(aura::Window* window) {
+ gfx::Rect bounds = window->bounds();
+ gfx::Transform scale = gfx::GetScaleTransform(
+ gfx::Point(kWindowAnimation_TranslateFactor * bounds.width(),
+ kWindowAnimation_TranslateFactor * bounds.height()),
+ kWindowAnimation_ScaleFactor);
+ return scale;
+}
+
+// Show/Hide windows using a shrink animation.
+void AnimateShowWindow_Drop(aura::Window* window) {
+ AnimateShowWindowCommon(window, GetScaleForWindow(window), gfx::Transform());
+}
+
+void AnimateHideWindow_Drop(aura::Window* window) {
+ AnimateHideWindowCommon(window, GetScaleForWindow(window));
+}
+
+// Show/Hide windows using a vertical Glenimation.
+void AnimateShowWindow_Vertical(aura::Window* window) {
+ gfx::Transform transform;
+ transform.Translate(0, window->GetProperty(
+ kWindowVisibilityAnimationVerticalPositionKey));
+ AnimateShowWindowCommon(window, transform, gfx::Transform());
+}
+
+void AnimateHideWindow_Vertical(aura::Window* window) {
+ gfx::Transform transform;
+ transform.Translate(0, window->GetProperty(
+ kWindowVisibilityAnimationVerticalPositionKey));
+ AnimateHideWindowCommon(window, transform);
+}
+
+// Show/Hide windows using a fade.
+void AnimateShowWindow_Fade(aura::Window* window) {
+ AnimateShowWindowCommon(window, gfx::Transform(), gfx::Transform());
+}
+
+void AnimateHideWindow_Fade(aura::Window* window) {
+ AnimateHideWindowCommon(window, gfx::Transform());
+}
+
+ui::LayerAnimationElement* CreateGrowShrinkElement(
+ aura::Window* window, bool grow) {
+ scoped_ptr<ui::InterpolatedTransform> scale(new ui::InterpolatedScale(
+ gfx::Point3F(kWindowAnimation_Bounce_Scale,
+ kWindowAnimation_Bounce_Scale,
+ 1),
+ gfx::Point3F(1, 1, 1)));
+ scoped_ptr<ui::InterpolatedTransform> scale_about_pivot(
+ new ui::InterpolatedTransformAboutPivot(
+ gfx::Point(window->bounds().width() * 0.5,
+ window->bounds().height() * 0.5),
+ scale.release()));
+ scale_about_pivot->SetReversed(grow);
+ scoped_ptr<ui::LayerAnimationElement> transition(
+ ui::LayerAnimationElement::CreateInterpolatedTransformElement(
+ scale_about_pivot.release(),
+ base::TimeDelta::FromMilliseconds(
+ kWindowAnimation_Bounce_DurationMS *
+ kWindowAnimation_Bounce_GrowShrinkDurationPercent / 100)));
+ transition->set_tween_type(grow ? gfx::Tween::EASE_OUT : gfx::Tween::EASE_IN);
+ return transition.release();
+}
+
+void AnimateBounce(aura::Window* window) {
+ ui::ScopedLayerAnimationSettings scoped_settings(
+ window->layer()->GetAnimator());
+ scoped_settings.SetPreemptionStrategy(
+ ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
+ scoped_ptr<ui::LayerAnimationSequence> sequence(
+ new ui::LayerAnimationSequence);
+ sequence->AddElement(CreateGrowShrinkElement(window, true));
+ sequence->AddElement(ui::LayerAnimationElement::CreatePauseElement(
+ ui::LayerAnimationElement::BOUNDS,
+ base::TimeDelta::FromMilliseconds(
+ kWindowAnimation_Bounce_DurationMS *
+ (100 - 2 * kWindowAnimation_Bounce_GrowShrinkDurationPercent) /
+ 100)));
+ sequence->AddElement(CreateGrowShrinkElement(window, false));
+ window->layer()->GetAnimator()->StartAnimation(sequence.release());
+}
+
+// A HidingWindowAnimationObserver that deletes observer and detached
+// layers when the last_sequence has been completed or aborted.
+class RotateHidingWindowAnimationObserver
+ : public HidingWindowAnimationObserverBase,
+ public ui::LayerAnimationObserver {
+ public:
+ RotateHidingWindowAnimationObserver(aura::Window* window)
+ : HidingWindowAnimationObserverBase(window), last_sequence_(NULL) {}
+ virtual ~RotateHidingWindowAnimationObserver() {}
+
+ void set_last_sequence(ui::LayerAnimationSequence* last_sequence) {
+ last_sequence_ = last_sequence;
+ }
+
+ // ui::LayerAnimationObserver:
+ virtual void OnLayerAnimationEnded(
+ ui::LayerAnimationSequence* sequence) OVERRIDE {
+ if (last_sequence_ == sequence)
+ OnAnimationCompleted();
+ }
+ virtual void OnLayerAnimationAborted(
+ ui::LayerAnimationSequence* sequence) OVERRIDE {
+ if (last_sequence_ == sequence)
+ OnAnimationCompleted();
+ }
+ virtual void OnLayerAnimationScheduled(
+ ui::LayerAnimationSequence* sequence) OVERRIDE {}
+
+ private:
+ ui::LayerAnimationSequence* last_sequence_;
+
+ DISALLOW_COPY_AND_ASSIGN(RotateHidingWindowAnimationObserver);
+};
+
+void AddLayerAnimationsForRotate(aura::Window* window, bool show) {
+ if (show)
+ window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
+
+ base::TimeDelta duration = base::TimeDelta::FromMilliseconds(
+ kWindowAnimation_Rotate_DurationMS);
+
+ RotateHidingWindowAnimationObserver* observer = NULL;
+
+ if (!show) {
+ observer = new RotateHidingWindowAnimationObserver(window);
+ window->layer()->GetAnimator()->SchedulePauseForProperties(
+ duration * (100 - kWindowAnimation_Rotate_OpacityDurationPercent) / 100,
+ ui::LayerAnimationElement::OPACITY);
+ }
+ scoped_ptr<ui::LayerAnimationElement> opacity(
+ ui::LayerAnimationElement::CreateOpacityElement(
+ show ? kWindowAnimation_ShowOpacity : kWindowAnimation_HideOpacity,
+ duration * kWindowAnimation_Rotate_OpacityDurationPercent / 100));
+ opacity->set_tween_type(gfx::Tween::EASE_IN_OUT);
+ window->layer()->GetAnimator()->ScheduleAnimation(
+ new ui::LayerAnimationSequence(opacity.release()));
+
+ float xcenter = window->bounds().width() * 0.5;
+
+ gfx::Transform transform;
+ transform.Translate(xcenter, 0);
+ transform.ApplyPerspectiveDepth(kWindowAnimation_Rotate_PerspectiveDepth);
+ transform.Translate(-xcenter, 0);
+ scoped_ptr<ui::InterpolatedTransform> perspective(
+ new ui::InterpolatedConstantTransform(transform));
+
+ scoped_ptr<ui::InterpolatedTransform> scale(
+ new ui::InterpolatedScale(1, kWindowAnimation_Rotate_ScaleFactor));
+ scoped_ptr<ui::InterpolatedTransform> scale_about_pivot(
+ new ui::InterpolatedTransformAboutPivot(
+ gfx::Point(xcenter, kWindowAnimation_Rotate_TranslateY),
+ scale.release()));
+
+ scoped_ptr<ui::InterpolatedTransform> translation(
+ new ui::InterpolatedTranslation(gfx::Point(), gfx::Point(
+ 0, kWindowAnimation_Rotate_TranslateY)));
+
+ scoped_ptr<ui::InterpolatedTransform> rotation(
+ new ui::InterpolatedAxisAngleRotation(
+ gfx::Vector3dF(1, 0, 0), 0, kWindowAnimation_Rotate_DegreesX));
+
+ scale_about_pivot->SetChild(perspective.release());
+ translation->SetChild(scale_about_pivot.release());
+ rotation->SetChild(translation.release());
+ rotation->SetReversed(show);
+
+ scoped_ptr<ui::LayerAnimationElement> transition(
+ ui::LayerAnimationElement::CreateInterpolatedTransformElement(
+ rotation.release(), duration));
+ ui::LayerAnimationSequence* last_sequence =
+ new ui::LayerAnimationSequence(transition.release());
+ window->layer()->GetAnimator()->ScheduleAnimation(last_sequence);
+ if (observer) {
+ observer->set_last_sequence(last_sequence);
+ observer->DetachAndRecreateLayers();
+ }
+}
+
+void AnimateShowWindow_Rotate(aura::Window* window) {
+ AddLayerAnimationsForRotate(window, true);
+}
+
+void AnimateHideWindow_Rotate(aura::Window* window) {
+ AddLayerAnimationsForRotate(window, false);
+}
+
+bool AnimateShowWindow(aura::Window* window) {
+ if (!HasWindowVisibilityAnimationTransition(window, ANIMATE_SHOW)) {
+ if (HasWindowVisibilityAnimationTransition(window, ANIMATE_HIDE)) {
+ // Since hide animation may have changed opacity and transform,
+ // reset them to show the window.
+ window->layer()->SetOpacity(kWindowAnimation_ShowOpacity);
+ window->layer()->SetTransform(gfx::Transform());
+ }
+ return false;
+ }
+
+ switch (GetWindowVisibilityAnimationType(window)) {
+ case WINDOW_VISIBILITY_ANIMATION_TYPE_DROP:
+ AnimateShowWindow_Drop(window);
+ return true;
+ case WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL:
+ AnimateShowWindow_Vertical(window);
+ return true;
+ case WINDOW_VISIBILITY_ANIMATION_TYPE_FADE:
+ AnimateShowWindow_Fade(window);
+ return true;
+ case WINDOW_VISIBILITY_ANIMATION_TYPE_ROTATE:
+ AnimateShowWindow_Rotate(window);
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool AnimateHideWindow(aura::Window* window) {
+ if (!HasWindowVisibilityAnimationTransition(window, ANIMATE_HIDE)) {
+ if (HasWindowVisibilityAnimationTransition(window, ANIMATE_SHOW)) {
+ // Since show animation may have changed opacity and transform,
+ // reset them, though the change should be hidden.
+ window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
+ window->layer()->SetTransform(gfx::Transform());
+ }
+ return false;
+ }
+
+ switch (GetWindowVisibilityAnimationType(window)) {
+ case WINDOW_VISIBILITY_ANIMATION_TYPE_DROP:
+ AnimateHideWindow_Drop(window);
+ return true;
+ case WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL:
+ AnimateHideWindow_Vertical(window);
+ return true;
+ case WINDOW_VISIBILITY_ANIMATION_TYPE_FADE:
+ AnimateHideWindow_Fade(window);
+ return true;
+ case WINDOW_VISIBILITY_ANIMATION_TYPE_ROTATE:
+ AnimateHideWindow_Rotate(window);
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// ImplicitHidingWindowAnimationObserver
+
+ImplicitHidingWindowAnimationObserver::ImplicitHidingWindowAnimationObserver(
+ aura::Window* window,
+ ui::ScopedLayerAnimationSettings* settings)
+ : HidingWindowAnimationObserverBase(window) {
+ settings->AddObserver(this);
+}
+
+void ImplicitHidingWindowAnimationObserver::OnImplicitAnimationsCompleted() {
+ OnAnimationCompleted();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ScopedHidingAnimationSettings
+
+ScopedHidingAnimationSettings::ScopedHidingAnimationSettings(
+ aura::Window* window)
+ : layer_animation_settings_(window->layer()->GetAnimator()),
+ observer_(new ImplicitHidingWindowAnimationObserver(
+ window,
+ &layer_animation_settings_)) {
+}
+
+ScopedHidingAnimationSettings::~ScopedHidingAnimationSettings() {
+ observer_->DetachAndRecreateLayers();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// External interface
+
+void SetWindowVisibilityAnimationType(aura::Window* window, int type) {
+ window->SetProperty(kWindowVisibilityAnimationTypeKey, type);
+}
+
+int GetWindowVisibilityAnimationType(aura::Window* window) {
+ return window->GetProperty(kWindowVisibilityAnimationTypeKey);
+}
+
+void SetWindowVisibilityAnimationTransition(
+ aura::Window* window,
+ WindowVisibilityAnimationTransition transition) {
+ window->SetProperty(kWindowVisibilityAnimationTransitionKey, transition);
+}
+
+bool HasWindowVisibilityAnimationTransition(
+ aura::Window* window,
+ WindowVisibilityAnimationTransition transition) {
+ WindowVisibilityAnimationTransition prop = window->GetProperty(
+ kWindowVisibilityAnimationTransitionKey);
+ return (prop & transition) != 0;
+}
+
+void SetWindowVisibilityAnimationDuration(aura::Window* window,
+ const base::TimeDelta& duration) {
+ window->SetProperty(kWindowVisibilityAnimationDurationKey,
+ static_cast<int>(duration.ToInternalValue()));
+}
+
+base::TimeDelta GetWindowVisibilityAnimationDuration(
+ const aura::Window& window) {
+ return base::TimeDelta::FromInternalValue(
+ window.GetProperty(kWindowVisibilityAnimationDurationKey));
+}
+
+void SetWindowVisibilityAnimationVerticalPosition(aura::Window* window,
+ float position) {
+ window->SetProperty(kWindowVisibilityAnimationVerticalPositionKey, position);
+}
+
+bool AnimateOnChildWindowVisibilityChanged(aura::Window* window, bool visible) {
+ if (WindowAnimationsDisabled(window))
+ return false;
+ if (visible)
+ return AnimateShowWindow(window);
+ // Don't start hiding the window again if it's already being hidden.
+ return window->layer()->GetTargetOpacity() != 0.0f &&
+ AnimateHideWindow(window);
+}
+
+bool AnimateWindow(aura::Window* window, WindowAnimationType type) {
+ switch (type) {
+ case WINDOW_ANIMATION_TYPE_BOUNCE:
+ AnimateBounce(window);
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+bool WindowAnimationsDisabled(aura::Window* window) {
+ return (!gfx::Animation::ShouldRenderRichAnimation() || (window &&
+ window->GetProperty(aura::client::kAnimationsDisabledKey)) ||
+ CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kWindowAnimationsDisabled));
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/window_animations.h b/chromium/ui/wm/core/window_animations.h
new file mode 100644
index 00000000000..422167e35d7
--- /dev/null
+++ b/chromium/ui/wm/core/window_animations.h
@@ -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.
+
+#ifndef UI_WM_CORE_WINDOW_ANIMATIONS_H_
+#define UI_WM_CORE_WINDOW_ANIMATIONS_H_
+
+#include <vector>
+
+#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/wm/wm_export.h"
+
+namespace aura {
+class Window;
+}
+namespace base {
+class TimeDelta;
+}
+namespace gfx {
+class Rect;
+}
+namespace ui {
+class ImplicitAnimationObserver;
+class Layer;
+class LayerAnimationSequence;
+}
+
+namespace wm {
+
+// A variety of canned animations for window transitions.
+enum WindowVisibilityAnimationType {
+ WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT = 0, // Default. Lets the system
+ // decide based on window
+ // type.
+ WINDOW_VISIBILITY_ANIMATION_TYPE_DROP, // Window shrinks in.
+ WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL, // Vertical Glenimation.
+ WINDOW_VISIBILITY_ANIMATION_TYPE_FADE, // Fades in/out.
+ WINDOW_VISIBILITY_ANIMATION_TYPE_ROTATE, // Window rotates in.
+
+ // Downstream library animations start above this point.
+ WINDOW_VISIBILITY_ANIMATION_MAX
+};
+
+// Canned animations that take effect once but don't have a symmetric pair as
+// visibility animations do.
+enum WindowAnimationType {
+ WINDOW_ANIMATION_TYPE_BOUNCE = 0, // Window scales up and down.
+};
+
+// Type of visibility change transition that a window should animate.
+// Default behavior is to animate both show and hide.
+enum WindowVisibilityAnimationTransition {
+ ANIMATE_SHOW = 0x1,
+ ANIMATE_HIDE = 0x2,
+ ANIMATE_BOTH = ANIMATE_SHOW | ANIMATE_HIDE,
+ ANIMATE_NONE = 0x4,
+};
+
+// These two methods use int for type rather than WindowVisibilityAnimationType
+// since downstream libraries can extend the set of animations.
+WM_EXPORT void SetWindowVisibilityAnimationType(aura::Window* window, int type);
+WM_EXPORT int GetWindowVisibilityAnimationType(aura::Window* window);
+
+WM_EXPORT void SetWindowVisibilityAnimationTransition(
+ aura::Window* window,
+ WindowVisibilityAnimationTransition transition);
+
+WM_EXPORT bool HasWindowVisibilityAnimationTransition(
+ aura::Window* window,
+ WindowVisibilityAnimationTransition transition);
+
+WM_EXPORT void SetWindowVisibilityAnimationDuration(
+ aura::Window* window,
+ const base::TimeDelta& duration);
+
+WM_EXPORT base::TimeDelta GetWindowVisibilityAnimationDuration(
+ const aura::Window& window);
+
+WM_EXPORT void SetWindowVisibilityAnimationVerticalPosition(
+ aura::Window* window,
+ float position);
+
+class ImplicitHidingWindowAnimationObserver;
+// A wrapper of ui::ScopedLayerAnimationSettings for implicit hiding animations.
+// Use this to ensure that the hiding animation is visible even after
+// the window is deleted or deactivated, instead of using
+// ui::ScopedLayerAnimationSettings directly.
+class WM_EXPORT ScopedHidingAnimationSettings {
+ public:
+ explicit ScopedHidingAnimationSettings(aura::Window* window);
+ ~ScopedHidingAnimationSettings();
+
+ // Returns the wrapped ScopedLayeAnimationSettings instance.
+ ui::ScopedLayerAnimationSettings* layer_animation_settings() {
+ return &layer_animation_settings_;
+ }
+
+ private:
+ ui::ScopedLayerAnimationSettings layer_animation_settings_;
+ ImplicitHidingWindowAnimationObserver* observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedHidingAnimationSettings);
+};
+
+// Returns false if the |window| didn't animate.
+WM_EXPORT bool AnimateOnChildWindowVisibilityChanged(aura::Window* window,
+ bool visible);
+WM_EXPORT bool AnimateWindow(aura::Window* window, WindowAnimationType type);
+
+// Returns true if window animations are disabled for |window|. Window
+// animations are enabled by default. If |window| is NULL, this just checks
+// if the global flag disabling window animations is present.
+WM_EXPORT bool WindowAnimationsDisabled(aura::Window* window);
+
+} // namespace wm
+
+#endif // UI_WM_CORE_WINDOW_ANIMATIONS_H_
diff --git a/chromium/ui/wm/core/window_animations_unittest.cc b/chromium/ui/wm/core/window_animations_unittest.cc
new file mode 100644
index 00000000000..5968bf32440
--- /dev/null
+++ b/chromium/ui/wm/core/window_animations_unittest.cc
@@ -0,0 +1,291 @@
+// 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/wm/core/window_animations.h"
+
+#include "base/time/time.h"
+#include "ui/aura/test/aura_test_base.h"
+#include "ui/aura/test/test_windows.h"
+#include "ui/aura/window.h"
+#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_animator.h"
+#include "ui/compositor/scoped_animation_duration_scale_mode.h"
+#include "ui/gfx/animation/animation_container_element.h"
+#include "ui/gfx/vector2d.h"
+#include "ui/wm/core/transient_window_manager.h"
+#include "ui/wm/core/transient_window_stacking_client.h"
+#include "ui/wm/core/window_util.h"
+#include "ui/wm/public/animation_host.h"
+
+using aura::Window;
+using ui::Layer;
+
+namespace wm {
+namespace {
+
+template<typename T>int GetZPosition(const T* child) {
+ const T* parent = child->parent();
+ const std::vector<T*> children = parent->children();
+ typename std::vector<T*>::const_iterator iter =
+ std::find(children.begin(), children.end(), child);
+ DCHECK(iter != children.end());
+ return iter - children.begin();
+}
+
+int GetWindowZPosition(const aura::Window* child) {
+ return GetZPosition<aura::Window>(child);
+}
+
+int GetLayerZPosition(const ui::Layer* child) {
+ return GetZPosition<ui::Layer>(child);
+}
+
+} // namespace
+
+class WindowAnimationsTest : public aura::test::AuraTestBase {
+ public:
+ WindowAnimationsTest() {}
+
+ virtual void TearDown() OVERRIDE {
+ AuraTestBase::TearDown();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WindowAnimationsTest);
+};
+
+TEST_F(WindowAnimationsTest, LayerTargetVisibility) {
+ scoped_ptr<aura::Window> window(
+ aura::test::CreateTestWindowWithId(0, NULL));
+
+ // Layer target visibility changes according to Show/Hide.
+ window->Show();
+ EXPECT_TRUE(window->layer()->GetTargetVisibility());
+ window->Hide();
+ EXPECT_FALSE(window->layer()->GetTargetVisibility());
+ window->Show();
+ EXPECT_TRUE(window->layer()->GetTargetVisibility());
+}
+
+TEST_F(WindowAnimationsTest, LayerTargetVisibility_AnimateShow) {
+ // Tests if opacity and transform are reset when only show animation is
+ // enabled. See also LayerTargetVisibility_AnimateHide.
+ // Since the window is not visible after Hide() is called, opacity and
+ // transform shouldn't matter in case of ANIMATE_SHOW, but we reset them
+ // to keep consistency.
+
+ scoped_ptr<aura::Window> window(aura::test::CreateTestWindowWithId(0, NULL));
+ SetWindowVisibilityAnimationTransition(window.get(), ANIMATE_SHOW);
+
+ // Layer target visibility and opacity change according to Show/Hide.
+ window->Show();
+ AnimateOnChildWindowVisibilityChanged(window.get(), true);
+ EXPECT_TRUE(window->layer()->GetTargetVisibility());
+ EXPECT_EQ(1, window->layer()->opacity());
+
+ window->Hide();
+ AnimateOnChildWindowVisibilityChanged(window.get(), false);
+ EXPECT_FALSE(window->layer()->GetTargetVisibility());
+ EXPECT_EQ(0, window->layer()->opacity());
+ EXPECT_EQ(gfx::Transform(), window->layer()->transform());
+
+ window->Show();
+ AnimateOnChildWindowVisibilityChanged(window.get(), true);
+ EXPECT_TRUE(window->layer()->GetTargetVisibility());
+ EXPECT_EQ(1, window->layer()->opacity());
+}
+
+TEST_F(WindowAnimationsTest, LayerTargetVisibility_AnimateHide) {
+ // Tests if opacity and transform are reset when only hide animation is
+ // enabled. Hide animation changes opacity and transform in addition to
+ // visibility, so we need to reset not only visibility but also opacity
+ // and transform to show the window.
+
+ scoped_ptr<aura::Window> window(aura::test::CreateTestWindowWithId(0, NULL));
+ SetWindowVisibilityAnimationTransition(window.get(), ANIMATE_HIDE);
+
+ // Layer target visibility and opacity change according to Show/Hide.
+ window->Show();
+ AnimateOnChildWindowVisibilityChanged(window.get(), true);
+ EXPECT_TRUE(window->layer()->GetTargetVisibility());
+ EXPECT_EQ(1, window->layer()->opacity());
+ EXPECT_EQ(gfx::Transform(), window->layer()->transform());
+
+ window->Hide();
+ AnimateOnChildWindowVisibilityChanged(window.get(), false);
+ EXPECT_FALSE(window->layer()->GetTargetVisibility());
+ EXPECT_EQ(0, window->layer()->opacity());
+
+ window->Show();
+ AnimateOnChildWindowVisibilityChanged(window.get(), true);
+ EXPECT_TRUE(window->layer()->GetTargetVisibility());
+ EXPECT_EQ(1, window->layer()->opacity());
+ EXPECT_EQ(gfx::Transform(), window->layer()->transform());
+}
+
+TEST_F(WindowAnimationsTest, HideAnimationDetachLayers) {
+ scoped_ptr<aura::Window> parent(aura::test::CreateTestWindowWithId(0, NULL));
+
+ scoped_ptr<aura::Window> other(
+ aura::test::CreateTestWindowWithId(1, parent.get()));
+
+ scoped_ptr<aura::Window> animating_window(
+ aura::test::CreateTestWindowWithId(2, parent.get()));
+ SetWindowVisibilityAnimationTransition(animating_window.get(), ANIMATE_HIDE);
+
+ EXPECT_EQ(0, GetWindowZPosition(other.get()));
+ EXPECT_EQ(1, GetWindowZPosition(animating_window.get()));
+ EXPECT_EQ(0, GetLayerZPosition(other->layer()));
+ EXPECT_EQ(1, GetLayerZPosition(animating_window->layer()));
+
+ {
+ ui::ScopedAnimationDurationScaleMode scale_mode(
+ ui::ScopedAnimationDurationScaleMode::FAST_DURATION);
+ ui::Layer* animating_layer = animating_window->layer();
+
+ animating_window->Hide();
+ EXPECT_TRUE(AnimateOnChildWindowVisibilityChanged(
+ animating_window.get(), false));
+ EXPECT_TRUE(animating_layer->GetAnimator()->is_animating());
+ EXPECT_FALSE(animating_layer->delegate());
+
+ // Make sure the Hide animation create another layer, and both are in
+ // the parent layer.
+ EXPECT_NE(animating_window->layer(), animating_layer);
+ EXPECT_TRUE(
+ std::find(parent->layer()->children().begin(),
+ parent->layer()->children().end(),
+ animating_layer) !=
+ parent->layer()->children().end());
+ EXPECT_TRUE(
+ std::find(parent->layer()->children().begin(),
+ parent->layer()->children().end(),
+ animating_window->layer()) !=
+ parent->layer()->children().end());
+ // Current layer must be already hidden.
+ EXPECT_FALSE(animating_window->layer()->visible());
+
+ EXPECT_EQ(1, GetWindowZPosition(animating_window.get()));
+ EXPECT_EQ(1, GetLayerZPosition(animating_window->layer()));
+ EXPECT_EQ(2, GetLayerZPosition(animating_layer));
+
+ parent->StackChildAtTop(other.get());
+ EXPECT_EQ(0, GetWindowZPosition(animating_window.get()));
+ EXPECT_EQ(1, GetWindowZPosition(other.get()));
+
+ EXPECT_EQ(0, GetLayerZPosition(animating_window->layer()));
+ EXPECT_EQ(1, GetLayerZPosition(other->layer()));
+ // Make sure the animating layer is on top.
+ EXPECT_EQ(2, GetLayerZPosition(animating_layer));
+
+ // Animating layer must be gone
+ animating_layer->GetAnimator()->StopAnimating();
+ EXPECT_TRUE(
+ std::find(parent->layer()->children().begin(),
+ parent->layer()->children().end(),
+ animating_layer) ==
+ parent->layer()->children().end());
+ }
+}
+
+TEST_F(WindowAnimationsTest, HideAnimationDetachLayersWithTransientChildren) {
+ TransientWindowStackingClient transient_stacking_client;
+
+ scoped_ptr<aura::Window> parent(aura::test::CreateTestWindowWithId(0, NULL));
+
+ scoped_ptr<aura::Window> other(
+ aura::test::CreateTestWindowWithId(1, parent.get()));
+
+ scoped_ptr<aura::Window> animating_window(
+ aura::test::CreateTestWindowWithId(2, parent.get()));
+ SetWindowVisibilityAnimationTransition(animating_window.get(), ANIMATE_HIDE);
+
+ scoped_ptr<aura::Window> transient1(
+ aura::test::CreateTestWindowWithId(3, parent.get()));
+ scoped_ptr<aura::Window> transient2(
+ aura::test::CreateTestWindowWithId(4, parent.get()));
+
+ TransientWindowManager::Get(animating_window.get());
+ AddTransientChild(animating_window.get(), transient1.get());
+ AddTransientChild(animating_window.get(), transient2.get());
+
+ EXPECT_EQ(0, GetWindowZPosition(other.get()));
+ EXPECT_EQ(1, GetWindowZPosition(animating_window.get()));
+ EXPECT_EQ(2, GetWindowZPosition(transient1.get()));
+ EXPECT_EQ(3, GetWindowZPosition(transient2.get()));
+
+ {
+ ui::ScopedAnimationDurationScaleMode scale_mode(
+ ui::ScopedAnimationDurationScaleMode::FAST_DURATION);
+ ui::Layer* animating_layer = animating_window->layer();
+
+ animating_window->Hide();
+ EXPECT_TRUE(AnimateOnChildWindowVisibilityChanged(
+ animating_window.get(), false));
+ EXPECT_TRUE(animating_layer->GetAnimator()->is_animating());
+ EXPECT_FALSE(animating_layer->delegate());
+
+ EXPECT_EQ(1, GetWindowZPosition(animating_window.get()));
+ EXPECT_EQ(2, GetWindowZPosition(transient1.get()));
+ EXPECT_EQ(3, GetWindowZPosition(transient2.get()));
+
+ EXPECT_EQ(1, GetLayerZPosition(animating_window->layer()));
+ EXPECT_EQ(2, GetLayerZPosition(transient1->layer()));
+ EXPECT_EQ(3, GetLayerZPosition(transient2->layer()));
+ EXPECT_EQ(4, GetLayerZPosition(animating_layer));
+
+ parent->StackChildAtTop(other.get());
+
+ EXPECT_EQ(0, GetWindowZPosition(animating_window.get()));
+ EXPECT_EQ(1, GetWindowZPosition(transient1.get()));
+ EXPECT_EQ(2, GetWindowZPosition(transient2.get()));
+ EXPECT_EQ(3, GetWindowZPosition(other.get()));
+
+ EXPECT_EQ(0, GetLayerZPosition(animating_window->layer()));
+ EXPECT_EQ(1, GetLayerZPosition(transient1->layer()));
+ EXPECT_EQ(2, GetLayerZPosition(transient2->layer()));
+ EXPECT_EQ(3, GetLayerZPosition(other->layer()));
+ // Make sure the animating layer is on top of all windows.
+ EXPECT_EQ(4, GetLayerZPosition(animating_layer));
+ }
+}
+
+// A simple AnimationHost implementation for the NotifyHideCompleted test.
+class NotifyHideCompletedAnimationHost : public aura::client::AnimationHost {
+ public:
+ NotifyHideCompletedAnimationHost() : hide_completed_(false) {}
+ virtual ~NotifyHideCompletedAnimationHost() {}
+
+ // Overridden from TestWindowDelegate:
+ virtual void OnWindowHidingAnimationCompleted() OVERRIDE {
+ hide_completed_ = true;
+ }
+
+ virtual void SetHostTransitionOffsets(
+ const gfx::Vector2d& top_left,
+ const gfx::Vector2d& bottom_right) OVERRIDE {}
+
+ bool hide_completed() const { return hide_completed_; }
+
+ private:
+ bool hide_completed_;
+
+ DISALLOW_COPY_AND_ASSIGN(NotifyHideCompletedAnimationHost);
+};
+
+TEST_F(WindowAnimationsTest, NotifyHideCompleted) {
+ NotifyHideCompletedAnimationHost animation_host;
+ scoped_ptr<aura::Window> window(aura::test::CreateTestWindowWithId(0, NULL));
+ aura::client::SetAnimationHost(window.get(), &animation_host);
+ wm::SetWindowVisibilityAnimationType(
+ window.get(), WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
+ AnimateOnChildWindowVisibilityChanged(window.get(), true);
+ EXPECT_TRUE(window->layer()->visible());
+
+ EXPECT_FALSE(animation_host.hide_completed());
+ AnimateOnChildWindowVisibilityChanged(window.get(), false);
+ EXPECT_TRUE(animation_host.hide_completed());
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/window_modality_controller.cc b/chromium/ui/wm/core/window_modality_controller.cc
new file mode 100644
index 00000000000..1df375b439a
--- /dev/null
+++ b/chromium/ui/wm/core/window_modality_controller.cc
@@ -0,0 +1,201 @@
+// 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/wm/core/window_modality_controller.h"
+
+#include <algorithm>
+
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/client/capture_client.h"
+#include "ui/aura/env.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_event_dispatcher.h"
+#include "ui/aura/window_property.h"
+#include "ui/base/ui_base_types.h"
+#include "ui/events/event.h"
+#include "ui/events/event_target.h"
+#include "ui/events/gestures/gesture_recognizer.h"
+#include "ui/wm/core/window_animations.h"
+#include "ui/wm/core/window_util.h"
+
+namespace wm {
+
+// Transient child's modal parent.
+extern const aura::WindowProperty<aura::Window*>* const kModalParentKey;
+DEFINE_WINDOW_PROPERTY_KEY(aura::Window*, kModalParentKey, NULL);
+
+namespace {
+
+bool HasAncestor(aura::Window* window, aura::Window* ancestor) {
+ if (!window)
+ return false;
+ if (window == ancestor)
+ return true;
+ return HasAncestor(window->parent(), ancestor);
+}
+
+bool TransientChildIsWindowModal(aura::Window* window) {
+ return window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_WINDOW;
+}
+
+bool TransientChildIsSystemModal(aura::Window* window) {
+ return window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_SYSTEM;
+}
+
+bool TransientChildIsChildModal(aura::Window* window) {
+ return window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_CHILD;
+}
+
+aura::Window* GetModalParent(aura::Window* window) {
+ return window->GetProperty(kModalParentKey);
+}
+
+bool IsModalTransientChild(aura::Window* transient, aura::Window* original) {
+ return transient->IsVisible() &&
+ (TransientChildIsWindowModal(transient) ||
+ TransientChildIsSystemModal(transient) ||
+ (TransientChildIsChildModal(transient) &&
+ (HasAncestor(original, GetModalParent(transient)))));
+}
+
+aura::Window* GetModalTransientChild(
+ aura::Window* activatable,
+ aura::Window* original) {
+ for (aura::Window::Windows::const_iterator it =
+ GetTransientChildren(activatable).begin();
+ it != GetTransientChildren(activatable).end();
+ ++it) {
+ aura::Window* transient = *it;
+ if (IsModalTransientChild(transient, original)) {
+ return GetTransientChildren(transient).empty() ?
+ transient : GetModalTransientChild(transient, original);
+ }
+ }
+ return NULL;
+}
+
+} // namespace
+
+void SetModalParent(aura::Window* child, aura::Window* parent) {
+ child->SetProperty(kModalParentKey, parent);
+}
+
+aura::Window* GetModalTransient(aura::Window* window) {
+ if (!window)
+ return NULL;
+
+ // We always want to check the for the transient child of the toplevel window.
+ aura::Window* toplevel = GetToplevelWindow(window);
+ if (!toplevel)
+ return NULL;
+
+ return GetModalTransientChild(toplevel, window);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowModalityController, public:
+
+WindowModalityController::WindowModalityController(
+ ui::EventTarget* event_target)
+ : event_target_(event_target) {
+ aura::Env::GetInstance()->AddObserver(this);
+ DCHECK(event_target->IsPreTargetListEmpty());
+ event_target_->AddPreTargetHandler(this);
+}
+
+WindowModalityController::~WindowModalityController() {
+ event_target_->RemovePreTargetHandler(this);
+ aura::Env::GetInstance()->RemoveObserver(this);
+ for (size_t i = 0; i < windows_.size(); ++i)
+ windows_[i]->RemoveObserver(this);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowModalityController, aura::EventFilter implementation:
+
+void WindowModalityController::OnKeyEvent(ui::KeyEvent* event) {
+ aura::Window* target = static_cast<aura::Window*>(event->target());
+ if (GetModalTransient(target))
+ event->SetHandled();
+}
+
+void WindowModalityController::OnMouseEvent(ui::MouseEvent* event) {
+ aura::Window* target = static_cast<aura::Window*>(event->target());
+ if (ProcessLocatedEvent(target, event))
+ event->SetHandled();
+}
+
+void WindowModalityController::OnTouchEvent(ui::TouchEvent* event) {
+ aura::Window* target = static_cast<aura::Window*>(event->target());
+ if (ProcessLocatedEvent(target, event))
+ event->SetHandled();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowModalityController, aura::EnvObserver implementation:
+
+void WindowModalityController::OnWindowInitialized(aura::Window* window) {
+ windows_.push_back(window);
+ window->AddObserver(this);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowModalityController, aura::WindowObserver implementation:
+
+void WindowModalityController::OnWindowPropertyChanged(aura::Window* window,
+ const void* key,
+ intptr_t old) {
+ // In tests, we sometimes create the modality relationship after a window is
+ // visible.
+ if (key == aura::client::kModalKey &&
+ window->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_NONE &&
+ window->IsVisible()) {
+ ActivateWindow(window);
+ ui::GestureRecognizer::Get()->TransferEventsTo(GetTransientParent(window),
+ NULL);
+ }
+}
+
+void WindowModalityController::OnWindowVisibilityChanged(
+ aura::Window* window,
+ bool visible) {
+ if (visible && window->GetProperty(aura::client::kModalKey) !=
+ ui::MODAL_TYPE_NONE) {
+ ui::GestureRecognizer::Get()->TransferEventsTo(GetTransientParent(window),
+ NULL);
+ // Make sure no other window has capture, otherwise |window| won't get mouse
+ // events.
+ aura::Window* capture_window = aura::client::GetCaptureWindow(window);
+ if (capture_window)
+ capture_window->ReleaseCapture();
+ }
+}
+
+void WindowModalityController::OnWindowDestroyed(aura::Window* window) {
+ windows_.erase(std::find(windows_.begin(), windows_.end(), window));
+ window->RemoveObserver(this);
+}
+
+bool WindowModalityController::ProcessLocatedEvent(aura::Window* target,
+ ui::LocatedEvent* event) {
+ if (event->handled())
+ return false;
+ aura::Window* modal_transient_child = GetModalTransient(target);
+ if (modal_transient_child && (event->type() == ui::ET_MOUSE_PRESSED ||
+ event->type() == ui::ET_TOUCH_PRESSED)) {
+ // Activate top window if transient child window is window modal.
+ if (TransientChildIsWindowModal(modal_transient_child)) {
+ aura::Window* toplevel = GetToplevelWindow(target);
+ DCHECK(toplevel);
+ ActivateWindow(toplevel);
+ }
+
+ AnimateWindow(modal_transient_child, WINDOW_ANIMATION_TYPE_BOUNCE);
+ }
+ if (event->type() == ui::ET_TOUCH_CANCELLED)
+ return false;
+ return !!modal_transient_child;
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/window_modality_controller.h b/chromium/ui/wm/core/window_modality_controller.h
new file mode 100644
index 00000000000..4d52c2190ba
--- /dev/null
+++ b/chromium/ui/wm/core/window_modality_controller.h
@@ -0,0 +1,71 @@
+// 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_WM_CORE_WINDOW_MODALITY_CONTROLLER_H_
+#define UI_WM_CORE_WINDOW_MODALITY_CONTROLLER_H_
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "ui/aura/env_observer.h"
+#include "ui/aura/window_observer.h"
+#include "ui/events/event_handler.h"
+#include "ui/wm/wm_export.h"
+
+namespace ui {
+class EventTarget;
+class LocatedEvent;
+}
+
+namespace wm {
+
+// Sets the modal parent for the child.
+WM_EXPORT void SetModalParent(aura::Window* child, aura::Window* parent);
+
+// Returns the modal transient child of |window|, or NULL if |window| does not
+// have any modal transient children.
+WM_EXPORT aura::Window* GetModalTransient(aura::Window* window);
+
+// WindowModalityController is an event filter that consumes events sent to
+// windows that are the transient parents of window-modal windows. This filter
+// must be added to the CompoundEventFilter so that activation works properly.
+class WM_EXPORT WindowModalityController : public ui::EventHandler,
+ public aura::EnvObserver,
+ public aura::WindowObserver {
+ public:
+ explicit WindowModalityController(ui::EventTarget* event_target);
+ virtual ~WindowModalityController();
+
+ // Overridden from ui::EventHandler:
+ virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
+ virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
+ virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE;
+
+ // Overridden from aura::EnvObserver:
+ virtual void OnWindowInitialized(aura::Window* window) OVERRIDE;
+
+ // Overridden from aura::WindowObserver:
+ virtual void OnWindowPropertyChanged(aura::Window* window,
+ const void* key,
+ intptr_t old) OVERRIDE;
+ virtual void OnWindowVisibilityChanged(aura::Window* window,
+ bool visible) OVERRIDE;
+ virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
+
+ private:
+ // Processes a mouse/touch event, and returns true if the event should be
+ // consumed.
+ bool ProcessLocatedEvent(aura::Window* target,
+ ui::LocatedEvent* event);
+
+ std::vector<aura::Window*> windows_;
+
+ ui::EventTarget* event_target_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowModalityController);
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_WINDOW_MODALITY_CONTROLLER_H_
diff --git a/chromium/ui/wm/core/window_util.cc b/chromium/ui/wm/core/window_util.cc
new file mode 100644
index 00000000000..6d8cdc97ba0
--- /dev/null
+++ b/chromium/ui/wm/core/window_util.cc
@@ -0,0 +1,129 @@
+// 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/wm/core/window_util.h"
+
+#include "ui/aura/window.h"
+#include "ui/aura/window_event_dispatcher.h"
+#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_tree_owner.h"
+#include "ui/wm/core/transient_window_manager.h"
+#include "ui/wm/public/activation_client.h"
+
+namespace {
+
+// Invokes RecreateLayer() on all the children of |to_clone|, adding the newly
+// cloned children to |parent|.
+//
+// WARNING: It is assumed that |parent| is ultimately owned by a LayerTreeOwner.
+void CloneChildren(ui::Layer* to_clone, ui::Layer* parent) {
+ typedef std::vector<ui::Layer*> Layers;
+ // Make a copy of the children since RecreateLayer() mutates it.
+ Layers children(to_clone->children());
+ for (Layers::const_iterator i = children.begin(); i != children.end(); ++i) {
+ ui::LayerOwner* owner = (*i)->owner();
+ ui::Layer* old_layer = owner ? owner->RecreateLayer().release() : NULL;
+ if (old_layer) {
+ parent->Add(old_layer);
+ // RecreateLayer() moves the existing children to the new layer. Create a
+ // copy of those.
+ CloneChildren(owner->layer(), old_layer);
+ }
+ }
+}
+
+} // namespace
+
+namespace wm {
+
+void ActivateWindow(aura::Window* window) {
+ DCHECK(window);
+ DCHECK(window->GetRootWindow());
+ aura::client::GetActivationClient(window->GetRootWindow())->ActivateWindow(
+ window);
+}
+
+void DeactivateWindow(aura::Window* window) {
+ DCHECK(window);
+ DCHECK(window->GetRootWindow());
+ aura::client::GetActivationClient(window->GetRootWindow())->DeactivateWindow(
+ window);
+}
+
+bool IsActiveWindow(aura::Window* window) {
+ DCHECK(window);
+ if (!window->GetRootWindow())
+ return false;
+ aura::client::ActivationClient* client =
+ aura::client::GetActivationClient(window->GetRootWindow());
+ return client && client->GetActiveWindow() == window;
+}
+
+bool CanActivateWindow(aura::Window* window) {
+ DCHECK(window);
+ if (!window->GetRootWindow())
+ return false;
+ aura::client::ActivationClient* client =
+ aura::client::GetActivationClient(window->GetRootWindow());
+ return client && client->CanActivateWindow(window);
+}
+
+aura::Window* GetActivatableWindow(aura::Window* window) {
+ aura::client::ActivationClient* client =
+ aura::client::GetActivationClient(window->GetRootWindow());
+ return client ? client->GetActivatableWindow(window) : NULL;
+}
+
+aura::Window* GetToplevelWindow(aura::Window* window) {
+ aura::client::ActivationClient* client =
+ aura::client::GetActivationClient(window->GetRootWindow());
+ return client ? client->GetToplevelWindow(window) : NULL;
+}
+
+scoped_ptr<ui::LayerTreeOwner> RecreateLayers(ui::LayerOwner* root) {
+ scoped_ptr<ui::LayerTreeOwner> old_layer(
+ new ui::LayerTreeOwner(root->RecreateLayer().release()));
+ if (old_layer->root())
+ CloneChildren(root->layer(), old_layer->root());
+ return old_layer.Pass();
+}
+
+aura::Window* GetTransientParent(aura::Window* window) {
+ return const_cast<aura::Window*>(GetTransientParent(
+ const_cast<const aura::Window*>(window)));
+}
+
+const aura::Window* GetTransientParent(const aura::Window* window) {
+ const TransientWindowManager* manager = TransientWindowManager::Get(window);
+ return manager ? manager->transient_parent() : NULL;
+}
+
+const std::vector<aura::Window*>& GetTransientChildren(
+ const aura::Window* window) {
+ const TransientWindowManager* manager = TransientWindowManager::Get(window);
+ if (manager)
+ return manager->transient_children();
+
+ static std::vector<aura::Window*>* shared = new std::vector<aura::Window*>;
+ return *shared;
+}
+
+void AddTransientChild(aura::Window* parent, aura::Window* child) {
+ TransientWindowManager::Get(parent)->AddTransientChild(child);
+}
+
+void RemoveTransientChild(aura::Window* parent, aura::Window* child) {
+ TransientWindowManager::Get(parent)->RemoveTransientChild(child);
+}
+
+bool HasTransientAncestor(const aura::Window* window,
+ const aura::Window* ancestor) {
+ const aura::Window* transient_parent = GetTransientParent(window);
+ if (transient_parent == ancestor)
+ return true;
+ return transient_parent ?
+ HasTransientAncestor(transient_parent, ancestor) : false;
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/window_util.h b/chromium/ui/wm/core/window_util.h
new file mode 100644
index 00000000000..78f2ffbf112
--- /dev/null
+++ b/chromium/ui/wm/core/window_util.h
@@ -0,0 +1,67 @@
+// 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_WM_CORE_WINDOW_UTIL_H_
+#define UI_WM_CORE_WINDOW_UTIL_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/wm/wm_export.h"
+
+namespace aura {
+class Window;
+}
+
+namespace ui {
+class Layer;
+class LayerOwner;
+class LayerTreeOwner;
+}
+
+namespace wm {
+
+WM_EXPORT void ActivateWindow(aura::Window* window);
+WM_EXPORT void DeactivateWindow(aura::Window* window);
+WM_EXPORT bool IsActiveWindow(aura::Window* window);
+WM_EXPORT bool CanActivateWindow(aura::Window* window);
+
+// Retrieves the activatable window for |window|. The ActivationClient makes
+// this determination.
+WM_EXPORT aura::Window* GetActivatableWindow(aura::Window* window);
+
+// Retrieves the toplevel window for |window|. The ActivationClient makes this
+// determination.
+WM_EXPORT aura::Window* GetToplevelWindow(aura::Window* window);
+
+// Returns the existing Layer for |root| (and all its descendants) and creates
+// a new layer for |root| and all its descendants. This is intended for
+// animations that want to animate between the existing visuals and a new state.
+//
+// As a result of this |root| has freshly created layers, meaning the layers
+// have not yet been painted to.
+WM_EXPORT scoped_ptr<ui::LayerTreeOwner> RecreateLayers(
+ ui::LayerOwner* root);
+
+// Convenience functions that get the TransientWindowManager for the window and
+// redirect appropriately. These are preferable to calling functions on
+// TransientWindowManager as they handle the appropriate NULL checks.
+WM_EXPORT aura::Window* GetTransientParent(aura::Window* window);
+WM_EXPORT const aura::Window* GetTransientParent(
+ const aura::Window* window);
+WM_EXPORT const std::vector<aura::Window*>& GetTransientChildren(
+ const aura::Window* window);
+WM_EXPORT void AddTransientChild(aura::Window* parent, aura::Window* child);
+WM_EXPORT void RemoveTransientChild(aura::Window* parent, aura::Window* child);
+
+// Returns true if |window| has |ancestor| as a transient ancestor. A transient
+// ancestor is found by following the transient parent chain of the window.
+WM_EXPORT bool HasTransientAncestor(const aura::Window* window,
+ const aura::Window* ancestor);
+
+} // namespace wm
+
+#endif // UI_WM_CORE_WINDOW_UTIL_H_
diff --git a/chromium/ui/wm/core/window_util_unittest.cc b/chromium/ui/wm/core/window_util_unittest.cc
new file mode 100644
index 00000000000..35be2259179
--- /dev/null
+++ b/chromium/ui/wm/core/window_util_unittest.cc
@@ -0,0 +1,52 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/wm/core/window_util.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/aura/test/aura_test_base.h"
+#include "ui/aura/test/test_windows.h"
+#include "ui/aura/window.h"
+#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_tree_owner.h"
+
+namespace wm {
+
+typedef aura::test::AuraTestBase WindowUtilTest;
+
+// Test if the recreate layers does not recreate layers that have
+// already been acquired.
+TEST_F(WindowUtilTest, RecreateLayers) {
+ scoped_ptr<aura::Window> window1(
+ aura::test::CreateTestWindowWithId(0, NULL));
+ scoped_ptr<aura::Window> window11(
+ aura::test::CreateTestWindowWithId(1, window1.get()));
+ scoped_ptr<aura::Window> window12(
+ aura::test::CreateTestWindowWithId(2, window1.get()));
+
+ ASSERT_EQ(2u, window1->layer()->children().size());
+
+ scoped_ptr<ui::Layer> acquired(window11->AcquireLayer());
+ EXPECT_TRUE(acquired.get());
+ EXPECT_EQ(acquired.get(), window11->layer());
+
+ scoped_ptr<ui::LayerTreeOwner> tree =
+ wm::RecreateLayers(window1.get());
+
+ // The detached layer should not have the layer that has
+ // already been detached.
+ ASSERT_EQ(1u, tree->root()->children().size());
+ // Child layer is new instance.
+ EXPECT_NE(window11->layer(), tree->root()->children()[0]);
+ EXPECT_NE(window12->layer(), tree->root()->children()[0]);
+
+ // The original window should have both.
+ ASSERT_EQ(2u, window1->layer()->children().size());
+ EXPECT_EQ(window11->layer(), window1->layer()->children()[0]);
+ EXPECT_EQ(window12->layer(), window1->layer()->children()[1]);
+
+ // Delete the window before the acquired layer is deleted.
+ window11.reset();
+}
+} // namespace wm
diff --git a/chromium/ui/wm/core/wm_core_switches.cc b/chromium/ui/wm/core/wm_core_switches.cc
new file mode 100644
index 00000000000..12fbfcca9df
--- /dev/null
+++ b/chromium/ui/wm/core/wm_core_switches.cc
@@ -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.
+
+#include "ui/wm/core/wm_core_switches.h"
+
+#include "base/command_line.h"
+
+namespace wm {
+namespace switches {
+
+// If present animations are disabled.
+const char kWindowAnimationsDisabled[] = "wm-window-animations-disabled";
+
+} // namespace switches
+} // namespace wm
diff --git a/chromium/ui/wm/core/wm_core_switches.h b/chromium/ui/wm/core/wm_core_switches.h
new file mode 100644
index 00000000000..05658d6abbb
--- /dev/null
+++ b/chromium/ui/wm/core/wm_core_switches.h
@@ -0,0 +1,24 @@
+// 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_WM_CORE_WM_CORE_SWITCHES_H_
+#define UI_WM_CORE_WM_CORE_SWITCHES_H_
+
+#include "build/build_config.h"
+#include "ui/wm/wm_export.h"
+
+namespace wm {
+namespace switches {
+
+// Note: If you add a switch, consider if it needs to be copied to a subsequent
+// command line if the process executes a new copy of itself. (For example,
+// see chromeos::LoginUtil::GetOffTheRecordCommandLine().)
+
+// Please keep alphabetized.
+WM_EXPORT extern const char kWindowAnimationsDisabled[];
+
+} // namespace switches
+} // namespace wm
+
+#endif // UI_WM_CORE_WM_CORE_SWITCHES_H_
diff --git a/chromium/ui/wm/core/wm_state.cc b/chromium/ui/wm/core/wm_state.cc
new file mode 100644
index 00000000000..6d580863394
--- /dev/null
+++ b/chromium/ui/wm/core/wm_state.cc
@@ -0,0 +1,30 @@
+// 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.
+
+#include "ui/wm/core/wm_state.h"
+
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/wm/core/transient_window_controller.h"
+#include "ui/wm/core/transient_window_stacking_client.h"
+
+namespace wm {
+
+WMState::WMState()
+ : window_stacking_client_(new TransientWindowStackingClient),
+ transient_window_client_(new TransientWindowController) {
+ aura::client::SetWindowStackingClient(window_stacking_client_.get());
+ aura::client::SetTransientWindowClient(transient_window_client_.get());
+}
+
+WMState::~WMState() {
+ if (aura::client::GetWindowStackingClient() == window_stacking_client_.get())
+ aura::client::SetWindowStackingClient(NULL);
+
+ if (aura::client::GetTransientWindowClient() ==
+ transient_window_client_.get()) {
+ aura::client::SetTransientWindowClient(NULL);
+ }
+}
+
+} // namespace wm
diff --git a/chromium/ui/wm/core/wm_state.h b/chromium/ui/wm/core/wm_state.h
new file mode 100644
index 00000000000..289c9dac5b5
--- /dev/null
+++ b/chromium/ui/wm/core/wm_state.h
@@ -0,0 +1,32 @@
+// 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_WM_CORE_WM_STATE_H_
+#define UI_WM_CORE_WM_STATE_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/wm/wm_export.h"
+
+namespace wm {
+
+class TransientWindowController;
+class TransientWindowStackingClient;
+
+// Installs state needed by the window manager.
+class WM_EXPORT WMState {
+ public:
+ WMState();
+ ~WMState();
+
+ // WindowStackingClient:
+ private:
+ scoped_ptr<TransientWindowStackingClient> window_stacking_client_;
+ scoped_ptr<TransientWindowController> transient_window_client_;
+
+ DISALLOW_COPY_AND_ASSIGN(WMState);
+};
+
+} // namespace wm
+
+#endif // UI_WM_CORE_WM_STATE_H_