|  | // 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 <algorithm> | 
|  | #include <set> | 
|  |  | 
|  | #include "base/basictypes.h" | 
|  | #include "base/bind.h" | 
|  | #include "base/memory/scoped_ptr.h" | 
|  | #include "base/message_loop/message_loop.h" | 
|  | #include "base/run_loop.h" | 
|  | #include "base/strings/utf_string_conversions.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  | #include "ui/base/hit_test.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/events/event_processor.h" | 
|  | #include "ui/events/event_utils.h" | 
|  | #include "ui/events/test/event_generator.h" | 
|  | #include "ui/gfx/native_widget_types.h" | 
|  | #include "ui/gfx/point.h" | 
|  | #include "ui/views/bubble/bubble_delegate.h" | 
|  | #include "ui/views/controls/textfield/textfield.h" | 
|  | #include "ui/views/test/test_views_delegate.h" | 
|  | #include "ui/views/test/widget_test.h" | 
|  | #include "ui/views/views_delegate.h" | 
|  | #include "ui/views/widget/native_widget_delegate.h" | 
|  | #include "ui/views/widget/root_view.h" | 
|  | #include "ui/views/widget/widget_deletion_observer.h" | 
|  | #include "ui/views/window/dialog_delegate.h" | 
|  | #include "ui/views/window/native_frame_view.h" | 
|  |  | 
|  | #if defined(OS_WIN) | 
|  | #include "ui/views/win/hwnd_util.h" | 
|  | #endif | 
|  |  | 
|  | namespace views { | 
|  | namespace test { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // TODO(tdanderson): This utility function is used in different unittest | 
|  | //                   files. Move to a common location to avoid | 
|  | //                   repeated code. | 
|  | gfx::Point ConvertPointFromWidgetToView(View* view, const gfx::Point& p) { | 
|  | gfx::Point tmp(p); | 
|  | View::ConvertPointToTarget(view->GetWidget()->GetRootView(), view, &tmp); | 
|  | return tmp; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // A view that keeps track of the events it receives, optionally consuming them. | 
|  | class EventCountView : public View { | 
|  | public: | 
|  | // Whether to call SetHandled() on events as they are received. For some event | 
|  | // types, this will allow EventCountView to receives future events in the | 
|  | // event sequence, such as a drag. | 
|  | enum HandleMode { | 
|  | PROPAGATE_EVENTS, | 
|  | CONSUME_EVENTS | 
|  | }; | 
|  |  | 
|  | EventCountView() | 
|  | : last_flags_(0), | 
|  | handle_mode_(PROPAGATE_EVENTS) {} | 
|  |  | 
|  | virtual ~EventCountView() {} | 
|  |  | 
|  | int GetEventCount(ui::EventType type) { | 
|  | return event_count_[type]; | 
|  | } | 
|  |  | 
|  | void ResetCounts() { | 
|  | event_count_.clear(); | 
|  | } | 
|  |  | 
|  | int last_flags() const { | 
|  | return last_flags_; | 
|  | } | 
|  |  | 
|  | void set_handle_mode(HandleMode handle_mode) { | 
|  | handle_mode_ = handle_mode; | 
|  | } | 
|  |  | 
|  | protected: | 
|  | // Overridden from View: | 
|  | virtual void OnMouseMoved(const ui::MouseEvent& event) override { | 
|  | // MouseMove events are not re-dispatched from the RootView. | 
|  | ++event_count_[ui::ET_MOUSE_MOVED]; | 
|  | last_flags_ = 0; | 
|  | } | 
|  |  | 
|  | // Overridden from ui::EventHandler: | 
|  | virtual void OnKeyEvent(ui::KeyEvent* event) override { | 
|  | RecordEvent(event); | 
|  | } | 
|  | virtual void OnMouseEvent(ui::MouseEvent* event) override { | 
|  | RecordEvent(event); | 
|  | } | 
|  | virtual void OnScrollEvent(ui::ScrollEvent* event) override { | 
|  | RecordEvent(event); | 
|  | } | 
|  | virtual void OnGestureEvent(ui::GestureEvent* event) override { | 
|  | RecordEvent(event); | 
|  | } | 
|  |  | 
|  | private: | 
|  | void RecordEvent(ui::Event* event) { | 
|  | ++event_count_[event->type()]; | 
|  | last_flags_ = event->flags(); | 
|  | if (handle_mode_ == CONSUME_EVENTS) | 
|  | event->SetHandled(); | 
|  | } | 
|  |  | 
|  | std::map<ui::EventType, int> event_count_; | 
|  | int last_flags_; | 
|  | HandleMode handle_mode_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(EventCountView); | 
|  | }; | 
|  |  | 
|  | // A view that keeps track of the events it receives, and consumes all scroll | 
|  | // gesture events and ui::ET_SCROLL events. | 
|  | class ScrollableEventCountView : public EventCountView { | 
|  | public: | 
|  | ScrollableEventCountView() {} | 
|  | virtual ~ScrollableEventCountView() {} | 
|  |  | 
|  | private: | 
|  | // Overridden from ui::EventHandler: | 
|  | virtual void OnGestureEvent(ui::GestureEvent* event) override { | 
|  | EventCountView::OnGestureEvent(event); | 
|  | switch (event->type()) { | 
|  | case ui::ET_GESTURE_SCROLL_BEGIN: | 
|  | case ui::ET_GESTURE_SCROLL_UPDATE: | 
|  | case ui::ET_GESTURE_SCROLL_END: | 
|  | case ui::ET_SCROLL_FLING_START: | 
|  | event->SetHandled(); | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | virtual void OnScrollEvent(ui::ScrollEvent* event) override { | 
|  | EventCountView::OnScrollEvent(event); | 
|  | if (event->type() == ui::ET_SCROLL) | 
|  | event->SetHandled(); | 
|  | } | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(ScrollableEventCountView); | 
|  | }; | 
|  |  | 
|  | // A view that implements GetMinimumSize. | 
|  | class MinimumSizeFrameView : public NativeFrameView { | 
|  | public: | 
|  | explicit MinimumSizeFrameView(Widget* frame): NativeFrameView(frame) {} | 
|  | virtual ~MinimumSizeFrameView() {} | 
|  |  | 
|  | private: | 
|  | // Overridden from View: | 
|  | virtual gfx::Size GetMinimumSize() const override { | 
|  | return gfx::Size(300, 400); | 
|  | } | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(MinimumSizeFrameView); | 
|  | }; | 
|  |  | 
|  | // An event handler that simply keeps a count of the different types of events | 
|  | // it receives. | 
|  | class EventCountHandler : public ui::EventHandler { | 
|  | public: | 
|  | EventCountHandler() {} | 
|  | virtual ~EventCountHandler() {} | 
|  |  | 
|  | int GetEventCount(ui::EventType type) { | 
|  | return event_count_[type]; | 
|  | } | 
|  |  | 
|  | void ResetCounts() { | 
|  | event_count_.clear(); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | // Overridden from ui::EventHandler: | 
|  | virtual void OnEvent(ui::Event* event) override { | 
|  | RecordEvent(*event); | 
|  | ui::EventHandler::OnEvent(event); | 
|  | } | 
|  |  | 
|  | private: | 
|  | void RecordEvent(const ui::Event& event) { | 
|  | ++event_count_[event.type()]; | 
|  | } | 
|  |  | 
|  | std::map<ui::EventType, int> event_count_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(EventCountHandler); | 
|  | }; | 
|  |  | 
|  | // Class that closes the widget (which ends up deleting it immediately) when the | 
|  | // appropriate event is received. | 
|  | class CloseWidgetView : public View { | 
|  | public: | 
|  | explicit CloseWidgetView(ui::EventType event_type) | 
|  | : event_type_(event_type) { | 
|  | } | 
|  |  | 
|  | // ui::EventHandler override: | 
|  | virtual void OnEvent(ui::Event* event) override { | 
|  | if (event->type() == event_type_) { | 
|  | // Go through NativeWidgetPrivate to simulate what happens if the OS | 
|  | // deletes the NativeWindow out from under us. | 
|  | GetWidget()->native_widget_private()->CloseNow(); | 
|  | } else { | 
|  | View::OnEvent(event); | 
|  | if (!event->IsTouchEvent()) | 
|  | event->SetHandled(); | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  | const ui::EventType event_type_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(CloseWidgetView); | 
|  | }; | 
|  |  | 
|  | ui::WindowShowState GetWidgetShowState(const Widget* widget) { | 
|  | // Use IsMaximized/IsMinimized/IsFullScreen instead of GetWindowPlacement | 
|  | // because the former is implemented on all platforms but the latter is not. | 
|  | return widget->IsFullscreen() ? ui::SHOW_STATE_FULLSCREEN : | 
|  | widget->IsMaximized() ? ui::SHOW_STATE_MAXIMIZED : | 
|  | widget->IsMinimized() ? ui::SHOW_STATE_MINIMIZED : | 
|  | widget->IsActive() ? ui::SHOW_STATE_NORMAL : | 
|  | ui::SHOW_STATE_INACTIVE; | 
|  | } | 
|  |  | 
|  | TEST_F(WidgetTest, WidgetInitParams) { | 
|  | // Widgets are not transparent by default. | 
|  | Widget::InitParams init1; | 
|  | EXPECT_EQ(Widget::InitParams::INFER_OPACITY, init1.opacity); | 
|  | } | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | // Widget::GetTopLevelWidget tests. | 
|  |  | 
|  | TEST_F(WidgetTest, GetTopLevelWidget_Native) { | 
|  | // Create a hierarchy of native widgets. | 
|  | Widget* toplevel = CreateTopLevelPlatformWidget(); | 
|  | gfx::NativeView parent = toplevel->GetNativeView(); | 
|  | Widget* child = CreateChildPlatformWidget(parent); | 
|  |  | 
|  | EXPECT_EQ(toplevel, toplevel->GetTopLevelWidget()); | 
|  | EXPECT_EQ(toplevel, child->GetTopLevelWidget()); | 
|  |  | 
|  | toplevel->CloseNow(); | 
|  | // |child| should be automatically destroyed with |toplevel|. | 
|  | } | 
|  |  | 
|  | // Test if a focus manager and an inputmethod work without CHECK failure | 
|  | // when window activation changes. | 
|  | TEST_F(WidgetTest, ChangeActivation) { | 
|  | Widget* top1 = CreateTopLevelPlatformWidget(); | 
|  | // CreateInputMethod before activated | 
|  | top1->GetInputMethod(); | 
|  | top1->Show(); | 
|  | RunPendingMessages(); | 
|  |  | 
|  | Widget* top2 = CreateTopLevelPlatformWidget(); | 
|  | top2->Show(); | 
|  | RunPendingMessages(); | 
|  |  | 
|  | top1->Activate(); | 
|  | RunPendingMessages(); | 
|  |  | 
|  | // Create InputMethod after deactivated. | 
|  | top2->GetInputMethod(); | 
|  | top2->Activate(); | 
|  | RunPendingMessages(); | 
|  |  | 
|  | top1->Activate(); | 
|  | RunPendingMessages(); | 
|  |  | 
|  | top1->CloseNow(); | 
|  | top2->CloseNow(); | 
|  | } | 
|  |  | 
|  | // Tests visibility of child widgets. | 
|  | TEST_F(WidgetTest, Visibility) { | 
|  | Widget* toplevel = CreateTopLevelPlatformWidget(); | 
|  | gfx::NativeView parent = toplevel->GetNativeView(); | 
|  | Widget* child = CreateChildPlatformWidget(parent); | 
|  |  | 
|  | EXPECT_FALSE(toplevel->IsVisible()); | 
|  | EXPECT_FALSE(child->IsVisible()); | 
|  |  | 
|  | child->Show(); | 
|  |  | 
|  | EXPECT_FALSE(toplevel->IsVisible()); | 
|  | EXPECT_FALSE(child->IsVisible()); | 
|  |  | 
|  | toplevel->Show(); | 
|  |  | 
|  | EXPECT_TRUE(toplevel->IsVisible()); | 
|  | EXPECT_TRUE(child->IsVisible()); | 
|  |  | 
|  | toplevel->CloseNow(); | 
|  | // |child| should be automatically destroyed with |toplevel|. | 
|  | } | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | // Widget ownership tests. | 
|  | // | 
|  | // Tests various permutations of Widget ownership specified in the | 
|  | // InitParams::Ownership param. | 
|  |  | 
|  | // A WidgetTest that supplies a toplevel widget for NativeWidget to parent to. | 
|  | class WidgetOwnershipTest : public WidgetTest { | 
|  | public: | 
|  | WidgetOwnershipTest() {} | 
|  | virtual ~WidgetOwnershipTest() {} | 
|  |  | 
|  | virtual void SetUp() { | 
|  | WidgetTest::SetUp(); | 
|  | desktop_widget_ = CreateTopLevelPlatformWidget(); | 
|  | } | 
|  |  | 
|  | virtual void TearDown() { | 
|  | desktop_widget_->CloseNow(); | 
|  | WidgetTest::TearDown(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | Widget* desktop_widget_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(WidgetOwnershipTest); | 
|  | }; | 
|  |  | 
|  | // A bag of state to monitor destructions. | 
|  | struct OwnershipTestState { | 
|  | OwnershipTestState() : widget_deleted(false), native_widget_deleted(false) {} | 
|  |  | 
|  | bool widget_deleted; | 
|  | bool native_widget_deleted; | 
|  | }; | 
|  |  | 
|  | // A platform NativeWidget subclass that updates a bag of state when it is | 
|  | // destroyed. | 
|  | class OwnershipTestNativeWidget : public PlatformNativeWidget { | 
|  | public: | 
|  | OwnershipTestNativeWidget(internal::NativeWidgetDelegate* delegate, | 
|  | OwnershipTestState* state) | 
|  | : PlatformNativeWidget(delegate), | 
|  | state_(state) { | 
|  | } | 
|  | virtual ~OwnershipTestNativeWidget() { | 
|  | state_->native_widget_deleted = true; | 
|  | } | 
|  |  | 
|  | private: | 
|  | OwnershipTestState* state_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(OwnershipTestNativeWidget); | 
|  | }; | 
|  |  | 
|  | // A views NativeWidget subclass that updates a bag of state when it is | 
|  | // destroyed. | 
|  | class OwnershipTestNativeWidgetAura : public NativeWidgetCapture { | 
|  | public: | 
|  | OwnershipTestNativeWidgetAura(internal::NativeWidgetDelegate* delegate, | 
|  | OwnershipTestState* state) | 
|  | : NativeWidgetCapture(delegate), | 
|  | state_(state) { | 
|  | } | 
|  | virtual ~OwnershipTestNativeWidgetAura() { | 
|  | state_->native_widget_deleted = true; | 
|  | } | 
|  |  | 
|  | private: | 
|  | OwnershipTestState* state_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(OwnershipTestNativeWidgetAura); | 
|  | }; | 
|  |  | 
|  | // A Widget subclass that updates a bag of state when it is destroyed. | 
|  | class OwnershipTestWidget : public Widget { | 
|  | public: | 
|  | explicit OwnershipTestWidget(OwnershipTestState* state) : state_(state) {} | 
|  | virtual ~OwnershipTestWidget() { | 
|  | state_->widget_deleted = true; | 
|  | } | 
|  |  | 
|  | private: | 
|  | OwnershipTestState* state_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(OwnershipTestWidget); | 
|  | }; | 
|  |  | 
|  | // Widget owns its NativeWidget, part 1: NativeWidget is a platform-native | 
|  | // widget. | 
|  | TEST_F(WidgetOwnershipTest, Ownership_WidgetOwnsPlatformNativeWidget) { | 
|  | OwnershipTestState state; | 
|  |  | 
|  | scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); | 
|  | Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); | 
|  | params.native_widget = | 
|  | new OwnershipTestNativeWidgetAura(widget.get(), &state); | 
|  | params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 
|  | widget->Init(params); | 
|  |  | 
|  | // Now delete the Widget, which should delete the NativeWidget. | 
|  | widget.reset(); | 
|  |  | 
|  | EXPECT_TRUE(state.widget_deleted); | 
|  | EXPECT_TRUE(state.native_widget_deleted); | 
|  |  | 
|  | // TODO(beng): write test for this ownership scenario and the NativeWidget | 
|  | //             being deleted out from under the Widget. | 
|  | } | 
|  |  | 
|  | // Widget owns its NativeWidget, part 2: NativeWidget is a NativeWidget. | 
|  | TEST_F(WidgetOwnershipTest, Ownership_WidgetOwnsViewsNativeWidget) { | 
|  | OwnershipTestState state; | 
|  |  | 
|  | scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); | 
|  | Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); | 
|  | params.native_widget = | 
|  | new OwnershipTestNativeWidgetAura(widget.get(), &state); | 
|  | params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 
|  | widget->Init(params); | 
|  |  | 
|  | // Now delete the Widget, which should delete the NativeWidget. | 
|  | widget.reset(); | 
|  |  | 
|  | EXPECT_TRUE(state.widget_deleted); | 
|  | EXPECT_TRUE(state.native_widget_deleted); | 
|  |  | 
|  | // TODO(beng): write test for this ownership scenario and the NativeWidget | 
|  | //             being deleted out from under the Widget. | 
|  | } | 
|  |  | 
|  | // Widget owns its NativeWidget, part 3: NativeWidget is a NativeWidget, | 
|  | // destroy the parent view. | 
|  | TEST_F(WidgetOwnershipTest, | 
|  | Ownership_WidgetOwnsViewsNativeWidget_DestroyParentView) { | 
|  | OwnershipTestState state; | 
|  |  | 
|  | Widget* toplevel = CreateTopLevelPlatformWidget(); | 
|  |  | 
|  | scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); | 
|  | Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); | 
|  | params.native_widget = | 
|  | new OwnershipTestNativeWidgetAura(widget.get(), &state); | 
|  | params.parent = toplevel->GetNativeView(); | 
|  | params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 
|  | widget->Init(params); | 
|  |  | 
|  | // Now close the toplevel, which deletes the view hierarchy. | 
|  | toplevel->CloseNow(); | 
|  |  | 
|  | RunPendingMessages(); | 
|  |  | 
|  | // This shouldn't delete the widget because it shouldn't be deleted | 
|  | // from the native side. | 
|  | EXPECT_FALSE(state.widget_deleted); | 
|  | EXPECT_FALSE(state.native_widget_deleted); | 
|  |  | 
|  | // Now delete it explicitly. | 
|  | widget.reset(); | 
|  |  | 
|  | EXPECT_TRUE(state.widget_deleted); | 
|  | EXPECT_TRUE(state.native_widget_deleted); | 
|  | } | 
|  |  | 
|  | // NativeWidget owns its Widget, part 1: NativeWidget is a platform-native | 
|  | // widget. | 
|  | TEST_F(WidgetOwnershipTest, Ownership_PlatformNativeWidgetOwnsWidget) { | 
|  | OwnershipTestState state; | 
|  |  | 
|  | Widget* widget = new OwnershipTestWidget(&state); | 
|  | Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); | 
|  | params.native_widget = | 
|  | new OwnershipTestNativeWidgetAura(widget, &state); | 
|  | widget->Init(params); | 
|  |  | 
|  | // Now destroy the native widget. | 
|  | widget->CloseNow(); | 
|  |  | 
|  | EXPECT_TRUE(state.widget_deleted); | 
|  | EXPECT_TRUE(state.native_widget_deleted); | 
|  | } | 
|  |  | 
|  | // NativeWidget owns its Widget, part 2: NativeWidget is a NativeWidget. | 
|  | TEST_F(WidgetOwnershipTest, Ownership_ViewsNativeWidgetOwnsWidget) { | 
|  | OwnershipTestState state; | 
|  |  | 
|  | Widget* toplevel = CreateTopLevelPlatformWidget(); | 
|  |  | 
|  | Widget* widget = new OwnershipTestWidget(&state); | 
|  | Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); | 
|  | params.native_widget = | 
|  | new OwnershipTestNativeWidgetAura(widget, &state); | 
|  | params.parent = toplevel->GetNativeView(); | 
|  | widget->Init(params); | 
|  |  | 
|  | // Now destroy the native widget. This is achieved by closing the toplevel. | 
|  | toplevel->CloseNow(); | 
|  |  | 
|  | // The NativeWidget won't be deleted until after a return to the message loop | 
|  | // so we have to run pending messages before testing the destruction status. | 
|  | RunPendingMessages(); | 
|  |  | 
|  | EXPECT_TRUE(state.widget_deleted); | 
|  | EXPECT_TRUE(state.native_widget_deleted); | 
|  | } | 
|  |  | 
|  | // NativeWidget owns its Widget, part 3: NativeWidget is a platform-native | 
|  | // widget, destroyed out from under it by the OS. | 
|  | TEST_F(WidgetOwnershipTest, | 
|  | Ownership_PlatformNativeWidgetOwnsWidget_NativeDestroy) { | 
|  | OwnershipTestState state; | 
|  |  | 
|  | Widget* widget = new OwnershipTestWidget(&state); | 
|  | Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); | 
|  | params.native_widget = | 
|  | new OwnershipTestNativeWidgetAura(widget, &state); | 
|  | widget->Init(params); | 
|  |  | 
|  | // Now simulate a destroy of the platform native widget from the OS: | 
|  | SimulateNativeDestroy(widget); | 
|  |  | 
|  | EXPECT_TRUE(state.widget_deleted); | 
|  | EXPECT_TRUE(state.native_widget_deleted); | 
|  | } | 
|  |  | 
|  | // NativeWidget owns its Widget, part 4: NativeWidget is a NativeWidget, | 
|  | // destroyed by the view hierarchy that contains it. | 
|  | TEST_F(WidgetOwnershipTest, | 
|  | Ownership_ViewsNativeWidgetOwnsWidget_NativeDestroy) { | 
|  | OwnershipTestState state; | 
|  |  | 
|  | Widget* toplevel = CreateTopLevelPlatformWidget(); | 
|  |  | 
|  | Widget* widget = new OwnershipTestWidget(&state); | 
|  | Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); | 
|  | params.native_widget = | 
|  | new OwnershipTestNativeWidgetAura(widget, &state); | 
|  | params.parent = toplevel->GetNativeView(); | 
|  | widget->Init(params); | 
|  |  | 
|  | // Destroy the widget (achieved by closing the toplevel). | 
|  | toplevel->CloseNow(); | 
|  |  | 
|  | // The NativeWidget won't be deleted until after a return to the message loop | 
|  | // so we have to run pending messages before testing the destruction status. | 
|  | RunPendingMessages(); | 
|  |  | 
|  | EXPECT_TRUE(state.widget_deleted); | 
|  | EXPECT_TRUE(state.native_widget_deleted); | 
|  | } | 
|  |  | 
|  | // NativeWidget owns its Widget, part 5: NativeWidget is a NativeWidget, | 
|  | // we close it directly. | 
|  | TEST_F(WidgetOwnershipTest, | 
|  | Ownership_ViewsNativeWidgetOwnsWidget_Close) { | 
|  | OwnershipTestState state; | 
|  |  | 
|  | Widget* toplevel = CreateTopLevelPlatformWidget(); | 
|  |  | 
|  | Widget* widget = new OwnershipTestWidget(&state); | 
|  | Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); | 
|  | params.native_widget = | 
|  | new OwnershipTestNativeWidgetAura(widget, &state); | 
|  | params.parent = toplevel->GetNativeView(); | 
|  | widget->Init(params); | 
|  |  | 
|  | // Destroy the widget. | 
|  | widget->Close(); | 
|  | toplevel->CloseNow(); | 
|  |  | 
|  | // The NativeWidget won't be deleted until after a return to the message loop | 
|  | // so we have to run pending messages before testing the destruction status. | 
|  | RunPendingMessages(); | 
|  |  | 
|  | EXPECT_TRUE(state.widget_deleted); | 
|  | EXPECT_TRUE(state.native_widget_deleted); | 
|  | } | 
|  |  | 
|  | // Widget owns its NativeWidget and has a WidgetDelegateView as its contents. | 
|  | TEST_F(WidgetOwnershipTest, | 
|  | Ownership_WidgetOwnsNativeWidgetWithWithWidgetDelegateView) { | 
|  | OwnershipTestState state; | 
|  |  | 
|  | WidgetDelegateView* delegate_view = new WidgetDelegateView; | 
|  |  | 
|  | scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); | 
|  | Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); | 
|  | params.native_widget = | 
|  | new OwnershipTestNativeWidgetAura(widget.get(), &state); | 
|  | params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 
|  | params.delegate = delegate_view; | 
|  | widget->Init(params); | 
|  | widget->SetContentsView(delegate_view); | 
|  |  | 
|  | // Now delete the Widget. There should be no crash or use-after-free. | 
|  | widget.reset(); | 
|  |  | 
|  | EXPECT_TRUE(state.widget_deleted); | 
|  | EXPECT_TRUE(state.native_widget_deleted); | 
|  | } | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | // Test to verify using various Widget methods doesn't crash when the underlying | 
|  | // NativeView is destroyed. | 
|  | // | 
|  | class WidgetWithDestroyedNativeViewTest : public ViewsTestBase { | 
|  | public: | 
|  | WidgetWithDestroyedNativeViewTest() {} | 
|  | virtual ~WidgetWithDestroyedNativeViewTest() {} | 
|  |  | 
|  | void InvokeWidgetMethods(Widget* widget) { | 
|  | widget->GetNativeView(); | 
|  | widget->GetNativeWindow(); | 
|  | ui::Accelerator accelerator; | 
|  | widget->GetAccelerator(0, &accelerator); | 
|  | widget->GetTopLevelWidget(); | 
|  | widget->GetWindowBoundsInScreen(); | 
|  | widget->GetClientAreaBoundsInScreen(); | 
|  | widget->SetBounds(gfx::Rect(0, 0, 100, 80)); | 
|  | widget->SetSize(gfx::Size(10, 11)); | 
|  | widget->SetBoundsConstrained(gfx::Rect(0, 0, 120, 140)); | 
|  | widget->SetVisibilityChangedAnimationsEnabled(false); | 
|  | widget->StackAtTop(); | 
|  | widget->IsClosed(); | 
|  | widget->Close(); | 
|  | widget->Hide(); | 
|  | widget->Activate(); | 
|  | widget->Deactivate(); | 
|  | widget->IsActive(); | 
|  | widget->DisableInactiveRendering(); | 
|  | widget->SetAlwaysOnTop(true); | 
|  | widget->IsAlwaysOnTop(); | 
|  | widget->Maximize(); | 
|  | widget->Minimize(); | 
|  | widget->Restore(); | 
|  | widget->IsMaximized(); | 
|  | widget->IsFullscreen(); | 
|  | widget->SetOpacity(0); | 
|  | widget->SetUseDragFrame(true); | 
|  | widget->FlashFrame(true); | 
|  | widget->IsVisible(); | 
|  | widget->GetThemeProvider(); | 
|  | widget->GetNativeTheme(); | 
|  | widget->GetFocusManager(); | 
|  | widget->GetInputMethod(); | 
|  | widget->SchedulePaintInRect(gfx::Rect(0, 0, 1, 2)); | 
|  | widget->IsMouseEventsEnabled(); | 
|  | widget->SetNativeWindowProperty("xx", widget); | 
|  | widget->GetNativeWindowProperty("xx"); | 
|  | widget->GetFocusTraversable(); | 
|  | widget->GetLayer(); | 
|  | widget->ReorderNativeViews(); | 
|  | widget->SetCapture(widget->GetRootView()); | 
|  | widget->ReleaseCapture(); | 
|  | widget->HasCapture(); | 
|  | widget->GetWorkAreaBoundsInScreen(); | 
|  | widget->IsTranslucentWindowOpacitySupported(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(WidgetWithDestroyedNativeViewTest); | 
|  | }; | 
|  |  | 
|  | TEST_F(WidgetWithDestroyedNativeViewTest, Test) { | 
|  | { | 
|  | Widget widget; | 
|  | Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); | 
|  | params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 
|  | widget.Init(params); | 
|  | widget.Show(); | 
|  |  | 
|  | widget.native_widget_private()->CloseNow(); | 
|  | InvokeWidgetMethods(&widget); | 
|  | } | 
|  | #if !defined(OS_CHROMEOS) | 
|  | { | 
|  | Widget widget; | 
|  | Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); | 
|  | params.native_widget = new PlatformDesktopNativeWidget(&widget); | 
|  | params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 
|  | widget.Init(params); | 
|  | widget.Show(); | 
|  |  | 
|  | widget.native_widget_private()->CloseNow(); | 
|  | InvokeWidgetMethods(&widget); | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | // Widget observer tests. | 
|  | // | 
|  |  | 
|  | class WidgetObserverTest : public WidgetTest, public WidgetObserver { | 
|  | public: | 
|  | WidgetObserverTest() | 
|  | : active_(NULL), | 
|  | widget_closed_(NULL), | 
|  | widget_activated_(NULL), | 
|  | widget_shown_(NULL), | 
|  | widget_hidden_(NULL), | 
|  | widget_bounds_changed_(NULL) { | 
|  | } | 
|  |  | 
|  | virtual ~WidgetObserverTest() {} | 
|  |  | 
|  | // Overridden from WidgetObserver: | 
|  | virtual void OnWidgetDestroying(Widget* widget) override { | 
|  | if (active_ == widget) | 
|  | active_ = NULL; | 
|  | widget_closed_ = widget; | 
|  | } | 
|  |  | 
|  | virtual void OnWidgetActivationChanged(Widget* widget, | 
|  | bool active) override { | 
|  | if (active) { | 
|  | if (widget_activated_) | 
|  | widget_activated_->Deactivate(); | 
|  | widget_activated_ = widget; | 
|  | active_ = widget; | 
|  | } else { | 
|  | if (widget_activated_ == widget) | 
|  | widget_activated_ = NULL; | 
|  | widget_deactivated_ = widget; | 
|  | } | 
|  | } | 
|  |  | 
|  | virtual void OnWidgetVisibilityChanged(Widget* widget, | 
|  | bool visible) override { | 
|  | if (visible) | 
|  | widget_shown_ = widget; | 
|  | else | 
|  | widget_hidden_ = widget; | 
|  | } | 
|  |  | 
|  | virtual void OnWidgetBoundsChanged(Widget* widget, | 
|  | const gfx::Rect& new_bounds) override { | 
|  | widget_bounds_changed_ = widget; | 
|  | } | 
|  |  | 
|  | void reset() { | 
|  | active_ = NULL; | 
|  | widget_closed_ = NULL; | 
|  | widget_activated_ = NULL; | 
|  | widget_deactivated_ = NULL; | 
|  | widget_shown_ = NULL; | 
|  | widget_hidden_ = NULL; | 
|  | widget_bounds_changed_ = NULL; | 
|  | } | 
|  |  | 
|  | Widget* NewWidget() { | 
|  | Widget* widget = CreateTopLevelNativeWidget(); | 
|  | widget->AddObserver(this); | 
|  | return widget; | 
|  | } | 
|  |  | 
|  | const Widget* active() const { return active_; } | 
|  | const Widget* widget_closed() const { return widget_closed_; } | 
|  | const Widget* widget_activated() const { return widget_activated_; } | 
|  | const Widget* widget_deactivated() const { return widget_deactivated_; } | 
|  | const Widget* widget_shown() const { return widget_shown_; } | 
|  | const Widget* widget_hidden() const { return widget_hidden_; } | 
|  | const Widget* widget_bounds_changed() const { return widget_bounds_changed_; } | 
|  |  | 
|  | private: | 
|  | Widget* active_; | 
|  |  | 
|  | Widget* widget_closed_; | 
|  | Widget* widget_activated_; | 
|  | Widget* widget_deactivated_; | 
|  | Widget* widget_shown_; | 
|  | Widget* widget_hidden_; | 
|  | Widget* widget_bounds_changed_; | 
|  | }; | 
|  |  | 
|  | TEST_F(WidgetObserverTest, DISABLED_ActivationChange) { | 
|  | Widget* toplevel = CreateTopLevelPlatformWidget(); | 
|  |  | 
|  | Widget* toplevel1 = NewWidget(); | 
|  | Widget* toplevel2 = NewWidget(); | 
|  |  | 
|  | toplevel1->Show(); | 
|  | toplevel2->Show(); | 
|  |  | 
|  | reset(); | 
|  |  | 
|  | toplevel1->Activate(); | 
|  |  | 
|  | RunPendingMessages(); | 
|  | EXPECT_EQ(toplevel1, widget_activated()); | 
|  |  | 
|  | toplevel2->Activate(); | 
|  | RunPendingMessages(); | 
|  | EXPECT_EQ(toplevel1, widget_deactivated()); | 
|  | EXPECT_EQ(toplevel2, widget_activated()); | 
|  | EXPECT_EQ(toplevel2, active()); | 
|  |  | 
|  | toplevel->CloseNow(); | 
|  | } | 
|  |  | 
|  | TEST_F(WidgetObserverTest, DISABLED_VisibilityChange) { | 
|  | Widget* toplevel = CreateTopLevelPlatformWidget(); | 
|  |  | 
|  | Widget* child1 = NewWidget(); | 
|  | Widget* child2 = NewWidget(); | 
|  |  | 
|  | toplevel->Show(); | 
|  | child1->Show(); | 
|  | child2->Show(); | 
|  |  | 
|  | reset(); | 
|  |  | 
|  | child1->Hide(); | 
|  | EXPECT_EQ(child1, widget_hidden()); | 
|  |  | 
|  | child2->Hide(); | 
|  | EXPECT_EQ(child2, widget_hidden()); | 
|  |  | 
|  | child1->Show(); | 
|  | EXPECT_EQ(child1, widget_shown()); | 
|  |  | 
|  | child2->Show(); | 
|  | EXPECT_EQ(child2, widget_shown()); | 
|  |  | 
|  | toplevel->CloseNow(); | 
|  | } | 
|  |  | 
|  | TEST_F(WidgetObserverTest, DestroyBubble) { | 
|  | Widget* anchor = CreateTopLevelPlatformWidget(); | 
|  | anchor->Show(); | 
|  |  | 
|  | BubbleDelegateView* bubble_delegate = | 
|  | new BubbleDelegateView(anchor->client_view(), BubbleBorder::NONE); | 
|  | Widget* bubble_widget(BubbleDelegateView::CreateBubble(bubble_delegate)); | 
|  | bubble_widget->Show(); | 
|  | bubble_widget->CloseNow(); | 
|  |  | 
|  | anchor->Hide(); | 
|  | anchor->CloseNow(); | 
|  | } | 
|  |  | 
|  | TEST_F(WidgetObserverTest, WidgetBoundsChanged) { | 
|  | Widget* child1 = NewWidget(); | 
|  | Widget* child2 = NewWidget(); | 
|  |  | 
|  | child1->OnNativeWidgetMove(); | 
|  | EXPECT_EQ(child1, widget_bounds_changed()); | 
|  |  | 
|  | child2->OnNativeWidgetMove(); | 
|  | EXPECT_EQ(child2, widget_bounds_changed()); | 
|  |  | 
|  | child1->OnNativeWidgetSizeChanged(gfx::Size()); | 
|  | EXPECT_EQ(child1, widget_bounds_changed()); | 
|  |  | 
|  | child2->OnNativeWidgetSizeChanged(gfx::Size()); | 
|  | EXPECT_EQ(child2, widget_bounds_changed()); | 
|  | } | 
|  |  | 
|  | // Tests that SetBounds() and GetWindowBoundsInScreen() is symmetric when the | 
|  | // widget is visible and not maximized or fullscreen. | 
|  | TEST_F(WidgetTest, GetWindowBoundsInScreen) { | 
|  | // Choose test coordinates away from edges and dimensions that are "small" | 
|  | // (but not too small) to ensure the OS doesn't try to adjust them. | 
|  | const gfx::Rect kTestBounds(150, 150, 400, 300); | 
|  | const gfx::Size kTestSize(200, 180); | 
|  |  | 
|  | // First test a toplevel widget. | 
|  | Widget* widget = CreateTopLevelPlatformWidget(); | 
|  | widget->Show(); | 
|  |  | 
|  | EXPECT_NE(kTestSize.ToString(), | 
|  | widget->GetWindowBoundsInScreen().size().ToString()); | 
|  | widget->SetSize(kTestSize); | 
|  | EXPECT_EQ(kTestSize.ToString(), | 
|  | widget->GetWindowBoundsInScreen().size().ToString()); | 
|  |  | 
|  | EXPECT_NE(kTestBounds.ToString(), | 
|  | widget->GetWindowBoundsInScreen().ToString()); | 
|  | widget->SetBounds(kTestBounds); | 
|  | EXPECT_EQ(kTestBounds.ToString(), | 
|  | widget->GetWindowBoundsInScreen().ToString()); | 
|  |  | 
|  | // Changing just the size should not change the origin. | 
|  | widget->SetSize(kTestSize); | 
|  | EXPECT_EQ(kTestBounds.origin().ToString(), | 
|  | widget->GetWindowBoundsInScreen().origin().ToString()); | 
|  |  | 
|  | widget->CloseNow(); | 
|  |  | 
|  | // Same tests with a frameless window. | 
|  | widget = CreateTopLevelFramelessPlatformWidget(); | 
|  | widget->Show(); | 
|  |  | 
|  | EXPECT_NE(kTestSize.ToString(), | 
|  | widget->GetWindowBoundsInScreen().size().ToString()); | 
|  | widget->SetSize(kTestSize); | 
|  | EXPECT_EQ(kTestSize.ToString(), | 
|  | widget->GetWindowBoundsInScreen().size().ToString()); | 
|  |  | 
|  | EXPECT_NE(kTestBounds.ToString(), | 
|  | widget->GetWindowBoundsInScreen().ToString()); | 
|  | widget->SetBounds(kTestBounds); | 
|  | EXPECT_EQ(kTestBounds.ToString(), | 
|  | widget->GetWindowBoundsInScreen().ToString()); | 
|  |  | 
|  | // For a frameless widget, the client bounds should also match. | 
|  | EXPECT_EQ(kTestBounds.ToString(), | 
|  | widget->GetClientAreaBoundsInScreen().ToString()); | 
|  |  | 
|  | // Verify origin is stable for a frameless window as well. | 
|  | widget->SetSize(kTestSize); | 
|  | EXPECT_EQ(kTestBounds.origin().ToString(), | 
|  | widget->GetWindowBoundsInScreen().origin().ToString()); | 
|  |  | 
|  | widget->CloseNow(); | 
|  | } | 
|  |  | 
|  | // Before being enabled on Mac, this was #ifdef(false). | 
|  | // TODO(tapted): Fix this for DesktopNativeWidgets on other platforms. | 
|  | #if defined(OS_MACOSX) | 
|  | // Aura needs shell to maximize/fullscreen window. | 
|  | // NativeWidgetGtk doesn't implement GetRestoredBounds. | 
|  | TEST_F(WidgetTest, GetRestoredBounds) { | 
|  | Widget* toplevel = CreateTopLevelPlatformWidget(); | 
|  | EXPECT_EQ(toplevel->GetWindowBoundsInScreen().ToString(), | 
|  | toplevel->GetRestoredBounds().ToString()); | 
|  | toplevel->Show(); | 
|  | toplevel->Maximize(); | 
|  | RunPendingMessages(); | 
|  | #if defined(OS_MACOSX) | 
|  | // Current expectation on Mac is to do nothing on Maximize. | 
|  | EXPECT_EQ(toplevel->GetWindowBoundsInScreen().ToString(), | 
|  | toplevel->GetRestoredBounds().ToString()); | 
|  | #else | 
|  | EXPECT_NE(toplevel->GetWindowBoundsInScreen().ToString(), | 
|  | toplevel->GetRestoredBounds().ToString()); | 
|  | #endif | 
|  | EXPECT_GT(toplevel->GetRestoredBounds().width(), 0); | 
|  | EXPECT_GT(toplevel->GetRestoredBounds().height(), 0); | 
|  |  | 
|  | toplevel->Restore(); | 
|  | RunPendingMessages(); | 
|  | EXPECT_EQ(toplevel->GetWindowBoundsInScreen().ToString(), | 
|  | toplevel->GetRestoredBounds().ToString()); | 
|  |  | 
|  | toplevel->SetFullscreen(true); | 
|  | RunPendingMessages(); | 
|  | EXPECT_NE(toplevel->GetWindowBoundsInScreen().ToString(), | 
|  | toplevel->GetRestoredBounds().ToString()); | 
|  | EXPECT_GT(toplevel->GetRestoredBounds().width(), 0); | 
|  | EXPECT_GT(toplevel->GetRestoredBounds().height(), 0); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | // Test that window state is not changed after getting out of full screen. | 
|  | TEST_F(WidgetTest, ExitFullscreenRestoreState) { | 
|  | Widget* toplevel = CreateTopLevelPlatformWidget(); | 
|  |  | 
|  | toplevel->Show(); | 
|  | RunPendingMessages(); | 
|  |  | 
|  | // This should be a normal state window. | 
|  | EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel)); | 
|  |  | 
|  | toplevel->SetFullscreen(true); | 
|  | EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel)); | 
|  | toplevel->SetFullscreen(false); | 
|  | EXPECT_NE(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel)); | 
|  |  | 
|  | // And it should still be in normal state after getting out of full screen. | 
|  | EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel)); | 
|  |  | 
|  | // On Mac, a "maximized" state is indistinguishable from a window that just | 
|  | // fills the screen, so nothing to check there. | 
|  | #if !defined(OS_MACOSX) | 
|  | // Now, make it maximized. | 
|  | toplevel->Maximize(); | 
|  | EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel)); | 
|  |  | 
|  | toplevel->SetFullscreen(true); | 
|  | EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel)); | 
|  | toplevel->SetFullscreen(false); | 
|  | EXPECT_NE(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel)); | 
|  |  | 
|  | // And it stays maximized after getting out of full screen. | 
|  | EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel)); | 
|  | #endif | 
|  |  | 
|  | // Clean up. | 
|  | toplevel->Close(); | 
|  | RunPendingMessages(); | 
|  | } | 
|  |  | 
|  | // The key-event propagation from Widget happens differently on aura and | 
|  | // non-aura systems because of the difference in IME. So this test works only on | 
|  | // aura. | 
|  | TEST_F(WidgetTest, KeyboardInputEvent) { | 
|  | Widget* toplevel = CreateTopLevelPlatformWidget(); | 
|  | View* container = toplevel->client_view(); | 
|  |  | 
|  | Textfield* textfield = new Textfield(); | 
|  | textfield->SetText(base::ASCIIToUTF16("some text")); | 
|  | container->AddChildView(textfield); | 
|  | toplevel->Show(); | 
|  | textfield->RequestFocus(); | 
|  |  | 
|  | // The press gets handled. The release doesn't have an effect. | 
|  | ui::KeyEvent backspace_p(ui::ET_KEY_PRESSED, ui::VKEY_DELETE, ui::EF_NONE); | 
|  | toplevel->OnKeyEvent(&backspace_p); | 
|  | EXPECT_TRUE(backspace_p.stopped_propagation()); | 
|  | ui::KeyEvent backspace_r(ui::ET_KEY_RELEASED, ui::VKEY_DELETE, ui::EF_NONE); | 
|  | toplevel->OnKeyEvent(&backspace_r); | 
|  | EXPECT_FALSE(backspace_r.handled()); | 
|  |  | 
|  | toplevel->Close(); | 
|  | } | 
|  |  | 
|  | // Verifies bubbles result in a focus lost when shown. | 
|  | // TODO(msw): this tests relies on focus, it needs to be in | 
|  | // interactive_ui_tests. | 
|  | TEST_F(WidgetTest, DISABLED_FocusChangesOnBubble) { | 
|  | // Create a widget, show and activate it and focus the contents view. | 
|  | View* contents_view = new View; | 
|  | contents_view->SetFocusable(true); | 
|  | Widget widget; | 
|  | Widget::InitParams init_params = | 
|  | CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); | 
|  | init_params.bounds = gfx::Rect(0, 0, 200, 200); | 
|  | init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 
|  | #if !defined(OS_CHROMEOS) | 
|  | init_params.native_widget = new PlatformDesktopNativeWidget(&widget); | 
|  | #endif | 
|  | widget.Init(init_params); | 
|  | widget.SetContentsView(contents_view); | 
|  | widget.Show(); | 
|  | widget.Activate(); | 
|  | contents_view->RequestFocus(); | 
|  | EXPECT_TRUE(contents_view->HasFocus()); | 
|  |  | 
|  | // Show a bubble. | 
|  | BubbleDelegateView* bubble_delegate_view = | 
|  | new BubbleDelegateView(contents_view, BubbleBorder::TOP_LEFT); | 
|  | bubble_delegate_view->SetFocusable(true); | 
|  | BubbleDelegateView::CreateBubble(bubble_delegate_view)->Show(); | 
|  | bubble_delegate_view->RequestFocus(); | 
|  |  | 
|  | // |contents_view_| should no longer have focus. | 
|  | EXPECT_FALSE(contents_view->HasFocus()); | 
|  | EXPECT_TRUE(bubble_delegate_view->HasFocus()); | 
|  |  | 
|  | bubble_delegate_view->GetWidget()->CloseNow(); | 
|  |  | 
|  | // Closing the bubble should result in focus going back to the contents view. | 
|  | EXPECT_TRUE(contents_view->HasFocus()); | 
|  | } | 
|  |  | 
|  | class TestBubbleDelegateView : public BubbleDelegateView { | 
|  | public: | 
|  | TestBubbleDelegateView(View* anchor) | 
|  | : BubbleDelegateView(anchor, BubbleBorder::NONE), | 
|  | reset_controls_called_(false) {} | 
|  | virtual ~TestBubbleDelegateView() {} | 
|  |  | 
|  | virtual bool ShouldShowCloseButton() const override { | 
|  | reset_controls_called_ = true; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | mutable bool reset_controls_called_; | 
|  | }; | 
|  |  | 
|  | TEST_F(WidgetTest, BubbleControlsResetOnInit) { | 
|  | Widget* anchor = CreateTopLevelPlatformWidget(); | 
|  | anchor->Show(); | 
|  |  | 
|  | TestBubbleDelegateView* bubble_delegate = | 
|  | new TestBubbleDelegateView(anchor->client_view()); | 
|  | Widget* bubble_widget(BubbleDelegateView::CreateBubble(bubble_delegate)); | 
|  | EXPECT_TRUE(bubble_delegate->reset_controls_called_); | 
|  | bubble_widget->Show(); | 
|  | bubble_widget->CloseNow(); | 
|  |  | 
|  | anchor->Hide(); | 
|  | anchor->CloseNow(); | 
|  | } | 
|  |  | 
|  | // Desktop native widget Aura tests are for non Chrome OS platforms. | 
|  | #if !defined(OS_CHROMEOS) | 
|  | // Test to ensure that after minimize, view width is set to zero. | 
|  | TEST_F(WidgetTest, TestViewWidthAfterMinimizingWidget) { | 
|  | // Create a widget. | 
|  | Widget widget; | 
|  | Widget::InitParams init_params = | 
|  | CreateParams(Widget::InitParams::TYPE_WINDOW); | 
|  | init_params.show_state = ui::SHOW_STATE_NORMAL; | 
|  | gfx::Rect initial_bounds(0, 0, 300, 400); | 
|  | init_params.bounds = initial_bounds; | 
|  | init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 
|  | init_params.native_widget = new PlatformDesktopNativeWidget(&widget); | 
|  | widget.Init(init_params); | 
|  | NonClientView* non_client_view = widget.non_client_view(); | 
|  | NonClientFrameView* frame_view = new MinimumSizeFrameView(&widget); | 
|  | non_client_view->SetFrameView(frame_view); | 
|  | widget.Show(); | 
|  | widget.Minimize(); | 
|  | EXPECT_EQ(0, frame_view->width()); | 
|  | } | 
|  |  | 
|  | // This class validates whether paints are received for a visible Widget. | 
|  | // To achieve this it overrides the Show and Close methods on the Widget class | 
|  | // and sets state whether subsequent paints are expected. | 
|  | class DesktopAuraTestValidPaintWidget : public views::Widget { | 
|  | public: | 
|  | DesktopAuraTestValidPaintWidget() | 
|  | : received_paint_(false), | 
|  | expect_paint_(true), | 
|  | received_paint_while_hidden_(false) {} | 
|  |  | 
|  | virtual ~DesktopAuraTestValidPaintWidget() {} | 
|  |  | 
|  | void InitForTest(Widget::InitParams create_params); | 
|  |  | 
|  | virtual void Show() override { | 
|  | expect_paint_ = true; | 
|  | views::Widget::Show(); | 
|  | } | 
|  |  | 
|  | virtual void Close() override { | 
|  | expect_paint_ = false; | 
|  | views::Widget::Close(); | 
|  | } | 
|  |  | 
|  | void Hide() { | 
|  | expect_paint_ = false; | 
|  | views::Widget::Hide(); | 
|  | } | 
|  |  | 
|  | virtual void OnNativeWidgetPaint(gfx::Canvas* canvas) override { | 
|  | received_paint_ = true; | 
|  | EXPECT_TRUE(expect_paint_); | 
|  | if (!expect_paint_) | 
|  | received_paint_while_hidden_ = true; | 
|  | views::Widget::OnNativeWidgetPaint(canvas); | 
|  | } | 
|  |  | 
|  | bool ReadReceivedPaintAndReset() { | 
|  | bool result = received_paint_; | 
|  | received_paint_ = false; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | bool received_paint_while_hidden() const { | 
|  | return received_paint_while_hidden_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | bool received_paint_; | 
|  | bool expect_paint_; | 
|  | bool received_paint_while_hidden_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(DesktopAuraTestValidPaintWidget); | 
|  | }; | 
|  |  | 
|  | void DesktopAuraTestValidPaintWidget::InitForTest(InitParams init_params) { | 
|  | init_params.bounds = gfx::Rect(0, 0, 200, 200); | 
|  | init_params.ownership = InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 
|  | init_params.native_widget = new PlatformDesktopNativeWidget(this); | 
|  | Init(init_params); | 
|  |  | 
|  | View* contents_view = new View; | 
|  | contents_view->SetFocusable(true); | 
|  | SetContentsView(contents_view); | 
|  |  | 
|  | Show(); | 
|  | Activate(); | 
|  | } | 
|  |  | 
|  | TEST_F(WidgetTest, DesktopNativeWidgetNoPaintAfterCloseTest) { | 
|  | DesktopAuraTestValidPaintWidget widget; | 
|  | widget.InitForTest(CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS)); | 
|  | RunPendingMessages(); | 
|  | EXPECT_TRUE(widget.ReadReceivedPaintAndReset()); | 
|  | widget.SchedulePaintInRect(widget.GetRestoredBounds()); | 
|  | widget.Close(); | 
|  | RunPendingMessages(); | 
|  | EXPECT_FALSE(widget.ReadReceivedPaintAndReset()); | 
|  | EXPECT_FALSE(widget.received_paint_while_hidden()); | 
|  | } | 
|  |  | 
|  | TEST_F(WidgetTest, DesktopNativeWidgetNoPaintAfterHideTest) { | 
|  | DesktopAuraTestValidPaintWidget widget; | 
|  | widget.InitForTest(CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS)); | 
|  | RunPendingMessages(); | 
|  | EXPECT_TRUE(widget.ReadReceivedPaintAndReset()); | 
|  | widget.SchedulePaintInRect(widget.GetRestoredBounds()); | 
|  | widget.Hide(); | 
|  | RunPendingMessages(); | 
|  | EXPECT_FALSE(widget.ReadReceivedPaintAndReset()); | 
|  | EXPECT_FALSE(widget.received_paint_while_hidden()); | 
|  | widget.Close(); | 
|  | } | 
|  |  | 
|  | // Test to ensure that the aura Window's visiblity state is set to visible if | 
|  | // the underlying widget is hidden and then shown. | 
|  | TEST_F(WidgetTest, TestWindowVisibilityAfterHide) { | 
|  | // Create a widget. | 
|  | Widget widget; | 
|  | Widget::InitParams init_params = | 
|  | CreateParams(Widget::InitParams::TYPE_WINDOW); | 
|  | init_params.show_state = ui::SHOW_STATE_NORMAL; | 
|  | gfx::Rect initial_bounds(0, 0, 300, 400); | 
|  | init_params.bounds = initial_bounds; | 
|  | init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 
|  | init_params.native_widget = new PlatformDesktopNativeWidget(&widget); | 
|  | widget.Init(init_params); | 
|  | NonClientView* non_client_view = widget.non_client_view(); | 
|  | NonClientFrameView* frame_view = new MinimumSizeFrameView(&widget); | 
|  | non_client_view->SetFrameView(frame_view); | 
|  |  | 
|  | widget.Show(); | 
|  | EXPECT_TRUE(IsNativeWindowVisible(widget.GetNativeWindow())); | 
|  | widget.Hide(); | 
|  | EXPECT_FALSE(IsNativeWindowVisible(widget.GetNativeWindow())); | 
|  | widget.Show(); | 
|  | EXPECT_TRUE(IsNativeWindowVisible(widget.GetNativeWindow())); | 
|  | } | 
|  |  | 
|  | // The following code verifies we can correctly destroy a Widget from a mouse | 
|  | // enter/exit. We could test move/drag/enter/exit but in general we don't run | 
|  | // nested message loops from such events, nor has the code ever really dealt | 
|  | // with this situation. | 
|  |  | 
|  | // Generates two moves (first generates enter, second real move), a press, drag | 
|  | // and release stopping at |last_event_type|. | 
|  | void GenerateMouseEvents(Widget* widget, ui::EventType last_event_type) { | 
|  | const gfx::Rect screen_bounds(widget->GetWindowBoundsInScreen()); | 
|  | ui::MouseEvent move_event(ui::ET_MOUSE_MOVED, screen_bounds.CenterPoint(), | 
|  | screen_bounds.CenterPoint(), 0, 0); | 
|  | ui::EventProcessor* dispatcher = WidgetTest::GetEventProcessor(widget); | 
|  | ui::EventDispatchDetails details = dispatcher->OnEventFromSource(&move_event); | 
|  | if (last_event_type == ui::ET_MOUSE_ENTERED || details.dispatcher_destroyed) | 
|  | return; | 
|  | details = dispatcher->OnEventFromSource(&move_event); | 
|  | if (last_event_type == ui::ET_MOUSE_MOVED || details.dispatcher_destroyed) | 
|  | return; | 
|  |  | 
|  | ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, screen_bounds.CenterPoint(), | 
|  | screen_bounds.CenterPoint(), 0, 0); | 
|  | details = dispatcher->OnEventFromSource(&press_event); | 
|  | if (last_event_type == ui::ET_MOUSE_PRESSED || details.dispatcher_destroyed) | 
|  | return; | 
|  |  | 
|  | gfx::Point end_point(screen_bounds.CenterPoint()); | 
|  | end_point.Offset(1, 1); | 
|  | ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, end_point, end_point, 0, 0); | 
|  | details = dispatcher->OnEventFromSource(&drag_event); | 
|  | if (last_event_type == ui::ET_MOUSE_DRAGGED || details.dispatcher_destroyed) | 
|  | return; | 
|  |  | 
|  | ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, end_point, end_point, 0, | 
|  | 0); | 
|  | details = dispatcher->OnEventFromSource(&release_event); | 
|  | if (details.dispatcher_destroyed) | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Creates a widget and invokes GenerateMouseEvents() with |last_event_type|. | 
|  | void RunCloseWidgetDuringDispatchTest(WidgetTest* test, | 
|  | ui::EventType last_event_type) { | 
|  | // |widget| is deleted by CloseWidgetView. | 
|  | Widget* widget = new Widget; | 
|  | Widget::InitParams params = | 
|  | test->CreateParams(Widget::InitParams::TYPE_POPUP); | 
|  | params.native_widget = new PlatformDesktopNativeWidget(widget); | 
|  | params.bounds = gfx::Rect(0, 0, 50, 100); | 
|  | widget->Init(params); | 
|  | widget->SetContentsView(new CloseWidgetView(last_event_type)); | 
|  | widget->Show(); | 
|  | GenerateMouseEvents(widget, last_event_type); | 
|  | } | 
|  |  | 
|  | // Verifies deleting the widget from a mouse pressed event doesn't crash. | 
|  | TEST_F(WidgetTest, CloseWidgetDuringMousePress) { | 
|  | RunCloseWidgetDuringDispatchTest(this, ui::ET_MOUSE_PRESSED); | 
|  | } | 
|  |  | 
|  | // Verifies deleting the widget from a mouse released event doesn't crash. | 
|  | TEST_F(WidgetTest, CloseWidgetDuringMouseReleased) { | 
|  | RunCloseWidgetDuringDispatchTest(this, ui::ET_MOUSE_RELEASED); | 
|  | } | 
|  |  | 
|  | #endif  // !defined(OS_CHROMEOS) | 
|  |  | 
|  | // Tests that wheel events generated from scroll events are targetted to the | 
|  | // views under the cursor when the focused view does not processed them. | 
|  | TEST_F(WidgetTest, WheelEventsFromScrollEventTarget) { | 
|  | EventCountView* cursor_view = new EventCountView; | 
|  | cursor_view->SetBounds(60, 0, 50, 40); | 
|  |  | 
|  | Widget* widget = CreateTopLevelPlatformWidget(); | 
|  | widget->GetRootView()->AddChildView(cursor_view); | 
|  |  | 
|  | // Generate a scroll event on the cursor view. | 
|  | ui::ScrollEvent scroll(ui::ET_SCROLL, | 
|  | gfx::Point(65, 5), | 
|  | ui::EventTimeForNow(), | 
|  | 0, | 
|  | 0, 20, | 
|  | 0, 20, | 
|  | 2); | 
|  | widget->OnScrollEvent(&scroll); | 
|  |  | 
|  | EXPECT_EQ(1, cursor_view->GetEventCount(ui::ET_SCROLL)); | 
|  | EXPECT_EQ(1, cursor_view->GetEventCount(ui::ET_MOUSEWHEEL)); | 
|  |  | 
|  | cursor_view->ResetCounts(); | 
|  |  | 
|  | ui::ScrollEvent scroll2(ui::ET_SCROLL, | 
|  | gfx::Point(5, 5), | 
|  | ui::EventTimeForNow(), | 
|  | 0, | 
|  | 0, 20, | 
|  | 0, 20, | 
|  | 2); | 
|  | widget->OnScrollEvent(&scroll2); | 
|  |  | 
|  | EXPECT_EQ(0, cursor_view->GetEventCount(ui::ET_SCROLL)); | 
|  | EXPECT_EQ(0, cursor_view->GetEventCount(ui::ET_MOUSEWHEEL)); | 
|  |  | 
|  | widget->CloseNow(); | 
|  | } | 
|  |  | 
|  | // Tests that if a scroll-begin gesture is not handled, then subsequent scroll | 
|  | // events are not dispatched to any view. | 
|  | TEST_F(WidgetTest, GestureScrollEventDispatching) { | 
|  | EventCountView* noscroll_view = new EventCountView; | 
|  | EventCountView* scroll_view = new ScrollableEventCountView; | 
|  |  | 
|  | noscroll_view->SetBounds(0, 0, 50, 40); | 
|  | scroll_view->SetBounds(60, 0, 40, 40); | 
|  |  | 
|  | Widget* widget = CreateTopLevelPlatformWidget(); | 
|  | widget->GetRootView()->AddChildView(noscroll_view); | 
|  | widget->GetRootView()->AddChildView(scroll_view); | 
|  |  | 
|  | { | 
|  | ui::GestureEvent begin( | 
|  | 5, | 
|  | 5, | 
|  | 0, | 
|  | base::TimeDelta(), | 
|  | ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN)); | 
|  | widget->OnGestureEvent(&begin); | 
|  | ui::GestureEvent update( | 
|  | 25, | 
|  | 15, | 
|  | 0, | 
|  | base::TimeDelta(), | 
|  | ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 20, 10)); | 
|  | widget->OnGestureEvent(&update); | 
|  | ui::GestureEvent end(25, | 
|  | 15, | 
|  | 0, | 
|  | base::TimeDelta(), | 
|  | ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END)); | 
|  | widget->OnGestureEvent(&end); | 
|  |  | 
|  | EXPECT_EQ(1, noscroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_BEGIN)); | 
|  | EXPECT_EQ(0, noscroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE)); | 
|  | EXPECT_EQ(0, noscroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_END)); | 
|  | } | 
|  |  | 
|  | { | 
|  | ui::GestureEvent begin( | 
|  | 65, | 
|  | 5, | 
|  | 0, | 
|  | base::TimeDelta(), | 
|  | ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN)); | 
|  | widget->OnGestureEvent(&begin); | 
|  | ui::GestureEvent update( | 
|  | 85, | 
|  | 15, | 
|  | 0, | 
|  | base::TimeDelta(), | 
|  | ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 20, 10)); | 
|  | widget->OnGestureEvent(&update); | 
|  | ui::GestureEvent end(85, | 
|  | 15, | 
|  | 0, | 
|  | base::TimeDelta(), | 
|  | ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END)); | 
|  | widget->OnGestureEvent(&end); | 
|  |  | 
|  | EXPECT_EQ(1, scroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_BEGIN)); | 
|  | EXPECT_EQ(1, scroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE)); | 
|  | EXPECT_EQ(1, scroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_END)); | 
|  | } | 
|  |  | 
|  | widget->CloseNow(); | 
|  | } | 
|  |  | 
|  | // Tests that event-handlers installed on the RootView get triggered correctly. | 
|  | // TODO(tdanderson): Clean up this test as part of crbug.com/355680. | 
|  | TEST_F(WidgetTest, EventHandlersOnRootView) { | 
|  | Widget* widget = CreateTopLevelNativeWidget(); | 
|  | View* root_view = widget->GetRootView(); | 
|  |  | 
|  | scoped_ptr<EventCountView> view(new EventCountView()); | 
|  | view->set_owned_by_client(); | 
|  | view->SetBounds(0, 0, 20, 20); | 
|  | root_view->AddChildView(view.get()); | 
|  |  | 
|  | EventCountHandler h1; | 
|  | root_view->AddPreTargetHandler(&h1); | 
|  |  | 
|  | EventCountHandler h2; | 
|  | root_view->AddPostTargetHandler(&h2); | 
|  |  | 
|  | widget->SetBounds(gfx::Rect(0, 0, 100, 100)); | 
|  | widget->Show(); | 
|  |  | 
|  | // Dispatch a ui::ET_SCROLL event. The event remains unhandled and should | 
|  | // bubble up the views hierarchy to be re-dispatched on the root view. | 
|  | ui::ScrollEvent scroll(ui::ET_SCROLL, | 
|  | gfx::Point(5, 5), | 
|  | ui::EventTimeForNow(), | 
|  | 0, | 
|  | 0, 20, | 
|  | 0, 20, | 
|  | 2); | 
|  | widget->OnScrollEvent(&scroll); | 
|  | EXPECT_EQ(2, h1.GetEventCount(ui::ET_SCROLL)); | 
|  | EXPECT_EQ(1, view->GetEventCount(ui::ET_SCROLL)); | 
|  | EXPECT_EQ(2, h2.GetEventCount(ui::ET_SCROLL)); | 
|  |  | 
|  | // Unhandled scroll events are turned into wheel events and re-dispatched. | 
|  | EXPECT_EQ(1, h1.GetEventCount(ui::ET_MOUSEWHEEL)); | 
|  | EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSEWHEEL)); | 
|  | EXPECT_EQ(1, h2.GetEventCount(ui::ET_MOUSEWHEEL)); | 
|  |  | 
|  | h1.ResetCounts(); | 
|  | view->ResetCounts(); | 
|  | h2.ResetCounts(); | 
|  |  | 
|  | // Dispatch a ui::ET_SCROLL_FLING_START event. The event remains unhandled and | 
|  | // should bubble up the views hierarchy to be re-dispatched on the root view. | 
|  | ui::ScrollEvent fling(ui::ET_SCROLL_FLING_START, | 
|  | gfx::Point(5, 5), | 
|  | ui::EventTimeForNow(), | 
|  | 0, | 
|  | 0, 20, | 
|  | 0, 20, | 
|  | 2); | 
|  | widget->OnScrollEvent(&fling); | 
|  | EXPECT_EQ(2, h1.GetEventCount(ui::ET_SCROLL_FLING_START)); | 
|  | EXPECT_EQ(1, view->GetEventCount(ui::ET_SCROLL_FLING_START)); | 
|  | EXPECT_EQ(2, h2.GetEventCount(ui::ET_SCROLL_FLING_START)); | 
|  |  | 
|  | // Unhandled scroll events which are not of type ui::ET_SCROLL should not | 
|  | // be turned into wheel events and re-dispatched. | 
|  | EXPECT_EQ(0, h1.GetEventCount(ui::ET_MOUSEWHEEL)); | 
|  | EXPECT_EQ(0, view->GetEventCount(ui::ET_MOUSEWHEEL)); | 
|  | EXPECT_EQ(0, h2.GetEventCount(ui::ET_MOUSEWHEEL)); | 
|  |  | 
|  | h1.ResetCounts(); | 
|  | view->ResetCounts(); | 
|  | h2.ResetCounts(); | 
|  |  | 
|  | // Change the handle mode of |view| so that events are marked as handled at | 
|  | // the target phase. | 
|  | view->set_handle_mode(EventCountView::CONSUME_EVENTS); | 
|  |  | 
|  | // Dispatch a ui::ET_GESTURE_TAP_DOWN and a ui::ET_GESTURE_TAP_CANCEL event. | 
|  | // The events are handled at the target phase and should not reach the | 
|  | // post-target handler. | 
|  | ui::GestureEvent tap_down(5, | 
|  | 5, | 
|  | 0, | 
|  | ui::EventTimeForNow(), | 
|  | ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN)); | 
|  | widget->OnGestureEvent(&tap_down); | 
|  | EXPECT_EQ(1, h1.GetEventCount(ui::ET_GESTURE_TAP_DOWN)); | 
|  | EXPECT_EQ(1, view->GetEventCount(ui::ET_GESTURE_TAP_DOWN)); | 
|  | EXPECT_EQ(0, h2.GetEventCount(ui::ET_GESTURE_TAP_DOWN)); | 
|  |  | 
|  | ui::GestureEvent tap_cancel( | 
|  | 5, | 
|  | 5, | 
|  | 0, | 
|  | ui::EventTimeForNow(), | 
|  | ui::GestureEventDetails(ui::ET_GESTURE_TAP_CANCEL)); | 
|  | widget->OnGestureEvent(&tap_cancel); | 
|  | EXPECT_EQ(1, h1.GetEventCount(ui::ET_GESTURE_TAP_CANCEL)); | 
|  | EXPECT_EQ(1, view->GetEventCount(ui::ET_GESTURE_TAP_CANCEL)); | 
|  | EXPECT_EQ(0, h2.GetEventCount(ui::ET_GESTURE_TAP_CANCEL)); | 
|  |  | 
|  | h1.ResetCounts(); | 
|  | view->ResetCounts(); | 
|  | h2.ResetCounts(); | 
|  |  | 
|  | // Dispatch a ui::ET_SCROLL event. The event is handled at the target phase | 
|  | // and should not reach the post-target handler. | 
|  | ui::ScrollEvent consumed_scroll(ui::ET_SCROLL, | 
|  | gfx::Point(5, 5), | 
|  | ui::EventTimeForNow(), | 
|  | 0, | 
|  | 0, 20, | 
|  | 0, 20, | 
|  | 2); | 
|  | widget->OnScrollEvent(&consumed_scroll); | 
|  | EXPECT_EQ(1, h1.GetEventCount(ui::ET_SCROLL)); | 
|  | EXPECT_EQ(1, view->GetEventCount(ui::ET_SCROLL)); | 
|  | EXPECT_EQ(0, h2.GetEventCount(ui::ET_SCROLL)); | 
|  |  | 
|  | // Handled scroll events are not turned into wheel events and re-dispatched. | 
|  | EXPECT_EQ(0, h1.GetEventCount(ui::ET_MOUSEWHEEL)); | 
|  | EXPECT_EQ(0, view->GetEventCount(ui::ET_MOUSEWHEEL)); | 
|  | EXPECT_EQ(0, h2.GetEventCount(ui::ET_MOUSEWHEEL)); | 
|  |  | 
|  | widget->CloseNow(); | 
|  | } | 
|  |  | 
|  | TEST_F(WidgetTest, SynthesizeMouseMoveEvent) { | 
|  | Widget* widget = CreateTopLevelNativeWidget(); | 
|  | View* root_view = widget->GetRootView(); | 
|  |  | 
|  | EventCountView* v1 = new EventCountView(); | 
|  | v1->SetBounds(0, 0, 10, 10); | 
|  | root_view->AddChildView(v1); | 
|  | EventCountView* v2 = new EventCountView(); | 
|  | v2->SetBounds(0, 10, 10, 10); | 
|  | root_view->AddChildView(v2); | 
|  |  | 
|  | gfx::Point cursor_location(5, 5); | 
|  | ui::MouseEvent move(ui::ET_MOUSE_MOVED, cursor_location, cursor_location, | 
|  | ui::EF_NONE, ui::EF_NONE); | 
|  | widget->OnMouseEvent(&move); | 
|  |  | 
|  | EXPECT_EQ(1, v1->GetEventCount(ui::ET_MOUSE_ENTERED)); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); | 
|  |  | 
|  | delete v1; | 
|  | v2->SetBounds(0, 0, 10, 10); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); | 
|  |  | 
|  | widget->SynthesizeMouseMoveEvent(); | 
|  | EXPECT_EQ(1, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // ui::EventHandler which handles all mouse press events. | 
|  | class MousePressEventConsumer : public ui::EventHandler { | 
|  | public: | 
|  | explicit MousePressEventConsumer() { | 
|  | } | 
|  |  | 
|  | virtual ~MousePressEventConsumer() { | 
|  | } | 
|  |  | 
|  | private: | 
|  | // ui::EventHandler: | 
|  | virtual void OnMouseEvent(ui::MouseEvent* event) override { | 
|  | if (event->type() == ui::ET_MOUSE_PRESSED) | 
|  | event->SetHandled(); | 
|  | } | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(MousePressEventConsumer); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Test that mouse presses and mouse releases are dispatched normally when a | 
|  | // touch is down. | 
|  | TEST_F(WidgetTest, MouseEventDispatchWhileTouchIsDown) { | 
|  | Widget* widget = CreateTopLevelNativeWidget(); | 
|  | widget->Show(); | 
|  | widget->SetSize(gfx::Size(300, 300)); | 
|  |  | 
|  | EventCountView* event_count_view = new EventCountView(); | 
|  | event_count_view->SetBounds(0, 0, 300, 300); | 
|  | widget->GetRootView()->AddChildView(event_count_view); | 
|  |  | 
|  | MousePressEventConsumer consumer; | 
|  | event_count_view->AddPostTargetHandler(&consumer); | 
|  |  | 
|  | ui::test::EventGenerator generator(GetContext(), widget->GetNativeWindow()); | 
|  | generator.PressTouch(); | 
|  | generator.ClickLeftButton(); | 
|  |  | 
|  | EXPECT_EQ(1, event_count_view->GetEventCount(ui::ET_MOUSE_PRESSED)); | 
|  | EXPECT_EQ(1, event_count_view->GetEventCount(ui::ET_MOUSE_RELEASED)); | 
|  |  | 
|  | widget->CloseNow(); | 
|  | } | 
|  |  | 
|  | // Used by SingleWindowClosing to count number of times WindowClosing() has | 
|  | // been invoked. | 
|  | class ClosingDelegate : public WidgetDelegate { | 
|  | public: | 
|  | ClosingDelegate() : count_(0), widget_(NULL) {} | 
|  |  | 
|  | int count() const { return count_; } | 
|  |  | 
|  | void set_widget(views::Widget* widget) { widget_ = widget; } | 
|  |  | 
|  | // WidgetDelegate overrides: | 
|  | virtual Widget* GetWidget() override { return widget_; } | 
|  | virtual const Widget* GetWidget() const override { return widget_; } | 
|  | virtual void WindowClosing() override { | 
|  | count_++; | 
|  | } | 
|  |  | 
|  | private: | 
|  | int count_; | 
|  | views::Widget* widget_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(ClosingDelegate); | 
|  | }; | 
|  |  | 
|  | // Verifies WindowClosing() is invoked correctly on the delegate when a Widget | 
|  | // is closed. | 
|  | TEST_F(WidgetTest, SingleWindowClosing) { | 
|  | scoped_ptr<ClosingDelegate> delegate(new ClosingDelegate()); | 
|  | Widget* widget = new Widget();  // Destroyed by CloseNow() below. | 
|  | Widget::InitParams init_params = | 
|  | CreateParams(Widget::InitParams::TYPE_WINDOW); | 
|  | init_params.bounds = gfx::Rect(0, 0, 200, 200); | 
|  | init_params.delegate = delegate.get(); | 
|  | #if !defined(OS_CHROMEOS) | 
|  | init_params.native_widget = new PlatformDesktopNativeWidget(widget); | 
|  | #endif | 
|  | widget->Init(init_params); | 
|  | EXPECT_EQ(0, delegate->count()); | 
|  | widget->CloseNow(); | 
|  | EXPECT_EQ(1, delegate->count()); | 
|  | } | 
|  |  | 
|  | class WidgetWindowTitleTest : public WidgetTest { | 
|  | protected: | 
|  | void RunTest(bool desktop_native_widget) { | 
|  | Widget* widget = new Widget();  // Destroyed by CloseNow() below. | 
|  | Widget::InitParams init_params = | 
|  | CreateParams(Widget::InitParams::TYPE_WINDOW); | 
|  | widget->Init(init_params); | 
|  |  | 
|  | #if !defined(OS_CHROMEOS) | 
|  | if (desktop_native_widget) | 
|  | init_params.native_widget = new PlatformDesktopNativeWidget(widget); | 
|  | #else | 
|  | DCHECK(!desktop_native_widget) | 
|  | << "DesktopNativeWidget does not exist on non-Aura or on ChromeOS."; | 
|  | #endif | 
|  |  | 
|  | internal::NativeWidgetPrivate* native_widget = | 
|  | widget->native_widget_private(); | 
|  |  | 
|  | base::string16 empty; | 
|  | base::string16 s1(base::UTF8ToUTF16("Title1")); | 
|  | base::string16 s2(base::UTF8ToUTF16("Title2")); | 
|  | base::string16 s3(base::UTF8ToUTF16("TitleLong")); | 
|  |  | 
|  | // The widget starts with no title, setting empty should not change | 
|  | // anything. | 
|  | EXPECT_FALSE(native_widget->SetWindowTitle(empty)); | 
|  | // Setting the title to something non-empty should cause a change. | 
|  | EXPECT_TRUE(native_widget->SetWindowTitle(s1)); | 
|  | // Setting the title to something else with the same length should cause a | 
|  | // change. | 
|  | EXPECT_TRUE(native_widget->SetWindowTitle(s2)); | 
|  | // Setting the title to something else with a different length should cause | 
|  | // a change. | 
|  | EXPECT_TRUE(native_widget->SetWindowTitle(s3)); | 
|  | // Setting the title to the same thing twice should not cause a change. | 
|  | EXPECT_FALSE(native_widget->SetWindowTitle(s3)); | 
|  |  | 
|  | widget->CloseNow(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | TEST_F(WidgetWindowTitleTest, SetWindowTitleChanged_NativeWidget) { | 
|  | // Use the default NativeWidget. | 
|  | bool desktop_native_widget = false; | 
|  | RunTest(desktop_native_widget); | 
|  | } | 
|  |  | 
|  | // DesktopNativeWidget does not exist on non-Aura or on ChromeOS. | 
|  | #if !defined(OS_CHROMEOS) | 
|  | TEST_F(WidgetWindowTitleTest, SetWindowTitleChanged_DesktopNativeWidget) { | 
|  | // Override to use a DesktopNativeWidget. | 
|  | bool desktop_native_widget = true; | 
|  | RunTest(desktop_native_widget); | 
|  | } | 
|  | #endif  // !OS_CHROMEOS | 
|  |  | 
|  | TEST_F(WidgetTest, WidgetDeleted_InOnMousePressed) { | 
|  | Widget* widget = new Widget; | 
|  | Widget::InitParams params = | 
|  | CreateParams(views::Widget::InitParams::TYPE_POPUP); | 
|  | widget->Init(params); | 
|  |  | 
|  | widget->SetContentsView(new CloseWidgetView(ui::ET_MOUSE_PRESSED)); | 
|  |  | 
|  | widget->SetSize(gfx::Size(100, 100)); | 
|  | widget->Show(); | 
|  |  | 
|  | ui::test::EventGenerator generator(GetContext(), widget->GetNativeWindow()); | 
|  |  | 
|  | WidgetDeletionObserver deletion_observer(widget); | 
|  | generator.ClickLeftButton(); | 
|  | EXPECT_FALSE(deletion_observer.IsWidgetAlive()); | 
|  |  | 
|  | // Yay we did not crash! | 
|  | } | 
|  |  | 
|  | TEST_F(WidgetTest, WidgetDeleted_InDispatchGestureEvent) { | 
|  | Widget* widget = new Widget; | 
|  | Widget::InitParams params = | 
|  | CreateParams(views::Widget::InitParams::TYPE_POPUP); | 
|  | widget->Init(params); | 
|  |  | 
|  | widget->SetContentsView(new CloseWidgetView(ui::ET_GESTURE_TAP_DOWN)); | 
|  |  | 
|  | widget->SetSize(gfx::Size(100, 100)); | 
|  | widget->Show(); | 
|  |  | 
|  | ui::test::EventGenerator generator(GetContext(), widget->GetNativeWindow()); | 
|  |  | 
|  | WidgetDeletionObserver deletion_observer(widget); | 
|  | generator.GestureTapAt(widget->GetWindowBoundsInScreen().CenterPoint()); | 
|  | EXPECT_FALSE(deletion_observer.IsWidgetAlive()); | 
|  |  | 
|  | // Yay we did not crash! | 
|  | } | 
|  |  | 
|  | // See description of RunGetNativeThemeFromDestructor() for details. | 
|  | class GetNativeThemeFromDestructorView : public WidgetDelegateView { | 
|  | public: | 
|  | GetNativeThemeFromDestructorView() {} | 
|  | virtual ~GetNativeThemeFromDestructorView() { | 
|  | VerifyNativeTheme(); | 
|  | } | 
|  |  | 
|  | virtual View* GetContentsView() override { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | private: | 
|  | void VerifyNativeTheme() { | 
|  | ASSERT_TRUE(GetNativeTheme() != NULL); | 
|  | } | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(GetNativeThemeFromDestructorView); | 
|  | }; | 
|  |  | 
|  | // Verifies GetNativeTheme() from the destructor of a WidgetDelegateView doesn't | 
|  | // crash. |is_first_run| is true if this is the first call. A return value of | 
|  | // true indicates this should be run again with a value of false. | 
|  | // First run uses DesktopNativeWidgetAura (if possible). Second run doesn't. | 
|  | bool RunGetNativeThemeFromDestructor(const Widget::InitParams& in_params, | 
|  | bool is_first_run) { | 
|  | bool needs_second_run = false; | 
|  | // Destroyed by CloseNow() below. | 
|  | Widget* widget = new Widget; | 
|  | Widget::InitParams params(in_params); | 
|  | // Deletes itself when the Widget is destroyed. | 
|  | params.delegate = new GetNativeThemeFromDestructorView; | 
|  | #if !defined(OS_CHROMEOS) | 
|  | if (is_first_run) { | 
|  | params.native_widget = new PlatformDesktopNativeWidget(widget); | 
|  | needs_second_run = true; | 
|  | } | 
|  | #endif | 
|  | widget->Init(params); | 
|  | widget->CloseNow(); | 
|  | return needs_second_run; | 
|  | } | 
|  |  | 
|  | // See description of RunGetNativeThemeFromDestructor() for details. | 
|  | TEST_F(WidgetTest, GetNativeThemeFromDestructor) { | 
|  | Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); | 
|  | if (RunGetNativeThemeFromDestructor(params, true)) | 
|  | RunGetNativeThemeFromDestructor(params, false); | 
|  | } | 
|  |  | 
|  | // Used by HideCloseDestroy. Allows setting a boolean when the widget is | 
|  | // destroyed. | 
|  | class CloseDestroysWidget : public Widget { | 
|  | public: | 
|  | explicit CloseDestroysWidget(bool* destroyed) | 
|  | : destroyed_(destroyed) { | 
|  | } | 
|  |  | 
|  | virtual ~CloseDestroysWidget() { | 
|  | if (destroyed_) { | 
|  | *destroyed_ = true; | 
|  | base::MessageLoop::current()->QuitNow(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Detach() { destroyed_ = NULL; } | 
|  |  | 
|  | private: | 
|  | // If non-null set to true from destructor. | 
|  | bool* destroyed_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(CloseDestroysWidget); | 
|  | }; | 
|  |  | 
|  | // An observer that registers that an animation has ended. | 
|  | class AnimationEndObserver : public ui::ImplicitAnimationObserver { | 
|  | public: | 
|  | AnimationEndObserver() : animation_completed_(false) {} | 
|  | virtual ~AnimationEndObserver() {} | 
|  |  | 
|  | bool animation_completed() const { return animation_completed_; } | 
|  |  | 
|  | // ui::ImplicitAnimationObserver: | 
|  | virtual void OnImplicitAnimationsCompleted() override { | 
|  | animation_completed_ = true; | 
|  | } | 
|  |  | 
|  | private: | 
|  | bool animation_completed_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(AnimationEndObserver); | 
|  | }; | 
|  |  | 
|  | // An observer that registers the bounds of a widget on destruction. | 
|  | class WidgetBoundsObserver : public WidgetObserver { | 
|  | public: | 
|  | WidgetBoundsObserver() {} | 
|  | virtual ~WidgetBoundsObserver() {} | 
|  |  | 
|  | gfx::Rect bounds() { return bounds_; } | 
|  |  | 
|  | // WidgetObserver: | 
|  | virtual void OnWidgetDestroying(Widget* widget) override { | 
|  | bounds_ = widget->GetWindowBoundsInScreen(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | gfx::Rect bounds_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(WidgetBoundsObserver); | 
|  | }; | 
|  |  | 
|  | // Verifies Close() results in destroying. | 
|  | TEST_F(WidgetTest, CloseDestroys) { | 
|  | bool destroyed = false; | 
|  | CloseDestroysWidget* widget = new CloseDestroysWidget(&destroyed); | 
|  | Widget::InitParams params = | 
|  | CreateParams(views::Widget::InitParams::TYPE_MENU); | 
|  | params.opacity = Widget::InitParams::OPAQUE_WINDOW; | 
|  | #if !defined(OS_CHROMEOS) | 
|  | params.native_widget = new PlatformDesktopNativeWidget(widget); | 
|  | #endif | 
|  | widget->Init(params); | 
|  | widget->Show(); | 
|  | widget->Hide(); | 
|  | widget->Close(); | 
|  | EXPECT_FALSE(destroyed); | 
|  | // Run the message loop as Close() asynchronously deletes. | 
|  | base::RunLoop().Run(); | 
|  | EXPECT_TRUE(destroyed); | 
|  | // Close() should destroy the widget. If not we'll cleanup to avoid leaks. | 
|  | if (!destroyed) { | 
|  | widget->Detach(); | 
|  | widget->CloseNow(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Tests that killing a widget while animating it does not crash. | 
|  | TEST_F(WidgetTest, CloseWidgetWhileAnimating) { | 
|  | scoped_ptr<Widget> widget(new Widget); | 
|  | Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); | 
|  | params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 
|  | params.bounds = gfx::Rect(50, 50, 250, 250); | 
|  | widget->Init(params); | 
|  | AnimationEndObserver animation_observer; | 
|  | WidgetBoundsObserver widget_observer; | 
|  | gfx::Rect bounds(0, 0, 50, 50); | 
|  | { | 
|  | // Normal animations for tests have ZERO_DURATION, make sure we are actually | 
|  | // animating the movement. | 
|  | ui::ScopedAnimationDurationScaleMode animation_scale_mode( | 
|  | ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); | 
|  | ui::ScopedLayerAnimationSettings animation_settings( | 
|  | widget->GetLayer()->GetAnimator()); | 
|  | animation_settings.AddObserver(&animation_observer); | 
|  | widget->AddObserver(&widget_observer); | 
|  | widget->Show(); | 
|  |  | 
|  | // Animate the bounds change. | 
|  | widget->SetBounds(bounds); | 
|  | widget.reset(); | 
|  | EXPECT_FALSE(animation_observer.animation_completed()); | 
|  | } | 
|  | EXPECT_TRUE(animation_observer.animation_completed()); | 
|  | EXPECT_EQ(widget_observer.bounds(), bounds); | 
|  | } | 
|  |  | 
|  | // A view that consumes mouse-pressed event and gesture-tap-down events. | 
|  | class RootViewTestView : public View { | 
|  | public: | 
|  | RootViewTestView(): View() {} | 
|  |  | 
|  | private: | 
|  | virtual bool OnMousePressed(const ui::MouseEvent& event) override { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | virtual void OnGestureEvent(ui::GestureEvent* event) override { | 
|  | if (event->type() == ui::ET_GESTURE_TAP_DOWN) | 
|  | event->SetHandled(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Checks if RootView::*_handler_ fields are unset when widget is hidden. | 
|  | // Fails on chromium.webkit Windows bot, see crbug.com/264872. | 
|  | #if defined(OS_WIN) | 
|  | #define MAYBE_DisableTestRootViewHandlersWhenHidden\ | 
|  | DISABLED_TestRootViewHandlersWhenHidden | 
|  | #else | 
|  | #define MAYBE_DisableTestRootViewHandlersWhenHidden\ | 
|  | TestRootViewHandlersWhenHidden | 
|  | #endif | 
|  | TEST_F(WidgetTest, MAYBE_DisableTestRootViewHandlersWhenHidden) { | 
|  | Widget* widget = CreateTopLevelNativeWidget(); | 
|  | widget->SetBounds(gfx::Rect(0, 0, 300, 300)); | 
|  | View* view = new RootViewTestView(); | 
|  | view->SetBounds(0, 0, 300, 300); | 
|  | internal::RootView* root_view = | 
|  | static_cast<internal::RootView*>(widget->GetRootView()); | 
|  | root_view->AddChildView(view); | 
|  |  | 
|  | // Check RootView::mouse_pressed_handler_. | 
|  | widget->Show(); | 
|  | EXPECT_EQ(NULL, GetMousePressedHandler(root_view)); | 
|  | gfx::Point click_location(45, 15); | 
|  | ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location, | 
|  | ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); | 
|  | widget->OnMouseEvent(&press); | 
|  | EXPECT_EQ(view, GetMousePressedHandler(root_view)); | 
|  | widget->Hide(); | 
|  | EXPECT_EQ(NULL, GetMousePressedHandler(root_view)); | 
|  |  | 
|  | // Check RootView::mouse_move_handler_. | 
|  | widget->Show(); | 
|  | EXPECT_EQ(NULL, GetMouseMoveHandler(root_view)); | 
|  | gfx::Point move_location(45, 15); | 
|  | ui::MouseEvent move(ui::ET_MOUSE_MOVED, move_location, move_location, 0, 0); | 
|  | widget->OnMouseEvent(&move); | 
|  | EXPECT_EQ(view, GetMouseMoveHandler(root_view)); | 
|  | widget->Hide(); | 
|  | EXPECT_EQ(NULL, GetMouseMoveHandler(root_view)); | 
|  |  | 
|  | // Check RootView::gesture_handler_. | 
|  | widget->Show(); | 
|  | EXPECT_EQ(NULL, GetGestureHandler(root_view)); | 
|  | ui::GestureEvent tap_down(15, | 
|  | 15, | 
|  | 0, | 
|  | base::TimeDelta(), | 
|  | ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN)); | 
|  | widget->OnGestureEvent(&tap_down); | 
|  | EXPECT_EQ(view, GetGestureHandler(root_view)); | 
|  | widget->Hide(); | 
|  | EXPECT_EQ(NULL, GetGestureHandler(root_view)); | 
|  |  | 
|  | widget->Close(); | 
|  | } | 
|  |  | 
|  | // Convenience to make constructing a GestureEvent simpler. | 
|  | class GestureEventForTest : public ui::GestureEvent { | 
|  | public: | 
|  | GestureEventForTest(ui::EventType type, int x, int y) | 
|  | : GestureEvent(x, | 
|  | y, | 
|  | 0, | 
|  | base::TimeDelta(), | 
|  | ui::GestureEventDetails(type)) {} | 
|  |  | 
|  | GestureEventForTest(ui::GestureEventDetails details, int x, int y) | 
|  | : GestureEvent(x, y, 0, base::TimeDelta(), details) {} | 
|  | }; | 
|  |  | 
|  | // Tests that the |gesture_handler_| member in RootView is always NULL | 
|  | // after the dispatch of a ui::ET_GESTURE_END event corresponding to | 
|  | // the release of the final touch point on the screen, but that | 
|  | // ui::ET_GESTURE_END events corresponding to the removal of any other touch | 
|  | // point do not modify |gesture_handler_|. | 
|  | TEST_F(WidgetTest, GestureEndEvents) { | 
|  | Widget* widget = CreateTopLevelNativeWidget(); | 
|  | widget->SetBounds(gfx::Rect(0, 0, 300, 300)); | 
|  | EventCountView* view = new EventCountView(); | 
|  | view->SetBounds(0, 0, 300, 300); | 
|  | internal::RootView* root_view = | 
|  | static_cast<internal::RootView*>(widget->GetRootView()); | 
|  | root_view->AddChildView(view); | 
|  | widget->Show(); | 
|  |  | 
|  | // If no gesture handler is set, a ui::ET_GESTURE_END event should not set | 
|  | // the gesture handler. | 
|  | EXPECT_EQ(NULL, GetGestureHandler(root_view)); | 
|  | GestureEventForTest end(ui::ET_GESTURE_END, 15, 15); | 
|  | widget->OnGestureEvent(&end); | 
|  | EXPECT_EQ(NULL, GetGestureHandler(root_view)); | 
|  |  | 
|  | // Change the handle mode of |view| to indicate that it would like | 
|  | // to handle all events, then send a GESTURE_TAP to set the gesture handler. | 
|  | view->set_handle_mode(EventCountView::CONSUME_EVENTS); | 
|  | GestureEventForTest tap(ui::ET_GESTURE_TAP, 15, 15); | 
|  | widget->OnGestureEvent(&tap); | 
|  | EXPECT_TRUE(tap.handled()); | 
|  | EXPECT_EQ(view, GetGestureHandler(root_view)); | 
|  |  | 
|  | // The gesture handler should remain unchanged on a ui::ET_GESTURE_END | 
|  | // corresponding to a second touch point, but should be reset to NULL by a | 
|  | // ui::ET_GESTURE_END corresponding to the final touch point. | 
|  | ui::GestureEventDetails details(ui::ET_GESTURE_END); | 
|  | details.set_touch_points(2); | 
|  | GestureEventForTest end_second_touch_point(details, 15, 15); | 
|  | widget->OnGestureEvent(&end_second_touch_point); | 
|  | EXPECT_EQ(view, GetGestureHandler(root_view)); | 
|  |  | 
|  | end = GestureEventForTest(ui::ET_GESTURE_END, 15, 15); | 
|  | widget->OnGestureEvent(&end); | 
|  | EXPECT_TRUE(end.handled()); | 
|  | EXPECT_EQ(NULL, GetGestureHandler(root_view)); | 
|  |  | 
|  | // Send a GESTURE_TAP to set the gesture handler, then change the handle | 
|  | // mode of |view| to indicate that it does not want to handle any | 
|  | // further events. | 
|  | tap = GestureEventForTest(ui::ET_GESTURE_TAP, 15, 15); | 
|  | widget->OnGestureEvent(&tap); | 
|  | EXPECT_TRUE(tap.handled()); | 
|  | EXPECT_EQ(view, GetGestureHandler(root_view)); | 
|  | view->set_handle_mode(EventCountView::PROPAGATE_EVENTS); | 
|  |  | 
|  | // The gesture handler should remain unchanged on a ui::ET_GESTURE_END | 
|  | // corresponding to a second touch point, but should be reset to NULL by a | 
|  | // ui::ET_GESTURE_END corresponding to the final touch point. | 
|  | end_second_touch_point = GestureEventForTest(details, 15, 15); | 
|  | widget->OnGestureEvent(&end_second_touch_point); | 
|  | EXPECT_EQ(view, GetGestureHandler(root_view)); | 
|  |  | 
|  | end = GestureEventForTest(ui::ET_GESTURE_END, 15, 15); | 
|  | widget->OnGestureEvent(&end); | 
|  | EXPECT_FALSE(end.handled()); | 
|  | EXPECT_EQ(NULL, GetGestureHandler(root_view)); | 
|  |  | 
|  | widget->Close(); | 
|  | } | 
|  |  | 
|  | // Tests that gesture events which should not be processed (because | 
|  | // RootView::OnEventProcessingStarted() has marked them as handled) are not | 
|  | // dispatched to any views. | 
|  | TEST_F(WidgetTest, GestureEventsNotProcessed) { | 
|  | Widget* widget = CreateTopLevelNativeWidget(); | 
|  | widget->SetBounds(gfx::Rect(0, 0, 300, 300)); | 
|  |  | 
|  | // Define a hierarchy of four views (coordinates are in | 
|  | // their parent coordinate space). | 
|  | // v1 (0, 0, 300, 300) | 
|  | //   v2 (0, 0, 100, 100) | 
|  | //     v3 (0, 0, 50, 50) | 
|  | //       v4(0, 0, 10, 10) | 
|  | EventCountView* v1 = new EventCountView(); | 
|  | v1->SetBounds(0, 0, 300, 300); | 
|  | EventCountView* v2 = new EventCountView(); | 
|  | v2->SetBounds(0, 0, 100, 100); | 
|  | EventCountView* v3 = new EventCountView(); | 
|  | v3->SetBounds(0, 0, 50, 50); | 
|  | EventCountView* v4 = new EventCountView(); | 
|  | v4->SetBounds(0, 0, 10, 10); | 
|  | internal::RootView* root_view = | 
|  | static_cast<internal::RootView*>(widget->GetRootView()); | 
|  | root_view->AddChildView(v1); | 
|  | v1->AddChildView(v2); | 
|  | v2->AddChildView(v3); | 
|  | v3->AddChildView(v4); | 
|  |  | 
|  | widget->Show(); | 
|  |  | 
|  | // ui::ET_GESTURE_BEGIN events should never be seen by any view, but | 
|  | // they should be marked as handled by OnEventProcessingStarted(). | 
|  | GestureEventForTest begin(ui::ET_GESTURE_BEGIN, 5, 5); | 
|  | widget->OnGestureEvent(&begin); | 
|  | EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_BEGIN)); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_BEGIN)); | 
|  | EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_BEGIN)); | 
|  | EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_BEGIN)); | 
|  | EXPECT_EQ(NULL, GetGestureHandler(root_view)); | 
|  | EXPECT_TRUE(begin.handled()); | 
|  | v1->ResetCounts(); | 
|  | v2->ResetCounts(); | 
|  | v3->ResetCounts(); | 
|  | v4->ResetCounts(); | 
|  |  | 
|  | // ui::ET_GESTURE_END events should not be seen by any view when there is | 
|  | // no default gesture handler set, but they should be marked as handled by | 
|  | // OnEventProcessingStarted(). | 
|  | GestureEventForTest end(ui::ET_GESTURE_END, 5, 5); | 
|  | widget->OnGestureEvent(&end); | 
|  | EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_END)); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_END)); | 
|  | EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_END)); | 
|  | EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_END)); | 
|  | EXPECT_EQ(NULL, GetGestureHandler(root_view)); | 
|  | EXPECT_TRUE(end.handled()); | 
|  | v1->ResetCounts(); | 
|  | v2->ResetCounts(); | 
|  | v3->ResetCounts(); | 
|  | v4->ResetCounts(); | 
|  |  | 
|  | // ui::ET_GESTURE_END events not corresponding to the release of the | 
|  | // final touch point should never be seen by any view, but they should | 
|  | // be marked as handled by OnEventProcessingStarted(). | 
|  | ui::GestureEventDetails details(ui::ET_GESTURE_END); | 
|  | details.set_touch_points(2); | 
|  | GestureEventForTest end_second_touch_point(details, 5, 5); | 
|  | widget->OnGestureEvent(&end_second_touch_point); | 
|  | EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_END)); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_END)); | 
|  | EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_END)); | 
|  | EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_END)); | 
|  | EXPECT_EQ(NULL, GetGestureHandler(root_view)); | 
|  | EXPECT_TRUE(end_second_touch_point.handled()); | 
|  | v1->ResetCounts(); | 
|  | v2->ResetCounts(); | 
|  | v3->ResetCounts(); | 
|  | v4->ResetCounts(); | 
|  |  | 
|  | // ui::ET_GESTURE_SCROLL_UPDATE events should never be seen by any view when | 
|  | // there is no default gesture handler set, but they should be marked as | 
|  | // handled by OnEventProcessingStarted(). | 
|  | GestureEventForTest scroll_update(ui::ET_GESTURE_SCROLL_UPDATE, 5, 5); | 
|  | widget->OnGestureEvent(&scroll_update); | 
|  | EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE)); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE)); | 
|  | EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE)); | 
|  | EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE)); | 
|  | EXPECT_EQ(NULL, GetGestureHandler(root_view)); | 
|  | EXPECT_TRUE(scroll_update.handled()); | 
|  | v1->ResetCounts(); | 
|  | v2->ResetCounts(); | 
|  | v3->ResetCounts(); | 
|  | v4->ResetCounts(); | 
|  |  | 
|  | // ui::ET_GESTURE_SCROLL_END events should never be seen by any view when | 
|  | // there is no default gesture handler set, but they should be marked as | 
|  | // handled by OnEventProcessingStarted(). | 
|  | GestureEventForTest scroll_end(ui::ET_GESTURE_SCROLL_END, 5, 5); | 
|  | widget->OnGestureEvent(&scroll_end); | 
|  | EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_SCROLL_END)); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_SCROLL_END)); | 
|  | EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_SCROLL_END)); | 
|  | EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_SCROLL_END)); | 
|  | EXPECT_EQ(NULL, GetGestureHandler(root_view)); | 
|  | EXPECT_TRUE(scroll_end.handled()); | 
|  | v1->ResetCounts(); | 
|  | v2->ResetCounts(); | 
|  | v3->ResetCounts(); | 
|  | v4->ResetCounts(); | 
|  |  | 
|  | // ui::ET_SCROLL_FLING_START events should never be seen by any view when | 
|  | // there is no default gesture handler set, but they should be marked as | 
|  | // handled by OnEventProcessingStarted(). | 
|  | GestureEventForTest scroll_fling_start(ui::ET_SCROLL_FLING_START, 5, 5); | 
|  | widget->OnGestureEvent(&scroll_fling_start); | 
|  | EXPECT_EQ(0, v1->GetEventCount(ui::ET_SCROLL_FLING_START)); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_SCROLL_FLING_START)); | 
|  | EXPECT_EQ(0, v3->GetEventCount(ui::ET_SCROLL_FLING_START)); | 
|  | EXPECT_EQ(0, v4->GetEventCount(ui::ET_SCROLL_FLING_START)); | 
|  | EXPECT_EQ(NULL, GetGestureHandler(root_view)); | 
|  | EXPECT_TRUE(scroll_fling_start.handled()); | 
|  | v1->ResetCounts(); | 
|  | v2->ResetCounts(); | 
|  | v3->ResetCounts(); | 
|  | v4->ResetCounts(); | 
|  |  | 
|  | widget->Close(); | 
|  | } | 
|  |  | 
|  | // Tests that a (non-scroll) gesture event is dispatched to the correct views | 
|  | // in a view hierarchy and that the default gesture handler in RootView is set | 
|  | // correctly. | 
|  | TEST_F(WidgetTest, GestureEventDispatch) { | 
|  | Widget* widget = CreateTopLevelNativeWidget(); | 
|  | widget->SetBounds(gfx::Rect(0, 0, 300, 300)); | 
|  |  | 
|  | // Define a hierarchy of four views (coordinates are in | 
|  | // their parent coordinate space). | 
|  | // v1 (0, 0, 300, 300) | 
|  | //   v2 (0, 0, 100, 100) | 
|  | //     v3 (0, 0, 50, 50) | 
|  | //       v4(0, 0, 10, 10) | 
|  | EventCountView* v1 = new EventCountView(); | 
|  | v1->SetBounds(0, 0, 300, 300); | 
|  | EventCountView* v2 = new EventCountView(); | 
|  | v2->SetBounds(0, 0, 100, 100); | 
|  | EventCountView* v3 = new EventCountView(); | 
|  | v3->SetBounds(0, 0, 50, 50); | 
|  | EventCountView* v4 = new EventCountView(); | 
|  | v4->SetBounds(0, 0, 10, 10); | 
|  | internal::RootView* root_view = | 
|  | static_cast<internal::RootView*>(widget->GetRootView()); | 
|  | root_view->AddChildView(v1); | 
|  | v1->AddChildView(v2); | 
|  | v2->AddChildView(v3); | 
|  | v3->AddChildView(v4); | 
|  |  | 
|  | widget->Show(); | 
|  |  | 
|  | // No gesture handler is set in the root view and none of the views in the | 
|  | // view hierarchy handle a ui::ET_GESTURE_TAP event. In this case the tap | 
|  | // event should be dispatched to all views in the hierarchy, the gesture | 
|  | // handler should remain unset, and the event should remain unhandled. | 
|  | GestureEventForTest tap(ui::ET_GESTURE_TAP, 5, 5); | 
|  | EXPECT_EQ(NULL, GetGestureHandler(root_view)); | 
|  | widget->OnGestureEvent(&tap); | 
|  | EXPECT_EQ(1, v1->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(1, v2->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(1, v3->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(1, v4->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(NULL, GetGestureHandler(root_view)); | 
|  | EXPECT_FALSE(tap.handled()); | 
|  |  | 
|  | // No gesture handler is set in the root view and |v1|, |v2|, and |v3| all | 
|  | // handle a ui::ET_GESTURE_TAP event. In this case the tap event should be | 
|  | // dispatched to |v4| and |v3|, the gesture handler should be set to |v3|, | 
|  | // and the event should be marked as handled. | 
|  | v1->ResetCounts(); | 
|  | v2->ResetCounts(); | 
|  | v3->ResetCounts(); | 
|  | v4->ResetCounts(); | 
|  | v1->set_handle_mode(EventCountView::CONSUME_EVENTS); | 
|  | v2->set_handle_mode(EventCountView::CONSUME_EVENTS); | 
|  | v3->set_handle_mode(EventCountView::CONSUME_EVENTS); | 
|  | tap = GestureEventForTest(ui::ET_GESTURE_TAP, 5, 5); | 
|  | widget->OnGestureEvent(&tap); | 
|  | EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(1, v3->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(1, v4->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(v3, GetGestureHandler(root_view)); | 
|  | EXPECT_TRUE(tap.handled()); | 
|  |  | 
|  | // The gesture handler is set to |v3| and all views handle all gesture event | 
|  | // types. In this case subsequent gesture events should only be dispatched to | 
|  | // |v3| and marked as handled. The gesture handler should remain as |v3|. | 
|  | v1->ResetCounts(); | 
|  | v2->ResetCounts(); | 
|  | v3->ResetCounts(); | 
|  | v4->ResetCounts(); | 
|  | v4->set_handle_mode(EventCountView::CONSUME_EVENTS); | 
|  | tap = GestureEventForTest(ui::ET_GESTURE_TAP, 5, 5); | 
|  | widget->OnGestureEvent(&tap); | 
|  | EXPECT_TRUE(tap.handled()); | 
|  | GestureEventForTest show_press(ui::ET_GESTURE_SHOW_PRESS, 5, 5); | 
|  | widget->OnGestureEvent(&show_press); | 
|  | tap = GestureEventForTest(ui::ET_GESTURE_TAP, 5, 5); | 
|  | widget->OnGestureEvent(&tap); | 
|  | EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(2, v3->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_SHOW_PRESS)); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_SHOW_PRESS)); | 
|  | EXPECT_EQ(1, v3->GetEventCount(ui::ET_GESTURE_SHOW_PRESS)); | 
|  | EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_SHOW_PRESS)); | 
|  | EXPECT_TRUE(tap.handled()); | 
|  | EXPECT_TRUE(show_press.handled()); | 
|  | EXPECT_EQ(v3, GetGestureHandler(root_view)); | 
|  |  | 
|  | // The gesture handler is set to |v3|, but |v3| does not handle | 
|  | // ui::ET_GESTURE_TAP events. In this case a tap gesture should be dispatched | 
|  | // only to |v3|, but the event should remain unhandled. The gesture handler | 
|  | // should remain as |v3|. | 
|  | v1->ResetCounts(); | 
|  | v2->ResetCounts(); | 
|  | v3->ResetCounts(); | 
|  | v4->ResetCounts(); | 
|  | v3->set_handle_mode(EventCountView::PROPAGATE_EVENTS); | 
|  | tap = GestureEventForTest(ui::ET_GESTURE_TAP, 5, 5); | 
|  | widget->OnGestureEvent(&tap); | 
|  | EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(1, v3->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_FALSE(tap.handled()); | 
|  | EXPECT_EQ(v3, GetGestureHandler(root_view)); | 
|  |  | 
|  | widget->Close(); | 
|  | } | 
|  |  | 
|  | // Tests that gesture scroll events will change the default gesture handler in | 
|  | // RootView if the current handler to which they are dispatched does not handle | 
|  | // gesture scroll events. | 
|  | TEST_F(WidgetTest, ScrollGestureEventDispatch) { | 
|  | Widget* widget = CreateTopLevelNativeWidget(); | 
|  | widget->SetBounds(gfx::Rect(0, 0, 300, 300)); | 
|  |  | 
|  | // Define a hierarchy of four views (coordinates are in | 
|  | // their parent coordinate space). | 
|  | // v1 (0, 0, 300, 300) | 
|  | //   v2 (0, 0, 100, 100) | 
|  | //     v3 (0, 0, 50, 50) | 
|  | //       v4(0, 0, 10, 10) | 
|  | EventCountView* v1 = new EventCountView(); | 
|  | v1->SetBounds(0, 0, 300, 300); | 
|  | EventCountView* v2 = new EventCountView(); | 
|  | v2->SetBounds(0, 0, 100, 100); | 
|  | EventCountView* v3 = new EventCountView(); | 
|  | v3->SetBounds(0, 0, 50, 50); | 
|  | EventCountView* v4 = new EventCountView(); | 
|  | v4->SetBounds(0, 0, 10, 10); | 
|  | internal::RootView* root_view = | 
|  | static_cast<internal::RootView*>(widget->GetRootView()); | 
|  | root_view->AddChildView(v1); | 
|  | v1->AddChildView(v2); | 
|  | v2->AddChildView(v3); | 
|  | v3->AddChildView(v4); | 
|  |  | 
|  | widget->Show(); | 
|  |  | 
|  | // Change the handle mode of |v3| to indicate that it would like to handle | 
|  | // gesture events. | 
|  | v3->set_handle_mode(EventCountView::CONSUME_EVENTS); | 
|  |  | 
|  | // When no gesture handler is set, dispatching a ui::ET_GESTURE_TAP_DOWN | 
|  | // should bubble up the views hierarchy until it reaches the first view | 
|  | // that will handle it (|v3|) and then sets the handler to |v3|. | 
|  | EXPECT_EQ(NULL, GetGestureHandler(root_view)); | 
|  | GestureEventForTest tap_down(ui::ET_GESTURE_TAP_DOWN, 5, 5); | 
|  | widget->OnGestureEvent(&tap_down); | 
|  | EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_TAP_DOWN)); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_TAP_DOWN)); | 
|  | EXPECT_EQ(1, v3->GetEventCount(ui::ET_GESTURE_TAP_DOWN)); | 
|  | EXPECT_EQ(1, v4->GetEventCount(ui::ET_GESTURE_TAP_DOWN)); | 
|  | EXPECT_EQ(v3, GetGestureHandler(root_view)); | 
|  | EXPECT_TRUE(tap_down.handled()); | 
|  | v1->ResetCounts(); | 
|  | v2->ResetCounts(); | 
|  | v3->ResetCounts(); | 
|  | v4->ResetCounts(); | 
|  |  | 
|  | // A ui::ET_GESTURE_TAP_CANCEL event should be dispatched to |v3| directly. | 
|  | GestureEventForTest tap_cancel(ui::ET_GESTURE_TAP_CANCEL, 5, 5); | 
|  | widget->OnGestureEvent(&tap_cancel); | 
|  | EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_TAP_CANCEL)); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_TAP_CANCEL)); | 
|  | EXPECT_EQ(1, v3->GetEventCount(ui::ET_GESTURE_TAP_CANCEL)); | 
|  | EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_TAP_CANCEL)); | 
|  | EXPECT_EQ(v3, GetGestureHandler(root_view)); | 
|  | EXPECT_TRUE(tap_cancel.handled()); | 
|  | v1->ResetCounts(); | 
|  | v2->ResetCounts(); | 
|  | v3->ResetCounts(); | 
|  | v4->ResetCounts(); | 
|  |  | 
|  | // Change the handle mode of |v3| to indicate that it would no longer like | 
|  | // to handle events, and change the mode of |v1| to indicate that it would | 
|  | // like to handle events. | 
|  | v3->set_handle_mode(EventCountView::PROPAGATE_EVENTS); | 
|  | v1->set_handle_mode(EventCountView::CONSUME_EVENTS); | 
|  |  | 
|  | // Dispatch a ui::ET_GESTURE_SCROLL_BEGIN event. Because the current gesture | 
|  | // handler (|v3|) does not handle scroll events, the event should bubble up | 
|  | // the views hierarchy until it reaches the first view that will handle | 
|  | // it (|v1|) and then sets the handler to |v1|. | 
|  | GestureEventForTest scroll_begin(ui::ET_GESTURE_SCROLL_BEGIN, 5, 5); | 
|  | widget->OnGestureEvent(&scroll_begin); | 
|  | EXPECT_EQ(1, v1->GetEventCount(ui::ET_GESTURE_SCROLL_BEGIN)); | 
|  | EXPECT_EQ(1, v2->GetEventCount(ui::ET_GESTURE_SCROLL_BEGIN)); | 
|  | EXPECT_EQ(1, v3->GetEventCount(ui::ET_GESTURE_SCROLL_BEGIN)); | 
|  | EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_SCROLL_BEGIN)); | 
|  | EXPECT_EQ(v1, GetGestureHandler(root_view)); | 
|  | EXPECT_TRUE(scroll_begin.handled()); | 
|  | v1->ResetCounts(); | 
|  | v2->ResetCounts(); | 
|  | v3->ResetCounts(); | 
|  | v4->ResetCounts(); | 
|  |  | 
|  | // A ui::ET_GESTURE_SCROLL_UPDATE event should be dispatched to |v1| | 
|  | // directly. | 
|  | GestureEventForTest scroll_update(ui::ET_GESTURE_SCROLL_UPDATE, 5, 5); | 
|  | widget->OnGestureEvent(&scroll_update); | 
|  | EXPECT_EQ(1, v1->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE)); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE)); | 
|  | EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE)); | 
|  | EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE)); | 
|  | EXPECT_EQ(v1, GetGestureHandler(root_view)); | 
|  | EXPECT_TRUE(scroll_update.handled()); | 
|  | v1->ResetCounts(); | 
|  | v2->ResetCounts(); | 
|  | v3->ResetCounts(); | 
|  | v4->ResetCounts(); | 
|  |  | 
|  | // A ui::ET_GESTURE_SCROLL_END event should be dispatched to |v1| | 
|  | // directly and should not reset the gesture handler. | 
|  | GestureEventForTest scroll_end(ui::ET_GESTURE_SCROLL_END, 5, 5); | 
|  | widget->OnGestureEvent(&scroll_end); | 
|  | EXPECT_EQ(1, v1->GetEventCount(ui::ET_GESTURE_SCROLL_END)); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_SCROLL_END)); | 
|  | EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_SCROLL_END)); | 
|  | EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_SCROLL_END)); | 
|  | EXPECT_EQ(v1, GetGestureHandler(root_view)); | 
|  | EXPECT_TRUE(scroll_end.handled()); | 
|  | v1->ResetCounts(); | 
|  | v2->ResetCounts(); | 
|  | v3->ResetCounts(); | 
|  | v4->ResetCounts(); | 
|  |  | 
|  | // A ui::ET_GESTURE_PINCH_BEGIN event (which is a non-scroll event) should | 
|  | // still be dispatched to |v1| directly. | 
|  | GestureEventForTest pinch_begin(ui::ET_GESTURE_PINCH_BEGIN, 5, 5); | 
|  | widget->OnGestureEvent(&pinch_begin); | 
|  | EXPECT_EQ(1, v1->GetEventCount(ui::ET_GESTURE_PINCH_BEGIN)); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_PINCH_BEGIN)); | 
|  | EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_PINCH_BEGIN)); | 
|  | EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_PINCH_BEGIN)); | 
|  | EXPECT_EQ(v1, GetGestureHandler(root_view)); | 
|  | EXPECT_TRUE(pinch_begin.handled()); | 
|  | v1->ResetCounts(); | 
|  | v2->ResetCounts(); | 
|  | v3->ResetCounts(); | 
|  | v4->ResetCounts(); | 
|  |  | 
|  | // A ui::ET_GESTURE_END event should be dispatched to |v1| and should | 
|  | // set the gesture handler to NULL. | 
|  | GestureEventForTest end(ui::ET_GESTURE_END, 5, 5); | 
|  | widget->OnGestureEvent(&end); | 
|  | EXPECT_EQ(1, v1->GetEventCount(ui::ET_GESTURE_END)); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_END)); | 
|  | EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_END)); | 
|  | EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_END)); | 
|  | EXPECT_EQ(NULL, GetGestureHandler(root_view)); | 
|  | EXPECT_TRUE(end.handled()); | 
|  |  | 
|  | widget->Close(); | 
|  | } | 
|  |  | 
|  | // A class used in WidgetTest.GestureEventLocationWhileBubbling to verify | 
|  | // that when a gesture event bubbles up a View hierarchy, the location | 
|  | // of a gesture event seen by each View is in the local coordinate space | 
|  | // of that View. | 
|  | class GestureLocationView : public EventCountView { | 
|  | public: | 
|  | GestureLocationView() {} | 
|  | virtual ~GestureLocationView() {} | 
|  |  | 
|  | void set_expected_location(gfx::Point expected_location) { | 
|  | expected_location_ = expected_location; | 
|  | } | 
|  |  | 
|  | // EventCountView: | 
|  | virtual void OnGestureEvent(ui::GestureEvent* event) override { | 
|  | EventCountView::OnGestureEvent(event); | 
|  |  | 
|  | // Verify that the location of |event| is in the local coordinate | 
|  | // space of |this|. | 
|  | EXPECT_EQ(expected_location_, event->location()); | 
|  | } | 
|  |  | 
|  | private: | 
|  | // The expected location of a gesture event dispatched to |this|. | 
|  | gfx::Point expected_location_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(GestureLocationView); | 
|  | }; | 
|  |  | 
|  | // Verifies that the location of a gesture event is always in the local | 
|  | // coordinate space of the View receiving the event while bubbling. | 
|  | TEST_F(WidgetTest, GestureEventLocationWhileBubbling) { | 
|  | Widget* widget = CreateTopLevelNativeWidget(); | 
|  | widget->SetBounds(gfx::Rect(0, 0, 300, 300)); | 
|  |  | 
|  | // Define a hierarchy of three views (coordinates shown below are in the | 
|  | // coordinate space of the root view, but the coordinates used for | 
|  | // SetBounds() are in their parent coordinate space). | 
|  | // v1 (50, 50, 150, 150) | 
|  | //   v2 (100, 70, 50, 80) | 
|  | //     v3 (120, 100, 10, 10) | 
|  | GestureLocationView* v1 = new GestureLocationView(); | 
|  | v1->SetBounds(50, 50, 150, 150); | 
|  | GestureLocationView* v2 = new GestureLocationView(); | 
|  | v2->SetBounds(50, 20, 50, 80); | 
|  | GestureLocationView* v3 = new GestureLocationView(); | 
|  | v3->SetBounds(20, 30, 10, 10); | 
|  | internal::RootView* root_view = | 
|  | static_cast<internal::RootView*>(widget->GetRootView()); | 
|  | root_view->AddChildView(v1); | 
|  | v1->AddChildView(v2); | 
|  | v2->AddChildView(v3); | 
|  |  | 
|  | widget->Show(); | 
|  |  | 
|  | // Define a GESTURE_TAP event located at (125, 105) in root view coordinates. | 
|  | // This event is contained within all of |v1|, |v2|, and |v3|. | 
|  | gfx::Point location_in_root(125, 105); | 
|  | GestureEventForTest tap( | 
|  | ui::ET_GESTURE_TAP, location_in_root.x(), location_in_root.y()); | 
|  |  | 
|  | // Calculate the location of the event in the local coordinate spaces | 
|  | // of each of the views. | 
|  | gfx::Point location_in_v1(ConvertPointFromWidgetToView(v1, location_in_root)); | 
|  | EXPECT_EQ(gfx::Point(75, 55), location_in_v1); | 
|  | gfx::Point location_in_v2(ConvertPointFromWidgetToView(v2, location_in_root)); | 
|  | EXPECT_EQ(gfx::Point(25, 35), location_in_v2); | 
|  | gfx::Point location_in_v3(ConvertPointFromWidgetToView(v3, location_in_root)); | 
|  | EXPECT_EQ(gfx::Point(5, 5), location_in_v3); | 
|  |  | 
|  | // Dispatch the event. When each view receives the event, its location should | 
|  | // be in the local coordinate space of that view (see the check made by | 
|  | // GestureLocationView). After dispatch is complete the event's location | 
|  | // should be in the root coordinate space. | 
|  | v1->set_expected_location(location_in_v1); | 
|  | v2->set_expected_location(location_in_v2); | 
|  | v3->set_expected_location(location_in_v3); | 
|  | widget->OnGestureEvent(&tap); | 
|  | EXPECT_EQ(location_in_root, tap.location()); | 
|  |  | 
|  | // Verify that each view did in fact see the event. | 
|  | EventCountView* view1 = v1; | 
|  | EventCountView* view2 = v2; | 
|  | EventCountView* view3 = v3; | 
|  | EXPECT_EQ(1, view1->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(1, view2->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(1, view3->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  |  | 
|  | widget->Close(); | 
|  | } | 
|  |  | 
|  | // Verifies that disabled views are permitted to be set as the default gesture | 
|  | // handler in RootView. Also verifies that gesture events targeted to a disabled | 
|  | // view are not actually dispatched to the view, but are still marked as | 
|  | // handled. | 
|  | TEST_F(WidgetTest, DisabledGestureEventTarget) { | 
|  | Widget* widget = CreateTopLevelNativeWidget(); | 
|  | widget->SetBounds(gfx::Rect(0, 0, 300, 300)); | 
|  |  | 
|  | // Define a hierarchy of four views (coordinates are in | 
|  | // their parent coordinate space). | 
|  | // v1 (0, 0, 300, 300) | 
|  | //   v2 (0, 0, 100, 100) | 
|  | //     v3 (0, 0, 50, 50) | 
|  | //       v4(0, 0, 10, 10) | 
|  | EventCountView* v1 = new EventCountView(); | 
|  | v1->SetBounds(0, 0, 300, 300); | 
|  | EventCountView* v2 = new EventCountView(); | 
|  | v2->SetBounds(0, 0, 100, 100); | 
|  | EventCountView* v3 = new EventCountView(); | 
|  | v3->SetBounds(0, 0, 50, 50); | 
|  | EventCountView* v4 = new EventCountView(); | 
|  | v4->SetBounds(0, 0, 10, 10); | 
|  | internal::RootView* root_view = | 
|  | static_cast<internal::RootView*>(widget->GetRootView()); | 
|  | root_view->AddChildView(v1); | 
|  | v1->AddChildView(v2); | 
|  | v2->AddChildView(v3); | 
|  | v3->AddChildView(v4); | 
|  |  | 
|  | widget->Show(); | 
|  |  | 
|  | // |v1|, |v2|, and |v3| all handle gesture events but |v3| is marked as | 
|  | // disabled. | 
|  | v1->set_handle_mode(EventCountView::CONSUME_EVENTS); | 
|  | v2->set_handle_mode(EventCountView::CONSUME_EVENTS); | 
|  | v3->set_handle_mode(EventCountView::CONSUME_EVENTS); | 
|  | v3->SetEnabled(false); | 
|  |  | 
|  | // No gesture handler is set in the root view. In this case the tap event | 
|  | // should be dispatched only to |v4|, the gesture handler should be set to | 
|  | // |v3|, and the event should be marked as handled. | 
|  | GestureEventForTest tap(ui::ET_GESTURE_TAP, 5, 5); | 
|  | widget->OnGestureEvent(&tap); | 
|  | EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(1, v4->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(v3, GetGestureHandler(root_view)); | 
|  | EXPECT_TRUE(tap.handled()); | 
|  | v1->ResetCounts(); | 
|  | v2->ResetCounts(); | 
|  | v3->ResetCounts(); | 
|  | v4->ResetCounts(); | 
|  |  | 
|  | // A subsequent gesture event should be marked as handled but not dispatched. | 
|  | tap = GestureEventForTest(ui::ET_GESTURE_TAP, 5, 5); | 
|  | widget->OnGestureEvent(&tap); | 
|  | EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(v3, GetGestureHandler(root_view)); | 
|  | EXPECT_TRUE(tap.handled()); | 
|  | v1->ResetCounts(); | 
|  | v2->ResetCounts(); | 
|  | v3->ResetCounts(); | 
|  | v4->ResetCounts(); | 
|  |  | 
|  | // A GESTURE_END should reset the default gesture handler to NULL. It should | 
|  | // also not be dispatched to |v3| but still marked as handled. | 
|  | GestureEventForTest end(ui::ET_GESTURE_END, 5, 5); | 
|  | widget->OnGestureEvent(&end); | 
|  | EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_END)); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_END)); | 
|  | EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_END)); | 
|  | EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_END)); | 
|  | EXPECT_EQ(NULL, GetGestureHandler(root_view)); | 
|  | EXPECT_TRUE(end.handled()); | 
|  | v1->ResetCounts(); | 
|  | v2->ResetCounts(); | 
|  | v3->ResetCounts(); | 
|  | v4->ResetCounts(); | 
|  |  | 
|  | // Change the handle mode of |v3| to indicate that it would no longer like | 
|  | // to handle events which are dispatched to it. | 
|  | v3->set_handle_mode(EventCountView::PROPAGATE_EVENTS); | 
|  |  | 
|  | // No gesture handler is set in the root view. In this case the tap event | 
|  | // should be dispatched only to |v4| and the event should be marked as | 
|  | // handled. Furthermore, the gesture handler should be set to | 
|  | // |v3|; even though |v3| does not explicitly handle events, it is a | 
|  | // valid target for the tap event because it is disabled. | 
|  | tap = GestureEventForTest(ui::ET_GESTURE_TAP, 5, 5); | 
|  | widget->OnGestureEvent(&tap); | 
|  | EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(1, v4->GetEventCount(ui::ET_GESTURE_TAP)); | 
|  | EXPECT_EQ(v3, GetGestureHandler(root_view)); | 
|  | EXPECT_TRUE(tap.handled()); | 
|  | v1->ResetCounts(); | 
|  | v2->ResetCounts(); | 
|  | v3->ResetCounts(); | 
|  | v4->ResetCounts(); | 
|  |  | 
|  | // A GESTURE_END should reset the default gesture handler to NULL. It should | 
|  | // also not be dispatched to |v3| but still marked as handled. | 
|  | end = GestureEventForTest(ui::ET_GESTURE_END, 5, 5); | 
|  | widget->OnGestureEvent(&end); | 
|  | EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_END)); | 
|  | EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_END)); | 
|  | EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_END)); | 
|  | EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_END)); | 
|  | EXPECT_EQ(NULL, GetGestureHandler(root_view)); | 
|  | EXPECT_TRUE(end.handled()); | 
|  |  | 
|  | widget->Close(); | 
|  | } | 
|  |  | 
|  | // Test the result of Widget::GetAllChildWidgets(). | 
|  | TEST_F(WidgetTest, GetAllChildWidgets) { | 
|  | // Create the following widget hierarchy: | 
|  | // | 
|  | // toplevel | 
|  | // +-- w1 | 
|  | //     +-- w11 | 
|  | // +-- w2 | 
|  | //     +-- w21 | 
|  | //     +-- w22 | 
|  | Widget* toplevel = CreateTopLevelPlatformWidget(); | 
|  | Widget* w1 = CreateChildPlatformWidget(toplevel->GetNativeView()); | 
|  | Widget* w11 = CreateChildPlatformWidget(w1->GetNativeView()); | 
|  | Widget* w2 = CreateChildPlatformWidget(toplevel->GetNativeView()); | 
|  | Widget* w21 = CreateChildPlatformWidget(w2->GetNativeView()); | 
|  | Widget* w22 = CreateChildPlatformWidget(w2->GetNativeView()); | 
|  |  | 
|  | std::set<Widget*> expected; | 
|  | expected.insert(toplevel); | 
|  | expected.insert(w1); | 
|  | expected.insert(w11); | 
|  | expected.insert(w2); | 
|  | expected.insert(w21); | 
|  | expected.insert(w22); | 
|  |  | 
|  | std::set<Widget*> widgets; | 
|  | Widget::GetAllChildWidgets(toplevel->GetNativeView(), &widgets); | 
|  |  | 
|  | EXPECT_EQ(expected.size(), widgets.size()); | 
|  | EXPECT_TRUE(std::equal(expected.begin(), expected.end(), widgets.begin())); | 
|  | } | 
|  |  | 
|  | // Used by DestroyChildWidgetsInOrder. On destruction adds the supplied name to | 
|  | // a vector. | 
|  | class DestroyedTrackingView : public View { | 
|  | public: | 
|  | DestroyedTrackingView(const std::string& name, | 
|  | std::vector<std::string>* add_to) | 
|  | : name_(name), | 
|  | add_to_(add_to) { | 
|  | } | 
|  |  | 
|  | virtual ~DestroyedTrackingView() { | 
|  | add_to_->push_back(name_); | 
|  | } | 
|  |  | 
|  | private: | 
|  | const std::string name_; | 
|  | std::vector<std::string>* add_to_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(DestroyedTrackingView); | 
|  | }; | 
|  |  | 
|  | class WidgetChildDestructionTest : public WidgetTest { | 
|  | public: | 
|  | WidgetChildDestructionTest() {} | 
|  |  | 
|  | // Creates a top level and a child, destroys the child and verifies the views | 
|  | // of the child are destroyed before the views of the parent. | 
|  | void RunDestroyChildWidgetsTest(bool top_level_has_desktop_native_widget_aura, | 
|  | bool child_has_desktop_native_widget_aura) { | 
|  | // When a View is destroyed its name is added here. | 
|  | std::vector<std::string> destroyed; | 
|  |  | 
|  | Widget* top_level = new Widget; | 
|  | Widget::InitParams params = | 
|  | CreateParams(views::Widget::InitParams::TYPE_WINDOW); | 
|  | #if !defined(OS_CHROMEOS) | 
|  | if (top_level_has_desktop_native_widget_aura) | 
|  | params.native_widget = new PlatformDesktopNativeWidget(top_level); | 
|  | #endif | 
|  | top_level->Init(params); | 
|  | top_level->GetRootView()->AddChildView( | 
|  | new DestroyedTrackingView("parent", &destroyed)); | 
|  | top_level->Show(); | 
|  |  | 
|  | Widget* child = new Widget; | 
|  | Widget::InitParams child_params = | 
|  | CreateParams(views::Widget::InitParams::TYPE_POPUP); | 
|  | child_params.parent = top_level->GetNativeView(); | 
|  | #if !defined(OS_CHROMEOS) | 
|  | if (child_has_desktop_native_widget_aura) | 
|  | child_params.native_widget = new PlatformDesktopNativeWidget(child); | 
|  | #endif | 
|  | child->Init(child_params); | 
|  | child->GetRootView()->AddChildView( | 
|  | new DestroyedTrackingView("child", &destroyed)); | 
|  | child->Show(); | 
|  |  | 
|  | // Should trigger destruction of the child too. | 
|  | top_level->native_widget_private()->CloseNow(); | 
|  |  | 
|  | // Child should be destroyed first. | 
|  | ASSERT_EQ(2u, destroyed.size()); | 
|  | EXPECT_EQ("child", destroyed[0]); | 
|  | EXPECT_EQ("parent", destroyed[1]); | 
|  | } | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(WidgetChildDestructionTest); | 
|  | }; | 
|  |  | 
|  | #if !defined(OS_CHROMEOS) | 
|  | // See description of RunDestroyChildWidgetsTest(). Parent uses | 
|  | // DesktopNativeWidgetAura. | 
|  | TEST_F(WidgetChildDestructionTest, | 
|  | DestroyChildWidgetsInOrderWithDesktopNativeWidget) { | 
|  | RunDestroyChildWidgetsTest(true, false); | 
|  | } | 
|  |  | 
|  | // See description of RunDestroyChildWidgetsTest(). Both parent and child use | 
|  | // DesktopNativeWidgetAura. | 
|  | TEST_F(WidgetChildDestructionTest, | 
|  | DestroyChildWidgetsInOrderWithDesktopNativeWidgetForBoth) { | 
|  | RunDestroyChildWidgetsTest(true, true); | 
|  | } | 
|  | #endif  // !defined(OS_CHROMEOS) | 
|  |  | 
|  | // See description of RunDestroyChildWidgetsTest(). | 
|  | TEST_F(WidgetChildDestructionTest, DestroyChildWidgetsInOrder) { | 
|  | RunDestroyChildWidgetsTest(false, false); | 
|  | } | 
|  |  | 
|  | #if !defined(OS_CHROMEOS) | 
|  | // Provides functionality to create a window modal dialog. | 
|  | class ModalDialogDelegate : public DialogDelegateView { | 
|  | public: | 
|  | ModalDialogDelegate() {} | 
|  | virtual ~ModalDialogDelegate() {} | 
|  |  | 
|  | // WidgetDelegate overrides. | 
|  | virtual ui::ModalType GetModalType() const override { | 
|  | return ui::MODAL_TYPE_WINDOW; | 
|  | } | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate); | 
|  | }; | 
|  |  | 
|  | // This test verifies that whether mouse events when a modal dialog is | 
|  | // displayed are eaten or recieved by the dialog. | 
|  | TEST_F(WidgetTest, WindowMouseModalityTest) { | 
|  | // Create a top level widget. | 
|  | Widget top_level_widget; | 
|  | Widget::InitParams init_params = | 
|  | CreateParams(Widget::InitParams::TYPE_WINDOW); | 
|  | init_params.show_state = ui::SHOW_STATE_NORMAL; | 
|  | gfx::Rect initial_bounds(0, 0, 500, 500); | 
|  | init_params.bounds = initial_bounds; | 
|  | init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 
|  | init_params.native_widget = | 
|  | new PlatformDesktopNativeWidget(&top_level_widget); | 
|  | top_level_widget.Init(init_params); | 
|  | top_level_widget.Show(); | 
|  | EXPECT_TRUE(top_level_widget.IsVisible()); | 
|  |  | 
|  | // Create a view and validate that a mouse moves makes it to the view. | 
|  | EventCountView* widget_view = new EventCountView(); | 
|  | widget_view->SetBounds(0, 0, 10, 10); | 
|  | top_level_widget.GetRootView()->AddChildView(widget_view); | 
|  |  | 
|  | gfx::Point cursor_location_main(5, 5); | 
|  | ui::MouseEvent move_main(ui::ET_MOUSE_MOVED, | 
|  | cursor_location_main, | 
|  | cursor_location_main, | 
|  | ui::EF_NONE, | 
|  | ui::EF_NONE); | 
|  | ui::EventDispatchDetails details = | 
|  | GetEventProcessor(&top_level_widget)->OnEventFromSource(&move_main); | 
|  | ASSERT_FALSE(details.dispatcher_destroyed); | 
|  |  | 
|  | EXPECT_EQ(1, widget_view->GetEventCount(ui::ET_MOUSE_ENTERED)); | 
|  | widget_view->ResetCounts(); | 
|  |  | 
|  | // Create a modal dialog and validate that a mouse down message makes it to | 
|  | // the main view within the dialog. | 
|  |  | 
|  | // This instance will be destroyed when the dialog is destroyed. | 
|  | ModalDialogDelegate* dialog_delegate = new ModalDialogDelegate; | 
|  |  | 
|  | Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget( | 
|  | dialog_delegate, NULL, top_level_widget.GetNativeView()); | 
|  | modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200)); | 
|  | EventCountView* dialog_widget_view = new EventCountView(); | 
|  | dialog_widget_view->SetBounds(0, 0, 50, 50); | 
|  | modal_dialog_widget->GetRootView()->AddChildView(dialog_widget_view); | 
|  | modal_dialog_widget->Show(); | 
|  | EXPECT_TRUE(modal_dialog_widget->IsVisible()); | 
|  |  | 
|  | gfx::Point cursor_location_dialog(100, 100); | 
|  | ui::MouseEvent mouse_down_dialog(ui::ET_MOUSE_PRESSED, | 
|  | cursor_location_dialog, | 
|  | cursor_location_dialog, | 
|  | ui::EF_NONE, | 
|  | ui::EF_NONE); | 
|  | details = GetEventProcessor(&top_level_widget)->OnEventFromSource( | 
|  | &mouse_down_dialog); | 
|  | ASSERT_FALSE(details.dispatcher_destroyed); | 
|  | EXPECT_EQ(1, dialog_widget_view->GetEventCount(ui::ET_MOUSE_PRESSED)); | 
|  |  | 
|  | // Send a mouse move message to the main window. It should not be received by | 
|  | // the main window as the modal dialog is still active. | 
|  | gfx::Point cursor_location_main2(6, 6); | 
|  | ui::MouseEvent mouse_down_main(ui::ET_MOUSE_MOVED, | 
|  | cursor_location_main2, | 
|  | cursor_location_main2, | 
|  | ui::EF_NONE, | 
|  | ui::EF_NONE); | 
|  | details = GetEventProcessor(&top_level_widget)->OnEventFromSource( | 
|  | &mouse_down_main); | 
|  | ASSERT_FALSE(details.dispatcher_destroyed); | 
|  | EXPECT_EQ(0, widget_view->GetEventCount(ui::ET_MOUSE_MOVED)); | 
|  |  | 
|  | modal_dialog_widget->CloseNow(); | 
|  | top_level_widget.CloseNow(); | 
|  | } | 
|  |  | 
|  | // Verifies nativeview visbility matches that of Widget visibility when | 
|  | // SetFullscreen is invoked. | 
|  | TEST_F(WidgetTest, FullscreenStatePropagated) { | 
|  | Widget::InitParams init_params = | 
|  | CreateParams(Widget::InitParams::TYPE_WINDOW); | 
|  | init_params.show_state = ui::SHOW_STATE_NORMAL; | 
|  | init_params.bounds = gfx::Rect(0, 0, 500, 500); | 
|  | init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 
|  |  | 
|  | { | 
|  | Widget top_level_widget; | 
|  | top_level_widget.Init(init_params); | 
|  | top_level_widget.SetFullscreen(true); | 
|  | EXPECT_EQ(top_level_widget.IsVisible(), | 
|  | IsNativeWindowVisible(top_level_widget.GetNativeWindow())); | 
|  | top_level_widget.CloseNow(); | 
|  | } | 
|  | #if !defined(OS_CHROMEOS) | 
|  | { | 
|  | Widget top_level_widget; | 
|  | init_params.native_widget = | 
|  | new PlatformDesktopNativeWidget(&top_level_widget); | 
|  | top_level_widget.Init(init_params); | 
|  | top_level_widget.SetFullscreen(true); | 
|  | EXPECT_EQ(top_level_widget.IsVisible(), | 
|  | IsNativeWindowVisible(top_level_widget.GetNativeWindow())); | 
|  | top_level_widget.CloseNow(); | 
|  | } | 
|  | #endif | 
|  | } | 
|  | #if defined(OS_WIN) | 
|  |  | 
|  | // Provides functionality to test widget activation via an activation flag | 
|  | // which can be set by an accessor. | 
|  | class ModalWindowTestWidgetDelegate : public WidgetDelegate { | 
|  | public: | 
|  | ModalWindowTestWidgetDelegate() | 
|  | : widget_(NULL), | 
|  | can_activate_(true) {} | 
|  |  | 
|  | virtual ~ModalWindowTestWidgetDelegate() {} | 
|  |  | 
|  | // Overridden from WidgetDelegate: | 
|  | virtual void DeleteDelegate() override { | 
|  | delete this; | 
|  | } | 
|  | virtual Widget* GetWidget() override { | 
|  | return widget_; | 
|  | } | 
|  | virtual const Widget* GetWidget() const override { | 
|  | return widget_; | 
|  | } | 
|  | virtual bool CanActivate() const override { | 
|  | return can_activate_; | 
|  | } | 
|  | virtual bool ShouldAdvanceFocusToTopLevelWidget() const override { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void set_can_activate(bool can_activate) { | 
|  | can_activate_ = can_activate; | 
|  | } | 
|  |  | 
|  | void set_widget(Widget* widget) { | 
|  | widget_ = widget; | 
|  | } | 
|  |  | 
|  | private: | 
|  | Widget* widget_; | 
|  | bool can_activate_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(ModalWindowTestWidgetDelegate); | 
|  | }; | 
|  |  | 
|  | // Tests whether we can activate the top level widget when a modal dialog is | 
|  | // active. | 
|  | TEST_F(WidgetTest, WindowModalityActivationTest) { | 
|  | // Destroyed when the top level widget created below is destroyed. | 
|  | ModalWindowTestWidgetDelegate* widget_delegate = | 
|  | new ModalWindowTestWidgetDelegate; | 
|  | // Create a top level widget. | 
|  | Widget top_level_widget; | 
|  | Widget::InitParams init_params = | 
|  | CreateParams(Widget::InitParams::TYPE_WINDOW); | 
|  | init_params.show_state = ui::SHOW_STATE_NORMAL; | 
|  | gfx::Rect initial_bounds(0, 0, 500, 500); | 
|  | init_params.bounds = initial_bounds; | 
|  | init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 
|  | init_params.native_widget = new DesktopNativeWidgetAura(&top_level_widget); | 
|  | init_params.delegate = widget_delegate; | 
|  | top_level_widget.Init(init_params); | 
|  | widget_delegate->set_widget(&top_level_widget); | 
|  | top_level_widget.Show(); | 
|  | EXPECT_TRUE(top_level_widget.IsVisible()); | 
|  |  | 
|  | HWND win32_window = views::HWNDForWidget(&top_level_widget); | 
|  | EXPECT_TRUE(::IsWindow(win32_window)); | 
|  |  | 
|  | // This instance will be destroyed when the dialog is destroyed. | 
|  | ModalDialogDelegate* dialog_delegate = new ModalDialogDelegate; | 
|  |  | 
|  | // We should be able to activate the window even if the WidgetDelegate | 
|  | // says no, when a modal dialog is active. | 
|  | widget_delegate->set_can_activate(false); | 
|  |  | 
|  | Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget( | 
|  | dialog_delegate, NULL, top_level_widget.GetNativeWindow()); | 
|  | modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200)); | 
|  | modal_dialog_widget->Show(); | 
|  | EXPECT_TRUE(modal_dialog_widget->IsVisible()); | 
|  |  | 
|  | LRESULT activate_result = ::SendMessage( | 
|  | win32_window, | 
|  | WM_MOUSEACTIVATE, | 
|  | reinterpret_cast<WPARAM>(win32_window), | 
|  | MAKELPARAM(WM_LBUTTONDOWN, HTCLIENT)); | 
|  | EXPECT_EQ(activate_result, MA_ACTIVATE); | 
|  |  | 
|  | modal_dialog_widget->CloseNow(); | 
|  | top_level_widget.CloseNow(); | 
|  | } | 
|  | #endif  // defined(OS_WIN) | 
|  | #endif  // !defined(OS_CHROMEOS) | 
|  |  | 
|  | TEST_F(WidgetTest, ShowCreatesActiveWindow) { | 
|  | Widget* widget = CreateTopLevelPlatformWidget(); | 
|  |  | 
|  | widget->Show(); | 
|  | EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL); | 
|  |  | 
|  | widget->CloseNow(); | 
|  | } | 
|  |  | 
|  | // OSX does not have a per-application "active" window such as provided by | 
|  | // ::GetActiveWindow() on Windows. There is only a system-wide "keyWindow" which | 
|  | // is updated asynchronously. | 
|  | #if defined(OS_MACOSX) | 
|  | #define MAYBE_ShowInactive DISABLED_ShowInactive | 
|  | #else | 
|  | #define MAYBE_ShowInactive ShowInactive | 
|  | #endif | 
|  | TEST_F(WidgetTest, MAYBE_ShowInactive) { | 
|  | Widget* widget = CreateTopLevelPlatformWidget(); | 
|  |  | 
|  | widget->ShowInactive(); | 
|  | EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_INACTIVE); | 
|  |  | 
|  | widget->CloseNow(); | 
|  | } | 
|  |  | 
|  | TEST_F(WidgetTest, InactiveBeforeShow) { | 
|  | Widget* widget = CreateTopLevelPlatformWidget(); | 
|  |  | 
|  | EXPECT_FALSE(widget->IsActive()); | 
|  | EXPECT_FALSE(widget->IsVisible()); | 
|  |  | 
|  | widget->Show(); | 
|  |  | 
|  | EXPECT_TRUE(widget->IsActive()); | 
|  | EXPECT_TRUE(widget->IsVisible()); | 
|  |  | 
|  | widget->CloseNow(); | 
|  | } | 
|  |  | 
|  | TEST_F(WidgetTest, ShowInactiveAfterShow) { | 
|  | // Create 2 widgets to ensure window layering does not change. | 
|  | Widget* widget = CreateTopLevelPlatformWidget(); | 
|  | Widget* widget2 = CreateTopLevelPlatformWidget(); | 
|  |  | 
|  | widget2->Show(); | 
|  | EXPECT_FALSE(widget->IsActive()); | 
|  | EXPECT_TRUE(widget2->IsVisible()); | 
|  | EXPECT_TRUE(widget2->IsActive()); | 
|  |  | 
|  | widget->Show(); | 
|  | EXPECT_TRUE(widget->IsActive()); | 
|  | EXPECT_FALSE(widget2->IsActive()); | 
|  | widget->ShowInactive(); | 
|  | EXPECT_TRUE(widget->IsActive()); | 
|  | EXPECT_FALSE(widget2->IsActive()); | 
|  | EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL); | 
|  |  | 
|  | widget2->CloseNow(); | 
|  | widget->CloseNow(); | 
|  | } | 
|  |  | 
|  | TEST_F(WidgetTest, ShowAfterShowInactive) { | 
|  | Widget* widget = CreateTopLevelPlatformWidget(); | 
|  |  | 
|  | widget->ShowInactive(); | 
|  | widget->Show(); | 
|  | EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL); | 
|  |  | 
|  | widget->CloseNow(); | 
|  | } | 
|  |  | 
|  | #if !defined(OS_CHROMEOS) | 
|  | TEST_F(WidgetTest, InactiveWidgetDoesNotGrabActivation) { | 
|  | Widget* widget = CreateTopLevelPlatformWidget(); | 
|  | widget->Show(); | 
|  | EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL); | 
|  |  | 
|  | Widget widget2; | 
|  | Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); | 
|  | params.native_widget = new PlatformDesktopNativeWidget(&widget2); | 
|  | params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 
|  | widget2.Init(params); | 
|  | widget2.Show(); | 
|  |  | 
|  | EXPECT_EQ(GetWidgetShowState(&widget2), ui::SHOW_STATE_INACTIVE); | 
|  | EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL); | 
|  |  | 
|  | widget->CloseNow(); | 
|  | widget2.CloseNow(); | 
|  | } | 
|  | #endif  // !defined(OS_CHROMEOS) | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class FullscreenAwareFrame : public views::NonClientFrameView { | 
|  | public: | 
|  | explicit FullscreenAwareFrame(views::Widget* widget) | 
|  | : widget_(widget), fullscreen_layout_called_(false) {} | 
|  | virtual ~FullscreenAwareFrame() {} | 
|  |  | 
|  | // views::NonClientFrameView overrides: | 
|  | virtual gfx::Rect GetBoundsForClientView() const override { | 
|  | return gfx::Rect(); | 
|  | } | 
|  | virtual gfx::Rect GetWindowBoundsForClientBounds( | 
|  | const gfx::Rect& client_bounds) const override { | 
|  | return gfx::Rect(); | 
|  | } | 
|  | virtual int NonClientHitTest(const gfx::Point& point) override { | 
|  | return HTNOWHERE; | 
|  | } | 
|  | virtual void GetWindowMask(const gfx::Size& size, | 
|  | gfx::Path* window_mask) override {} | 
|  | virtual void ResetWindowControls() override {} | 
|  | virtual void UpdateWindowIcon() override {} | 
|  | virtual void UpdateWindowTitle() override {} | 
|  | virtual void SizeConstraintsChanged() override {} | 
|  |  | 
|  | // views::View overrides: | 
|  | virtual void Layout() override { | 
|  | if (widget_->IsFullscreen()) | 
|  | fullscreen_layout_called_ = true; | 
|  | } | 
|  |  | 
|  | bool fullscreen_layout_called() const { return fullscreen_layout_called_; } | 
|  |  | 
|  | private: | 
|  | views::Widget* widget_; | 
|  | bool fullscreen_layout_called_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(FullscreenAwareFrame); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Tests that frame Layout is called when a widget goes fullscreen without | 
|  | // changing its size or title. | 
|  | TEST_F(WidgetTest, FullscreenFrameLayout) { | 
|  | Widget* widget = CreateTopLevelPlatformWidget(); | 
|  | FullscreenAwareFrame* frame = new FullscreenAwareFrame(widget); | 
|  | widget->non_client_view()->SetFrameView(frame);  // Owns |frame|. | 
|  |  | 
|  | widget->Maximize(); | 
|  | RunPendingMessages(); | 
|  |  | 
|  | EXPECT_FALSE(frame->fullscreen_layout_called()); | 
|  | widget->SetFullscreen(true); | 
|  | widget->Show(); | 
|  | RunPendingMessages(); | 
|  | EXPECT_TRUE(frame->fullscreen_layout_called()); | 
|  |  | 
|  | widget->CloseNow(); | 
|  | } | 
|  |  | 
|  | #if !defined(OS_CHROMEOS) | 
|  | namespace { | 
|  |  | 
|  | // Trivial WidgetObserverTest that invokes Widget::IsActive() from | 
|  | // OnWindowDestroying. | 
|  | class IsActiveFromDestroyObserver : public WidgetObserver { | 
|  | public: | 
|  | IsActiveFromDestroyObserver() {} | 
|  | virtual ~IsActiveFromDestroyObserver() {} | 
|  | virtual void OnWidgetDestroying(Widget* widget) override { | 
|  | widget->IsActive(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(IsActiveFromDestroyObserver); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Verifies Widget::IsActive() invoked from | 
|  | // WidgetObserver::OnWidgetDestroying() in a child widget doesn't crash. | 
|  | TEST_F(WidgetTest, IsActiveFromDestroy) { | 
|  | // Create two widgets, one a child of the other. | 
|  | IsActiveFromDestroyObserver observer; | 
|  | Widget parent_widget; | 
|  | Widget::InitParams parent_params = | 
|  | CreateParams(Widget::InitParams::TYPE_POPUP); | 
|  | parent_params.native_widget = new PlatformDesktopNativeWidget(&parent_widget); | 
|  | parent_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 
|  | parent_widget.Init(parent_params); | 
|  | parent_widget.Show(); | 
|  |  | 
|  | Widget child_widget; | 
|  | Widget::InitParams child_params = | 
|  | CreateParams(Widget::InitParams::TYPE_POPUP); | 
|  | child_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 
|  | child_params.context = parent_widget.GetNativeWindow(); | 
|  | child_widget.Init(child_params); | 
|  | child_widget.AddObserver(&observer); | 
|  | child_widget.Show(); | 
|  |  | 
|  | parent_widget.CloseNow(); | 
|  | } | 
|  | #endif  // !defined(OS_CHROMEOS) | 
|  |  | 
|  | // Tests that events propagate through from the dispatcher with the correct | 
|  | // event type, and that the different platforms behave the same. | 
|  | TEST_F(WidgetTest, MouseEventTypesViaGenerator) { | 
|  | EventCountView* view = new EventCountView; | 
|  | view->set_handle_mode(EventCountView::CONSUME_EVENTS); | 
|  | view->SetBounds(10, 10, 50, 40); | 
|  |  | 
|  | Widget* widget = CreateTopLevelFramelessPlatformWidget(); | 
|  | widget->GetRootView()->AddChildView(view); | 
|  |  | 
|  | widget->SetBounds(gfx::Rect(0, 0, 100, 80)); | 
|  | widget->Show(); | 
|  |  | 
|  | ui::test::EventGenerator generator(GetContext(), widget->GetNativeWindow()); | 
|  | generator.set_current_location(gfx::Point(20, 20)); | 
|  |  | 
|  | generator.ClickLeftButton(); | 
|  | EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSE_PRESSED)); | 
|  | EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSE_RELEASED)); | 
|  | EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, view->last_flags()); | 
|  |  | 
|  | generator.PressRightButton(); | 
|  | EXPECT_EQ(2, view->GetEventCount(ui::ET_MOUSE_PRESSED)); | 
|  | EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSE_RELEASED)); | 
|  | EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, view->last_flags()); | 
|  |  | 
|  | generator.ReleaseRightButton(); | 
|  | EXPECT_EQ(2, view->GetEventCount(ui::ET_MOUSE_PRESSED)); | 
|  | EXPECT_EQ(2, view->GetEventCount(ui::ET_MOUSE_RELEASED)); | 
|  | EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, view->last_flags()); | 
|  |  | 
|  | // Test mouse move events. | 
|  | EXPECT_EQ(0, view->GetEventCount(ui::ET_MOUSE_MOVED)); | 
|  | EXPECT_EQ(0, view->GetEventCount(ui::ET_MOUSE_ENTERED)); | 
|  |  | 
|  | // Move the mouse within the view (20, 20) -> (30, 30). | 
|  | generator.MoveMouseTo(gfx::Point(30, 30)); | 
|  | EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSE_MOVED)); | 
|  | EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSE_ENTERED)); | 
|  | EXPECT_EQ(ui::EF_NONE, view->last_flags()); | 
|  |  | 
|  | // Move it again - entered count shouldn't change. | 
|  | generator.MoveMouseTo(gfx::Point(31, 31)); | 
|  | EXPECT_EQ(2, view->GetEventCount(ui::ET_MOUSE_MOVED)); | 
|  | EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSE_ENTERED)); | 
|  | EXPECT_EQ(0, view->GetEventCount(ui::ET_MOUSE_EXITED)); | 
|  |  | 
|  | // Move it off the view. | 
|  | generator.MoveMouseTo(gfx::Point(5, 5)); | 
|  | EXPECT_EQ(2, view->GetEventCount(ui::ET_MOUSE_MOVED)); | 
|  | EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSE_ENTERED)); | 
|  | EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSE_EXITED)); | 
|  |  | 
|  | // Move it back on. | 
|  | generator.MoveMouseTo(gfx::Point(20, 20)); | 
|  | EXPECT_EQ(3, view->GetEventCount(ui::ET_MOUSE_MOVED)); | 
|  | EXPECT_EQ(2, view->GetEventCount(ui::ET_MOUSE_ENTERED)); | 
|  | EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSE_EXITED)); | 
|  |  | 
|  | // Drargging. Cover HasCapture() and NativeWidgetPrivate::IsMouseButtonDown(). | 
|  | generator.DragMouseTo(gfx::Point(40, 40)); | 
|  | EXPECT_EQ(3, view->GetEventCount(ui::ET_MOUSE_PRESSED)); | 
|  | EXPECT_EQ(3, view->GetEventCount(ui::ET_MOUSE_RELEASED)); | 
|  | EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSE_DRAGGED)); | 
|  | EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, view->last_flags()); | 
|  |  | 
|  | widget->CloseNow(); | 
|  | } | 
|  |  | 
|  | // Tests that a view does not receive entered, dragged, or moved events if | 
|  | // a mouse cursor is moved into it while the left mouse button is pressed. | 
|  | TEST_F(WidgetTest, DragIntoView) { | 
|  | EventCountView* container = new EventCountView; | 
|  | container->SetBounds(0, 0, 100, 80); | 
|  |  | 
|  | EventCountView* consume_view = new EventCountView; | 
|  | consume_view->set_handle_mode(EventCountView::CONSUME_EVENTS); | 
|  | consume_view->SetBounds(10, 10, 50, 40); | 
|  |  | 
|  | Widget* widget = CreateTopLevelFramelessPlatformWidget(); | 
|  | widget->SetBounds(gfx::Rect(0, 0, 100, 80)); | 
|  | widget->SetContentsView(container); | 
|  | container->AddChildView(consume_view); | 
|  |  | 
|  | widget->Show(); | 
|  |  | 
|  | ui::test::EventGenerator generator(GetContext(), widget->GetNativeWindow()); | 
|  | generator.set_current_location(gfx::Point(75, 15)); | 
|  |  | 
|  | generator.PressLeftButton(); | 
|  | generator.MoveMouseTo(gfx::Point(20, 20)); | 
|  | EXPECT_EQ(0, consume_view->GetEventCount(ui::ET_MOUSE_ENTERED)); | 
|  | EXPECT_EQ(0, consume_view->GetEventCount(ui::ET_MOUSE_DRAGGED)); | 
|  | EXPECT_EQ(0, consume_view->GetEventCount(ui::ET_MOUSE_MOVED)); | 
|  |  | 
|  | widget->CloseNow(); | 
|  | } | 
|  |  | 
|  | // Tests that a view receives the correct mouse events if a mouse cursor | 
|  | // is moved out of its bounds while the left mouse button is pressed. | 
|  | TEST_F(WidgetTest, DragOutOfView) { | 
|  | EventCountView* container = new EventCountView; | 
|  | container->SetBounds(0, 0, 100, 80); | 
|  |  | 
|  | EventCountView* consume_view = new EventCountView; | 
|  | consume_view->set_handle_mode(EventCountView::CONSUME_EVENTS); | 
|  | consume_view->SetBounds(10, 10, 50, 40); | 
|  |  | 
|  | Widget* widget = CreateTopLevelFramelessPlatformWidget(); | 
|  | widget->SetBounds(gfx::Rect(0, 0, 100, 80)); | 
|  | widget->SetContentsView(container); | 
|  | container->AddChildView(consume_view); | 
|  |  | 
|  | widget->Show(); | 
|  |  | 
|  | ui::test::EventGenerator generator(GetContext(), widget->GetNativeWindow()); | 
|  | generator.set_current_location(gfx::Point(20, 20)); | 
|  |  | 
|  | generator.PressLeftButton(); | 
|  | EXPECT_EQ(1, consume_view->GetEventCount(ui::ET_MOUSE_PRESSED)); | 
|  | EXPECT_EQ(0, container->GetEventCount(ui::ET_MOUSE_PRESSED)); | 
|  | consume_view->ResetCounts(); | 
|  |  | 
|  | generator.MoveMouseTo(gfx::Point(70, 70)); | 
|  | EXPECT_EQ(0, consume_view->GetEventCount(ui::ET_MOUSE_EXITED)); | 
|  | EXPECT_EQ(1, consume_view->GetEventCount(ui::ET_MOUSE_DRAGGED)); | 
|  | EXPECT_EQ(0, consume_view->GetEventCount(ui::ET_MOUSE_MOVED)); | 
|  | EXPECT_EQ(0, container->GetEventCount(ui::ET_MOUSE_ENTERED)); | 
|  | EXPECT_EQ(0, container->GetEventCount(ui::ET_MOUSE_DRAGGED)); | 
|  | EXPECT_EQ(0, container->GetEventCount(ui::ET_MOUSE_MOVED)); | 
|  | consume_view->ResetCounts(); | 
|  |  | 
|  | generator.MoveMouseTo(gfx::Point(71, 71)); | 
|  | EXPECT_EQ(1, consume_view->GetEventCount(ui::ET_MOUSE_DRAGGED)); | 
|  | EXPECT_EQ(0, consume_view->GetEventCount(ui::ET_MOUSE_MOVED)); | 
|  | EXPECT_EQ(0, container->GetEventCount(ui::ET_MOUSE_DRAGGED)); | 
|  | EXPECT_EQ(0, container->GetEventCount(ui::ET_MOUSE_MOVED)); | 
|  | consume_view->ResetCounts(); | 
|  |  | 
|  | generator.ReleaseLeftButton(); | 
|  | EXPECT_EQ(1, consume_view->GetEventCount(ui::ET_MOUSE_RELEASED)); | 
|  | EXPECT_EQ(0, container->GetEventCount(ui::ET_MOUSE_RELEASED)); | 
|  | consume_view->ResetCounts(); | 
|  |  | 
|  | widget->CloseNow(); | 
|  | } | 
|  |  | 
|  | // Tests that the root view is correctly set up for Widget types that do not | 
|  | // require a non-client view, before any other views are added to the widget. | 
|  | // That is, before Widget::ReorderNativeViews() is called which, if called with | 
|  | // a root view not set, could cause the root view to get resized to the widget. | 
|  | TEST_F(WidgetTest, NonClientWindowValidAfterInit) { | 
|  | Widget* widget = CreateTopLevelFramelessPlatformWidget(); | 
|  | View* root_view = widget->GetRootView(); | 
|  |  | 
|  | // Size the root view to exceed the widget bounds. | 
|  | const gfx::Rect test_rect(0, 0, 500, 500); | 
|  | root_view->SetBoundsRect(test_rect); | 
|  |  | 
|  | EXPECT_NE(test_rect.size(), widget->GetWindowBoundsInScreen().size()); | 
|  |  | 
|  | EXPECT_EQ(test_rect, root_view->bounds()); | 
|  | widget->ReorderNativeViews(); | 
|  | EXPECT_EQ(test_rect, root_view->bounds()); | 
|  |  | 
|  | widget->CloseNow(); | 
|  | } | 
|  |  | 
|  | }  // namespace test | 
|  | }  // namespace views |