|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "ui/aura/window.h" | 
|  |  | 
|  | #include <limits.h> | 
|  |  | 
|  | #include <string> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "base/compiler_specific.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/strings/string_number_conversions.h" | 
|  | #include "base/strings/string_util.h" | 
|  | #include "base/strings/stringprintf.h" | 
|  | #include "build/build_config.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  | #include "ui/aura/client/capture_client.h" | 
|  | #include "ui/aura/client/focus_change_observer.h" | 
|  | #include "ui/aura/client/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/aura_test_utils.h" | 
|  | #include "ui/aura/test/test_window_delegate.h" | 
|  | #include "ui/aura/test/test_windows.h" | 
|  | #include "ui/aura/test/window_test_api.h" | 
|  | #include "ui/aura/window_delegate.h" | 
|  | #include "ui/aura/window_event_dispatcher.h" | 
|  | #include "ui/aura/window_observer.h" | 
|  | #include "ui/aura/window_property.h" | 
|  | #include "ui/aura/window_tree_host.h" | 
|  | #include "ui/base/hit_test.h" | 
|  | #include "ui/compositor/layer.h" | 
|  | #include "ui/compositor/layer_animation_observer.h" | 
|  | #include "ui/compositor/scoped_animation_duration_scale_mode.h" | 
|  | #include "ui/compositor/scoped_layer_animation_settings.h" | 
|  | #include "ui/compositor/test/test_layers.h" | 
|  | #include "ui/display/screen.h" | 
|  | #include "ui/events/event.h" | 
|  | #include "ui/events/event_utils.h" | 
|  | #include "ui/events/gesture_detection/gesture_configuration.h" | 
|  | #include "ui/events/keycodes/keyboard_codes.h" | 
|  | #include "ui/events/test/event_generator.h" | 
|  | #include "ui/gfx/canvas.h" | 
|  | #include "ui/gfx/geometry/vector2d.h" | 
|  | #include "ui/gfx/skia_util.h" | 
|  |  | 
|  | DECLARE_WINDOW_PROPERTY_TYPE(const char*) | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | enum class DeletionOrder { | 
|  | LAYOUT_MANAGER_FIRST, | 
|  | PROPERTY_FIRST, | 
|  | UNKNOWN, | 
|  | }; | 
|  |  | 
|  | class DeletionTracker { | 
|  | public: | 
|  | DeletionTracker() {} | 
|  | ~DeletionTracker() {} | 
|  |  | 
|  | DeletionOrder order() const { return order_; } | 
|  | bool property_deleted() const { return property_deleted_; } | 
|  | bool layout_manager_deleted() const { return layout_manager_deleted_; } | 
|  |  | 
|  | void PropertyDeleted() { | 
|  | property_deleted_ = true; | 
|  | if (order_ == DeletionOrder::UNKNOWN) | 
|  | order_ = DeletionOrder::PROPERTY_FIRST; | 
|  | } | 
|  |  | 
|  | void LayoutManagerDeleted() { | 
|  | layout_manager_deleted_ = true; | 
|  | if (order_ == DeletionOrder::UNKNOWN) | 
|  | order_ = DeletionOrder::LAYOUT_MANAGER_FIRST; | 
|  | } | 
|  |  | 
|  | private: | 
|  | bool property_deleted_ = false; | 
|  | bool layout_manager_deleted_ = false; | 
|  | DeletionOrder order_ = DeletionOrder::UNKNOWN; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(DeletionTracker); | 
|  | }; | 
|  |  | 
|  | class DeletionTestProperty { | 
|  | public: | 
|  | explicit DeletionTestProperty(DeletionTracker* tracker) : tracker_(tracker) {} | 
|  | ~DeletionTestProperty() { tracker_->PropertyDeleted(); } | 
|  |  | 
|  | private: | 
|  | DeletionTracker* tracker_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(DeletionTestProperty); | 
|  | }; | 
|  |  | 
|  | class TestProperty { | 
|  | public: | 
|  | TestProperty() {} | 
|  | ~TestProperty() { | 
|  | last_deleted_ = this; | 
|  | } | 
|  | static TestProperty* last_deleted() { return last_deleted_; } | 
|  |  | 
|  | private: | 
|  | static TestProperty* last_deleted_; | 
|  | DISALLOW_COPY_AND_ASSIGN(TestProperty); | 
|  | }; | 
|  |  | 
|  | TestProperty* TestProperty::last_deleted_ = nullptr; | 
|  |  | 
|  | DEFINE_OWNED_WINDOW_PROPERTY_KEY(TestProperty, kOwnedKey, NULL); | 
|  | DEFINE_OWNED_WINDOW_PROPERTY_KEY(DeletionTestProperty, | 
|  | kDeletionTestPropertyKey, | 
|  | nullptr); | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | DECLARE_WINDOW_PROPERTY_TYPE(TestProperty*); | 
|  |  | 
|  | DECLARE_WINDOW_PROPERTY_TYPE(DeletionTestProperty*); | 
|  |  | 
|  | namespace aura { | 
|  | namespace test { | 
|  |  | 
|  | class WindowTest : public AuraTestBase { | 
|  | public: | 
|  | WindowTest() : max_separation_(0) { | 
|  | } | 
|  |  | 
|  | void SetUp() override { | 
|  | AuraTestBase::SetUp(); | 
|  | // TODO: there needs to be an easier way to do this. | 
|  | max_separation_ = ui::GestureConfiguration::GetInstance() | 
|  | ->max_separation_for_gesture_touches_in_pixels(); | 
|  | ui::GestureConfiguration::GetInstance() | 
|  | ->set_max_separation_for_gesture_touches_in_pixels(0); | 
|  | } | 
|  |  | 
|  | void TearDown() override { | 
|  | AuraTestBase::TearDown(); | 
|  | ui::GestureConfiguration::GetInstance() | 
|  | ->set_max_separation_for_gesture_touches_in_pixels(max_separation_); | 
|  | } | 
|  |  | 
|  | private: | 
|  | float max_separation_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(WindowTest); | 
|  | }; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Used for verifying destruction methods are invoked. | 
|  | class DestroyTrackingDelegateImpl : public TestWindowDelegate { | 
|  | public: | 
|  | DestroyTrackingDelegateImpl() | 
|  | : destroying_count_(0), | 
|  | destroyed_count_(0), | 
|  | in_destroying_(false) {} | 
|  |  | 
|  | void clear_destroying_count() { destroying_count_ = 0; } | 
|  | int destroying_count() const { return destroying_count_; } | 
|  |  | 
|  | void clear_destroyed_count() { destroyed_count_ = 0; } | 
|  | int destroyed_count() const { return destroyed_count_; } | 
|  |  | 
|  | bool in_destroying() const { return in_destroying_; } | 
|  |  | 
|  | void OnWindowDestroying(Window* window) override { | 
|  | EXPECT_FALSE(in_destroying_); | 
|  | in_destroying_ = true; | 
|  | destroying_count_++; | 
|  | } | 
|  |  | 
|  | void OnWindowDestroyed(Window* window) override { | 
|  | EXPECT_TRUE(in_destroying_); | 
|  | in_destroying_ = false; | 
|  | destroyed_count_++; | 
|  | } | 
|  |  | 
|  | private: | 
|  | int destroying_count_; | 
|  | int destroyed_count_; | 
|  | bool in_destroying_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(DestroyTrackingDelegateImpl); | 
|  | }; | 
|  |  | 
|  | // Used to verify that when OnWindowDestroying is invoked the parent is also | 
|  | // is in the process of being destroyed. | 
|  | class ChildWindowDelegateImpl : public DestroyTrackingDelegateImpl { | 
|  | public: | 
|  | explicit ChildWindowDelegateImpl( | 
|  | DestroyTrackingDelegateImpl* parent_delegate) | 
|  | : parent_delegate_(parent_delegate) { | 
|  | } | 
|  |  | 
|  | void OnWindowDestroying(Window* window) override { | 
|  | EXPECT_TRUE(parent_delegate_->in_destroying()); | 
|  | DestroyTrackingDelegateImpl::OnWindowDestroying(window); | 
|  | } | 
|  |  | 
|  | private: | 
|  | DestroyTrackingDelegateImpl* parent_delegate_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(ChildWindowDelegateImpl); | 
|  | }; | 
|  |  | 
|  | // Used to verify that a Window is removed from its parent when | 
|  | // OnWindowDestroyed is called. | 
|  | class DestroyOrphanDelegate : public TestWindowDelegate { | 
|  | public: | 
|  | DestroyOrphanDelegate() : window_(NULL) { | 
|  | } | 
|  |  | 
|  | void set_window(Window* window) { window_ = window; } | 
|  |  | 
|  | void OnWindowDestroyed(Window* window) override { | 
|  | EXPECT_FALSE(window_->parent()); | 
|  | } | 
|  |  | 
|  | private: | 
|  | Window* window_; | 
|  | DISALLOW_COPY_AND_ASSIGN(DestroyOrphanDelegate); | 
|  | }; | 
|  |  | 
|  | // Used in verifying mouse capture. | 
|  | class CaptureWindowDelegateImpl : public TestWindowDelegate { | 
|  | public: | 
|  | CaptureWindowDelegateImpl() { | 
|  | ResetCounts(); | 
|  | } | 
|  |  | 
|  | void ResetCounts() { | 
|  | capture_changed_event_count_ = 0; | 
|  | capture_lost_count_ = 0; | 
|  | mouse_event_count_ = 0; | 
|  | touch_event_count_ = 0; | 
|  | gesture_event_count_ = 0; | 
|  | } | 
|  |  | 
|  | int capture_changed_event_count() const { | 
|  | return capture_changed_event_count_; | 
|  | } | 
|  | int capture_lost_count() const { return capture_lost_count_; } | 
|  | int mouse_event_count() const { return mouse_event_count_; } | 
|  | int touch_event_count() const { return touch_event_count_; } | 
|  | int gesture_event_count() const { return gesture_event_count_; } | 
|  |  | 
|  | void OnMouseEvent(ui::MouseEvent* event) override { | 
|  | if (event->type() == ui::ET_MOUSE_CAPTURE_CHANGED) | 
|  | capture_changed_event_count_++; | 
|  | mouse_event_count_++; | 
|  | } | 
|  | void OnTouchEvent(ui::TouchEvent* event) override { touch_event_count_++; } | 
|  | void OnGestureEvent(ui::GestureEvent* event) override { | 
|  | gesture_event_count_++; | 
|  | } | 
|  | void OnCaptureLost() override { capture_lost_count_++; } | 
|  |  | 
|  | private: | 
|  | int capture_changed_event_count_; | 
|  | int capture_lost_count_; | 
|  | int mouse_event_count_; | 
|  | int touch_event_count_; | 
|  | int gesture_event_count_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(CaptureWindowDelegateImpl); | 
|  | }; | 
|  |  | 
|  | // Keeps track of the location of the gesture. | 
|  | class GestureTrackPositionDelegate : public TestWindowDelegate { | 
|  | public: | 
|  | GestureTrackPositionDelegate() {} | 
|  |  | 
|  | void OnGestureEvent(ui::GestureEvent* event) override { | 
|  | position_ = event->location(); | 
|  | event->StopPropagation(); | 
|  | } | 
|  |  | 
|  | const gfx::Point& position() const { return position_; } | 
|  |  | 
|  | private: | 
|  | gfx::Point position_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(GestureTrackPositionDelegate); | 
|  | }; | 
|  |  | 
|  | base::TimeTicks getTime() { | 
|  | return ui::EventTimeForNow(); | 
|  | } | 
|  |  | 
|  | class SelfEventHandlingWindowDelegate : public TestWindowDelegate { | 
|  | public: | 
|  | SelfEventHandlingWindowDelegate() {} | 
|  |  | 
|  | bool ShouldDescendIntoChildForEventHandling( | 
|  | Window* child, | 
|  | const gfx::Point& location) override { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(SelfEventHandlingWindowDelegate); | 
|  | }; | 
|  |  | 
|  | // The delegate deletes itself when the window is being destroyed. | 
|  | class DestroyWindowDelegate : public TestWindowDelegate { | 
|  | public: | 
|  | DestroyWindowDelegate() {} | 
|  |  | 
|  | private: | 
|  | ~DestroyWindowDelegate() override {} | 
|  |  | 
|  | // Overridden from WindowDelegate. | 
|  | void OnWindowDestroyed(Window* window) override { delete this; } | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(DestroyWindowDelegate); | 
|  | }; | 
|  |  | 
|  | void OffsetBounds(Window* window, int horizontal, int vertical) { | 
|  | gfx::Rect bounds = window->bounds(); | 
|  | bounds.Offset(horizontal, vertical); | 
|  | window->SetBounds(bounds); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TEST_F(WindowTest, GetChildById) { | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithId(1, root_window())); | 
|  | std::unique_ptr<Window> w11(CreateTestWindowWithId(11, w1.get())); | 
|  | std::unique_ptr<Window> w111(CreateTestWindowWithId(111, w11.get())); | 
|  | std::unique_ptr<Window> w12(CreateTestWindowWithId(12, w1.get())); | 
|  |  | 
|  | EXPECT_EQ(NULL, w1->GetChildById(57)); | 
|  | EXPECT_EQ(w12.get(), w1->GetChildById(12)); | 
|  | EXPECT_EQ(w111.get(), w1->GetChildById(111)); | 
|  | } | 
|  |  | 
|  | // Make sure that Window::Contains correctly handles children, grandchildren, | 
|  | // and not containing NULL or parents. | 
|  | TEST_F(WindowTest, Contains) { | 
|  | Window parent(NULL); | 
|  | parent.Init(ui::LAYER_NOT_DRAWN); | 
|  | Window child1(NULL); | 
|  | child1.Init(ui::LAYER_NOT_DRAWN); | 
|  | Window child2(NULL); | 
|  | child2.Init(ui::LAYER_NOT_DRAWN); | 
|  |  | 
|  | parent.AddChild(&child1); | 
|  | child1.AddChild(&child2); | 
|  |  | 
|  | EXPECT_TRUE(parent.Contains(&parent)); | 
|  | EXPECT_TRUE(parent.Contains(&child1)); | 
|  | EXPECT_TRUE(parent.Contains(&child2)); | 
|  |  | 
|  | EXPECT_FALSE(parent.Contains(NULL)); | 
|  | EXPECT_FALSE(child1.Contains(&parent)); | 
|  | EXPECT_FALSE(child2.Contains(&child1)); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, ContainsPointInRoot) { | 
|  | std::unique_ptr<Window> w(CreateTestWindow( | 
|  | SK_ColorWHITE, 1, gfx::Rect(10, 10, 5, 5), root_window())); | 
|  | EXPECT_FALSE(w->ContainsPointInRoot(gfx::Point(9, 9))); | 
|  | EXPECT_TRUE(w->ContainsPointInRoot(gfx::Point(10, 10))); | 
|  | EXPECT_TRUE(w->ContainsPointInRoot(gfx::Point(14, 14))); | 
|  | EXPECT_FALSE(w->ContainsPointInRoot(gfx::Point(15, 15))); | 
|  | EXPECT_FALSE(w->ContainsPointInRoot(gfx::Point(20, 20))); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, ContainsPoint) { | 
|  | std::unique_ptr<Window> w(CreateTestWindow( | 
|  | SK_ColorWHITE, 1, gfx::Rect(10, 10, 5, 5), root_window())); | 
|  | EXPECT_TRUE(w->ContainsPoint(gfx::Point(0, 0))); | 
|  | EXPECT_TRUE(w->ContainsPoint(gfx::Point(4, 4))); | 
|  | EXPECT_FALSE(w->ContainsPoint(gfx::Point(5, 5))); | 
|  | EXPECT_FALSE(w->ContainsPoint(gfx::Point(10, 10))); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, ConvertPointToWindow) { | 
|  | // Window::ConvertPointToWindow is mostly identical to | 
|  | // Layer::ConvertPointToLayer, except NULL values for |source| are permitted, | 
|  | // in which case the function just returns. | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithId(1, root_window())); | 
|  | gfx::Point reference_point(100, 100); | 
|  | gfx::Point test_point = reference_point; | 
|  | Window::ConvertPointToTarget(NULL, w1.get(), &test_point); | 
|  | EXPECT_EQ(reference_point, test_point); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, MoveCursorTo) { | 
|  | std::unique_ptr<Window> w1(CreateTestWindow( | 
|  | SK_ColorWHITE, 1, gfx::Rect(10, 10, 500, 500), root_window())); | 
|  | std::unique_ptr<Window> w11( | 
|  | CreateTestWindow(SK_ColorGREEN, 11, gfx::Rect(5, 5, 100, 100), w1.get())); | 
|  | std::unique_ptr<Window> w111( | 
|  | CreateTestWindow(SK_ColorCYAN, 111, gfx::Rect(5, 5, 75, 75), w11.get())); | 
|  | std::unique_ptr<Window> w1111( | 
|  | CreateTestWindow(SK_ColorRED, 1111, gfx::Rect(5, 5, 50, 50), w111.get())); | 
|  |  | 
|  | Window* root = root_window(); | 
|  | root->MoveCursorTo(gfx::Point(10, 10)); | 
|  | EXPECT_EQ("10,10", | 
|  | display::Screen::GetScreen()->GetCursorScreenPoint().ToString()); | 
|  | w1->MoveCursorTo(gfx::Point(10, 10)); | 
|  | EXPECT_EQ("20,20", | 
|  | display::Screen::GetScreen()->GetCursorScreenPoint().ToString()); | 
|  | w11->MoveCursorTo(gfx::Point(10, 10)); | 
|  | EXPECT_EQ("25,25", | 
|  | display::Screen::GetScreen()->GetCursorScreenPoint().ToString()); | 
|  | w111->MoveCursorTo(gfx::Point(10, 10)); | 
|  | EXPECT_EQ("30,30", | 
|  | display::Screen::GetScreen()->GetCursorScreenPoint().ToString()); | 
|  | w1111->MoveCursorTo(gfx::Point(10, 10)); | 
|  | EXPECT_EQ("35,35", | 
|  | display::Screen::GetScreen()->GetCursorScreenPoint().ToString()); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, ContainsMouse) { | 
|  | std::unique_ptr<Window> w(CreateTestWindow( | 
|  | SK_ColorWHITE, 1, gfx::Rect(10, 10, 500, 500), root_window())); | 
|  | w->Show(); | 
|  | WindowTestApi w_test_api(w.get()); | 
|  | Window* root = root_window(); | 
|  | root->MoveCursorTo(gfx::Point(10, 10)); | 
|  | EXPECT_TRUE(w_test_api.ContainsMouse()); | 
|  | root->MoveCursorTo(gfx::Point(9, 10)); | 
|  | EXPECT_FALSE(w_test_api.ContainsMouse()); | 
|  | } | 
|  |  | 
|  | // Test Window::ConvertPointToWindow() with transform to root_window. | 
|  | TEST_F(WindowTest, MoveCursorToWithTransformRootWindow) { | 
|  | gfx::Transform transform; | 
|  | transform.Translate(100.0, 100.0); | 
|  | transform.Rotate(90.0); | 
|  | transform.Scale(2.0, 5.0); | 
|  | host()->SetRootTransform(transform); | 
|  | host()->MoveCursorTo(gfx::Point(10, 10)); | 
|  | #if !defined(OS_WIN) | 
|  | // TODO(yoshiki): fix this to build on Windows. See crbug.com/133413.OD | 
|  | EXPECT_EQ("50,120", QueryLatestMousePositionRequestInHost(host()).ToString()); | 
|  | #endif | 
|  | EXPECT_EQ("10,10", | 
|  | display::Screen::GetScreen()->GetCursorScreenPoint().ToString()); | 
|  | } | 
|  |  | 
|  | // Tests Window::ConvertPointToWindow() with transform to non-root windows. | 
|  | TEST_F(WindowTest, MoveCursorToWithTransformWindow) { | 
|  | std::unique_ptr<Window> w1(CreateTestWindow( | 
|  | SK_ColorWHITE, 1, gfx::Rect(10, 10, 500, 500), root_window())); | 
|  |  | 
|  | gfx::Transform transform1; | 
|  | transform1.Scale(2, 2); | 
|  | w1->SetTransform(transform1); | 
|  | w1->MoveCursorTo(gfx::Point(10, 10)); | 
|  | EXPECT_EQ("30,30", | 
|  | display::Screen::GetScreen()->GetCursorScreenPoint().ToString()); | 
|  |  | 
|  | gfx::Transform transform2; | 
|  | transform2.Translate(-10, 20); | 
|  | w1->SetTransform(transform2); | 
|  | w1->MoveCursorTo(gfx::Point(10, 10)); | 
|  | EXPECT_EQ("10,40", | 
|  | display::Screen::GetScreen()->GetCursorScreenPoint().ToString()); | 
|  |  | 
|  | gfx::Transform transform3; | 
|  | transform3.Rotate(90.0); | 
|  | w1->SetTransform(transform3); | 
|  | w1->MoveCursorTo(gfx::Point(5, 5)); | 
|  | EXPECT_EQ("5,15", | 
|  | display::Screen::GetScreen()->GetCursorScreenPoint().ToString()); | 
|  |  | 
|  | gfx::Transform transform4; | 
|  | transform4.Translate(100.0, 100.0); | 
|  | transform4.Rotate(90.0); | 
|  | transform4.Scale(2.0, 5.0); | 
|  | w1->SetTransform(transform4); | 
|  | w1->MoveCursorTo(gfx::Point(10, 10)); | 
|  | EXPECT_EQ("60,130", | 
|  | display::Screen::GetScreen()->GetCursorScreenPoint().ToString()); | 
|  | } | 
|  |  | 
|  | // Test Window::ConvertPointToWindow() with complex transforms to both root and | 
|  | // non-root windows. | 
|  | // Test Window::ConvertPointToWindow() with transform to root_window. | 
|  | TEST_F(WindowTest, MoveCursorToWithComplexTransform) { | 
|  | std::unique_ptr<Window> w1(CreateTestWindow( | 
|  | SK_ColorWHITE, 1, gfx::Rect(10, 10, 500, 500), root_window())); | 
|  | std::unique_ptr<Window> w11( | 
|  | CreateTestWindow(SK_ColorGREEN, 11, gfx::Rect(5, 5, 100, 100), w1.get())); | 
|  | std::unique_ptr<Window> w111( | 
|  | CreateTestWindow(SK_ColorCYAN, 111, gfx::Rect(5, 5, 75, 75), w11.get())); | 
|  | std::unique_ptr<Window> w1111( | 
|  | CreateTestWindow(SK_ColorRED, 1111, gfx::Rect(5, 5, 50, 50), w111.get())); | 
|  |  | 
|  | // The root window expects transforms that produce integer rects. | 
|  | gfx::Transform root_transform; | 
|  | root_transform.Translate(60.0, 70.0); | 
|  | root_transform.Rotate(-90.0); | 
|  | root_transform.Translate(-50.0, -50.0); | 
|  | root_transform.Scale(2.0, 3.0); | 
|  |  | 
|  | gfx::Transform transform; | 
|  | transform.Translate(10.0, 20.0); | 
|  | transform.Rotate(10.0); | 
|  | transform.Scale(0.3f, 0.5f); | 
|  | host()->SetRootTransform(root_transform); | 
|  | w1->SetTransform(transform); | 
|  | w11->SetTransform(transform); | 
|  | w111->SetTransform(transform); | 
|  | w1111->SetTransform(transform); | 
|  |  | 
|  | w1111->MoveCursorTo(gfx::Point(10, 10)); | 
|  |  | 
|  | #if !defined(OS_WIN) | 
|  | // TODO(yoshiki): fix this to build on Windows. See crbug.com/133413. | 
|  | EXPECT_EQ("169,80", QueryLatestMousePositionRequestInHost(host()).ToString()); | 
|  | #endif | 
|  | EXPECT_EQ("20,53", | 
|  | display::Screen::GetScreen()->GetCursorScreenPoint().ToString()); | 
|  | } | 
|  |  | 
|  | // Tests that we do not crash when a Window is destroyed by going out of | 
|  | // scope (as opposed to being explicitly deleted by its WindowDelegate). | 
|  | TEST_F(WindowTest, NoCrashOnWindowDelete) { | 
|  | CaptureWindowDelegateImpl delegate; | 
|  | std::unique_ptr<Window> window(CreateTestWindowWithDelegate( | 
|  | &delegate, 0, gfx::Rect(0, 0, 20, 20), root_window())); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, GetEventHandlerForPoint) { | 
|  | std::unique_ptr<Window> w1(CreateTestWindow( | 
|  | SK_ColorWHITE, 1, gfx::Rect(10, 10, 500, 500), root_window())); | 
|  | std::unique_ptr<Window> w11( | 
|  | CreateTestWindow(SK_ColorGREEN, 11, gfx::Rect(5, 5, 100, 100), w1.get())); | 
|  | std::unique_ptr<Window> w111( | 
|  | CreateTestWindow(SK_ColorCYAN, 111, gfx::Rect(5, 5, 75, 75), w11.get())); | 
|  | std::unique_ptr<Window> w1111( | 
|  | CreateTestWindow(SK_ColorRED, 1111, gfx::Rect(5, 5, 50, 50), w111.get())); | 
|  | std::unique_ptr<Window> w12(CreateTestWindow( | 
|  | SK_ColorMAGENTA, 12, gfx::Rect(10, 420, 25, 25), w1.get())); | 
|  | std::unique_ptr<Window> w121( | 
|  | CreateTestWindow(SK_ColorYELLOW, 121, gfx::Rect(5, 5, 5, 5), w12.get())); | 
|  | std::unique_ptr<Window> w13( | 
|  | CreateTestWindow(SK_ColorGRAY, 13, gfx::Rect(5, 470, 50, 50), w1.get())); | 
|  |  | 
|  | Window* root = root_window(); | 
|  | w1->parent()->SetBounds(gfx::Rect(500, 500)); | 
|  | EXPECT_EQ(NULL, root->GetEventHandlerForPoint(gfx::Point(5, 5))); | 
|  | EXPECT_EQ(w1.get(), root->GetEventHandlerForPoint(gfx::Point(11, 11))); | 
|  | EXPECT_EQ(w11.get(), root->GetEventHandlerForPoint(gfx::Point(16, 16))); | 
|  | EXPECT_EQ(w111.get(), root->GetEventHandlerForPoint(gfx::Point(21, 21))); | 
|  | EXPECT_EQ(w1111.get(), root->GetEventHandlerForPoint(gfx::Point(26, 26))); | 
|  | EXPECT_EQ(w12.get(), root->GetEventHandlerForPoint(gfx::Point(21, 431))); | 
|  | EXPECT_EQ(w121.get(), root->GetEventHandlerForPoint(gfx::Point(26, 436))); | 
|  | EXPECT_EQ(w13.get(), root->GetEventHandlerForPoint(gfx::Point(26, 481))); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, GetEventHandlerForPointWithOverride) { | 
|  | // If our child is flush to our top-left corner it gets events just inside the | 
|  | // window edges. | 
|  | std::unique_ptr<Window> parent(CreateTestWindow( | 
|  | SK_ColorWHITE, 1, gfx::Rect(10, 20, 400, 500), root_window())); | 
|  | std::unique_ptr<Window> child( | 
|  | CreateTestWindow(SK_ColorRED, 2, gfx::Rect(0, 0, 60, 70), parent.get())); | 
|  | EXPECT_EQ(child.get(), parent->GetEventHandlerForPoint(gfx::Point(0, 0))); | 
|  | EXPECT_EQ(child.get(), parent->GetEventHandlerForPoint(gfx::Point(1, 1))); | 
|  |  | 
|  | // We can override the hit test bounds of the parent to make the parent grab | 
|  | // events along that edge. | 
|  | parent->set_hit_test_bounds_override_inner(gfx::Insets(1, 1, 1, 1)); | 
|  | EXPECT_EQ(parent.get(), parent->GetEventHandlerForPoint(gfx::Point(0, 0))); | 
|  | EXPECT_EQ(child.get(),  parent->GetEventHandlerForPoint(gfx::Point(1, 1))); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, GetEventHandlerForPointWithOverrideDescendingOrder) { | 
|  | std::unique_ptr<SelfEventHandlingWindowDelegate> parent_delegate( | 
|  | new SelfEventHandlingWindowDelegate); | 
|  | std::unique_ptr<Window> parent(CreateTestWindowWithDelegate( | 
|  | parent_delegate.get(), 1, gfx::Rect(10, 20, 400, 500), root_window())); | 
|  | std::unique_ptr<Window> child(CreateTestWindow( | 
|  | SK_ColorRED, 2, gfx::Rect(0, 0, 390, 480), parent.get())); | 
|  |  | 
|  | // We can override ShouldDescendIntoChildForEventHandling to make the parent | 
|  | // grab all events. | 
|  | EXPECT_EQ(parent.get(), parent->GetEventHandlerForPoint(gfx::Point(0, 0))); | 
|  | EXPECT_EQ(parent.get(), parent->GetEventHandlerForPoint(gfx::Point(50, 50))); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, GetTopWindowContainingPoint) { | 
|  | Window* root = root_window(); | 
|  | root->SetBounds(gfx::Rect(0, 0, 300, 300)); | 
|  |  | 
|  | std::unique_ptr<Window> w1(CreateTestWindow( | 
|  | SK_ColorWHITE, 1, gfx::Rect(10, 10, 100, 100), root_window())); | 
|  | std::unique_ptr<Window> w11( | 
|  | CreateTestWindow(SK_ColorGREEN, 11, gfx::Rect(0, 0, 120, 120), w1.get())); | 
|  |  | 
|  | std::unique_ptr<Window> w2( | 
|  | CreateTestWindow(SK_ColorRED, 2, gfx::Rect(5, 5, 55, 55), root_window())); | 
|  |  | 
|  | std::unique_ptr<Window> w3(CreateTestWindowWithDelegate( | 
|  | NULL, 3, gfx::Rect(200, 200, 100, 100), root_window())); | 
|  | std::unique_ptr<Window> w31( | 
|  | CreateTestWindow(SK_ColorCYAN, 31, gfx::Rect(0, 0, 50, 50), w3.get())); | 
|  | std::unique_ptr<Window> w311( | 
|  | CreateTestWindow(SK_ColorBLUE, 311, gfx::Rect(0, 0, 10, 10), w31.get())); | 
|  |  | 
|  | EXPECT_EQ(NULL, root->GetTopWindowContainingPoint(gfx::Point(0, 0))); | 
|  | EXPECT_EQ(w2.get(), root->GetTopWindowContainingPoint(gfx::Point(5, 5))); | 
|  | EXPECT_EQ(w2.get(), root->GetTopWindowContainingPoint(gfx::Point(10, 10))); | 
|  | EXPECT_EQ(w2.get(), root->GetTopWindowContainingPoint(gfx::Point(59, 59))); | 
|  | EXPECT_EQ(w1.get(), root->GetTopWindowContainingPoint(gfx::Point(60, 60))); | 
|  | EXPECT_EQ(w1.get(), root->GetTopWindowContainingPoint(gfx::Point(109, 109))); | 
|  | EXPECT_EQ(NULL, root->GetTopWindowContainingPoint(gfx::Point(110, 110))); | 
|  | EXPECT_EQ(w31.get(), root->GetTopWindowContainingPoint(gfx::Point(200, 200))); | 
|  | EXPECT_EQ(w31.get(), root->GetTopWindowContainingPoint(gfx::Point(220, 220))); | 
|  | EXPECT_EQ(NULL, root->GetTopWindowContainingPoint(gfx::Point(260, 260))); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, GetToplevelWindow) { | 
|  | const gfx::Rect kBounds(0, 0, 10, 10); | 
|  | TestWindowDelegate delegate; | 
|  |  | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithId(1, root_window())); | 
|  | std::unique_ptr<Window> w11( | 
|  | CreateTestWindowWithDelegate(&delegate, 11, kBounds, w1.get())); | 
|  | std::unique_ptr<Window> w111(CreateTestWindowWithId(111, w11.get())); | 
|  | std::unique_ptr<Window> w1111( | 
|  | CreateTestWindowWithDelegate(&delegate, 1111, kBounds, w111.get())); | 
|  |  | 
|  | EXPECT_TRUE(root_window()->GetToplevelWindow() == NULL); | 
|  | EXPECT_TRUE(w1->GetToplevelWindow() == NULL); | 
|  | EXPECT_EQ(w11.get(), w11->GetToplevelWindow()); | 
|  | EXPECT_EQ(w11.get(), w111->GetToplevelWindow()); | 
|  | EXPECT_EQ(w11.get(), w1111->GetToplevelWindow()); | 
|  | } | 
|  |  | 
|  | class AddedToRootWindowObserver : public WindowObserver { | 
|  | public: | 
|  | AddedToRootWindowObserver() : called_(false) {} | 
|  |  | 
|  | void OnWindowAddedToRootWindow(Window* window) override { called_ = true; } | 
|  |  | 
|  | bool called() const { return called_; } | 
|  |  | 
|  | private: | 
|  | bool called_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(AddedToRootWindowObserver); | 
|  | }; | 
|  |  | 
|  | TEST_F(WindowTest, WindowAddedToRootWindowShouldNotifyChildAndNotParent) { | 
|  | AddedToRootWindowObserver parent_observer; | 
|  | AddedToRootWindowObserver child_observer; | 
|  | std::unique_ptr<Window> parent_window( | 
|  | CreateTestWindowWithId(1, root_window())); | 
|  | std::unique_ptr<Window> child_window(new Window(NULL)); | 
|  | child_window->Init(ui::LAYER_TEXTURED); | 
|  | child_window->Show(); | 
|  |  | 
|  | parent_window->AddObserver(&parent_observer); | 
|  | child_window->AddObserver(&child_observer); | 
|  |  | 
|  | parent_window->AddChild(child_window.get()); | 
|  |  | 
|  | EXPECT_FALSE(parent_observer.called()); | 
|  | EXPECT_TRUE(child_observer.called()); | 
|  |  | 
|  | parent_window->RemoveObserver(&parent_observer); | 
|  | child_window->RemoveObserver(&child_observer); | 
|  | } | 
|  |  | 
|  | // Various destruction assertions. | 
|  | TEST_F(WindowTest, DestroyTest) { | 
|  | DestroyTrackingDelegateImpl parent_delegate; | 
|  | ChildWindowDelegateImpl child_delegate(&parent_delegate); | 
|  | { | 
|  | std::unique_ptr<Window> parent(CreateTestWindowWithDelegate( | 
|  | &parent_delegate, 0, gfx::Rect(), root_window())); | 
|  | CreateTestWindowWithDelegate(&child_delegate, 0, gfx::Rect(), parent.get()); | 
|  | } | 
|  | // Both the parent and child should have been destroyed. | 
|  | EXPECT_EQ(1, parent_delegate.destroying_count()); | 
|  | EXPECT_EQ(1, parent_delegate.destroyed_count()); | 
|  | EXPECT_EQ(1, child_delegate.destroying_count()); | 
|  | EXPECT_EQ(1, child_delegate.destroyed_count()); | 
|  | } | 
|  |  | 
|  | // Tests that a window is orphaned before OnWindowDestroyed is called. | 
|  | TEST_F(WindowTest, OrphanedBeforeOnDestroyed) { | 
|  | TestWindowDelegate parent_delegate; | 
|  | DestroyOrphanDelegate child_delegate; | 
|  | { | 
|  | std::unique_ptr<Window> parent(CreateTestWindowWithDelegate( | 
|  | &parent_delegate, 0, gfx::Rect(), root_window())); | 
|  | std::unique_ptr<Window> child(CreateTestWindowWithDelegate( | 
|  | &child_delegate, 0, gfx::Rect(), parent.get())); | 
|  | child_delegate.set_window(child.get()); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Make sure StackChildAtTop moves both the window and layer to the front. | 
|  | TEST_F(WindowTest, StackChildAtTop) { | 
|  | Window parent(NULL); | 
|  | parent.Init(ui::LAYER_NOT_DRAWN); | 
|  | Window child1(NULL); | 
|  | child1.Init(ui::LAYER_NOT_DRAWN); | 
|  | Window child2(NULL); | 
|  | child2.Init(ui::LAYER_NOT_DRAWN); | 
|  |  | 
|  | parent.AddChild(&child1); | 
|  | parent.AddChild(&child2); | 
|  | ASSERT_EQ(2u, parent.children().size()); | 
|  | EXPECT_EQ(&child1, parent.children()[0]); | 
|  | EXPECT_EQ(&child2, parent.children()[1]); | 
|  | ASSERT_EQ(2u, parent.layer()->children().size()); | 
|  | EXPECT_EQ(child1.layer(), parent.layer()->children()[0]); | 
|  | EXPECT_EQ(child2.layer(), parent.layer()->children()[1]); | 
|  |  | 
|  | parent.StackChildAtTop(&child1); | 
|  | ASSERT_EQ(2u, parent.children().size()); | 
|  | EXPECT_EQ(&child1, parent.children()[1]); | 
|  | EXPECT_EQ(&child2, parent.children()[0]); | 
|  | ASSERT_EQ(2u, parent.layer()->children().size()); | 
|  | EXPECT_EQ(child1.layer(), parent.layer()->children()[1]); | 
|  | EXPECT_EQ(child2.layer(), parent.layer()->children()[0]); | 
|  | } | 
|  |  | 
|  | // Make sure StackChildBelow works. | 
|  | TEST_F(WindowTest, StackChildBelow) { | 
|  | Window parent(NULL); | 
|  | parent.Init(ui::LAYER_NOT_DRAWN); | 
|  | Window child1(NULL); | 
|  | child1.Init(ui::LAYER_NOT_DRAWN); | 
|  | child1.set_id(1); | 
|  | Window child2(NULL); | 
|  | child2.Init(ui::LAYER_NOT_DRAWN); | 
|  | child2.set_id(2); | 
|  | Window child3(NULL); | 
|  | child3.Init(ui::LAYER_NOT_DRAWN); | 
|  | child3.set_id(3); | 
|  |  | 
|  | parent.AddChild(&child1); | 
|  | parent.AddChild(&child2); | 
|  | parent.AddChild(&child3); | 
|  | EXPECT_EQ("1 2 3", ChildWindowIDsAsString(&parent)); | 
|  |  | 
|  | parent.StackChildBelow(&child1, &child2); | 
|  | EXPECT_EQ("1 2 3", ChildWindowIDsAsString(&parent)); | 
|  |  | 
|  | parent.StackChildBelow(&child2, &child1); | 
|  | EXPECT_EQ("2 1 3", ChildWindowIDsAsString(&parent)); | 
|  |  | 
|  | parent.StackChildBelow(&child3, &child2); | 
|  | EXPECT_EQ("3 2 1", ChildWindowIDsAsString(&parent)); | 
|  |  | 
|  | parent.StackChildBelow(&child3, &child1); | 
|  | EXPECT_EQ("2 3 1", ChildWindowIDsAsString(&parent)); | 
|  | } | 
|  |  | 
|  | // Various assertions for StackChildAbove. | 
|  | TEST_F(WindowTest, StackChildAbove) { | 
|  | Window parent(NULL); | 
|  | parent.Init(ui::LAYER_NOT_DRAWN); | 
|  | Window child1(NULL); | 
|  | child1.Init(ui::LAYER_NOT_DRAWN); | 
|  | Window child2(NULL); | 
|  | child2.Init(ui::LAYER_NOT_DRAWN); | 
|  | Window child3(NULL); | 
|  | child3.Init(ui::LAYER_NOT_DRAWN); | 
|  |  | 
|  | parent.AddChild(&child1); | 
|  | parent.AddChild(&child2); | 
|  |  | 
|  | // Move 1 in front of 2. | 
|  | parent.StackChildAbove(&child1, &child2); | 
|  | ASSERT_EQ(2u, parent.children().size()); | 
|  | EXPECT_EQ(&child2, parent.children()[0]); | 
|  | EXPECT_EQ(&child1, parent.children()[1]); | 
|  | ASSERT_EQ(2u, parent.layer()->children().size()); | 
|  | EXPECT_EQ(child2.layer(), parent.layer()->children()[0]); | 
|  | EXPECT_EQ(child1.layer(), parent.layer()->children()[1]); | 
|  |  | 
|  | // Add 3, resulting in order [2, 1, 3], then move 2 in front of 1, resulting | 
|  | // in [1, 2, 3]. | 
|  | parent.AddChild(&child3); | 
|  | parent.StackChildAbove(&child2, &child1); | 
|  | ASSERT_EQ(3u, parent.children().size()); | 
|  | EXPECT_EQ(&child1, parent.children()[0]); | 
|  | EXPECT_EQ(&child2, parent.children()[1]); | 
|  | EXPECT_EQ(&child3, parent.children()[2]); | 
|  | ASSERT_EQ(3u, parent.layer()->children().size()); | 
|  | EXPECT_EQ(child1.layer(), parent.layer()->children()[0]); | 
|  | EXPECT_EQ(child2.layer(), parent.layer()->children()[1]); | 
|  | EXPECT_EQ(child3.layer(), parent.layer()->children()[2]); | 
|  |  | 
|  | // Move 1 in front of 3, resulting in [2, 3, 1]. | 
|  | parent.StackChildAbove(&child1, &child3); | 
|  | ASSERT_EQ(3u, parent.children().size()); | 
|  | EXPECT_EQ(&child2, parent.children()[0]); | 
|  | EXPECT_EQ(&child3, parent.children()[1]); | 
|  | EXPECT_EQ(&child1, parent.children()[2]); | 
|  | ASSERT_EQ(3u, parent.layer()->children().size()); | 
|  | EXPECT_EQ(child2.layer(), parent.layer()->children()[0]); | 
|  | EXPECT_EQ(child3.layer(), parent.layer()->children()[1]); | 
|  | EXPECT_EQ(child1.layer(), parent.layer()->children()[2]); | 
|  |  | 
|  | // Moving 1 in front of 2 should lower it, resulting in [2, 1, 3]. | 
|  | parent.StackChildAbove(&child1, &child2); | 
|  | ASSERT_EQ(3u, parent.children().size()); | 
|  | EXPECT_EQ(&child2, parent.children()[0]); | 
|  | EXPECT_EQ(&child1, parent.children()[1]); | 
|  | EXPECT_EQ(&child3, parent.children()[2]); | 
|  | ASSERT_EQ(3u, parent.layer()->children().size()); | 
|  | EXPECT_EQ(child2.layer(), parent.layer()->children()[0]); | 
|  | EXPECT_EQ(child1.layer(), parent.layer()->children()[1]); | 
|  | EXPECT_EQ(child3.layer(), parent.layer()->children()[2]); | 
|  | } | 
|  |  | 
|  | // Various capture assertions. | 
|  | TEST_F(WindowTest, CaptureTests) { | 
|  | CaptureWindowDelegateImpl delegate; | 
|  | std::unique_ptr<Window> window(CreateTestWindowWithDelegate( | 
|  | &delegate, 0, gfx::Rect(0, 0, 20, 20), root_window())); | 
|  | EXPECT_FALSE(window->HasCapture()); | 
|  |  | 
|  | delegate.ResetCounts(); | 
|  |  | 
|  | // Do a capture. | 
|  | window->SetCapture(); | 
|  | EXPECT_TRUE(window->HasCapture()); | 
|  | EXPECT_EQ(0, delegate.capture_lost_count()); | 
|  | EXPECT_EQ(0, delegate.capture_changed_event_count()); | 
|  | ui::test::EventGenerator generator(root_window(), gfx::Point(50, 50)); | 
|  | generator.PressLeftButton(); | 
|  | EXPECT_EQ(1, delegate.mouse_event_count()); | 
|  | generator.ReleaseLeftButton(); | 
|  |  | 
|  | EXPECT_EQ(2, delegate.mouse_event_count()); | 
|  | delegate.ResetCounts(); | 
|  |  | 
|  | ui::TouchEvent touchev( | 
|  | ui::ET_TOUCH_PRESSED, gfx::Point(50, 50), 0, getTime()); | 
|  | DispatchEventUsingWindowDispatcher(&touchev); | 
|  | EXPECT_EQ(1, delegate.touch_event_count()); | 
|  | delegate.ResetCounts(); | 
|  |  | 
|  | window->ReleaseCapture(); | 
|  | EXPECT_FALSE(window->HasCapture()); | 
|  | EXPECT_EQ(1, delegate.capture_lost_count()); | 
|  | EXPECT_EQ(1, delegate.capture_changed_event_count()); | 
|  | EXPECT_EQ(1, delegate.mouse_event_count()); | 
|  | EXPECT_EQ(0, delegate.touch_event_count()); | 
|  |  | 
|  | generator.PressLeftButton(); | 
|  | EXPECT_EQ(1, delegate.mouse_event_count()); | 
|  |  | 
|  | ui::TouchEvent touchev2( | 
|  | ui::ET_TOUCH_PRESSED, gfx::Point(250, 250), 1, getTime()); | 
|  | DispatchEventUsingWindowDispatcher(&touchev2); | 
|  | EXPECT_EQ(0, delegate.touch_event_count()); | 
|  |  | 
|  | // Removing the capture window from parent should reset the capture window | 
|  | // in the root window. | 
|  | window->SetCapture(); | 
|  | EXPECT_EQ(window.get(), aura::client::GetCaptureWindow(root_window())); | 
|  | window->parent()->RemoveChild(window.get()); | 
|  | EXPECT_FALSE(window->HasCapture()); | 
|  | EXPECT_EQ(NULL, aura::client::GetCaptureWindow(root_window())); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, TouchCaptureCancelsOtherTouches) { | 
|  | CaptureWindowDelegateImpl delegate1; | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithDelegate( | 
|  | &delegate1, 0, gfx::Rect(0, 0, 50, 50), root_window())); | 
|  | CaptureWindowDelegateImpl delegate2; | 
|  | std::unique_ptr<Window> w2(CreateTestWindowWithDelegate( | 
|  | &delegate2, 0, gfx::Rect(50, 50, 50, 50), root_window())); | 
|  |  | 
|  | // Press on w1. | 
|  | ui::TouchEvent press1( | 
|  | ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), 0, getTime()); | 
|  | DispatchEventUsingWindowDispatcher(&press1); | 
|  | // We will get both GESTURE_BEGIN and GESTURE_TAP_DOWN. | 
|  | EXPECT_EQ(2, delegate1.gesture_event_count()); | 
|  | delegate1.ResetCounts(); | 
|  |  | 
|  | // Capturing to w2 should cause the touch to be canceled. | 
|  | w2->SetCapture(); | 
|  | EXPECT_EQ(1, delegate1.touch_event_count()); | 
|  | EXPECT_EQ(0, delegate2.touch_event_count()); | 
|  | delegate1.ResetCounts(); | 
|  | delegate2.ResetCounts(); | 
|  |  | 
|  | // Events are now untargetted. | 
|  | ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(10, 20), 0, getTime()); | 
|  | DispatchEventUsingWindowDispatcher(&move); | 
|  | EXPECT_EQ(0, delegate1.gesture_event_count()); | 
|  | EXPECT_EQ(0, delegate1.touch_event_count()); | 
|  | EXPECT_EQ(0, delegate2.gesture_event_count()); | 
|  | EXPECT_EQ(0, delegate2.touch_event_count()); | 
|  |  | 
|  | ui::TouchEvent release( | 
|  | ui::ET_TOUCH_RELEASED, gfx::Point(10, 20), 0, getTime()); | 
|  | DispatchEventUsingWindowDispatcher(&release); | 
|  | EXPECT_EQ(0, delegate1.gesture_event_count()); | 
|  | EXPECT_EQ(0, delegate2.gesture_event_count()); | 
|  |  | 
|  | // A new press is captured by w2. | 
|  | ui::TouchEvent press2( | 
|  | ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), 0, getTime()); | 
|  | DispatchEventUsingWindowDispatcher(&press2); | 
|  | EXPECT_EQ(0, delegate1.gesture_event_count()); | 
|  | // We will get both GESTURE_BEGIN and GESTURE_TAP_DOWN. | 
|  | EXPECT_EQ(2, delegate2.gesture_event_count()); | 
|  | delegate1.ResetCounts(); | 
|  | delegate2.ResetCounts(); | 
|  |  | 
|  | // And releasing capture changes nothing. | 
|  | w2->ReleaseCapture(); | 
|  | EXPECT_EQ(0, delegate1.gesture_event_count()); | 
|  | EXPECT_EQ(0, delegate1.touch_event_count()); | 
|  | EXPECT_EQ(0, delegate2.gesture_event_count()); | 
|  | EXPECT_EQ(0, delegate2.touch_event_count()); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, TouchCaptureDoesntCancelCapturedTouches) { | 
|  | CaptureWindowDelegateImpl delegate; | 
|  | std::unique_ptr<Window> window(CreateTestWindowWithDelegate( | 
|  | &delegate, 0, gfx::Rect(0, 0, 50, 50), root_window())); | 
|  | base::TimeTicks time = getTime(); | 
|  | const int kTimeDelta = 100; | 
|  |  | 
|  | ui::TouchEvent press( | 
|  | ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), 0, time); | 
|  | DispatchEventUsingWindowDispatcher(&press); | 
|  |  | 
|  | // We will get both GESTURE_BEGIN and GESTURE_TAP_DOWN. | 
|  | EXPECT_EQ(2, delegate.gesture_event_count()); | 
|  | EXPECT_EQ(1, delegate.touch_event_count()); | 
|  | delegate.ResetCounts(); | 
|  |  | 
|  | window->SetCapture(); | 
|  | EXPECT_EQ(0, delegate.gesture_event_count()); | 
|  | EXPECT_EQ(0, delegate.touch_event_count()); | 
|  | delegate.ResetCounts(); | 
|  |  | 
|  | // On move We will get TOUCH_MOVED, GESTURE_TAP_CANCEL, | 
|  | // GESTURE_SCROLL_START and GESTURE_SCROLL_UPDATE. | 
|  | time += base::TimeDelta::FromMilliseconds(kTimeDelta); | 
|  | ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(10, 20), 0, time); | 
|  | DispatchEventUsingWindowDispatcher(&move); | 
|  | EXPECT_EQ(1, delegate.touch_event_count()); | 
|  | EXPECT_EQ(3, delegate.gesture_event_count()); | 
|  | delegate.ResetCounts(); | 
|  |  | 
|  | // Release capture shouldn't change anything. | 
|  | window->ReleaseCapture(); | 
|  | EXPECT_EQ(0, delegate.touch_event_count()); | 
|  | EXPECT_EQ(0, delegate.gesture_event_count()); | 
|  | delegate.ResetCounts(); | 
|  |  | 
|  | // On move we still get TOUCH_MOVED and GESTURE_SCROLL_UPDATE. | 
|  | time += base::TimeDelta::FromMilliseconds(kTimeDelta); | 
|  | ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(10, 30), 0, time); | 
|  | DispatchEventUsingWindowDispatcher(&move2); | 
|  | EXPECT_EQ(1, delegate.touch_event_count()); | 
|  | EXPECT_EQ(1, delegate.gesture_event_count()); | 
|  | delegate.ResetCounts(); | 
|  |  | 
|  | // And on release we get TOUCH_RELEASED, GESTURE_SCROLL_END, GESTURE_END | 
|  | time += base::TimeDelta::FromMilliseconds(kTimeDelta); | 
|  | ui::TouchEvent release( | 
|  | ui::ET_TOUCH_RELEASED, gfx::Point(10, 20), 0, time); | 
|  | DispatchEventUsingWindowDispatcher(&release); | 
|  | EXPECT_EQ(1, delegate.touch_event_count()); | 
|  | EXPECT_EQ(2, delegate.gesture_event_count()); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Assertions around SetCapture() and touch/gestures. | 
|  | TEST_F(WindowTest, TransferCaptureTouchEvents) { | 
|  | // Touch on |w1|. | 
|  | CaptureWindowDelegateImpl d1; | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithDelegate( | 
|  | &d1, 0, gfx::Rect(0, 0, 20, 20), root_window())); | 
|  | ui::TouchEvent p1(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), 0, getTime()); | 
|  | DispatchEventUsingWindowDispatcher(&p1); | 
|  | // We will get both GESTURE_BEGIN and GESTURE_TAP_DOWN. | 
|  | EXPECT_EQ(1, d1.touch_event_count()); | 
|  | EXPECT_EQ(2, d1.gesture_event_count()); | 
|  | d1.ResetCounts(); | 
|  |  | 
|  | // Touch on |w2| with a different id. | 
|  | CaptureWindowDelegateImpl d2; | 
|  | std::unique_ptr<Window> w2(CreateTestWindowWithDelegate( | 
|  | &d2, 0, gfx::Rect(40, 0, 40, 20), root_window())); | 
|  | ui::TouchEvent p2(ui::ET_TOUCH_PRESSED, gfx::Point(41, 10), 1, getTime()); | 
|  | DispatchEventUsingWindowDispatcher(&p2); | 
|  | EXPECT_EQ(0, d1.touch_event_count()); | 
|  | EXPECT_EQ(0, d1.gesture_event_count()); | 
|  | // We will get both GESTURE_BEGIN and GESTURE_TAP_DOWN for new target window. | 
|  | EXPECT_EQ(1, d2.touch_event_count()); | 
|  | EXPECT_EQ(2, d2.gesture_event_count()); | 
|  | d1.ResetCounts(); | 
|  | d2.ResetCounts(); | 
|  |  | 
|  | // Set capture on |w2|, this should send a cancel (TAP_CANCEL, END) to |w1| | 
|  | // but not |w2|. | 
|  | w2->SetCapture(); | 
|  | EXPECT_EQ(1, d1.touch_event_count()); | 
|  | EXPECT_EQ(2, d1.gesture_event_count()); | 
|  | EXPECT_EQ(0, d2.touch_event_count()); | 
|  | EXPECT_EQ(0, d2.gesture_event_count()); | 
|  | d1.ResetCounts(); | 
|  | d2.ResetCounts(); | 
|  |  | 
|  | CaptureWindowDelegateImpl d3; | 
|  | std::unique_ptr<Window> w3(CreateTestWindowWithDelegate( | 
|  | &d3, 0, gfx::Rect(0, 0, 100, 101), root_window())); | 
|  | // Set capture on |w3|. All touches have already been cancelled. | 
|  | w3->SetCapture(); | 
|  | EXPECT_EQ(0, d1.touch_event_count()); | 
|  | EXPECT_EQ(0, d1.gesture_event_count()); | 
|  | EXPECT_EQ(1, d2.touch_event_count()); | 
|  | EXPECT_EQ(2, d2.gesture_event_count()); | 
|  | EXPECT_EQ(0, d3.touch_event_count()); | 
|  | EXPECT_EQ(0, d3.gesture_event_count()); | 
|  | d2.ResetCounts(); | 
|  |  | 
|  | // Move touch id originally associated with |w2|. The touch has been | 
|  | // cancelled, so no events should be dispatched. | 
|  | ui::TouchEvent m3(ui::ET_TOUCH_MOVED, gfx::Point(110, 105), 1, getTime()); | 
|  | DispatchEventUsingWindowDispatcher(&m3); | 
|  | EXPECT_EQ(0, d1.touch_event_count()); | 
|  | EXPECT_EQ(0, d1.gesture_event_count()); | 
|  | EXPECT_EQ(0, d2.touch_event_count()); | 
|  | EXPECT_EQ(0, d2.gesture_event_count()); | 
|  | EXPECT_EQ(0, d3.touch_event_count()); | 
|  | EXPECT_EQ(0, d3.gesture_event_count()); | 
|  |  | 
|  | // When we release capture, no touches are canceled. | 
|  | w3->ReleaseCapture(); | 
|  | EXPECT_EQ(0, d1.touch_event_count()); | 
|  | EXPECT_EQ(0, d1.gesture_event_count()); | 
|  | EXPECT_EQ(0, d2.touch_event_count()); | 
|  | EXPECT_EQ(0, d2.gesture_event_count()); | 
|  | EXPECT_EQ(0, d3.touch_event_count()); | 
|  | EXPECT_EQ(0, d3.gesture_event_count()); | 
|  |  | 
|  | // The touch has been cancelled, so no events are dispatched. | 
|  | ui::TouchEvent m4(ui::ET_TOUCH_MOVED, gfx::Point(120, 105), 1, getTime()); | 
|  | DispatchEventUsingWindowDispatcher(&m4); | 
|  | EXPECT_EQ(0, d1.touch_event_count()); | 
|  | EXPECT_EQ(0, d1.gesture_event_count()); | 
|  | EXPECT_EQ(0, d2.touch_event_count()); | 
|  | EXPECT_EQ(0, d2.gesture_event_count()); | 
|  | EXPECT_EQ(0, d3.touch_event_count()); | 
|  | EXPECT_EQ(0, d3.gesture_event_count()); | 
|  | } | 
|  |  | 
|  | // Changes capture while capture is already ongoing. | 
|  | TEST_F(WindowTest, ChangeCaptureWhileMouseDown) { | 
|  | CaptureWindowDelegateImpl delegate; | 
|  | std::unique_ptr<Window> window(CreateTestWindowWithDelegate( | 
|  | &delegate, 0, gfx::Rect(0, 0, 20, 20), root_window())); | 
|  | CaptureWindowDelegateImpl delegate2; | 
|  | std::unique_ptr<Window> w2(CreateTestWindowWithDelegate( | 
|  | &delegate2, 0, gfx::Rect(20, 20, 20, 20), root_window())); | 
|  |  | 
|  | // Execute the scheduled draws so that mouse events are not | 
|  | // aggregated. | 
|  | RunAllPendingInMessageLoop(); | 
|  |  | 
|  | EXPECT_FALSE(window->HasCapture()); | 
|  |  | 
|  | // Do a capture. | 
|  | delegate.ResetCounts(); | 
|  | window->SetCapture(); | 
|  | EXPECT_TRUE(window->HasCapture()); | 
|  | EXPECT_EQ(0, delegate.capture_lost_count()); | 
|  | EXPECT_EQ(0, delegate.capture_changed_event_count()); | 
|  | ui::test::EventGenerator generator(root_window(), gfx::Point(50, 50)); | 
|  | generator.PressLeftButton(); | 
|  | EXPECT_EQ(0, delegate.capture_lost_count()); | 
|  | EXPECT_EQ(0, delegate.capture_changed_event_count()); | 
|  | EXPECT_EQ(1, delegate.mouse_event_count()); | 
|  |  | 
|  | // Set capture to |w2|, should implicitly unset capture for |window|. | 
|  | delegate.ResetCounts(); | 
|  | delegate2.ResetCounts(); | 
|  | w2->SetCapture(); | 
|  |  | 
|  | generator.MoveMouseTo(gfx::Point(40, 40), 2); | 
|  | EXPECT_EQ(1, delegate.capture_lost_count()); | 
|  | EXPECT_EQ(1, delegate.capture_changed_event_count()); | 
|  | EXPECT_EQ(1, delegate.mouse_event_count()); | 
|  | EXPECT_EQ(2, delegate2.mouse_event_count()); | 
|  | } | 
|  |  | 
|  | // Verifies capture is reset when a window is destroyed. | 
|  | TEST_F(WindowTest, ReleaseCaptureOnDestroy) { | 
|  | CaptureWindowDelegateImpl delegate; | 
|  | std::unique_ptr<Window> window(CreateTestWindowWithDelegate( | 
|  | &delegate, 0, gfx::Rect(0, 0, 20, 20), root_window())); | 
|  | EXPECT_FALSE(window->HasCapture()); | 
|  |  | 
|  | // Do a capture. | 
|  | window->SetCapture(); | 
|  | EXPECT_TRUE(window->HasCapture()); | 
|  |  | 
|  | // Destroy the window. | 
|  | window.reset(); | 
|  |  | 
|  | // Make sure the root window doesn't reference the window anymore. | 
|  | EXPECT_EQ(NULL, host()->dispatcher()->mouse_pressed_handler()); | 
|  | EXPECT_EQ(NULL, aura::client::GetCaptureWindow(root_window())); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, GetBoundsInRootWindow) { | 
|  | std::unique_ptr<Window> viewport( | 
|  | CreateTestWindowWithBounds(gfx::Rect(0, 0, 300, 300), root_window())); | 
|  | std::unique_ptr<Window> child( | 
|  | CreateTestWindowWithBounds(gfx::Rect(0, 0, 100, 100), viewport.get())); | 
|  | // Sanity check. | 
|  | EXPECT_EQ("0,0 100x100", child->GetBoundsInRootWindow().ToString()); | 
|  |  | 
|  | // The |child| window's screen bounds should move along with the |viewport|. | 
|  | viewport->SetBounds(gfx::Rect(-100, -100, 300, 300)); | 
|  | EXPECT_EQ("-100,-100 100x100", child->GetBoundsInRootWindow().ToString()); | 
|  |  | 
|  | // The |child| window is moved to the 0,0 in screen coordinates. | 
|  | // |GetBoundsInRootWindow()| should return 0,0. | 
|  | child->SetBounds(gfx::Rect(100, 100, 100, 100)); | 
|  | EXPECT_EQ("0,0 100x100", child->GetBoundsInRootWindow().ToString()); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, GetBoundsInRootWindowWithLayers) { | 
|  | std::unique_ptr<Window> viewport( | 
|  | CreateTestWindowWithBounds(gfx::Rect(0, 0, 300, 300), root_window())); | 
|  |  | 
|  | std::unique_ptr<Window> widget( | 
|  | CreateTestWindowWithBounds(gfx::Rect(0, 0, 200, 200), viewport.get())); | 
|  |  | 
|  | std::unique_ptr<Window> child( | 
|  | CreateTestWindowWithBounds(gfx::Rect(0, 0, 100, 100), widget.get())); | 
|  |  | 
|  | // Sanity check. | 
|  | EXPECT_EQ("0,0 100x100", child->GetBoundsInRootWindow().ToString()); | 
|  |  | 
|  | // The |child| window's screen bounds should move along with the |viewport|. | 
|  | OffsetBounds(viewport.get(), -100, -100); | 
|  | EXPECT_EQ("-100,-100 100x100", child->GetBoundsInRootWindow().ToString()); | 
|  |  | 
|  | OffsetBounds(widget.get(), 50, 50); | 
|  | EXPECT_EQ("-50,-50 100x100", child->GetBoundsInRootWindow().ToString()); | 
|  |  | 
|  | // The |child| window is moved to the 0,0 in screen coordinates. | 
|  | // |GetBoundsInRootWindow()| should return 0,0. | 
|  | OffsetBounds(child.get(), 50, 50); | 
|  | EXPECT_EQ("0,0 100x100", child->GetBoundsInRootWindow().ToString()); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, GetBoundsInRootWindowWithLayersAndTranslations) { | 
|  | std::unique_ptr<Window> viewport( | 
|  | CreateTestWindowWithBounds(gfx::Rect(0, 0, 300, 300), root_window())); | 
|  |  | 
|  | std::unique_ptr<Window> widget( | 
|  | CreateTestWindowWithBounds(gfx::Rect(0, 0, 200, 200), viewport.get())); | 
|  |  | 
|  | std::unique_ptr<Window> child( | 
|  | CreateTestWindowWithBounds(gfx::Rect(0, 0, 100, 100), widget.get())); | 
|  |  | 
|  | // Sanity check. | 
|  | EXPECT_EQ("0,0 100x100", child->GetBoundsInRootWindow().ToString()); | 
|  |  | 
|  | // The |child| window's screen bounds should move along with the |viewport|. | 
|  | viewport->SetBounds(gfx::Rect(-100, -100, 300, 300)); | 
|  | EXPECT_EQ("-100,-100 100x100", child->GetBoundsInRootWindow().ToString()); | 
|  |  | 
|  | widget->SetBounds(gfx::Rect(50, 50, 200, 200)); | 
|  | EXPECT_EQ("-50,-50 100x100", child->GetBoundsInRootWindow().ToString()); | 
|  |  | 
|  | // The |child| window is moved to the 0,0 in screen coordinates. | 
|  | // |GetBoundsInRootWindow()| should return 0,0. | 
|  | child->SetBounds(gfx::Rect(50, 50, 100, 100)); | 
|  | EXPECT_EQ("0,0 100x100", child->GetBoundsInRootWindow().ToString()); | 
|  |  | 
|  | gfx::Transform transform1; | 
|  | transform1.Translate(-10, 20); | 
|  | viewport->SetTransform(transform1); | 
|  | EXPECT_EQ("-10,20 100x100", child->GetBoundsInRootWindow().ToString()); | 
|  |  | 
|  | gfx::Transform transform2; | 
|  | transform2.Translate(40, 100); | 
|  | widget->SetTransform(transform2); | 
|  | EXPECT_EQ("30,120 100x100", child->GetBoundsInRootWindow().ToString()); | 
|  |  | 
|  | // Testing potentially buggy place | 
|  | gfx::Transform transform3; | 
|  | transform3.Translate(-30, -120); | 
|  | child->SetTransform(transform3); | 
|  | EXPECT_EQ("0,0 100x100", child->GetBoundsInRootWindow().ToString()); | 
|  | } | 
|  |  | 
|  | // TODO(tdanderson): Remove this class and use | 
|  | //                   test::EventCountDelegate in its place. | 
|  | class MouseEnterExitWindowDelegate : public TestWindowDelegate { | 
|  | public: | 
|  | MouseEnterExitWindowDelegate() : entered_(false), exited_(false) {} | 
|  |  | 
|  | void OnMouseEvent(ui::MouseEvent* event) override { | 
|  | switch (event->type()) { | 
|  | case ui::ET_MOUSE_ENTERED: | 
|  | EXPECT_TRUE(event->flags() & ui::EF_IS_SYNTHESIZED); | 
|  | entered_ = true; | 
|  | break; | 
|  | case ui::ET_MOUSE_EXITED: | 
|  | EXPECT_TRUE(event->flags() & ui::EF_IS_SYNTHESIZED); | 
|  | exited_ = true; | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool entered() const { return entered_; } | 
|  | bool exited() const { return exited_; } | 
|  |  | 
|  | // Clear the entered / exited states. | 
|  | void ResetExpectations() { | 
|  | entered_ = false; | 
|  | exited_ = false; | 
|  | } | 
|  |  | 
|  | private: | 
|  | bool entered_; | 
|  | bool exited_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(MouseEnterExitWindowDelegate); | 
|  | }; | 
|  |  | 
|  | // Verifies that the WindowDelegate receives MouseExit and MouseEnter events for | 
|  | // mouse transitions from window to window. | 
|  | TEST_F(WindowTest, MouseEnterExit) { | 
|  | MouseEnterExitWindowDelegate d1; | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithDelegate( | 
|  | &d1, 1, gfx::Rect(10, 10, 50, 50), root_window())); | 
|  | MouseEnterExitWindowDelegate d2; | 
|  | std::unique_ptr<Window> w2(CreateTestWindowWithDelegate( | 
|  | &d2, 2, gfx::Rect(70, 70, 50, 50), root_window())); | 
|  |  | 
|  | ui::test::EventGenerator generator(root_window()); | 
|  | generator.MoveMouseToCenterOf(w1.get()); | 
|  | EXPECT_TRUE(d1.entered()); | 
|  | EXPECT_FALSE(d1.exited()); | 
|  | EXPECT_FALSE(d2.entered()); | 
|  | EXPECT_FALSE(d2.exited()); | 
|  |  | 
|  | generator.MoveMouseToCenterOf(w2.get()); | 
|  | EXPECT_TRUE(d1.entered()); | 
|  | EXPECT_TRUE(d1.exited()); | 
|  | EXPECT_TRUE(d2.entered()); | 
|  | EXPECT_FALSE(d2.exited()); | 
|  | } | 
|  |  | 
|  | // Verifies that the WindowDelegate receives MouseExit from ET_MOUSE_EXITED. | 
|  | TEST_F(WindowTest, WindowTreeHostExit) { | 
|  | MouseEnterExitWindowDelegate d1; | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithDelegate( | 
|  | &d1, 1, gfx::Rect(10, 10, 50, 50), root_window())); | 
|  |  | 
|  | ui::test::EventGenerator generator(root_window()); | 
|  | generator.MoveMouseToCenterOf(w1.get()); | 
|  | EXPECT_TRUE(d1.entered()); | 
|  | EXPECT_FALSE(d1.exited()); | 
|  | d1.ResetExpectations(); | 
|  |  | 
|  | ui::MouseEvent exit_event(ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(), | 
|  | ui::EventTimeForNow(), 0, 0); | 
|  | DispatchEventUsingWindowDispatcher(&exit_event); | 
|  | EXPECT_FALSE(d1.entered()); | 
|  | EXPECT_TRUE(d1.exited()); | 
|  | } | 
|  |  | 
|  | // Verifies that the WindowDelegate receives MouseExit and MouseEnter events for | 
|  | // mouse transitions from window to window, even if the entered window sets | 
|  | // and releases capture. | 
|  | TEST_F(WindowTest, MouseEnterExitWithClick) { | 
|  | MouseEnterExitWindowDelegate d1; | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithDelegate( | 
|  | &d1, 1, gfx::Rect(10, 10, 50, 50), root_window())); | 
|  | MouseEnterExitWindowDelegate d2; | 
|  | std::unique_ptr<Window> w2(CreateTestWindowWithDelegate( | 
|  | &d2, 2, gfx::Rect(70, 70, 50, 50), root_window())); | 
|  |  | 
|  | ui::test::EventGenerator generator(root_window()); | 
|  | generator.MoveMouseToCenterOf(w1.get()); | 
|  | EXPECT_TRUE(d1.entered()); | 
|  | EXPECT_FALSE(d1.exited()); | 
|  | EXPECT_FALSE(d2.entered()); | 
|  | EXPECT_FALSE(d2.exited()); | 
|  |  | 
|  | // Emmulate what Views does on a click by grabbing and releasing capture. | 
|  | generator.PressLeftButton(); | 
|  | w1->SetCapture(); | 
|  | w1->ReleaseCapture(); | 
|  | generator.ReleaseLeftButton(); | 
|  |  | 
|  | generator.MoveMouseToCenterOf(w2.get()); | 
|  | EXPECT_TRUE(d1.entered()); | 
|  | EXPECT_TRUE(d1.exited()); | 
|  | EXPECT_TRUE(d2.entered()); | 
|  | EXPECT_FALSE(d2.exited()); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, MouseEnterExitWhenDeleteWithCapture) { | 
|  | MouseEnterExitWindowDelegate delegate; | 
|  | std::unique_ptr<Window> window(CreateTestWindowWithDelegate( | 
|  | &delegate, 1, gfx::Rect(10, 10, 50, 50), root_window())); | 
|  |  | 
|  | ui::test::EventGenerator generator(root_window()); | 
|  | generator.MoveMouseToCenterOf(window.get()); | 
|  | EXPECT_TRUE(delegate.entered()); | 
|  | EXPECT_FALSE(delegate.exited()); | 
|  |  | 
|  | // Emmulate what Views does on a click by grabbing and releasing capture. | 
|  | generator.PressLeftButton(); | 
|  | window->SetCapture(); | 
|  |  | 
|  | delegate.ResetExpectations(); | 
|  | generator.MoveMouseTo(0, 0); | 
|  | EXPECT_FALSE(delegate.entered()); | 
|  | EXPECT_FALSE(delegate.exited()); | 
|  |  | 
|  | delegate.ResetExpectations(); | 
|  | window.reset(); | 
|  | EXPECT_FALSE(delegate.entered()); | 
|  | EXPECT_FALSE(delegate.exited()); | 
|  | } | 
|  |  | 
|  | // Verifies that the correct enter / exits are sent if windows appear and are | 
|  | // deleted under the current mouse position. | 
|  | TEST_F(WindowTest, MouseEnterExitWithWindowAppearAndDelete) { | 
|  | MouseEnterExitWindowDelegate d1; | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithDelegate( | 
|  | &d1, 1, gfx::Rect(10, 10, 50, 50), root_window())); | 
|  |  | 
|  | // The cursor is moved into the bounds of |w1|. We expect the delegate | 
|  | // of |w1| to see an ET_MOUSE_ENTERED event. | 
|  | ui::test::EventGenerator generator(root_window()); | 
|  | generator.MoveMouseToCenterOf(w1.get()); | 
|  | EXPECT_TRUE(d1.entered()); | 
|  | EXPECT_FALSE(d1.exited()); | 
|  | d1.ResetExpectations(); | 
|  |  | 
|  | MouseEnterExitWindowDelegate d2; | 
|  | { | 
|  | std::unique_ptr<Window> w2(CreateTestWindowWithDelegate( | 
|  | &d2, 2, gfx::Rect(10, 10, 50, 50), root_window())); | 
|  | // Enters / exits can be sent asynchronously. | 
|  | RunAllPendingInMessageLoop(); | 
|  |  | 
|  | // |w2| appears over top of |w1|. We expect the delegate of |w1| to see | 
|  | // an ET_MOUSE_EXITED and the delegate of |w2| to see an ET_MOUSE_ENTERED. | 
|  | EXPECT_FALSE(d1.entered()); | 
|  | EXPECT_TRUE(d1.exited()); | 
|  | EXPECT_TRUE(d2.entered()); | 
|  | EXPECT_FALSE(d2.exited()); | 
|  | d1.ResetExpectations(); | 
|  | d2.ResetExpectations(); | 
|  | } | 
|  |  | 
|  | // Enters / exits can be sent asynchronously. | 
|  | RunAllPendingInMessageLoop(); | 
|  |  | 
|  | // |w2| has been destroyed, so its delegate should see no further events. | 
|  | // The delegate of |w1| should see an ET_MOUSE_ENTERED event. | 
|  | EXPECT_TRUE(d1.entered()); | 
|  | EXPECT_FALSE(d1.exited()); | 
|  | EXPECT_FALSE(d2.entered()); | 
|  | EXPECT_FALSE(d2.exited()); | 
|  | } | 
|  |  | 
|  | // Verifies that enter / exits are sent if windows appear and are hidden | 
|  | // under the current mouse position.. | 
|  | TEST_F(WindowTest, MouseEnterExitWithHide) { | 
|  | MouseEnterExitWindowDelegate d1; | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithDelegate( | 
|  | &d1, 1, gfx::Rect(10, 10, 50, 50), root_window())); | 
|  |  | 
|  | ui::test::EventGenerator generator(root_window()); | 
|  | generator.MoveMouseToCenterOf(w1.get()); | 
|  | EXPECT_TRUE(d1.entered()); | 
|  | EXPECT_FALSE(d1.exited()); | 
|  |  | 
|  | MouseEnterExitWindowDelegate d2; | 
|  | std::unique_ptr<Window> w2(CreateTestWindowWithDelegate( | 
|  | &d2, 2, gfx::Rect(10, 10, 50, 50), root_window())); | 
|  | // Enters / exits can be send asynchronously. | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(d1.entered()); | 
|  | EXPECT_TRUE(d1.exited()); | 
|  | EXPECT_TRUE(d2.entered()); | 
|  | EXPECT_FALSE(d2.exited()); | 
|  |  | 
|  | d1.ResetExpectations(); | 
|  | w2->Hide(); | 
|  | // Enters / exits can be send asynchronously. | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(d2.exited()); | 
|  | EXPECT_TRUE(d1.entered()); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, MouseEnterExitWithParentHide) { | 
|  | MouseEnterExitWindowDelegate d1; | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithDelegate( | 
|  | &d1, 1, gfx::Rect(10, 10, 50, 50), root_window())); | 
|  | MouseEnterExitWindowDelegate d2; | 
|  | Window* w2 = CreateTestWindowWithDelegate(&d2, 2, gfx::Rect(10, 10, 50, 50), | 
|  | w1.get()); | 
|  | ui::test::EventGenerator generator(root_window()); | 
|  | generator.MoveMouseToCenterOf(w2); | 
|  | // Enters / exits can be send asynchronously. | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(d2.entered()); | 
|  | EXPECT_FALSE(d2.exited()); | 
|  |  | 
|  | d2.ResetExpectations(); | 
|  | w1->Hide(); | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_FALSE(d2.entered()); | 
|  | EXPECT_TRUE(d2.exited()); | 
|  |  | 
|  | w1.reset(); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, MouseEnterExitWithParentDelete) { | 
|  | MouseEnterExitWindowDelegate d1; | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithDelegate( | 
|  | &d1, 1, gfx::Rect(10, 10, 50, 50), root_window())); | 
|  | MouseEnterExitWindowDelegate d2; | 
|  | Window* w2 = CreateTestWindowWithDelegate(&d2, 2, gfx::Rect(10, 10, 50, 50), | 
|  | w1.get()); | 
|  | ui::test::EventGenerator generator(root_window()); | 
|  | generator.MoveMouseToCenterOf(w2); | 
|  |  | 
|  | // Enters / exits can be send asynchronously. | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(d2.entered()); | 
|  | EXPECT_FALSE(d2.exited()); | 
|  |  | 
|  | d2.ResetExpectations(); | 
|  | w1.reset(); | 
|  | RunAllPendingInMessageLoop(); | 
|  |  | 
|  | // Both windows are in the process of destroying, so their delegates should | 
|  | // not see any mouse events. | 
|  | EXPECT_FALSE(d1.entered()); | 
|  | EXPECT_FALSE(d1.exited()); | 
|  | EXPECT_FALSE(d2.entered()); | 
|  | EXPECT_FALSE(d2.exited()); | 
|  | } | 
|  |  | 
|  | // Creates a window with a delegate (w111) that can handle events at a lower | 
|  | // z-index than a window without a delegate (w12). w12 is sized to fill the | 
|  | // entire bounds of the container. This test verifies that | 
|  | // GetEventHandlerForPoint() skips w12 even though its bounds contain the event, | 
|  | // because it has no children that can handle the event and it has no delegate | 
|  | // allowing it to handle the event itself. | 
|  | TEST_F(WindowTest, GetEventHandlerForPoint_NoDelegate) { | 
|  | TestWindowDelegate d111; | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithDelegate( | 
|  | NULL, 1, gfx::Rect(0, 0, 500, 500), root_window())); | 
|  | std::unique_ptr<Window> w11(CreateTestWindowWithDelegate( | 
|  | NULL, 11, gfx::Rect(0, 0, 500, 500), w1.get())); | 
|  | std::unique_ptr<Window> w111(CreateTestWindowWithDelegate( | 
|  | &d111, 111, gfx::Rect(50, 50, 450, 450), w11.get())); | 
|  | std::unique_ptr<Window> w12(CreateTestWindowWithDelegate( | 
|  | NULL, 12, gfx::Rect(0, 0, 500, 500), w1.get())); | 
|  |  | 
|  | gfx::Point target_point = w111->bounds().CenterPoint(); | 
|  | EXPECT_EQ(w111.get(), w1->GetEventHandlerForPoint(target_point)); | 
|  | } | 
|  |  | 
|  | class VisibilityWindowDelegate : public TestWindowDelegate { | 
|  | public: | 
|  | VisibilityWindowDelegate() | 
|  | : shown_(0), | 
|  | hidden_(0) { | 
|  | } | 
|  |  | 
|  | int shown() const { return shown_; } | 
|  | int hidden() const { return hidden_; } | 
|  | void Clear() { | 
|  | shown_ = 0; | 
|  | hidden_ = 0; | 
|  | } | 
|  |  | 
|  | void OnWindowTargetVisibilityChanged(bool visible) override { | 
|  | if (visible) | 
|  | shown_++; | 
|  | else | 
|  | hidden_++; | 
|  | } | 
|  |  | 
|  | private: | 
|  | int shown_; | 
|  | int hidden_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(VisibilityWindowDelegate); | 
|  | }; | 
|  |  | 
|  | // Verifies show/hide propagate correctly to children and the layer. | 
|  | TEST_F(WindowTest, Visibility) { | 
|  | VisibilityWindowDelegate d; | 
|  | VisibilityWindowDelegate d2; | 
|  | std::unique_ptr<Window> w1( | 
|  | CreateTestWindowWithDelegate(&d, 1, gfx::Rect(), root_window())); | 
|  | std::unique_ptr<Window> w2( | 
|  | CreateTestWindowWithDelegate(&d2, 2, gfx::Rect(), w1.get())); | 
|  | std::unique_ptr<Window> w3(CreateTestWindowWithId(3, w2.get())); | 
|  |  | 
|  | // Create shows all the windows. | 
|  | EXPECT_TRUE(w1->IsVisible()); | 
|  | EXPECT_TRUE(w2->IsVisible()); | 
|  | EXPECT_TRUE(w3->IsVisible()); | 
|  | EXPECT_EQ(1, d.shown()); | 
|  |  | 
|  | d.Clear(); | 
|  | w1->Hide(); | 
|  | EXPECT_FALSE(w1->IsVisible()); | 
|  | EXPECT_FALSE(w2->IsVisible()); | 
|  | EXPECT_FALSE(w3->IsVisible()); | 
|  | EXPECT_EQ(1, d.hidden()); | 
|  | EXPECT_EQ(0, d.shown()); | 
|  |  | 
|  | w2->Show(); | 
|  | EXPECT_FALSE(w1->IsVisible()); | 
|  | EXPECT_FALSE(w2->IsVisible()); | 
|  | EXPECT_FALSE(w3->IsVisible()); | 
|  |  | 
|  | w3->Hide(); | 
|  | EXPECT_FALSE(w1->IsVisible()); | 
|  | EXPECT_FALSE(w2->IsVisible()); | 
|  | EXPECT_FALSE(w3->IsVisible()); | 
|  |  | 
|  | d.Clear(); | 
|  | w1->Show(); | 
|  | EXPECT_TRUE(w1->IsVisible()); | 
|  | EXPECT_TRUE(w2->IsVisible()); | 
|  | EXPECT_FALSE(w3->IsVisible()); | 
|  | EXPECT_EQ(0, d.hidden()); | 
|  | EXPECT_EQ(1, d.shown()); | 
|  |  | 
|  | w3->Show(); | 
|  | EXPECT_TRUE(w1->IsVisible()); | 
|  | EXPECT_TRUE(w2->IsVisible()); | 
|  | EXPECT_TRUE(w3->IsVisible()); | 
|  |  | 
|  | // Verify that if an ancestor isn't visible and we change the visibility of a | 
|  | // child window that OnChildWindowVisibilityChanged() is still invoked. | 
|  | w1->Hide(); | 
|  | d2.Clear(); | 
|  | w2->Hide(); | 
|  | EXPECT_EQ(1, d2.hidden()); | 
|  | EXPECT_EQ(0, d2.shown()); | 
|  | d2.Clear(); | 
|  | w2->Show(); | 
|  | EXPECT_EQ(0, d2.hidden()); | 
|  | EXPECT_EQ(1, d2.shown()); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, IgnoreEventsTest) { | 
|  | TestWindowDelegate d11; | 
|  | TestWindowDelegate d12; | 
|  | TestWindowDelegate d111; | 
|  | TestWindowDelegate d121; | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithDelegate( | 
|  | NULL, 1, gfx::Rect(0, 0, 500, 500), root_window())); | 
|  | std::unique_ptr<Window> w11(CreateTestWindowWithDelegate( | 
|  | &d11, 11, gfx::Rect(0, 0, 500, 500), w1.get())); | 
|  | std::unique_ptr<Window> w111(CreateTestWindowWithDelegate( | 
|  | &d111, 111, gfx::Rect(50, 50, 450, 450), w11.get())); | 
|  | std::unique_ptr<Window> w12(CreateTestWindowWithDelegate( | 
|  | &d12, 12, gfx::Rect(0, 0, 500, 500), w1.get())); | 
|  | std::unique_ptr<Window> w121(CreateTestWindowWithDelegate( | 
|  | &d121, 121, gfx::Rect(150, 150, 50, 50), w12.get())); | 
|  |  | 
|  | EXPECT_EQ(w12.get(), w1->GetEventHandlerForPoint(gfx::Point(10, 10))); | 
|  | w12->set_ignore_events(true); | 
|  | EXPECT_EQ(w11.get(), w1->GetEventHandlerForPoint(gfx::Point(10, 10))); | 
|  | w12->set_ignore_events(false); | 
|  |  | 
|  | EXPECT_EQ(w121.get(), w1->GetEventHandlerForPoint(gfx::Point(160, 160))); | 
|  | w121->set_ignore_events(true); | 
|  | EXPECT_EQ(w12.get(), w1->GetEventHandlerForPoint(gfx::Point(160, 160))); | 
|  | w12->set_ignore_events(true); | 
|  | EXPECT_EQ(w111.get(), w1->GetEventHandlerForPoint(gfx::Point(160, 160))); | 
|  | w111->set_ignore_events(true); | 
|  | EXPECT_EQ(w11.get(), w1->GetEventHandlerForPoint(gfx::Point(160, 160))); | 
|  | } | 
|  |  | 
|  | // Tests transformation on the root window. | 
|  | TEST_F(WindowTest, Transform) { | 
|  | gfx::Size size = host()->GetBounds().size(); | 
|  | EXPECT_EQ(gfx::Rect(size), display::Screen::GetScreen() | 
|  | ->GetDisplayNearestPoint(gfx::Point()) | 
|  | .bounds()); | 
|  |  | 
|  | // Rotate it clock-wise 90 degrees. | 
|  | gfx::Transform transform; | 
|  | transform.Translate(size.height(), 0); | 
|  | transform.Rotate(90.0); | 
|  | host()->SetRootTransform(transform); | 
|  |  | 
|  | // The size should be the transformed size. | 
|  | gfx::Size transformed_size(size.height(), size.width()); | 
|  | EXPECT_EQ(transformed_size.ToString(), | 
|  | root_window()->bounds().size().ToString()); | 
|  | EXPECT_EQ(gfx::Rect(transformed_size).ToString(), | 
|  | display::Screen::GetScreen() | 
|  | ->GetDisplayNearestPoint(gfx::Point()) | 
|  | .bounds() | 
|  | .ToString()); | 
|  |  | 
|  | // Host size shouldn't change. | 
|  | EXPECT_EQ(size.ToString(), host()->GetBounds().size().ToString()); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, TransformGesture) { | 
|  | gfx::Size size = host()->GetBounds().size(); | 
|  |  | 
|  | std::unique_ptr<GestureTrackPositionDelegate> delegate( | 
|  | new GestureTrackPositionDelegate); | 
|  | std::unique_ptr<Window> window(CreateTestWindowWithDelegate( | 
|  | delegate.get(), -1234, gfx::Rect(0, 0, 20, 20), root_window())); | 
|  |  | 
|  | // Rotate the root-window clock-wise 90 degrees. | 
|  | gfx::Transform transform; | 
|  | transform.Translate(size.height(), 0.0); | 
|  | transform.Rotate(90.0); | 
|  | host()->SetRootTransform(transform); | 
|  |  | 
|  | ui::TouchEvent press( | 
|  | ui::ET_TOUCH_PRESSED, gfx::Point(size.height() - 10, 10), 0, getTime()); | 
|  | DispatchEventUsingWindowDispatcher(&press); | 
|  | EXPECT_EQ(gfx::Point(10, 10).ToString(), delegate->position().ToString()); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | DEFINE_WINDOW_PROPERTY_KEY(int, kIntKey, -2); | 
|  | DEFINE_WINDOW_PROPERTY_KEY(const char*, kStringKey, "squeamish"); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, Property) { | 
|  | std::unique_ptr<Window> w(CreateTestWindowWithId(0, root_window())); | 
|  |  | 
|  | static const char native_prop_key[] = "fnord"; | 
|  |  | 
|  | // Non-existent properties should return the default values. | 
|  | EXPECT_EQ(-2, w->GetProperty(kIntKey)); | 
|  | EXPECT_EQ(std::string("squeamish"), w->GetProperty(kStringKey)); | 
|  | EXPECT_EQ(NULL, w->GetNativeWindowProperty(native_prop_key)); | 
|  |  | 
|  | // A set property value should be returned again (even if it's the default | 
|  | // value). | 
|  | w->SetProperty(kIntKey, INT_MAX); | 
|  | EXPECT_EQ(INT_MAX, w->GetProperty(kIntKey)); | 
|  | w->SetProperty(kIntKey, -2); | 
|  | EXPECT_EQ(-2, w->GetProperty(kIntKey)); | 
|  | w->SetProperty(kIntKey, INT_MIN); | 
|  | EXPECT_EQ(INT_MIN, w->GetProperty(kIntKey)); | 
|  |  | 
|  | w->SetProperty(kStringKey, static_cast<const char*>(NULL)); | 
|  | EXPECT_EQ(NULL, w->GetProperty(kStringKey)); | 
|  | w->SetProperty(kStringKey, "squeamish"); | 
|  | EXPECT_EQ(std::string("squeamish"), w->GetProperty(kStringKey)); | 
|  | w->SetProperty(kStringKey, "ossifrage"); | 
|  | EXPECT_EQ(std::string("ossifrage"), w->GetProperty(kStringKey)); | 
|  |  | 
|  | w->SetNativeWindowProperty(native_prop_key, &*w); | 
|  | EXPECT_EQ(&*w, w->GetNativeWindowProperty(native_prop_key)); | 
|  | w->SetNativeWindowProperty(native_prop_key, NULL); | 
|  | EXPECT_EQ(NULL, w->GetNativeWindowProperty(native_prop_key)); | 
|  |  | 
|  | // ClearProperty should restore the default value. | 
|  | w->ClearProperty(kIntKey); | 
|  | EXPECT_EQ(-2, w->GetProperty(kIntKey)); | 
|  | w->ClearProperty(kStringKey); | 
|  | EXPECT_EQ(std::string("squeamish"), w->GetProperty(kStringKey)); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, OwnedProperty) { | 
|  | std::unique_ptr<Window> w(CreateTestWindowWithId(0, root_window())); | 
|  | EXPECT_EQ(NULL, w->GetProperty(kOwnedKey)); | 
|  | TestProperty* last_deleted = TestProperty::last_deleted(); | 
|  | TestProperty* p1 = new TestProperty(); | 
|  | w->SetProperty(kOwnedKey, p1); | 
|  | EXPECT_EQ(p1, w->GetProperty(kOwnedKey)); | 
|  | EXPECT_EQ(last_deleted, TestProperty::last_deleted()); | 
|  |  | 
|  | TestProperty* p2 = new TestProperty(); | 
|  | w->SetProperty(kOwnedKey, p2); | 
|  | EXPECT_EQ(p2, w->GetProperty(kOwnedKey)); | 
|  | EXPECT_EQ(p1, TestProperty::last_deleted()); | 
|  |  | 
|  | w->ClearProperty(kOwnedKey); | 
|  | EXPECT_EQ(NULL, w->GetProperty(kOwnedKey)); | 
|  | EXPECT_EQ(p2, TestProperty::last_deleted()); | 
|  |  | 
|  | TestProperty* p3 = new TestProperty(); | 
|  | w->SetProperty(kOwnedKey, p3); | 
|  | EXPECT_EQ(p3, w->GetProperty(kOwnedKey)); | 
|  | EXPECT_EQ(p2, TestProperty::last_deleted()); | 
|  | w.reset(); | 
|  | EXPECT_EQ(p3, TestProperty::last_deleted()); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class DeletionTestLayoutManager : public LayoutManager { | 
|  | public: | 
|  | explicit DeletionTestLayoutManager(DeletionTracker* tracker) | 
|  | : tracker_(tracker) {} | 
|  | ~DeletionTestLayoutManager() override { tracker_->LayoutManagerDeleted(); } | 
|  |  | 
|  | private: | 
|  | // LayoutManager: | 
|  | void OnWindowResized() override {} | 
|  | void OnWindowAddedToLayout(Window* child) override {} | 
|  | void OnWillRemoveWindowFromLayout(Window* child) override {} | 
|  | void OnWindowRemovedFromLayout(Window* child) override {} | 
|  | void OnChildWindowVisibilityChanged(Window* child, bool visible) override {} | 
|  | void SetChildBounds(Window* child, | 
|  | const gfx::Rect& requested_bounds) override {} | 
|  |  | 
|  | DeletionTracker* tracker_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(DeletionTestLayoutManager); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TEST_F(WindowTest, DeleteLayoutManagerBeforeOwnedProps) { | 
|  | DeletionTracker tracker; | 
|  | { | 
|  | Window w(nullptr); | 
|  | w.Init(ui::LAYER_NOT_DRAWN); | 
|  | w.SetLayoutManager(new DeletionTestLayoutManager(&tracker)); | 
|  | w.SetProperty(kDeletionTestPropertyKey, new DeletionTestProperty(&tracker)); | 
|  | } | 
|  | EXPECT_TRUE(tracker.property_deleted()); | 
|  | EXPECT_TRUE(tracker.layout_manager_deleted()); | 
|  | EXPECT_EQ(DeletionOrder::LAYOUT_MANAGER_FIRST, tracker.order()); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, SetBoundsInternalShouldCheckTargetBounds) { | 
|  | // We cannot short-circuit animations in this test. | 
|  | ui::ScopedAnimationDurationScaleMode test_duration_mode( | 
|  | ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); | 
|  |  | 
|  | std::unique_ptr<Window> w1( | 
|  | CreateTestWindowWithBounds(gfx::Rect(0, 0, 100, 100), root_window())); | 
|  |  | 
|  | EXPECT_TRUE(w1->layer()); | 
|  | w1->layer()->GetAnimator()->set_disable_timer_for_test(true); | 
|  | ui::LayerAnimator* animator = w1->layer()->GetAnimator(); | 
|  |  | 
|  | EXPECT_EQ("0,0 100x100", w1->bounds().ToString()); | 
|  | EXPECT_EQ("0,0 100x100", w1->layer()->GetTargetBounds().ToString()); | 
|  |  | 
|  | // Animate to a different position. | 
|  | { | 
|  | ui::ScopedLayerAnimationSettings settings(w1->layer()->GetAnimator()); | 
|  | w1->SetBounds(gfx::Rect(100, 100, 100, 100)); | 
|  | } | 
|  |  | 
|  | EXPECT_EQ("0,0 100x100", w1->bounds().ToString()); | 
|  | EXPECT_EQ("100,100 100x100", w1->layer()->GetTargetBounds().ToString()); | 
|  |  | 
|  | // Animate back to the first position. The animation hasn't started yet, so | 
|  | // the current bounds are still (0, 0, 100, 100), but the target bounds are | 
|  | // (100, 100, 100, 100). If we step the animator ahead, we should find that | 
|  | // we're at (0, 0, 100, 100). That is, the second animation should be applied. | 
|  | { | 
|  | ui::ScopedLayerAnimationSettings settings(w1->layer()->GetAnimator()); | 
|  | w1->SetBounds(gfx::Rect(0, 0, 100, 100)); | 
|  | } | 
|  |  | 
|  | EXPECT_EQ("0,0 100x100", w1->bounds().ToString()); | 
|  | EXPECT_EQ("0,0 100x100", w1->layer()->GetTargetBounds().ToString()); | 
|  |  | 
|  | // Confirm that the target bounds are reached. | 
|  | base::TimeTicks start_time = | 
|  | w1->layer()->GetAnimator()->last_step_time(); | 
|  |  | 
|  | animator->Step(start_time + base::TimeDelta::FromMilliseconds(1000)); | 
|  |  | 
|  | EXPECT_EQ("0,0 100x100", w1->bounds().ToString()); | 
|  | } | 
|  |  | 
|  |  | 
|  | typedef std::pair<const void*, intptr_t> PropertyChangeInfo; | 
|  |  | 
|  | class WindowObserverTest : public WindowTest, | 
|  | public WindowObserver { | 
|  | public: | 
|  | struct VisibilityInfo { | 
|  | bool window_visible; | 
|  | bool visible_param; | 
|  | int changed_count; | 
|  | }; | 
|  |  | 
|  | WindowObserverTest() | 
|  | : added_count_(0), | 
|  | removed_count_(0), | 
|  | destroyed_count_(0), | 
|  | old_property_value_(-3) { | 
|  | } | 
|  |  | 
|  | ~WindowObserverTest() override {} | 
|  |  | 
|  | const VisibilityInfo* GetVisibilityInfo() const { | 
|  | return visibility_info_.get(); | 
|  | } | 
|  |  | 
|  | void ResetVisibilityInfo() { | 
|  | visibility_info_.reset(); | 
|  | } | 
|  |  | 
|  | // Returns a description of the WindowObserver methods that have been invoked. | 
|  | std::string WindowObserverCountStateAndClear() { | 
|  | std::string result( | 
|  | base::StringPrintf("added=%d removed=%d", | 
|  | added_count_, removed_count_)); | 
|  | added_count_ = removed_count_ = 0; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | int DestroyedCountAndClear() { | 
|  | int result = destroyed_count_; | 
|  | destroyed_count_ = 0; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | // Return a tuple of the arguments passed in OnPropertyChanged callback. | 
|  | PropertyChangeInfo PropertyChangeInfoAndClear() { | 
|  | PropertyChangeInfo result(property_key_, old_property_value_); | 
|  | property_key_ = NULL; | 
|  | old_property_value_ = -3; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | std::string TransformNotificationsAndClear() { | 
|  | std::string result; | 
|  | for (std::vector<std::pair<int, int> >::iterator it = | 
|  | transform_notifications_.begin(); | 
|  | it != transform_notifications_.end(); | 
|  | ++it) { | 
|  | base::StringAppendF(&result, "(%d,%d)", it->first, it->second); | 
|  | } | 
|  | transform_notifications_.clear(); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | private: | 
|  | void OnWindowAdded(Window* new_window) override { added_count_++; } | 
|  |  | 
|  | void OnWillRemoveWindow(Window* window) override { removed_count_++; } | 
|  |  | 
|  | void OnWindowVisibilityChanged(Window* window, bool visible) override { | 
|  | if (!visibility_info_) { | 
|  | visibility_info_.reset(new VisibilityInfo); | 
|  | visibility_info_->changed_count = 0; | 
|  | } | 
|  | visibility_info_->window_visible = window->IsVisible(); | 
|  | visibility_info_->visible_param = visible; | 
|  | visibility_info_->changed_count++; | 
|  | } | 
|  |  | 
|  | void OnWindowDestroyed(Window* window) override { | 
|  | EXPECT_FALSE(window->parent()); | 
|  | destroyed_count_++; | 
|  | } | 
|  |  | 
|  | void OnWindowPropertyChanged(Window* window, | 
|  | const void* key, | 
|  | intptr_t old) override { | 
|  | property_key_ = key; | 
|  | old_property_value_ = old; | 
|  | } | 
|  |  | 
|  | void OnAncestorWindowTransformed(Window* source, Window* window) override { | 
|  | transform_notifications_.push_back( | 
|  | std::make_pair(source->id(), window->id())); | 
|  | } | 
|  |  | 
|  | int added_count_; | 
|  | int removed_count_; | 
|  | int destroyed_count_; | 
|  | std::unique_ptr<VisibilityInfo> visibility_info_; | 
|  | const void* property_key_; | 
|  | intptr_t old_property_value_; | 
|  | std::vector<std::pair<int, int> > transform_notifications_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(WindowObserverTest); | 
|  | }; | 
|  |  | 
|  | // Various assertions for WindowObserver. | 
|  | TEST_F(WindowObserverTest, WindowObserver) { | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithId(1, root_window())); | 
|  | w1->AddObserver(this); | 
|  |  | 
|  | // Create a new window as a child of w1, our observer should be notified. | 
|  | std::unique_ptr<Window> w2(CreateTestWindowWithId(2, w1.get())); | 
|  | EXPECT_EQ("added=1 removed=0", WindowObserverCountStateAndClear()); | 
|  |  | 
|  | // Delete w2, which should result in the remove notification. | 
|  | w2.reset(); | 
|  | EXPECT_EQ("added=0 removed=1", WindowObserverCountStateAndClear()); | 
|  |  | 
|  | // Create a window that isn't parented to w1, we shouldn't get any | 
|  | // notification. | 
|  | std::unique_ptr<Window> w3(CreateTestWindowWithId(3, root_window())); | 
|  | EXPECT_EQ("added=0 removed=0", WindowObserverCountStateAndClear()); | 
|  |  | 
|  | // Similarly destroying w3 shouldn't notify us either. | 
|  | w3.reset(); | 
|  | EXPECT_EQ("added=0 removed=0", WindowObserverCountStateAndClear()); | 
|  | w1->RemoveObserver(this); | 
|  | } | 
|  |  | 
|  | // Test if OnWindowVisibilityChanged is invoked with expected | 
|  | // parameters. | 
|  | TEST_F(WindowObserverTest, WindowVisibility) { | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithId(1, root_window())); | 
|  | std::unique_ptr<Window> w2(CreateTestWindowWithId(1, w1.get())); | 
|  | w2->AddObserver(this); | 
|  |  | 
|  | // Hide should make the window invisible and the passed visible | 
|  | // parameter is false. | 
|  | w2->Hide(); | 
|  | EXPECT_TRUE(GetVisibilityInfo()); | 
|  | EXPECT_TRUE(GetVisibilityInfo()); | 
|  | if (!GetVisibilityInfo()) | 
|  | return; | 
|  | EXPECT_FALSE(GetVisibilityInfo()->window_visible); | 
|  | EXPECT_FALSE(GetVisibilityInfo()->visible_param); | 
|  | EXPECT_EQ(1, GetVisibilityInfo()->changed_count); | 
|  |  | 
|  | // If parent isn't visible, showing window won't make the window visible, but | 
|  | // passed visible value must be true. | 
|  | w1->Hide(); | 
|  | ResetVisibilityInfo(); | 
|  | EXPECT_TRUE(!GetVisibilityInfo()); | 
|  | w2->Show(); | 
|  | EXPECT_TRUE(GetVisibilityInfo()); | 
|  | if (!GetVisibilityInfo()) | 
|  | return; | 
|  | EXPECT_FALSE(GetVisibilityInfo()->window_visible); | 
|  | EXPECT_TRUE(GetVisibilityInfo()->visible_param); | 
|  | EXPECT_EQ(1, GetVisibilityInfo()->changed_count); | 
|  |  | 
|  | // If parent is visible, showing window will make the window | 
|  | // visible and the passed visible value is true. | 
|  | w1->Show(); | 
|  | w2->Hide(); | 
|  | ResetVisibilityInfo(); | 
|  | w2->Show(); | 
|  | EXPECT_TRUE(GetVisibilityInfo()); | 
|  | if (!GetVisibilityInfo()) | 
|  | return; | 
|  | EXPECT_TRUE(GetVisibilityInfo()->window_visible); | 
|  | EXPECT_TRUE(GetVisibilityInfo()->visible_param); | 
|  | EXPECT_EQ(1, GetVisibilityInfo()->changed_count); | 
|  |  | 
|  | // Verify that the OnWindowVisibilityChanged only once | 
|  | // per visibility change. | 
|  | w2->Hide(); | 
|  | EXPECT_EQ(2, GetVisibilityInfo()->changed_count); | 
|  |  | 
|  | w2->Hide(); | 
|  | EXPECT_EQ(2, GetVisibilityInfo()->changed_count); | 
|  | } | 
|  |  | 
|  | // Test if OnWindowDestroyed is invoked as expected. | 
|  | TEST_F(WindowObserverTest, WindowDestroyed) { | 
|  | // Delete a window should fire a destroyed notification. | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithId(1, root_window())); | 
|  | w1->AddObserver(this); | 
|  | w1.reset(); | 
|  | EXPECT_EQ(1, DestroyedCountAndClear()); | 
|  |  | 
|  | // Observe on child and delete parent window should fire a notification. | 
|  | std::unique_ptr<Window> parent(CreateTestWindowWithId(1, root_window())); | 
|  | Window* child = CreateTestWindowWithId(1, parent.get());  // owned by parent | 
|  | child->AddObserver(this); | 
|  | parent.reset(); | 
|  | EXPECT_EQ(1, DestroyedCountAndClear()); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowObserverTest, PropertyChanged) { | 
|  | // Setting property should fire a property change notification. | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithId(1, root_window())); | 
|  | w1->AddObserver(this); | 
|  |  | 
|  | static const WindowProperty<int> prop = {-2}; | 
|  | static const char native_prop_key[] = "fnord"; | 
|  |  | 
|  | w1->SetProperty(&prop, 1); | 
|  | EXPECT_EQ(PropertyChangeInfo(&prop, -2), PropertyChangeInfoAndClear()); | 
|  | w1->SetProperty(&prop, -2); | 
|  | EXPECT_EQ(PropertyChangeInfo(&prop, 1), PropertyChangeInfoAndClear()); | 
|  | w1->SetProperty(&prop, 3); | 
|  | EXPECT_EQ(PropertyChangeInfo(&prop, -2), PropertyChangeInfoAndClear()); | 
|  | w1->ClearProperty(&prop); | 
|  | EXPECT_EQ(PropertyChangeInfo(&prop, 3), PropertyChangeInfoAndClear()); | 
|  |  | 
|  | w1->SetNativeWindowProperty(native_prop_key, &*w1); | 
|  | EXPECT_EQ(PropertyChangeInfo(native_prop_key, 0), | 
|  | PropertyChangeInfoAndClear()); | 
|  | w1->SetNativeWindowProperty(native_prop_key, NULL); | 
|  | EXPECT_EQ(PropertyChangeInfo(native_prop_key, | 
|  | reinterpret_cast<intptr_t>(&*w1)), | 
|  | PropertyChangeInfoAndClear()); | 
|  |  | 
|  | // Sanity check to see if |PropertyChangeInfoAndClear| really clears. | 
|  | EXPECT_EQ(PropertyChangeInfo( | 
|  | reinterpret_cast<const void*>(NULL), -3), PropertyChangeInfoAndClear()); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowObserverTest, AncestorTransformed) { | 
|  | // Create following window hierarchy: | 
|  | //   root_window | 
|  | //   +-- w1 | 
|  | //       +-- w2 | 
|  | //       +-- w3 | 
|  | //           +-- w4 | 
|  | // Then, apply a transform to |w1| and ensure all its descendants are | 
|  | // notified. | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithId(1, root_window())); | 
|  | w1->AddObserver(this); | 
|  | std::unique_ptr<Window> w2(CreateTestWindowWithId(2, w1.get())); | 
|  | w2->AddObserver(this); | 
|  | std::unique_ptr<Window> w3(CreateTestWindowWithId(3, w1.get())); | 
|  | w3->AddObserver(this); | 
|  | std::unique_ptr<Window> w4(CreateTestWindowWithId(4, w3.get())); | 
|  | w4->AddObserver(this); | 
|  |  | 
|  | EXPECT_EQ(std::string(), TransformNotificationsAndClear()); | 
|  |  | 
|  | gfx::Transform transform; | 
|  | transform.Translate(10, 10); | 
|  | w1->SetTransform(transform); | 
|  |  | 
|  | EXPECT_EQ("(1,1)(1,2)(1,3)(1,4)", TransformNotificationsAndClear()); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, AcquireLayer) { | 
|  | std::unique_ptr<Window> window1(CreateTestWindowWithId(1, root_window())); | 
|  | std::unique_ptr<Window> window2(CreateTestWindowWithId(2, root_window())); | 
|  | ui::Layer* parent = window1->parent()->layer(); | 
|  | EXPECT_EQ(2U, parent->children().size()); | 
|  |  | 
|  | WindowTestApi window1_test_api(window1.get()); | 
|  | WindowTestApi window2_test_api(window2.get()); | 
|  |  | 
|  | EXPECT_TRUE(window1_test_api.OwnsLayer()); | 
|  | EXPECT_TRUE(window2_test_api.OwnsLayer()); | 
|  |  | 
|  | // After acquisition, window1 should not own its layer, but it should still | 
|  | // be available to the window. | 
|  | std::unique_ptr<ui::Layer> window1_layer(window1->AcquireLayer()); | 
|  | EXPECT_FALSE(window1_test_api.OwnsLayer()); | 
|  | EXPECT_TRUE(window1_layer.get() == window1->layer()); | 
|  |  | 
|  | // The acquired layer's owner should be set NULL and re-acquring | 
|  | // should return NULL. | 
|  | EXPECT_FALSE(window1_layer->owner()); | 
|  | std::unique_ptr<ui::Layer> window1_layer_reacquired(window1->AcquireLayer()); | 
|  | EXPECT_FALSE(window1_layer_reacquired.get()); | 
|  |  | 
|  | // Upon destruction, window1's layer should still be valid, and in the layer | 
|  | // hierarchy, but window2's should be gone, and no longer in the hierarchy. | 
|  | window1.reset(); | 
|  | window2.reset(); | 
|  |  | 
|  | // This should be set by the window's destructor. | 
|  | EXPECT_TRUE(window1_layer->delegate() == NULL); | 
|  | EXPECT_EQ(1U, parent->children().size()); | 
|  | } | 
|  |  | 
|  | // Make sure that properties which should persist from the old layer to the new | 
|  | // layer actually do. | 
|  | TEST_F(WindowTest, RecreateLayer) { | 
|  | // Set properties to non default values. | 
|  | gfx::Rect window_bounds(100, 100); | 
|  | Window w(new ColorTestWindowDelegate(SK_ColorWHITE)); | 
|  | w.set_id(1); | 
|  | w.Init(ui::LAYER_SOLID_COLOR); | 
|  | w.SetBounds(window_bounds); | 
|  |  | 
|  | ui::Layer* layer = w.layer(); | 
|  | layer->SetVisible(false); | 
|  | layer->SetMasksToBounds(true); | 
|  |  | 
|  | ui::Layer child_layer; | 
|  | layer->Add(&child_layer); | 
|  |  | 
|  | std::unique_ptr<ui::Layer> old_layer(w.RecreateLayer()); | 
|  | layer = w.layer(); | 
|  | EXPECT_EQ(ui::LAYER_SOLID_COLOR, layer->type()); | 
|  | EXPECT_FALSE(layer->visible()); | 
|  | EXPECT_EQ(1u, layer->children().size()); | 
|  | EXPECT_TRUE(layer->GetMasksToBounds()); | 
|  | EXPECT_EQ("0,0 100x100", w.bounds().ToString()); | 
|  | EXPECT_EQ("0,0 100x100", layer->bounds().ToString()); | 
|  | } | 
|  |  | 
|  | // Verify that RecreateLayer() stacks the old layer above the newly creatd | 
|  | // layer. | 
|  | TEST_F(WindowTest, RecreateLayerZOrder) { | 
|  | std::unique_ptr<Window> w(CreateTestWindow( | 
|  | SK_ColorWHITE, 1, gfx::Rect(0, 0, 100, 100), root_window())); | 
|  | std::unique_ptr<ui::Layer> old_layer(w->RecreateLayer()); | 
|  |  | 
|  | const std::vector<ui::Layer*>& child_layers = | 
|  | root_window()->layer()->children(); | 
|  | ASSERT_EQ(2u, child_layers.size()); | 
|  | EXPECT_EQ(w->layer(), child_layers[0]); | 
|  | EXPECT_EQ(old_layer.get(), child_layers[1]); | 
|  | } | 
|  |  | 
|  | // Ensure that acquiring a layer then recreating a layer does not crash | 
|  | // and that RecreateLayer returns null. | 
|  | TEST_F(WindowTest, AcquireThenRecreateLayer) { | 
|  | std::unique_ptr<Window> w(CreateTestWindow( | 
|  | SK_ColorWHITE, 1, gfx::Rect(0, 0, 100, 100), root_window())); | 
|  | std::unique_ptr<ui::Layer> acquired_layer(w->AcquireLayer()); | 
|  | std::unique_ptr<ui::Layer> doubly_acquired_layer(w->RecreateLayer()); | 
|  | EXPECT_EQ(NULL, doubly_acquired_layer.get()); | 
|  |  | 
|  | // Destroy window before layer gets destroyed. | 
|  | w.reset(); | 
|  | } | 
|  |  | 
|  | class TestVisibilityClient : public client::VisibilityClient { | 
|  | public: | 
|  | explicit TestVisibilityClient(Window* root_window) | 
|  | : ignore_visibility_changes_(false) { | 
|  | client::SetVisibilityClient(root_window, this); | 
|  | } | 
|  | ~TestVisibilityClient() override {} | 
|  |  | 
|  | void set_ignore_visibility_changes(bool ignore_visibility_changes) { | 
|  | ignore_visibility_changes_ = ignore_visibility_changes; | 
|  | } | 
|  |  | 
|  | // Overridden from client::VisibilityClient: | 
|  | void UpdateLayerVisibility(aura::Window* window, bool visible) override { | 
|  | if (!ignore_visibility_changes_) | 
|  | window->layer()->SetVisible(visible); | 
|  | } | 
|  |  | 
|  | private: | 
|  | bool ignore_visibility_changes_; | 
|  | DISALLOW_COPY_AND_ASSIGN(TestVisibilityClient); | 
|  | }; | 
|  |  | 
|  | TEST_F(WindowTest, VisibilityClientIsVisible) { | 
|  | TestVisibilityClient client(root_window()); | 
|  |  | 
|  | std::unique_ptr<Window> window(CreateTestWindowWithId(1, root_window())); | 
|  | EXPECT_TRUE(window->IsVisible()); | 
|  | EXPECT_TRUE(window->layer()->visible()); | 
|  |  | 
|  | window->Hide(); | 
|  | EXPECT_FALSE(window->IsVisible()); | 
|  | EXPECT_FALSE(window->layer()->visible()); | 
|  | window->Show(); | 
|  |  | 
|  | client.set_ignore_visibility_changes(true); | 
|  | window->Hide(); | 
|  | EXPECT_FALSE(window->IsVisible()); | 
|  | EXPECT_TRUE(window->layer()->visible()); | 
|  | } | 
|  |  | 
|  | // Tests the mouse events seen by WindowDelegates in a Window hierarchy when | 
|  | // changing the properties of a leaf Window. | 
|  | TEST_F(WindowTest, MouseEventsOnLeafWindowChange) { | 
|  | ui::test::EventGenerator generator(root_window()); | 
|  | generator.MoveMouseTo(50, 50); | 
|  |  | 
|  | EventCountDelegate d1; | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithDelegate( | 
|  | &d1, 1, gfx::Rect(0, 0, 100, 100), root_window())); | 
|  | RunAllPendingInMessageLoop(); | 
|  | // The format of result is "Enter/Move/Leave". | 
|  | EXPECT_EQ("1 1 0", d1.GetMouseMotionCountsAndReset()); | 
|  |  | 
|  | // Add new window |w11| on top of |w1| which contains the cursor. | 
|  | EventCountDelegate d11; | 
|  | std::unique_ptr<Window> w11(CreateTestWindowWithDelegate( | 
|  | &d11, 1, gfx::Rect(0, 0, 100, 100), w1.get())); | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_EQ("0 0 1", d1.GetMouseMotionCountsAndReset()); | 
|  | EXPECT_EQ("1 1 0", d11.GetMouseMotionCountsAndReset()); | 
|  |  | 
|  | // Resize |w11| so that it does not contain the cursor. | 
|  | w11->SetBounds(gfx::Rect(0, 0, 10, 10)); | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_EQ("1 1 0", d1.GetMouseMotionCountsAndReset()); | 
|  | EXPECT_EQ("0 0 1", d11.GetMouseMotionCountsAndReset()); | 
|  |  | 
|  | // Resize |w11| so that it does contain the cursor. | 
|  | w11->SetBounds(gfx::Rect(0, 0, 60, 60)); | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_EQ("0 0 1", d1.GetMouseMotionCountsAndReset()); | 
|  | EXPECT_EQ("1 1 0", d11.GetMouseMotionCountsAndReset()); | 
|  |  | 
|  | // Detach |w11| from |w1|. | 
|  | w1->RemoveChild(w11.get()); | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_EQ("1 1 0", d1.GetMouseMotionCountsAndReset()); | 
|  | EXPECT_EQ("0 0 1", d11.GetMouseMotionCountsAndReset()); | 
|  |  | 
|  | // Re-attach |w11| to |w1|. | 
|  | w1->AddChild(w11.get()); | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_EQ("0 0 1", d1.GetMouseMotionCountsAndReset()); | 
|  | EXPECT_EQ("1 1 0", d11.GetMouseMotionCountsAndReset()); | 
|  |  | 
|  | // Hide |w11|. | 
|  | w11->Hide(); | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_EQ("1 1 0", d1.GetMouseMotionCountsAndReset()); | 
|  | EXPECT_EQ("0 0 1", d11.GetMouseMotionCountsAndReset()); | 
|  |  | 
|  | // Show |w11|. | 
|  | w11->Show(); | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_EQ("0 0 1", d1.GetMouseMotionCountsAndReset()); | 
|  | EXPECT_EQ("1 1 0", d11.GetMouseMotionCountsAndReset()); | 
|  |  | 
|  | // Translate |w11| so that it does not contain the mouse cursor. | 
|  | gfx::Transform transform; | 
|  | transform.Translate(100, 100); | 
|  | w11->SetTransform(transform); | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_EQ("1 1 0", d1.GetMouseMotionCountsAndReset()); | 
|  | EXPECT_EQ("0 0 1", d11.GetMouseMotionCountsAndReset()); | 
|  |  | 
|  | // Clear the transform on |w11| so that it does contain the cursor. | 
|  | w11->SetTransform(gfx::Transform()); | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_EQ("0 0 1", d1.GetMouseMotionCountsAndReset()); | 
|  | EXPECT_EQ("1 1 0", d11.GetMouseMotionCountsAndReset()); | 
|  |  | 
|  | // Close |w11|. Note that since |w11| is being destroyed, its delegate should | 
|  | // not see any further events. | 
|  | w11.reset(); | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_EQ("1 1 0", d1.GetMouseMotionCountsAndReset()); | 
|  | EXPECT_EQ("0 0 0", d11.GetMouseMotionCountsAndReset()); | 
|  |  | 
|  | // Move the mouse outside the bounds of the root window. Since the mouse | 
|  | // cursor is no longer within their bounds, the delegates of the child | 
|  | // windows should not see any mouse events. | 
|  | generator.MoveMouseTo(-10, -10); | 
|  | EXPECT_EQ("0 0 1", d1.GetMouseMotionCountsAndReset()); | 
|  | EXPECT_EQ("0 0 0", d11.GetMouseMotionCountsAndReset()); | 
|  |  | 
|  | // Add |w11|. | 
|  | w11.reset(CreateTestWindowWithDelegate( | 
|  | &d11, 1, gfx::Rect(0, 0, 100, 100), w1.get())); | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_EQ("0 0 0", d1.GetMouseMotionCountsAndReset()); | 
|  | EXPECT_EQ("0 0 0", d11.GetMouseMotionCountsAndReset()); | 
|  |  | 
|  | // Close |w11|. | 
|  | w11.reset(); | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_EQ("0 0 0", d1.GetMouseMotionCountsAndReset()); | 
|  | EXPECT_EQ("0 0 0", d11.GetMouseMotionCountsAndReset()); | 
|  | } | 
|  |  | 
|  | // Tests the mouse events seen by WindowDelegates in a Window hierarchy when | 
|  | // deleting a non-leaf Window. | 
|  | TEST_F(WindowTest, MouseEventsOnNonLeafWindowDelete) { | 
|  | ui::test::EventGenerator generator(root_window()); | 
|  | generator.MoveMouseTo(50, 50); | 
|  |  | 
|  | EventCountDelegate d1; | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithDelegate( | 
|  | &d1, 1, gfx::Rect(0, 0, 100, 100), root_window())); | 
|  | RunAllPendingInMessageLoop(); | 
|  | // The format of result is "Enter/Move/Leave". | 
|  | EXPECT_EQ("1 1 0", d1.GetMouseMotionCountsAndReset()); | 
|  |  | 
|  | // Add new window |w2| on top of |w1| which contains the cursor. | 
|  | EventCountDelegate d2; | 
|  | std::unique_ptr<Window> w2(CreateTestWindowWithDelegate( | 
|  | &d2, 1, gfx::Rect(0, 0, 100, 100), w1.get())); | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_EQ("0 0 1", d1.GetMouseMotionCountsAndReset()); | 
|  | EXPECT_EQ("1 1 0", d2.GetMouseMotionCountsAndReset()); | 
|  |  | 
|  | // Add new window on top of |w2| which contains the cursor. | 
|  | EventCountDelegate d3; | 
|  | CreateTestWindowWithDelegate( | 
|  | &d3, 1, gfx::Rect(0, 0, 100, 100), w2.get()); | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_EQ("0 0 0", d1.GetMouseMotionCountsAndReset()); | 
|  | EXPECT_EQ("0 0 1", d2.GetMouseMotionCountsAndReset()); | 
|  | EXPECT_EQ("1 1 0", d3.GetMouseMotionCountsAndReset()); | 
|  |  | 
|  | // Delete |w2|, which will also delete its owned child window. Since |w2| and | 
|  | // its child are in the process of being destroyed, their delegates should | 
|  | // not see any further events. | 
|  | w2.reset(); | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_EQ("1 1 0", d1.GetMouseMotionCountsAndReset()); | 
|  | EXPECT_EQ("0 0 0", d2.GetMouseMotionCountsAndReset()); | 
|  | EXPECT_EQ("0 0 0", d3.GetMouseMotionCountsAndReset()); | 
|  | } | 
|  |  | 
|  | class RootWindowAttachmentObserver : public WindowObserver { | 
|  | public: | 
|  | RootWindowAttachmentObserver() : added_count_(0), removed_count_(0) {} | 
|  | ~RootWindowAttachmentObserver() override {} | 
|  |  | 
|  | int added_count() const { return added_count_; } | 
|  | int removed_count() const { return removed_count_; } | 
|  |  | 
|  | void Clear() { | 
|  | added_count_ = 0; | 
|  | removed_count_ = 0; | 
|  | } | 
|  |  | 
|  | // Overridden from WindowObserver: | 
|  | void OnWindowAddedToRootWindow(Window* window) override { ++added_count_; } | 
|  | void OnWindowRemovingFromRootWindow(Window* window, | 
|  | Window* new_root) override { | 
|  | ++removed_count_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | int added_count_; | 
|  | int removed_count_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(RootWindowAttachmentObserver); | 
|  | }; | 
|  |  | 
|  | TEST_F(WindowTest, RootWindowAttachment) { | 
|  | RootWindowAttachmentObserver observer; | 
|  |  | 
|  | // Test a direct add/remove from the RootWindow. | 
|  | std::unique_ptr<Window> w1(new Window(NULL)); | 
|  | w1->Init(ui::LAYER_NOT_DRAWN); | 
|  | w1->AddObserver(&observer); | 
|  |  | 
|  | ParentWindow(w1.get()); | 
|  | EXPECT_EQ(1, observer.added_count()); | 
|  | EXPECT_EQ(0, observer.removed_count()); | 
|  |  | 
|  | w1.reset(); | 
|  | EXPECT_EQ(1, observer.added_count()); | 
|  | EXPECT_EQ(1, observer.removed_count()); | 
|  |  | 
|  | observer.Clear(); | 
|  |  | 
|  | // Test an indirect add/remove from the RootWindow. | 
|  | w1.reset(new Window(NULL)); | 
|  | w1->Init(ui::LAYER_NOT_DRAWN); | 
|  | Window* w11 = new Window(NULL); | 
|  | w11->Init(ui::LAYER_NOT_DRAWN); | 
|  | w11->AddObserver(&observer); | 
|  | w1->AddChild(w11); | 
|  | EXPECT_EQ(0, observer.added_count()); | 
|  | EXPECT_EQ(0, observer.removed_count()); | 
|  |  | 
|  | ParentWindow(w1.get()); | 
|  | EXPECT_EQ(1, observer.added_count()); | 
|  | EXPECT_EQ(0, observer.removed_count()); | 
|  |  | 
|  | w1.reset();  // Deletes w11. | 
|  | w11 = NULL; | 
|  | EXPECT_EQ(1, observer.added_count()); | 
|  | EXPECT_EQ(1, observer.removed_count()); | 
|  |  | 
|  | observer.Clear(); | 
|  |  | 
|  | // Test an indirect add/remove with nested observers. | 
|  | w1.reset(new Window(NULL)); | 
|  | w1->Init(ui::LAYER_NOT_DRAWN); | 
|  | w11 = new Window(NULL); | 
|  | w11->Init(ui::LAYER_NOT_DRAWN); | 
|  | w11->AddObserver(&observer); | 
|  | w1->AddChild(w11); | 
|  | Window* w111 = new Window(NULL); | 
|  | w111->Init(ui::LAYER_NOT_DRAWN); | 
|  | w111->AddObserver(&observer); | 
|  | w11->AddChild(w111); | 
|  |  | 
|  | EXPECT_EQ(0, observer.added_count()); | 
|  | EXPECT_EQ(0, observer.removed_count()); | 
|  |  | 
|  | ParentWindow(w1.get()); | 
|  | EXPECT_EQ(2, observer.added_count()); | 
|  | EXPECT_EQ(0, observer.removed_count()); | 
|  |  | 
|  | w1.reset();  // Deletes w11 and w111. | 
|  | w11 = NULL; | 
|  | w111 = NULL; | 
|  | EXPECT_EQ(2, observer.added_count()); | 
|  | EXPECT_EQ(2, observer.removed_count()); | 
|  | } | 
|  |  | 
|  | class BoundsChangedWindowObserver : public WindowObserver { | 
|  | public: | 
|  | BoundsChangedWindowObserver() : root_set_(false) {} | 
|  |  | 
|  | void OnWindowBoundsChanged(Window* window, | 
|  | const gfx::Rect& old_bounds, | 
|  | const gfx::Rect& new_bounds) override { | 
|  | root_set_ = window->GetRootWindow() != NULL; | 
|  | } | 
|  |  | 
|  | bool root_set() const { return root_set_; } | 
|  |  | 
|  | private: | 
|  | bool root_set_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(BoundsChangedWindowObserver); | 
|  | }; | 
|  |  | 
|  | TEST_F(WindowTest, RootWindowSetWhenReparenting) { | 
|  | Window parent1(NULL); | 
|  | parent1.Init(ui::LAYER_NOT_DRAWN); | 
|  | Window parent2(NULL); | 
|  | parent2.Init(ui::LAYER_NOT_DRAWN); | 
|  | ParentWindow(&parent1); | 
|  | ParentWindow(&parent2); | 
|  | parent1.SetBounds(gfx::Rect(10, 10, 300, 300)); | 
|  | parent2.SetBounds(gfx::Rect(20, 20, 300, 300)); | 
|  |  | 
|  | BoundsChangedWindowObserver observer; | 
|  | Window child(NULL); | 
|  | child.Init(ui::LAYER_NOT_DRAWN); | 
|  | child.SetBounds(gfx::Rect(5, 5, 100, 100)); | 
|  | parent1.AddChild(&child); | 
|  |  | 
|  | // We need animations to start in order to observe the bounds changes. | 
|  | ui::ScopedAnimationDurationScaleMode test_duration_mode( | 
|  | ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); | 
|  | ui::ScopedLayerAnimationSettings settings1(child.layer()->GetAnimator()); | 
|  | settings1.SetTransitionDuration(base::TimeDelta::FromMilliseconds(100)); | 
|  | gfx::Rect new_bounds(gfx::Rect(35, 35, 50, 50)); | 
|  | child.SetBounds(new_bounds); | 
|  |  | 
|  | child.AddObserver(&observer); | 
|  |  | 
|  | // Reparenting the |child| will cause it to get moved. During this move | 
|  | // the window should still have root window set. | 
|  | parent2.AddChild(&child); | 
|  | EXPECT_TRUE(observer.root_set()); | 
|  |  | 
|  | // Animations should stop and the bounds should be as set before the |child| | 
|  | // got reparented. | 
|  | EXPECT_EQ(new_bounds.ToString(), child.GetTargetBounds().ToString()); | 
|  | EXPECT_EQ(new_bounds.ToString(), child.bounds().ToString()); | 
|  | EXPECT_EQ("55,55 50x50", child.GetBoundsInRootWindow().ToString()); | 
|  | } | 
|  |  | 
|  | TEST_F(WindowTest, OwnedByParentFalse) { | 
|  | // By default, a window is owned by its parent. If this is set to false, the | 
|  | // window will not be destroyed when its parent is. | 
|  |  | 
|  | std::unique_ptr<Window> w1(new Window(NULL)); | 
|  | w1->Init(ui::LAYER_NOT_DRAWN); | 
|  | std::unique_ptr<Window> w2(new Window(NULL)); | 
|  | w2->set_owned_by_parent(false); | 
|  | w2->Init(ui::LAYER_NOT_DRAWN); | 
|  | w1->AddChild(w2.get()); | 
|  |  | 
|  | w1.reset(); | 
|  |  | 
|  | // We should be able to deref w2 still, but its parent should now be NULL. | 
|  | EXPECT_EQ(NULL, w2->parent()); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Used By DeleteWindowFromOnWindowDestroyed. Destroys a Window from | 
|  | // OnWindowDestroyed(). | 
|  | class OwningWindowDelegate : public TestWindowDelegate { | 
|  | public: | 
|  | OwningWindowDelegate() {} | 
|  |  | 
|  | void SetOwnedWindow(Window* window) { | 
|  | owned_window_.reset(window); | 
|  | } | 
|  |  | 
|  | void OnWindowDestroyed(Window* window) override { owned_window_.reset(NULL); } | 
|  |  | 
|  | private: | 
|  | std::unique_ptr<Window> owned_window_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(OwningWindowDelegate); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Creates a window with two child windows. When the first child window is | 
|  | // destroyed (WindowDelegate::OnWindowDestroyed) it deletes the second child. | 
|  | // This synthesizes BrowserView and the status bubble. Both are children of the | 
|  | // same parent and destroying BrowserView triggers it destroying the status | 
|  | // bubble. | 
|  | TEST_F(WindowTest, DeleteWindowFromOnWindowDestroyed) { | 
|  | std::unique_ptr<Window> parent(new Window(NULL)); | 
|  | parent->Init(ui::LAYER_NOT_DRAWN); | 
|  | OwningWindowDelegate delegate; | 
|  | Window* c1 = new Window(&delegate); | 
|  | c1->Init(ui::LAYER_NOT_DRAWN); | 
|  | parent->AddChild(c1); | 
|  | Window* c2 = new Window(NULL); | 
|  | c2->Init(ui::LAYER_NOT_DRAWN); | 
|  | parent->AddChild(c2); | 
|  | delegate.SetOwnedWindow(c2); | 
|  | parent.reset(); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Used by DelegateNotifiedAsBoundsChange to verify OnBoundsChanged() is | 
|  | // invoked. | 
|  | class BoundsChangeDelegate : public TestWindowDelegate { | 
|  | public: | 
|  | BoundsChangeDelegate() : bounds_changed_(false) {} | 
|  |  | 
|  | void clear_bounds_changed() { bounds_changed_ = false; } | 
|  | bool bounds_changed() const { | 
|  | return bounds_changed_; | 
|  | } | 
|  |  | 
|  | // Window | 
|  | void OnBoundsChanged(const gfx::Rect& old_bounds, | 
|  | const gfx::Rect& new_bounds) override { | 
|  | bounds_changed_ = true; | 
|  | } | 
|  |  | 
|  | private: | 
|  | // Was OnBoundsChanged() invoked? | 
|  | bool bounds_changed_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(BoundsChangeDelegate); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Verifies the delegate is notified when the actual bounds of the layer | 
|  | // change. | 
|  | TEST_F(WindowTest, DelegateNotifiedAsBoundsChange) { | 
|  | BoundsChangeDelegate delegate; | 
|  |  | 
|  | // We cannot short-circuit animations in this test. | 
|  | ui::ScopedAnimationDurationScaleMode test_duration_mode( | 
|  | ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); | 
|  |  | 
|  | std::unique_ptr<Window> window(CreateTestWindowWithDelegate( | 
|  | &delegate, 1, gfx::Rect(0, 0, 100, 100), root_window())); | 
|  | window->layer()->GetAnimator()->set_disable_timer_for_test(true); | 
|  |  | 
|  | delegate.clear_bounds_changed(); | 
|  |  | 
|  | // Animate to a different position. | 
|  | { | 
|  | ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator()); | 
|  | window->SetBounds(gfx::Rect(100, 100, 100, 100)); | 
|  | } | 
|  |  | 
|  | // Bounds shouldn't immediately have changed. | 
|  | EXPECT_EQ("0,0 100x100", window->bounds().ToString()); | 
|  | EXPECT_FALSE(delegate.bounds_changed()); | 
|  |  | 
|  | // Animate to the end, which should notify of the change. | 
|  | base::TimeTicks start_time = | 
|  | window->layer()->GetAnimator()->last_step_time(); | 
|  | ui::LayerAnimator* animator = window->layer()->GetAnimator(); | 
|  | animator->Step(start_time + base::TimeDelta::FromMilliseconds(1000)); | 
|  | EXPECT_TRUE(delegate.bounds_changed()); | 
|  | EXPECT_NE("0,0 100x100", window->bounds().ToString()); | 
|  | } | 
|  |  | 
|  | // Verifies the delegate is notified when the actual bounds of the layer | 
|  | // change even when the window is not the layer's delegate | 
|  | TEST_F(WindowTest, DelegateNotifiedAsBoundsChangeInHiddenLayer) { | 
|  | BoundsChangeDelegate delegate; | 
|  |  | 
|  | // We cannot short-circuit animations in this test. | 
|  | ui::ScopedAnimationDurationScaleMode test_duration_mode( | 
|  | ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); | 
|  |  | 
|  | std::unique_ptr<Window> window(CreateTestWindowWithDelegate( | 
|  | &delegate, 1, gfx::Rect(0, 0, 100, 100), root_window())); | 
|  | window->layer()->GetAnimator()->set_disable_timer_for_test(true); | 
|  |  | 
|  | delegate.clear_bounds_changed(); | 
|  |  | 
|  | // Suppress paint on the window since it is hidden (should reset the layer's | 
|  | // delegate to NULL) | 
|  | window->SuppressPaint(); | 
|  | EXPECT_EQ(NULL, window->layer()->delegate()); | 
|  |  | 
|  | // Animate to a different position. | 
|  | { | 
|  | ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator()); | 
|  | window->SetBounds(gfx::Rect(100, 100, 110, 100)); | 
|  | } | 
|  |  | 
|  | // Layer delegate is NULL but we should still get bounds changed notification. | 
|  | EXPECT_EQ("100,100 110x100", window->GetTargetBounds().ToString()); | 
|  | EXPECT_TRUE(delegate.bounds_changed()); | 
|  |  | 
|  | delegate.clear_bounds_changed(); | 
|  |  | 
|  | // Animate to the end: will *not* notify of the change since we are hidden. | 
|  | base::TimeTicks start_time = | 
|  | window->layer()->GetAnimator()->last_step_time(); | 
|  | ui::LayerAnimator* animator = window->layer()->GetAnimator(); | 
|  | animator->Step(start_time + base::TimeDelta::FromMilliseconds(1000)); | 
|  |  | 
|  | // No bounds changed notification at the end of animation since layer | 
|  | // delegate is NULL. | 
|  | EXPECT_FALSE(delegate.bounds_changed()); | 
|  | EXPECT_NE("0,0 100x100", window->layer()->bounds().ToString()); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Used by AddChildNotifications to track notification counts. | 
|  | class AddChildNotificationsObserver : public WindowObserver { | 
|  | public: | 
|  | AddChildNotificationsObserver() : added_count_(0), removed_count_(0) {} | 
|  |  | 
|  | std::string CountStringAndReset() { | 
|  | std::string result = base::IntToString(added_count_) + " " + | 
|  | base::IntToString(removed_count_); | 
|  | added_count_ = removed_count_ = 0; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | // WindowObserver overrides: | 
|  | void OnWindowAddedToRootWindow(Window* window) override { added_count_++; } | 
|  | void OnWindowRemovingFromRootWindow(Window* window, | 
|  | Window* new_root) override { | 
|  | removed_count_++; | 
|  | } | 
|  |  | 
|  | private: | 
|  | int added_count_; | 
|  | int removed_count_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(AddChildNotificationsObserver); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Assertions around when root window notifications are sent. | 
|  | TEST_F(WindowTest, AddChildNotifications) { | 
|  | AddChildNotificationsObserver observer; | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithId(1, root_window())); | 
|  | std::unique_ptr<Window> w2(CreateTestWindowWithId(1, root_window())); | 
|  | w2->AddObserver(&observer); | 
|  | w2->Focus(); | 
|  | EXPECT_TRUE(w2->HasFocus()); | 
|  |  | 
|  | // Move |w2| to be a child of |w1|. | 
|  | w1->AddChild(w2.get()); | 
|  | // Sine we moved in the same root, observer shouldn't be notified. | 
|  | EXPECT_EQ("0 0", observer.CountStringAndReset()); | 
|  | // |w2| should still have focus after moving. | 
|  | EXPECT_TRUE(w2->HasFocus()); | 
|  | } | 
|  |  | 
|  | // Tests that a delegate that destroys itself when the window is destroyed does | 
|  | // not break. | 
|  | TEST_F(WindowTest, DelegateDestroysSelfOnWindowDestroy) { | 
|  | std::unique_ptr<Window> w1( | 
|  | CreateTestWindowWithDelegate(new DestroyWindowDelegate(), 0, | 
|  | gfx::Rect(10, 20, 30, 40), root_window())); | 
|  | } | 
|  |  | 
|  | class HierarchyObserver : public WindowObserver { | 
|  | public: | 
|  | explicit HierarchyObserver(Window* target) : target_(target) { | 
|  | target_->AddObserver(this); | 
|  | } | 
|  | ~HierarchyObserver() override { target_->RemoveObserver(this); } | 
|  |  | 
|  | void ValidateState( | 
|  | int index, | 
|  | const WindowObserver::HierarchyChangeParams& params) const { | 
|  | ParamsMatch(params_[index], params); | 
|  | } | 
|  |  | 
|  | void Reset() { | 
|  | params_.clear(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | // Overridden from WindowObserver: | 
|  | void OnWindowHierarchyChanging(const HierarchyChangeParams& params) override { | 
|  | params_.push_back(params); | 
|  | } | 
|  | void OnWindowHierarchyChanged(const HierarchyChangeParams& params) override { | 
|  | params_.push_back(params); | 
|  | } | 
|  |  | 
|  | void ParamsMatch(const WindowObserver::HierarchyChangeParams& p1, | 
|  | const WindowObserver::HierarchyChangeParams& p2) const { | 
|  | EXPECT_EQ(p1.phase, p2.phase); | 
|  | EXPECT_EQ(p1.target, p2.target); | 
|  | EXPECT_EQ(p1.new_parent, p2.new_parent); | 
|  | EXPECT_EQ(p1.old_parent, p2.old_parent); | 
|  | EXPECT_EQ(p1.receiver, p2.receiver); | 
|  | } | 
|  |  | 
|  | Window* target_; | 
|  | std::vector<WindowObserver::HierarchyChangeParams> params_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(HierarchyObserver); | 
|  | }; | 
|  |  | 
|  | // Tests hierarchy change notifications. | 
|  | TEST_F(WindowTest, OnWindowHierarchyChange) { | 
|  | { | 
|  | // Simple add & remove. | 
|  | HierarchyObserver oroot(root_window()); | 
|  |  | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithId(1, NULL)); | 
|  | HierarchyObserver o1(w1.get()); | 
|  |  | 
|  | // Add. | 
|  | root_window()->AddChild(w1.get()); | 
|  |  | 
|  | WindowObserver::HierarchyChangeParams params; | 
|  | params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGING; | 
|  | params.target = w1.get(); | 
|  | params.old_parent = NULL; | 
|  | params.new_parent = root_window(); | 
|  | params.receiver = w1.get(); | 
|  | o1.ValidateState(0, params); | 
|  |  | 
|  | params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGED; | 
|  | params.receiver = w1.get(); | 
|  | o1.ValidateState(1, params); | 
|  |  | 
|  | params.receiver = root_window(); | 
|  | oroot.ValidateState(0, params); | 
|  |  | 
|  | // Remove. | 
|  | o1.Reset(); | 
|  | oroot.Reset(); | 
|  |  | 
|  | root_window()->RemoveChild(w1.get()); | 
|  |  | 
|  | params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGING; | 
|  | params.old_parent = root_window(); | 
|  | params.new_parent = NULL; | 
|  | params.receiver = w1.get(); | 
|  |  | 
|  | o1.ValidateState(0, params); | 
|  |  | 
|  | params.receiver = root_window(); | 
|  | oroot.ValidateState(0, params); | 
|  |  | 
|  | params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGED; | 
|  | params.receiver = w1.get(); | 
|  | o1.ValidateState(1, params); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Add & remove of hierarchy. Tests notification order per documentation in | 
|  | // WindowObserver. | 
|  | HierarchyObserver o(root_window()); | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithId(1, NULL)); | 
|  | Window* w11 = CreateTestWindowWithId(11, w1.get()); | 
|  | w1->AddObserver(&o); | 
|  | w11->AddObserver(&o); | 
|  |  | 
|  | // Add. | 
|  | root_window()->AddChild(w1.get()); | 
|  |  | 
|  | // Dispatched to target first. | 
|  | int index = 0; | 
|  | WindowObserver::HierarchyChangeParams params; | 
|  | params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGING; | 
|  | params.target = w1.get(); | 
|  | params.old_parent = NULL; | 
|  | params.new_parent = root_window(); | 
|  | params.receiver = w1.get(); | 
|  | o.ValidateState(index++, params); | 
|  |  | 
|  | // Dispatched to target's children. | 
|  | params.receiver = w11; | 
|  | o.ValidateState(index++, params); | 
|  |  | 
|  | params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGED; | 
|  |  | 
|  | // Now process the "changed" phase. | 
|  | params.receiver = w1.get(); | 
|  | o.ValidateState(index++, params); | 
|  | params.receiver = w11; | 
|  | o.ValidateState(index++, params); | 
|  | params.receiver = root_window(); | 
|  | o.ValidateState(index++, params); | 
|  |  | 
|  | // Remove. | 
|  | root_window()->RemoveChild(w1.get()); | 
|  | params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGING; | 
|  | params.old_parent = root_window(); | 
|  | params.new_parent = NULL; | 
|  | params.receiver = w1.get(); | 
|  | o.ValidateState(index++, params); | 
|  | params.receiver = w11; | 
|  | o.ValidateState(index++, params); | 
|  | params.receiver = root_window(); | 
|  | o.ValidateState(index++, params); | 
|  | params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGED; | 
|  | params.receiver = w1.get(); | 
|  | o.ValidateState(index++, params); | 
|  | params.receiver = w11; | 
|  | o.ValidateState(index++, params); | 
|  |  | 
|  | w1.reset(); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Reparent. Tests notification order per documentation in WindowObserver. | 
|  | std::unique_ptr<Window> w1(CreateTestWindowWithId(1, root_window())); | 
|  | Window* w11 = CreateTestWindowWithId(11, w1.get()); | 
|  | Window* w111 = CreateTestWindowWithId(111, w11); | 
|  | std::unique_ptr<Window> w2(CreateTestWindowWithId(2, root_window())); | 
|  |  | 
|  | HierarchyObserver o(root_window()); | 
|  | w1->AddObserver(&o); | 
|  | w11->AddObserver(&o); | 
|  | w111->AddObserver(&o); | 
|  | w2->AddObserver(&o); | 
|  |  | 
|  | w2->AddChild(w11); | 
|  |  | 
|  | // Dispatched to target first. | 
|  | int index = 0; | 
|  | WindowObserver::HierarchyChangeParams params; | 
|  | params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGING; | 
|  | params.target = w11; | 
|  | params.old_parent = w1.get(); | 
|  | params.new_parent = w2.get(); | 
|  | params.receiver = w11; | 
|  | o.ValidateState(index++, params); | 
|  |  | 
|  | // Then to target's children. | 
|  | params.receiver = w111; | 
|  | o.ValidateState(index++, params); | 
|  |  | 
|  | // Then to target's old parent chain. | 
|  | params.receiver = w1.get(); | 
|  | o.ValidateState(index++, params); | 
|  | params.receiver = root_window(); | 
|  | o.ValidateState(index++, params); | 
|  |  | 
|  | // "Changed" phase. | 
|  | params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGED; | 
|  | params.receiver = w11; | 
|  | o.ValidateState(index++, params); | 
|  | params.receiver = w111; | 
|  | o.ValidateState(index++, params); | 
|  | params.receiver = w2.get(); | 
|  | o.ValidateState(index++, params); | 
|  | params.receiver = root_window(); | 
|  | o.ValidateState(index++, params); | 
|  |  | 
|  | w1.reset(); | 
|  | w2.reset(); | 
|  | } | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class TestLayerAnimationObserver : public ui::LayerAnimationObserver { | 
|  | public: | 
|  | TestLayerAnimationObserver() | 
|  | : animation_completed_(false), | 
|  | animation_aborted_(false) {} | 
|  | ~TestLayerAnimationObserver() override {} | 
|  |  | 
|  | bool animation_completed() const { return animation_completed_; } | 
|  | bool animation_aborted() const { return animation_aborted_; } | 
|  |  | 
|  | void Reset() { | 
|  | animation_completed_ = false; | 
|  | animation_aborted_ = false; | 
|  | } | 
|  |  | 
|  | private: | 
|  | // ui::LayerAnimationObserver: | 
|  | void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override { | 
|  | animation_completed_ = true; | 
|  | } | 
|  |  | 
|  | void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override { | 
|  | animation_aborted_ = true; | 
|  | } | 
|  |  | 
|  | void OnLayerAnimationScheduled( | 
|  | ui::LayerAnimationSequence* sequence) override {} | 
|  |  | 
|  | bool animation_completed_; | 
|  | bool animation_aborted_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(TestLayerAnimationObserver); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TEST_F(WindowTest, WindowDestroyCompletesAnimations) { | 
|  | ui::ScopedAnimationDurationScaleMode test_duration_mode( | 
|  | ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); | 
|  | scoped_refptr<ui::LayerAnimator> animator = | 
|  | ui::LayerAnimator::CreateImplicitAnimator(); | 
|  | TestLayerAnimationObserver observer; | 
|  | animator->AddObserver(&observer); | 
|  | // Make sure destroying a Window completes the animation. | 
|  | { | 
|  | std::unique_ptr<Window> window(CreateTestWindowWithId(1, root_window())); | 
|  | window->layer()->SetAnimator(animator.get()); | 
|  |  | 
|  | gfx::Transform transform; | 
|  | transform.Scale(0.5f, 0.5f); | 
|  | window->SetTransform(transform); | 
|  |  | 
|  | EXPECT_TRUE(animator->is_animating()); | 
|  | EXPECT_FALSE(observer.animation_completed()); | 
|  | } | 
|  | EXPECT_TRUE(animator.get()); | 
|  | EXPECT_FALSE(animator->is_animating()); | 
|  | EXPECT_TRUE(observer.animation_completed()); | 
|  | EXPECT_FALSE(observer.animation_aborted()); | 
|  | animator->RemoveObserver(&observer); | 
|  | observer.Reset(); | 
|  |  | 
|  | animator = ui::LayerAnimator::CreateImplicitAnimator(); | 
|  | animator->AddObserver(&observer); | 
|  | ui::Layer layer; | 
|  | layer.SetAnimator(animator.get()); | 
|  | { | 
|  | std::unique_ptr<Window> window(CreateTestWindowWithId(1, root_window())); | 
|  | window->layer()->Add(&layer); | 
|  |  | 
|  | gfx::Transform transform; | 
|  | transform.Scale(0.5f, 0.5f); | 
|  | layer.SetTransform(transform); | 
|  |  | 
|  | EXPECT_TRUE(animator->is_animating()); | 
|  | EXPECT_FALSE(observer.animation_completed()); | 
|  | } | 
|  |  | 
|  | EXPECT_TRUE(animator.get()); | 
|  | EXPECT_FALSE(animator->is_animating()); | 
|  | EXPECT_TRUE(observer.animation_completed()); | 
|  | EXPECT_FALSE(observer.animation_aborted()); | 
|  | animator->RemoveObserver(&observer); | 
|  | } | 
|  |  | 
|  | }  // namespace test | 
|  | }  // namespace aura |