| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/views/widget/native_widget_aura.h" |
| |
| #include <memory> |
| |
| #include "base/command_line.h" |
| #include "base/macros.h" |
| #include "base/run_loop.h" |
| #include "services/ws/public/mojom/window_tree_constants.mojom.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/aura/env.h" |
| #include "ui/aura/layout_manager.h" |
| #include "ui/aura/test/aura_test_base.h" |
| #include "ui/aura/window.h" |
| #include "ui/aura/window_observer.h" |
| #include "ui/aura/window_tree_host.h" |
| #include "ui/events/event.h" |
| #include "ui/events/event_utils.h" |
| #include "ui/views/layout/fill_layout.h" |
| #include "ui/views/test/widget_test.h" |
| #include "ui/views/widget/root_view.h" |
| #include "ui/views/widget/widget_delegate.h" |
| #include "ui/wm/core/base_focus_rules.h" |
| #include "ui/wm/core/default_activation_client.h" |
| #include "ui/wm/core/focus_controller.h" |
| #include "ui/wm/core/transient_window_manager.h" |
| |
| namespace views { |
| namespace { |
| |
| NativeWidgetAura* Init(aura::Window* parent, Widget* widget) { |
| Widget::InitParams params(Widget::InitParams::TYPE_POPUP); |
| params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| params.parent = parent; |
| widget->Init(params); |
| return static_cast<NativeWidgetAura*>(widget->native_widget()); |
| } |
| |
| // TestFocusRules is intended to provide a way to manually set a window's |
| // activatability so that the focus rules can be tested. |
| class TestFocusRules : public wm::BaseFocusRules { |
| public: |
| TestFocusRules() {} |
| ~TestFocusRules() override {} |
| |
| void set_can_activate(bool can_activate) { can_activate_ = can_activate; } |
| |
| // wm::BaseFocusRules overrides: |
| bool SupportsChildActivation(aura::Window* window) const override { |
| return true; |
| } |
| |
| bool CanActivateWindow(aura::Window* window) const override { |
| return can_activate_; |
| } |
| |
| private: |
| bool can_activate_ = true; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestFocusRules); |
| }; |
| |
| class NativeWidgetAuraTest : public aura::test::AuraTestBase { |
| public: |
| NativeWidgetAuraTest() {} |
| ~NativeWidgetAuraTest() override {} |
| |
| TestFocusRules* test_focus_rules() { return test_focus_rules_; } |
| |
| // testing::Test overrides: |
| void SetUp() override { |
| AuraTestBase::SetUp(); |
| test_focus_rules_ = new TestFocusRules; |
| focus_controller_.reset(new wm::FocusController(test_focus_rules_)); |
| wm::SetActivationClient(root_window(), focus_controller_.get()); |
| host()->SetBoundsInPixels(gfx::Rect(640, 480)); |
| } |
| |
| private: |
| std::unique_ptr<wm::FocusController> focus_controller_; |
| TestFocusRules* test_focus_rules_; |
| |
| DISALLOW_COPY_AND_ASSIGN(NativeWidgetAuraTest); |
| }; |
| |
| TEST_F(NativeWidgetAuraTest, CenterWindowLargeParent) { |
| // Make a parent window larger than the host represented by |
| // WindowEventDispatcher. |
| std::unique_ptr<aura::Window> parent(new aura::Window(nullptr)); |
| parent->Init(ui::LAYER_NOT_DRAWN); |
| parent->SetBounds(gfx::Rect(0, 0, 1024, 800)); |
| std::unique_ptr<Widget> widget(new Widget()); |
| NativeWidgetAura* window = Init(parent.get(), widget.get()); |
| |
| window->CenterWindow(gfx::Size(100, 100)); |
| EXPECT_EQ(gfx::Rect( (640 - 100) / 2, |
| (480 - 100) / 2, |
| 100, 100), |
| window->GetNativeWindow()->bounds()); |
| widget->CloseNow(); |
| } |
| |
| TEST_F(NativeWidgetAuraTest, CenterWindowSmallParent) { |
| // Make a parent window smaller than the host represented by |
| // WindowEventDispatcher. |
| std::unique_ptr<aura::Window> parent(new aura::Window(nullptr)); |
| parent->Init(ui::LAYER_NOT_DRAWN); |
| parent->SetBounds(gfx::Rect(0, 0, 480, 320)); |
| std::unique_ptr<Widget> widget(new Widget()); |
| NativeWidgetAura* window = Init(parent.get(), widget.get()); |
| |
| window->CenterWindow(gfx::Size(100, 100)); |
| EXPECT_EQ(gfx::Rect( (480 - 100) / 2, |
| (320 - 100) / 2, |
| 100, 100), |
| window->GetNativeWindow()->bounds()); |
| widget->CloseNow(); |
| } |
| |
| // Verifies CenterWindow() constrains to parent size. |
| TEST_F(NativeWidgetAuraTest, CenterWindowSmallParentNotAtOrigin) { |
| // Make a parent window smaller than the host represented by |
| // WindowEventDispatcher and offset it slightly from the origin. |
| std::unique_ptr<aura::Window> parent(new aura::Window(nullptr)); |
| parent->Init(ui::LAYER_NOT_DRAWN); |
| parent->SetBounds(gfx::Rect(20, 40, 480, 320)); |
| std::unique_ptr<Widget> widget(new Widget()); |
| NativeWidgetAura* window = Init(parent.get(), widget.get()); |
| window->CenterWindow(gfx::Size(500, 600)); |
| |
| // |window| should be no bigger than |parent|. |
| EXPECT_EQ("20,40 480x320", window->GetNativeWindow()->bounds().ToString()); |
| widget->CloseNow(); |
| } |
| |
| TEST_F(NativeWidgetAuraTest, CreateMinimized) { |
| Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); |
| params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| params.parent = nullptr; |
| params.context = root_window(); |
| params.show_state = ui::SHOW_STATE_MINIMIZED; |
| params.bounds.SetRect(0, 0, 1024, 800); |
| std::unique_ptr<Widget> widget(new Widget()); |
| widget->Init(params); |
| widget->Show(); |
| |
| EXPECT_TRUE(widget->IsMinimized()); |
| widget->CloseNow(); |
| } |
| |
| // A WindowObserver that counts kShowStateKey property changes. |
| class TestWindowObserver : public aura::WindowObserver { |
| public: |
| explicit TestWindowObserver(gfx::NativeWindow window) : window_(window) { |
| window_->AddObserver(this); |
| } |
| ~TestWindowObserver() override { |
| window_->RemoveObserver(this); |
| } |
| |
| // aura::WindowObserver: |
| void OnWindowPropertyChanged(aura::Window* window, |
| const void* key, |
| intptr_t old) override { |
| if (key != aura::client::kShowStateKey) |
| return; |
| count_++; |
| state_ = window_->GetProperty(aura::client::kShowStateKey); |
| } |
| |
| int count() const { return count_; } |
| ui::WindowShowState state() const { return state_; } |
| void Reset() { count_ = 0; } |
| |
| private: |
| gfx::NativeWindow window_; |
| int count_ = 0; |
| ui::WindowShowState state_ = ui::WindowShowState::SHOW_STATE_DEFAULT; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestWindowObserver); |
| }; |
| |
| // Tests that window transitions from normal to minimized and back do not |
| // involve extra show state transitions. |
| TEST_F(NativeWidgetAuraTest, ToggleState) { |
| Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); |
| params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| params.parent = nullptr; |
| params.context = root_window(); |
| params.show_state = ui::SHOW_STATE_NORMAL; |
| params.bounds.SetRect(0, 0, 1024, 800); |
| Widget widget; |
| widget.Init(params); |
| std::unique_ptr<TestWindowObserver> observer( |
| new TestWindowObserver(widget.GetNativeWindow())); |
| widget.Show(); |
| EXPECT_FALSE(widget.IsMinimized()); |
| EXPECT_EQ(0, observer->count()); |
| EXPECT_EQ(ui::WindowShowState::SHOW_STATE_DEFAULT, observer->state()); |
| |
| widget.Minimize(); |
| EXPECT_TRUE(widget.IsMinimized()); |
| EXPECT_EQ(1, observer->count()); |
| EXPECT_EQ(ui::WindowShowState::SHOW_STATE_MINIMIZED, observer->state()); |
| observer->Reset(); |
| |
| widget.Show(); |
| widget.Restore(); |
| EXPECT_EQ(1, observer->count()); |
| EXPECT_EQ(ui::WindowShowState::SHOW_STATE_NORMAL, observer->state()); |
| |
| observer.reset(); |
| EXPECT_FALSE(widget.IsMinimized()); |
| widget.CloseNow(); |
| } |
| |
| class TestLayoutManagerBase : public aura::LayoutManager { |
| public: |
| TestLayoutManagerBase() {} |
| ~TestLayoutManagerBase() override {} |
| |
| // aura::LayoutManager: |
| void OnWindowResized() override {} |
| void OnWindowAddedToLayout(aura::Window* child) override {} |
| void OnWillRemoveWindowFromLayout(aura::Window* child) override {} |
| void OnWindowRemovedFromLayout(aura::Window* child) override {} |
| void OnChildWindowVisibilityChanged(aura::Window* child, |
| bool visible) override {} |
| void SetChildBounds(aura::Window* child, |
| const gfx::Rect& requested_bounds) override {} |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(TestLayoutManagerBase); |
| }; |
| |
| // Used by ShowMaximizedDoesntBounceAround. See it for details. |
| class MaximizeLayoutManager : public TestLayoutManagerBase { |
| public: |
| MaximizeLayoutManager() {} |
| ~MaximizeLayoutManager() override {} |
| |
| private: |
| // aura::LayoutManager: |
| void OnWindowAddedToLayout(aura::Window* child) override { |
| // This simulates what happens when adding a maximized window. |
| SetChildBoundsDirect(child, gfx::Rect(0, 0, 300, 300)); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(MaximizeLayoutManager); |
| }; |
| |
| // This simulates BrowserView, which creates a custom RootView so that |
| // OnNativeWidgetSizeChanged that is invoked during Init matters. |
| class TestWidget : public views::Widget { |
| public: |
| TestWidget() : did_size_change_more_than_once_(false) { |
| } |
| |
| // Returns true if the size changes to a non-empty size, and then to another |
| // size. |
| bool did_size_change_more_than_once() const { |
| return did_size_change_more_than_once_; |
| } |
| |
| void OnNativeWidgetSizeChanged(const gfx::Size& new_size) override { |
| if (last_size_.IsEmpty()) |
| last_size_ = new_size; |
| else if (!did_size_change_more_than_once_ && new_size != last_size_) |
| did_size_change_more_than_once_ = true; |
| Widget::OnNativeWidgetSizeChanged(new_size); |
| } |
| |
| private: |
| bool did_size_change_more_than_once_; |
| gfx::Size last_size_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestWidget); |
| }; |
| |
| // Verifies the size of the widget doesn't change more than once during Init if |
| // the window ends up maximized. This is important as otherwise |
| // RenderWidgetHostViewAura ends up getting resized during construction, which |
| // leads to noticable flashes. |
| TEST_F(NativeWidgetAuraTest, ShowMaximizedDoesntBounceAround) { |
| root_window()->SetBounds(gfx::Rect(0, 0, 640, 480)); |
| root_window()->SetLayoutManager(new MaximizeLayoutManager); |
| std::unique_ptr<TestWidget> widget(new TestWidget()); |
| Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); |
| params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| params.parent = nullptr; |
| params.context = root_window(); |
| params.show_state = ui::SHOW_STATE_MAXIMIZED; |
| params.bounds = gfx::Rect(10, 10, 100, 200); |
| widget->Init(params); |
| EXPECT_FALSE(widget->did_size_change_more_than_once()); |
| widget->CloseNow(); |
| } |
| |
| class PropertyTestLayoutManager : public TestLayoutManagerBase { |
| public: |
| PropertyTestLayoutManager() : added_(false) {} |
| ~PropertyTestLayoutManager() override {} |
| |
| bool added() const { return added_; } |
| |
| private: |
| // aura::LayoutManager: |
| void OnWindowAddedToLayout(aura::Window* child) override { |
| EXPECT_EQ(ws::mojom::kResizeBehaviorCanResize | |
| ws::mojom::kResizeBehaviorCanMaximize | |
| ws::mojom::kResizeBehaviorCanMinimize, |
| child->GetProperty(aura::client::kResizeBehaviorKey)); |
| added_ = true; |
| } |
| |
| bool added_; |
| |
| DISALLOW_COPY_AND_ASSIGN(PropertyTestLayoutManager); |
| }; |
| |
| class PropertyTestWidgetDelegate : public views::WidgetDelegate { |
| public: |
| explicit PropertyTestWidgetDelegate(Widget* widget) : widget_(widget) {} |
| ~PropertyTestWidgetDelegate() override {} |
| |
| private: |
| // views::WidgetDelegate: |
| bool CanMaximize() const override { return true; } |
| bool CanMinimize() const override { return true; } |
| bool CanResize() const override { return true; } |
| void DeleteDelegate() override { delete this; } |
| Widget* GetWidget() override { return widget_; } |
| const Widget* GetWidget() const override { return widget_; } |
| |
| Widget* widget_; |
| DISALLOW_COPY_AND_ASSIGN(PropertyTestWidgetDelegate); |
| }; |
| |
| // Verifies the resize behavior when added to the layout manager. |
| TEST_F(NativeWidgetAuraTest, TestPropertiesWhenAddedToLayout) { |
| root_window()->SetBounds(gfx::Rect(0, 0, 640, 480)); |
| PropertyTestLayoutManager* layout_manager = new PropertyTestLayoutManager(); |
| root_window()->SetLayoutManager(layout_manager); |
| std::unique_ptr<TestWidget> widget(new TestWidget()); |
| Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); |
| params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| params.delegate = new PropertyTestWidgetDelegate(widget.get()); |
| params.parent = nullptr; |
| params.context = root_window(); |
| widget->Init(params); |
| EXPECT_TRUE(layout_manager->added()); |
| widget->CloseNow(); |
| } |
| |
| TEST_F(NativeWidgetAuraTest, GetClientAreaScreenBounds) { |
| // Create a widget. |
| Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); |
| params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| params.context = root_window(); |
| params.bounds.SetRect(10, 20, 300, 400); |
| std::unique_ptr<Widget> widget(new Widget()); |
| widget->Init(params); |
| |
| // For Aura, client area bounds match window bounds. |
| gfx::Rect client_bounds = widget->GetClientAreaBoundsInScreen(); |
| EXPECT_EQ(10, client_bounds.x()); |
| EXPECT_EQ(20, client_bounds.y()); |
| EXPECT_EQ(300, client_bounds.width()); |
| EXPECT_EQ(400, client_bounds.height()); |
| } |
| |
| // View subclass that tracks whether it has gotten a gesture event. |
| class GestureTrackingView : public views::View { |
| public: |
| GestureTrackingView() |
| : got_gesture_event_(false), |
| consume_gesture_event_(true) {} |
| |
| void set_consume_gesture_event(bool value) { |
| consume_gesture_event_ = value; |
| } |
| |
| void clear_got_gesture_event() { |
| got_gesture_event_ = false; |
| } |
| bool got_gesture_event() const { |
| return got_gesture_event_; |
| } |
| |
| // View overrides: |
| void OnGestureEvent(ui::GestureEvent* event) override { |
| got_gesture_event_ = true; |
| if (consume_gesture_event_) |
| event->StopPropagation(); |
| } |
| |
| private: |
| // Was OnGestureEvent() invoked? |
| bool got_gesture_event_; |
| |
| // Dictates what OnGestureEvent() returns. |
| bool consume_gesture_event_; |
| |
| DISALLOW_COPY_AND_ASSIGN(GestureTrackingView); |
| }; |
| |
| // Verifies a capture isn't set on touch press and that the view that gets |
| // the press gets the release. |
| TEST_F(NativeWidgetAuraTest, DontCaptureOnGesture) { |
| // Create two views (both sized the same). |child| is configured not to |
| // consume the gesture event. |
| GestureTrackingView* view = new GestureTrackingView(); |
| GestureTrackingView* child = new GestureTrackingView(); |
| child->set_consume_gesture_event(false); |
| view->SetLayoutManager(std::make_unique<FillLayout>()); |
| view->AddChildView(child); |
| std::unique_ptr<TestWidget> widget(new TestWidget()); |
| Widget::InitParams params(Widget::InitParams::TYPE_WINDOW_FRAMELESS); |
| params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| params.context = root_window(); |
| params.bounds = gfx::Rect(0, 0, 100, 200); |
| widget->Init(params); |
| widget->SetContentsView(view); |
| widget->Show(); |
| |
| ui::TouchEvent press( |
| ui::ET_TOUCH_PRESSED, gfx::Point(41, 51), ui::EventTimeForNow(), |
| ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 1)); |
| ui::EventDispatchDetails details = event_sink()->OnEventFromSource(&press); |
| ASSERT_FALSE(details.dispatcher_destroyed); |
| // Both views should get the press. |
| EXPECT_TRUE(view->got_gesture_event()); |
| EXPECT_TRUE(child->got_gesture_event()); |
| view->clear_got_gesture_event(); |
| child->clear_got_gesture_event(); |
| // Touch events should not automatically grab capture. |
| EXPECT_FALSE(widget->HasCapture()); |
| |
| // Release touch. Only |view| should get the release since that it consumed |
| // the press. |
| ui::TouchEvent release( |
| ui::ET_TOUCH_RELEASED, gfx::Point(250, 251), ui::EventTimeForNow(), |
| ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 1)); |
| details = event_sink()->OnEventFromSource(&release); |
| ASSERT_FALSE(details.dispatcher_destroyed); |
| EXPECT_TRUE(view->got_gesture_event()); |
| EXPECT_FALSE(child->got_gesture_event()); |
| view->clear_got_gesture_event(); |
| |
| // Work around for bug in NativeWidgetAura. |
| // TODO: fix bug and remove this. |
| widget->Close(); |
| } |
| |
| // Verifies views with layers are targeted for events properly. |
| TEST_F(NativeWidgetAuraTest, PreferViewLayersToChildWindows) { |
| // Create two widgets: |parent| and |child|. |child| is a child of |parent|. |
| views::View* parent_root = new views::View; |
| std::unique_ptr<Widget> parent(new Widget()); |
| Widget::InitParams parent_params(Widget::InitParams::TYPE_WINDOW_FRAMELESS); |
| parent_params.ownership = |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| parent_params.context = root_window(); |
| parent->Init(parent_params); |
| parent->SetContentsView(parent_root); |
| parent->SetBounds(gfx::Rect(0, 0, 400, 400)); |
| parent->Show(); |
| |
| std::unique_ptr<Widget> child(new Widget()); |
| Widget::InitParams child_params(Widget::InitParams::TYPE_CONTROL); |
| child_params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| child_params.parent = parent->GetNativeWindow(); |
| child->Init(child_params); |
| child->SetBounds(gfx::Rect(0, 0, 200, 200)); |
| child->Show(); |
| |
| // Point is over |child|. |
| EXPECT_EQ(child->GetNativeWindow(), |
| parent->GetNativeWindow()->GetEventHandlerForPoint( |
| gfx::Point(50, 50))); |
| |
| // Create a view with a layer and stack it at the bottom (below |child|). |
| views::View* view_with_layer = new views::View; |
| parent_root->AddChildView(view_with_layer); |
| view_with_layer->SetBounds(0, 0, 50, 50); |
| view_with_layer->SetPaintToLayer(); |
| |
| // Make sure that |child| still gets the event. |
| EXPECT_EQ(child->GetNativeWindow(), |
| parent->GetNativeWindow()->GetEventHandlerForPoint( |
| gfx::Point(20, 20))); |
| |
| // Move |view_with_layer| to the top and make sure it gets the |
| // event when the point is within |view_with_layer|'s bounds. |
| view_with_layer->layer()->parent()->StackAtTop( |
| view_with_layer->layer()); |
| EXPECT_EQ(parent->GetNativeWindow(), |
| parent->GetNativeWindow()->GetEventHandlerForPoint( |
| gfx::Point(20, 20))); |
| |
| // Point is over |child|, it should get the event. |
| EXPECT_EQ(child->GetNativeWindow(), |
| parent->GetNativeWindow()->GetEventHandlerForPoint( |
| gfx::Point(70, 70))); |
| |
| delete view_with_layer; |
| view_with_layer = nullptr; |
| |
| EXPECT_EQ(child->GetNativeWindow(), |
| parent->GetNativeWindow()->GetEventHandlerForPoint( |
| gfx::Point(20, 20))); |
| |
| // Work around for bug in NativeWidgetAura. |
| // TODO: fix bug and remove this. |
| parent->Close(); |
| } |
| |
| // Verifies views with layers are targeted for events properly. |
| TEST_F(NativeWidgetAuraTest, |
| ShouldDescendIntoChildForEventHandlingChecksVisibleBounds) { |
| // Create two widgets: |parent| and |child|. |child| is a child of |parent|. |
| View* parent_root_view = new View; |
| Widget parent; |
| Widget::InitParams parent_params(Widget::InitParams::TYPE_WINDOW_FRAMELESS); |
| parent_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| parent_params.context = root_window(); |
| parent.Init(parent_params); |
| parent.SetContentsView(parent_root_view); |
| parent.SetBounds(gfx::Rect(0, 0, 400, 400)); |
| parent.Show(); |
| |
| Widget child; |
| Widget::InitParams child_params(Widget::InitParams::TYPE_CONTROL); |
| child_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| child_params.parent = parent.GetNativeWindow(); |
| child.Init(child_params); |
| child.SetBounds(gfx::Rect(0, 0, 200, 200)); |
| child.Show(); |
| |
| // Point is over |child|. |
| EXPECT_EQ( |
| child.GetNativeWindow(), |
| parent.GetNativeWindow()->GetEventHandlerForPoint(gfx::Point(50, 50))); |
| |
| View* parent_root_view_child = new View; |
| parent_root_view->AddChildView(parent_root_view_child); |
| parent_root_view_child->SetBounds(0, 0, 10, 10); |
| |
| // Create a View whose layer extends outside the bounds of its parent. Event |
| // targetting should only consider the visible bounds. |
| View* parent_root_view_child_child = new View; |
| parent_root_view_child->AddChildView(parent_root_view_child_child); |
| parent_root_view_child_child->SetBounds(0, 0, 100, 100); |
| parent_root_view_child_child->SetPaintToLayer(); |
| parent_root_view_child_child->layer()->parent()->StackAtTop( |
| parent_root_view_child_child->layer()); |
| |
| // 20,20 is over |parent_root_view_child_child|'s layer, but not the visible |
| // bounds of |parent_root_view_child_child|, so |child| should be the event |
| // target. |
| EXPECT_EQ( |
| child.GetNativeWindow(), |
| parent.GetNativeWindow()->GetEventHandlerForPoint(gfx::Point(20, 20))); |
| } |
| |
| // Verifies that widget->FlashFrame() sets aura::client::kDrawAttentionKey, |
| // and activating the window clears it. |
| TEST_F(NativeWidgetAuraTest, FlashFrame) { |
| std::unique_ptr<Widget> widget(new Widget()); |
| Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); |
| params.context = root_window(); |
| params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| widget->Init(params); |
| aura::Window* window = widget->GetNativeWindow(); |
| EXPECT_FALSE(window->GetProperty(aura::client::kDrawAttentionKey)); |
| widget->FlashFrame(true); |
| EXPECT_TRUE(window->GetProperty(aura::client::kDrawAttentionKey)); |
| widget->FlashFrame(false); |
| EXPECT_FALSE(window->GetProperty(aura::client::kDrawAttentionKey)); |
| widget->FlashFrame(true); |
| EXPECT_TRUE(window->GetProperty(aura::client::kDrawAttentionKey)); |
| widget->Activate(); |
| EXPECT_FALSE(window->GetProperty(aura::client::kDrawAttentionKey)); |
| } |
| |
| TEST_F(NativeWidgetAuraTest, NoCrashOnThemeAfterClose) { |
| std::unique_ptr<aura::Window> parent(new aura::Window(nullptr)); |
| parent->Init(ui::LAYER_NOT_DRAWN); |
| parent->SetBounds(gfx::Rect(0, 0, 480, 320)); |
| std::unique_ptr<Widget> widget(new Widget()); |
| Init(parent.get(), widget.get()); |
| widget->Show(); |
| widget->Close(); |
| base::RunLoop().RunUntilIdle(); |
| widget->GetNativeTheme(); // Shouldn't crash. |
| } |
| |
| // Used to track calls to WidgetDelegate::OnWidgetMove(). |
| class MoveTestWidgetDelegate : public WidgetDelegateView { |
| public: |
| MoveTestWidgetDelegate() : got_move_(false) {} |
| ~MoveTestWidgetDelegate() override {} |
| |
| void ClearGotMove() { got_move_ = false; } |
| bool got_move() const { return got_move_; } |
| |
| // WidgetDelegate overrides: |
| void OnWidgetMove() override { got_move_ = true; } |
| |
| private: |
| bool got_move_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MoveTestWidgetDelegate); |
| }; |
| |
| // This test simulates what happens when a window is normally maximized. That |
| // is, it's layer is acquired for animation then the window is maximized. |
| // Acquiring the layer resets the bounds of the window. This test verifies the |
| // Widget is still notified correctly of a move in this case. |
| TEST_F(NativeWidgetAuraTest, OnWidgetMovedInvokedAfterAcquireLayer) { |
| // |delegate| deletes itself when the widget is destroyed. |
| MoveTestWidgetDelegate* delegate = new MoveTestWidgetDelegate; |
| Widget* widget = |
| Widget::CreateWindowWithContextAndBounds(delegate, |
| root_window(), |
| gfx::Rect(10, 10, 100, 200)); |
| widget->Show(); |
| delegate->ClearGotMove(); |
| // Simulate a maximize with animation. |
| delete widget->GetNativeView()->RecreateLayer().release(); |
| widget->SetBounds(gfx::Rect(0, 0, 500, 500)); |
| EXPECT_TRUE(delegate->got_move()); |
| widget->CloseNow(); |
| } |
| |
| // Tests that if a widget has a view which should be initially focused when the |
| // widget is shown, this view should not get focused if the associated window |
| // can not be activated. |
| TEST_F(NativeWidgetAuraTest, PreventFocusOnNonActivableWindow) { |
| test_focus_rules()->set_can_activate(false); |
| views::test::TestInitialFocusWidgetDelegate delegate(root_window()); |
| delegate.GetWidget()->Show(); |
| EXPECT_FALSE(delegate.view()->HasFocus()); |
| |
| test_focus_rules()->set_can_activate(true); |
| views::test::TestInitialFocusWidgetDelegate delegate2(root_window()); |
| delegate2.GetWidget()->Show(); |
| EXPECT_TRUE(delegate2.view()->HasFocus()); |
| } |
| |
| // Tests that the transient child bubble window is only visible if the parent is |
| // visible. |
| TEST_F(NativeWidgetAuraTest, VisibilityOfChildBubbleWindow) { |
| // Create a parent window. |
| Widget parent; |
| Widget::InitParams parent_params(Widget::InitParams::TYPE_WINDOW); |
| parent_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| parent_params.context = root_window(); |
| parent.Init(parent_params); |
| parent.SetBounds(gfx::Rect(0, 0, 480, 320)); |
| |
| // Add a child bubble window to the above parent window and show it. |
| Widget child; |
| Widget::InitParams child_params(Widget::InitParams::TYPE_BUBBLE); |
| child_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| child_params.parent = parent.GetNativeWindow(); |
| child.Init(child_params); |
| child.SetBounds(gfx::Rect(0, 0, 200, 200)); |
| child.Show(); |
| |
| // Check that the bubble window is added as the transient child and it is |
| // hidden because parent window is hidden. |
| wm::TransientWindowManager* manager = |
| wm::TransientWindowManager::GetOrCreate(child.GetNativeWindow()); |
| EXPECT_EQ(parent.GetNativeWindow(), manager->transient_parent()); |
| EXPECT_FALSE(parent.IsVisible()); |
| EXPECT_FALSE(child.IsVisible()); |
| |
| // Show the parent window should make the transient child bubble visible. |
| parent.Show(); |
| EXPECT_TRUE(parent.IsVisible()); |
| EXPECT_TRUE(child.IsVisible()); |
| } |
| |
| } // namespace |
| } // namespace views |