diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2015-10-13 13:24:50 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2015-10-14 10:57:25 +0000 |
commit | af3d4809763ef308f08ced947a73b624729ac7ea (patch) | |
tree | 4402b911e30383f6c6dace1e8cf3b8e85355db3a /chromium/ui/views/widget | |
parent | 0e8ff63a407fe323e215bb1a2c423c09a4747c8a (diff) | |
download | qtwebengine-chromium-af3d4809763ef308f08ced947a73b624729ac7ea.tar.gz |
BASELINE: Update Chromium to 47.0.2526.14
Also adding in sources needed for spellchecking.
Change-Id: Idd44170fa1616f26315188970a8d5ba7d472b18a
Reviewed-by: Michael BrĂ¼ning <michael.bruning@theqtcompany.com>
Diffstat (limited to 'chromium/ui/views/widget')
23 files changed, 746 insertions, 410 deletions
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h index 888791bd34f..8b040cae30c 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h @@ -242,7 +242,7 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11 // Reprocesses the most recent mouse move event if the mouse has not moved // in a while in case the window stacking order has changed and // |source_current_window_| needs to be updated. - base::OneShotTimer<DesktopDragDropClientAuraX11> repeat_mouse_move_timer_; + base::OneShotTimer repeat_mouse_move_timer_; // When the mouse is released, we need to wait for the last XdndStatus message // only if we have previously received a status message from @@ -271,7 +271,7 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11 // Ends the move loop if the target is too slow to respond after the mouse is // released. - base::OneShotTimer<DesktopDragDropClientAuraX11> end_move_loop_timer_; + base::OneShotTimer end_move_loop_timer_; // Widget that the user drags around. May be NULL. scoped_ptr<Widget> drag_widget_; diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc index 8afa69bdcbf..1549b2e45f0 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc @@ -5,7 +5,6 @@ #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h" #include "base/metrics/histogram_macros.h" -#include "base/tracked_objects.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/drag_source_win.h" #include "ui/base/dragdrop/drop_target_event.h" @@ -52,13 +51,9 @@ int DesktopDragDropClientWin::StartDragAndDrop( UMA_HISTOGRAM_ENUMERATION("Event.DragDrop.Start", source, ui::DragDropTypes::DRAG_EVENT_SOURCE_COUNT); - // Use task stopwatch to exclude the drag-drop time current task, if any. - tracked_objects::TaskStopwatch stopwatch; - stopwatch.Start(); HRESULT result = DoDragDrop( ui::OSExchangeDataProviderWin::GetIDataObject(data), drag_source_.Get(), ui::DragDropTypes::DragOperationToDropEffect(operation), &effect); - stopwatch.Stop(); drag_source_copy->set_data(nullptr); if (alive) diff --git a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc index 64f7c42cd37..b2d301d018f 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc @@ -20,8 +20,13 @@ #include "ui/views/test/views_test_base.h" #include "ui/views/test/widget_test.h" #include "ui/views/widget/widget.h" +#include "ui/views/window/dialog_delegate.h" #include "ui/wm/public/dispatcher_client.h" +#if defined(OS_WIN) +#include "ui/views/win/hwnd_util.h" +#endif + namespace views { namespace test { @@ -504,5 +509,128 @@ TEST_F(DesktopAuraWidgetTest, CloseWidgetDuringMouseReleased) { RunCloseWidgetDuringDispatchTest(this, ui::ET_MOUSE_RELEASED); } +// Provides functionality to create a window modal dialog. +class ModalDialogDelegate : public DialogDelegateView { + public: + ModalDialogDelegate() {} + ~ModalDialogDelegate() override {} + + // WidgetDelegate overrides. + 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::EventTimeForNow(), + 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::EventTimeForNow(), 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::EventTimeForNow(), + 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(); +} + +#if defined(OS_WIN) +// Tests whether we can activate the top level widget when a modal dialog is +// active. +TEST_F(WidgetTest, WindowModalityActivationTest) { + TestDesktopWidgetDelegate widget_delegate; + widget_delegate.InitWidget(CreateParams(Widget::InitParams::TYPE_WINDOW)); + + Widget* top_level_widget = widget_delegate.GetWidget(); + 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->GetNativeView()); + 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(); +} +#endif // defined(OS_WIN) + } // namespace test } // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc index fc477e61d5d..827f294f4d0 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc @@ -184,11 +184,10 @@ gfx::Display DesktopScreenX11::GetDisplayNearestWindow( } gfx::Display DesktopScreenX11::GetDisplayNearestPoint( - const gfx::Point& requested_point) const { - const gfx::Point point_in_pixel = DIPToPixelPoint(requested_point); + const gfx::Point& point) const { for (std::vector<gfx::Display>::const_iterator it = displays_.begin(); it != displays_.end(); ++it) { - if (it->bounds().Contains(point_in_pixel)) + if (it->bounds().Contains(point)) return *it; } @@ -239,7 +238,7 @@ uint32_t DesktopScreenX11::DispatchEvent(const ui::PlatformEvent& event) { if (configure_timer_.get() && configure_timer_->IsRunning()) { configure_timer_->Reset(); } else { - configure_timer_.reset(new base::OneShotTimer<DesktopScreenX11>()); + configure_timer_.reset(new base::OneShotTimer()); configure_timer_->Start( FROM_HERE, base::TimeDelta::FromMilliseconds(kConfigureDelayMs), @@ -326,9 +325,8 @@ std::vector<gfx::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() { gfx::ToFlooredPoint( gfx::ScalePoint(intersection_in_pixels.origin(), 1.0f / display.device_scale_factor())), - gfx::ToFlooredSize( - gfx::ScaleSize(intersection_in_pixels.size(), - 1.0f / display.device_scale_factor())))); + gfx::ScaleToFlooredSize(intersection_in_pixels.size(), + 1.0f / display.device_scale_factor()))); } switch (crtc->rotation) { diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h index 0f9149796e6..4466ba48e91 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h @@ -75,7 +75,7 @@ class VIEWS_EXPORT DesktopScreenX11 : public gfx::Screen, // The timer to delay configuring outputs. See also the comments in // Dispatch(). - scoped_ptr<base::OneShotTimer<DesktopScreenX11> > configure_timer_; + scoped_ptr<base::OneShotTimer> configure_timer_; gfx::DisplayChangeNotifier change_notifier_; diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc index 83bf7d90dc5..466bb79d393 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc @@ -205,6 +205,8 @@ TEST_F(DesktopScreenX11Test, GetDisplayNearestPoint) { gfx::Rect(640, 0, 1024, 768))); NotifyDisplaysChanged(displays); + EXPECT_EQ(kFirstDisplay, + screen()->GetDisplayNearestPoint(gfx::Point(630, 10)).id()); EXPECT_EQ(kSecondDisplay, screen()->GetDisplayNearestPoint(gfx::Point(650, 10)).id()); EXPECT_EQ(kFirstDisplay, diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc index 9278c2968c9..48368f962f2 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc @@ -140,12 +140,13 @@ void DesktopWindowTreeHostWin::Init(aura::Window* content_window, gfx::Rect pixel_bounds = gfx::win::DIPToScreenRect(params.bounds); message_handler_->Init(parent_hwnd, pixel_bounds); - if (params.type == Widget::InitParams::TYPE_MENU) { + if (params.force_software_compositing) { ::SetProp(GetAcceleratedWidget(), kForceSoftwareCompositor, reinterpret_cast<HANDLE>(true)); } - CreateCompositor(GetAcceleratedWidget()); + CreateCompositor(); + OnAcceleratedWidgetAvailable(); } void DesktopWindowTreeHostWin::OnNativeWidgetCreated( @@ -804,15 +805,8 @@ bool DesktopWindowTreeHostWin::HandleMouseEvent(const ui::MouseEvent& event) { return event.handled(); } -bool DesktopWindowTreeHostWin::HandleKeyEvent(const ui::KeyEvent& event) { - return false; -} - -bool DesktopWindowTreeHostWin::HandleUntranslatedKeyEvent( - const ui::KeyEvent& event) { - ui::KeyEvent duplicate_event(event); - SendEventToProcessor(&duplicate_event); - return duplicate_event.handled(); +void DesktopWindowTreeHostWin::HandleKeyEvent(ui::KeyEvent* event) { + GetInputMethod()->DispatchKeyEvent(event); } void DesktopWindowTreeHostWin::HandleTouchEvent( diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h index 047498f81b6..363c0193819 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h @@ -170,8 +170,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin void HandleNativeFocus(HWND last_focused_window) override; void HandleNativeBlur(HWND focused_window) override; bool HandleMouseEvent(const ui::MouseEvent& event) override; - bool HandleKeyEvent(const ui::KeyEvent& event) override; - bool HandleUntranslatedKeyEvent(const ui::KeyEvent& event) override; + void HandleKeyEvent(ui::KeyEvent* event) override; void HandleTouchEvent(const ui::TouchEvent& event) override; bool HandleIMEMessage(UINT message, WPARAM w_param, diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc index a56deb7fc9f..ed0cb1d6f98 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc @@ -23,6 +23,7 @@ #include "ui/aura/window_property.h" #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h" #include "ui/base/hit_test.h" +#include "ui/base/ime/input_method.h" #include "ui/base/x/x11_util.h" #include "ui/events/devices/x11/device_data_manager_x11.h" #include "ui/events/devices/x11/device_list_cache_x11.h" @@ -975,16 +976,17 @@ void DesktopWindowTreeHostX11::SetBounds( unsigned value_mask = 0; if (size_changed) { + // Update the minimum and maximum sizes in case they have changed. + UpdateMinAndMaxSize(); + if (bounds_in_pixels.width() < min_size_in_pixels_.width() || bounds_in_pixels.height() < min_size_in_pixels_.height() || (!max_size_in_pixels_.IsEmpty() && (bounds_in_pixels.width() > max_size_in_pixels_.width() || bounds_in_pixels.height() > max_size_in_pixels_.height()))) { - // Update the minimum and maximum sizes in case they have changed. - UpdateMinAndMaxSize(); - gfx::Size size_in_pixels = bounds_in_pixels.size(); - size_in_pixels.SetToMin(max_size_in_pixels_); + if (!max_size_in_pixels_.IsEmpty()) + size_in_pixels.SetToMin(max_size_in_pixels_); size_in_pixels.SetToMax(min_size_in_pixels_); bounds_in_pixels.set_size(size_in_pixels); } @@ -1274,7 +1276,8 @@ void DesktopWindowTreeHostX11::InitX11Window( if (window_icon) { SetWindowIcons(gfx::ImageSkia(), *window_icon); } - CreateCompositor(GetAcceleratedWidget()); + CreateCompositor(); + OnAcceleratedWidgetAvailable(); } gfx::Size DesktopWindowTreeHostX11::AdjustSize( @@ -1517,6 +1520,10 @@ void DesktopWindowTreeHostX11::DispatchTouchEvent(ui::TouchEvent* event) { } } +void DesktopWindowTreeHostX11::DispatchKeyEvent(ui::KeyEvent* event) { + GetInputMethod()->DispatchKeyEvent(event); +} + void DesktopWindowTreeHostX11::ConvertEventToDifferentHost( ui::LocatedEvent* located_event, DesktopWindowTreeHostX11* host) { @@ -1745,7 +1752,7 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent( } case KeyPress: { ui::KeyEvent keydown_event(xev); - SendEventToProcessor(&keydown_event); + DispatchKeyEvent(&keydown_event); break; } case KeyRelease: { @@ -1756,7 +1763,7 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent( break; ui::KeyEvent key_event(xev); - SendEventToProcessor(&key_event); + DispatchKeyEvent(&key_event); break; } case ButtonPress: @@ -1881,7 +1888,7 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent( case ui::ET_KEY_PRESSED: case ui::ET_KEY_RELEASED: { ui::KeyEvent key_event(xev); - SendEventToProcessor(&key_event); + DispatchKeyEvent(&key_event); break; } case ui::ET_UNKNOWN: @@ -2021,14 +2028,14 @@ gfx::Rect DesktopWindowTreeHostX11::GetWorkAreaBoundsInPixels() const { gfx::Rect DesktopWindowTreeHostX11::ToDIPRect( const gfx::Rect& rect_in_pixels) const { - gfx::RectF rect_in_dip = rect_in_pixels; + gfx::RectF rect_in_dip = gfx::RectF(rect_in_pixels); GetRootTransform().TransformRectReverse(&rect_in_dip); return gfx::ToEnclosingRect(rect_in_dip); } gfx::Rect DesktopWindowTreeHostX11::ToPixelRect( const gfx::Rect& rect_in_dip) const { - gfx::RectF rect_in_pixels = rect_in_dip; + gfx::RectF rect_in_pixels = gfx::RectF(rect_in_dip); GetRootTransform().TransformRect(&rect_in_pixels); return gfx::ToEnclosingRect(rect_in_pixels); } diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h index 787372d9c26..73e9e3e6fa7 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h @@ -209,6 +209,9 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 // and dispatched to that host instead. void DispatchTouchEvent(ui::TouchEvent* event); + // Dispatches a key event. + void DispatchKeyEvent(ui::KeyEvent* event); + // Updates the location of |located_event| to be in |host|'s coordinate system // so that it can be dispatched to |host|. void ConvertEventToDifferentHost(ui::LocatedEvent* located_event, diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc index 3d066539b63..0a947b830f2 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc @@ -23,7 +23,7 @@ #include "ui/events/platform/x11/x11_event_source.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/x/x11_atom_cache.h" -#include "ui/gl/gl_surface.h" +#include "ui/gl/test/gl_surface_test_support.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/test/views_test_base.h" #include "ui/views/test/x11_property_change_waiter.h" @@ -126,7 +126,7 @@ class DesktopWindowTreeHostX11Test : public ViewsTestBase { ~DesktopWindowTreeHostX11Test() override {} static void SetUpTestCase() { - gfx::GLSurface::InitializeOneOffForTests(); + gfx::GLSurfaceTestSupport::InitializeOneOff(); ui::RegisterPathProvider(); base::FilePath ui_test_pak_path; ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path)); diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc index e821b128610..3215f3a4efe 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc @@ -23,6 +23,7 @@ #include "ui/base/x/x11_util.h" #include "ui/events/devices/x11/touch_factory_x11.h" #include "ui/events/platform/x11/x11_event_source.h" +#include "ui/events/test/platform_event_source_test_api.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/path.h" @@ -486,6 +487,48 @@ TEST_F(DesktopWindowTreeHostX11Test, ChildWindowDestructionDuringTearDown) { EXPECT_TRUE(DesktopWindowTreeHostX11::GetAllOpenWindows().empty()); } +// A Widget that allows setting the min/max size for the widget. +class CustomSizeWidget : public Widget { + public: + CustomSizeWidget() {} + ~CustomSizeWidget() override {} + + void set_min_size(const gfx::Size& size) { min_size_ = size; } + void set_max_size(const gfx::Size& size) { max_size_ = size; } + + // Widget: + gfx::Size GetMinimumSize() const override { return min_size_; } + gfx::Size GetMaximumSize() const override { return max_size_; } + + private: + gfx::Size min_size_; + gfx::Size max_size_; + + DISALLOW_COPY_AND_ASSIGN(CustomSizeWidget); +}; + +TEST_F(DesktopWindowTreeHostX11Test, SetBoundsWithMinMax) { + CustomSizeWidget widget; + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.native_widget = new DesktopNativeWidgetAura(&widget); + params.bounds = gfx::Rect(200, 100); + widget.Init(params); + widget.Show(); + ui::X11EventSource::GetInstance()->DispatchXEvents(); + + EXPECT_EQ(gfx::Size(200, 100).ToString(), + widget.GetWindowBoundsInScreen().size().ToString()); + widget.SetBounds(gfx::Rect(300, 200)); + EXPECT_EQ(gfx::Size(300, 200).ToString(), + widget.GetWindowBoundsInScreen().size().ToString()); + + widget.set_min_size(gfx::Size(100, 100)); + widget.SetBounds(gfx::Rect(50, 500)); + EXPECT_EQ(gfx::Size(100, 500).ToString(), + widget.GetWindowBoundsInScreen().size().ToString()); +} + class MouseEventRecorder : public ui::EventHandler { public: MouseEventRecorder() {} @@ -508,25 +551,11 @@ class MouseEventRecorder : public ui::EventHandler { DISALLOW_COPY_AND_ASSIGN(MouseEventRecorder); }; -// A custom event-source that can be used to directly dispatch synthetic X11 -// events. -class CustomX11EventSource : public ui::X11EventSource { - public: - CustomX11EventSource() : X11EventSource(gfx::GetXDisplay()) {} - ~CustomX11EventSource() override {} - - void DispatchSingleEvent(XEvent* xevent) { - PlatformEventSource::DispatchEvent(xevent); - } - - private: - DISALLOW_COPY_AND_ASSIGN(CustomX11EventSource); -}; - class DesktopWindowTreeHostX11HighDPITest : public DesktopWindowTreeHostX11Test { public: - DesktopWindowTreeHostX11HighDPITest() {} + DesktopWindowTreeHostX11HighDPITest() + : event_source_(ui::PlatformEventSource::GetInstance()) {} ~DesktopWindowTreeHostX11HighDPITest() override {} void DispatchSingleEventToWidget(XEvent* event, Widget* widget) { @@ -535,7 +564,7 @@ class DesktopWindowTreeHostX11HighDPITest static_cast<XIDeviceEvent*>(event->xcookie.data); device_event->event = widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget(); - event_source_.DispatchSingleEvent(event); + event_source_.Dispatch(event); } void PretendCapture(views::Widget* capture_widget) { @@ -560,7 +589,7 @@ class DesktopWindowTreeHostX11HighDPITest DesktopWindowTreeHostX11Test::SetUp(); } - CustomX11EventSource event_source_; + ui::test::PlatformEventSourceTestAPI event_source_; DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostX11HighDPITest); }; diff --git a/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.h b/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.h index f0acc70c389..48072e8d67c 100644 --- a/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.h +++ b/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.h @@ -17,7 +17,9 @@ #include "ui/gfx/x/x11_types.h" #include "ui/views/views_export.h" +namespace base { template <typename T> struct DefaultSingletonTraits; +} namespace views { diff --git a/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc b/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc index 05bd690c6de..d7453258a56 100644 --- a/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc +++ b/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc @@ -26,7 +26,7 @@ #include "ui/gfx/path.h" #include "ui/gfx/path_x11.h" #include "ui/gfx/x/x11_atom_cache.h" -#include "ui/gl/gl_surface.h" +#include "ui/gl/test/gl_surface_test_support.h" #include "ui/views/test/views_test_base.h" #include "ui/views/test/x11_property_change_waiter.h" #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" @@ -194,7 +194,7 @@ class X11TopmostWindowFinderTest : public ViewsTestBase { } static void SetUpTestCase() { - gfx::GLSurface::InitializeOneOffForTests(); + gfx::GLSurfaceTestSupport::InitializeOneOff(); ui::RegisterPathProvider(); base::FilePath ui_test_pak_path; ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path)); diff --git a/chromium/ui/views/widget/native_widget_aura.h b/chromium/ui/views/widget/native_widget_aura.h index 2e1db2e77e9..abe3a8a3d6f 100644 --- a/chromium/ui/views/widget/native_widget_aura.h +++ b/chromium/ui/views/widget/native_widget_aura.h @@ -5,7 +5,6 @@ #ifndef UI_VIEWS_WIDGET_NATIVE_WIDGET_AURA_H_ #define UI_VIEWS_WIDGET_NATIVE_WIDGET_AURA_H_ -#include "base/memory/scoped_vector.h" #include "base/memory/weak_ptr.h" #include "ui/aura/client/focus_change_observer.h" #include "ui/aura/window_delegate.h" diff --git a/chromium/ui/views/widget/native_widget_mac.h b/chromium/ui/views/widget/native_widget_mac.h index b050a4be2a0..b2ed6325c54 100644 --- a/chromium/ui/views/widget/native_widget_mac.h +++ b/chromium/ui/views/widget/native_widget_mac.h @@ -8,6 +8,12 @@ #include "ui/gfx/native_widget_types.h" #include "ui/views/widget/native_widget_private.h" +#if defined(__OBJC__) +@class NativeWidgetMacNSWindow; +#else +class NativeWidgetMacNSWindow; +#endif + namespace views { namespace test { class HitTestNativeWidgetMac; @@ -36,6 +42,10 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate { // override this method for an early hook into the native window teardown. virtual void OnWindowWillClose(); + // Returns the vertical position that sheets should be anchored, in pixels + // from the bottom of the window. + virtual int SheetPositionY(); + // internal::NativeWidgetPrivate: void InitNativeWidget(const Widget::InitParams& params) override; NonClientFrameView* CreateNonClientFrameView() override; @@ -125,7 +135,8 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate { protected: // Creates the NSWindow that will be passed to the BridgedNativeWidget. // Called by InitNativeWidget. The return value will be autoreleased. - virtual gfx::NativeWindow CreateNSWindow(const Widget::InitParams& params); + virtual NativeWidgetMacNSWindow* CreateNSWindow( + const Widget::InitParams& params); internal::NativeWidgetDelegate* delegate() { return delegate_; } diff --git a/chromium/ui/views/widget/native_widget_mac.mm b/chromium/ui/views/widget/native_widget_mac.mm index 63465a81b46..1b5b2eb12bc 100644 --- a/chromium/ui/views/widget/native_widget_mac.mm +++ b/chromium/ui/views/widget/native_widget_mac.mm @@ -38,6 +38,13 @@ namespace views { namespace { NSInteger StyleMaskForParams(const Widget::InitParams& params) { + // If the Widget is modal, it will be displayed as a sheet. This works best if + // it has NSTitledWindowMask. For example, with NSBorderlessWindowMask, the + // parent window still accepts input. + if (params.delegate && + params.delegate->GetModalType() == ui::MODAL_TYPE_WINDOW) + return NSTitledWindowMask; + // TODO(tapted): Determine better masks when there are use cases for it. if (params.remove_standard_frame) return NSBorderlessWindowMask; @@ -86,15 +93,24 @@ bool NativeWidgetMac::IsWindowModalSheet() const { } void NativeWidgetMac::OnWindowWillClose() { - delegate_->OnNativeWidgetDestroying(); // Note: If closed via CloseNow(), |bridge_| will already be reset. If closed - // by the user, or via Close() and a RunLoop, this will reset it. - bridge_.reset(); + // by the user, or via Close() and a RunLoop, notify observers while |bridge_| + // is still a valid pointer, then reset it. + if (bridge_) { + delegate_->OnNativeWidgetDestroying(); + bridge_.reset(); + } delegate_->OnNativeWidgetDestroyed(); if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) delete this; } +int NativeWidgetMac::SheetPositionY() { + NSView* view = GetNativeView(); + return + [view convertPoint:NSMakePoint(0, NSHeight([view frame])) toView:nil].y; +} + //////////////////////////////////////////////////////////////////////////////// // NativeWidgetMac, internal::NativeWidgetPrivate implementation: @@ -337,6 +353,11 @@ void NativeWidgetMac::Close() { } void NativeWidgetMac::CloseNow() { + if (!bridge_) + return; + + // Notify observers while |bridged_| is still valid. + delegate_->OnNativeWidgetDestroying(); // Reset |bridge_| to NULL before destroying it. scoped_ptr<BridgedNativeWidget> bridge(bridge_.Pass()); } @@ -500,8 +521,7 @@ void NativeWidgetMac::ClearNativeFocus() { } gfx::Rect NativeWidgetMac::GetWorkAreaBoundsInScreen() const { - NOTIMPLEMENTED(); - return gfx::Rect(); + return gfx::ScreenRectFromNSRect([[GetNativeWindow() screen] visibleFrame]); } Widget::MoveLoopResult NativeWidgetMac::RunMoveLoop( @@ -555,7 +575,8 @@ void NativeWidgetMac::RepostNativeEvent(gfx::NativeEvent native_event) { //////////////////////////////////////////////////////////////////////////////// // NativeWidgetMac, protected: -NSWindow* NativeWidgetMac::CreateNSWindow(const Widget::InitParams& params) { +NativeWidgetMacNSWindow* NativeWidgetMac::CreateNSWindow( + const Widget::InitParams& params) { return [[[NativeWidgetMacNSWindow alloc] initWithContentRect:ui::kWindowSizeDeterminedLater styleMask:StyleMaskForParams(params) @@ -650,7 +671,14 @@ void NativeWidgetPrivate::GetAllOwnedWidgets(gfx::NativeView native_view, // static void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view, gfx::NativeView new_parent) { - NOTIMPLEMENTED(); + BridgedNativeWidget* bridge = + NativeWidgetMac::GetBridgeForNativeWindow([native_view window]); + if (bridge && bridge->parent() && + bridge->parent()->GetNSWindow() == [new_parent window]) + return; // Nothing to do. + + // Not supported. See http://crbug.com/514920. + NOTREACHED(); } // static diff --git a/chromium/ui/views/widget/native_widget_mac_interactive_uitest.mm b/chromium/ui/views/widget/native_widget_mac_interactive_uitest.mm index 8596c000eb2..08bdde3deff 100644 --- a/chromium/ui/views/widget/native_widget_mac_interactive_uitest.mm +++ b/chromium/ui/views/widget/native_widget_mac_interactive_uitest.mm @@ -6,6 +6,8 @@ #import <Cocoa/Cocoa.h> +#import "base/mac/scoped_nsobject.h" +#import "ui/base/test/windowed_nsnotification_observer.h" #include "ui/views/test/test_widget_observer.h" #include "ui/views/test/widget_test.h" @@ -94,38 +96,39 @@ TEST_P(NativeWidgetMacInteractiveUITest, ShowAttainsKeyStatus) { EXPECT_EQ(2, activationCount_); } -// Test that ShowInactive does not take keyWindow status from an active window. +// Test that ShowInactive does not take keyWindow status. TEST_P(NativeWidgetMacInteractiveUITest, ShowInactiveIgnoresKeyStatus) { Widget* widget = MakeWidget(); - // In an application with only a single window, that window is always "active" - // for the application unless that window is not visible. However, it will not - // be key. + base::scoped_nsobject<WindowedNSNotificationObserver> waiter( + [[WindowedNSNotificationObserver alloc] + initForNotification:NSWindowDidBecomeKeyNotification + object:widget->GetNativeWindow()]); + + EXPECT_FALSE(widget->IsVisible()); + EXPECT_FALSE([widget->GetNativeWindow() isVisible]); EXPECT_FALSE(widget->IsActive()); - widget->ShowInactive(); - EXPECT_TRUE(widget->IsActive()); - RunPendingMessages(); EXPECT_FALSE([widget->GetNativeWindow() isKeyWindow]); - - // Creating a second widget should now keep that widget active. - Widget* widget2 = MakeWidget(); - widget2->Show(); widget->ShowInactive(); + EXPECT_TRUE(widget->IsVisible()); + EXPECT_TRUE([widget->GetNativeWindow() isVisible]); EXPECT_FALSE(widget->IsActive()); - EXPECT_TRUE(widget2->IsActive()); + EXPECT_FALSE([widget->GetNativeWindow() isKeyWindow]); + + // If the window were to become active, this would activate it. RunPendingMessages(); + EXPECT_FALSE(widget->IsActive()); EXPECT_FALSE([widget->GetNativeWindow() isKeyWindow]); - EXPECT_TRUE([widget2->GetNativeWindow() isKeyWindow]); + EXPECT_EQ(0, [waiter notificationCount]); - // And finally activating the inactive widget should activate it and make it - // key. + // Activating the inactive widget should make it key, asynchronously. widget->Activate(); + [waiter wait]; + EXPECT_EQ(1, [waiter notificationCount]); EXPECT_TRUE(widget->IsActive()); - RunPendingMessages(); EXPECT_TRUE([widget->GetNativeWindow() isKeyWindow]); - widget2->CloseNow(); widget->CloseNow(); } diff --git a/chromium/ui/views/widget/native_widget_mac_unittest.mm b/chromium/ui/views/widget/native_widget_mac_unittest.mm index 2f0104c38b3..0ff4fbf20e9 100644 --- a/chromium/ui/views/widget/native_widget_mac_unittest.mm +++ b/chromium/ui/views/widget/native_widget_mac_unittest.mm @@ -6,6 +6,7 @@ #import <Cocoa/Cocoa.h> +#import "base/mac/foundation_util.h" #import "base/mac/scoped_nsobject.h" #import "base/mac/scoped_objc_class_swizzler.h" #include "base/run_loop.h" @@ -13,11 +14,15 @@ #include "base/strings/sys_string_conversions.h" #include "base/test/test_timeouts.h" #import "testing/gtest_mac.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkCanvas.h" #import "ui/base/cocoa/constrained_window/constrained_window_animation.h" +#import "ui/base/cocoa/window_size_constants.h" #import "ui/events/test/cocoa_test_event_utils.h" #include "ui/events/test/event_generator.h" #import "ui/gfx/mac/coordinate_conversion.h" #import "ui/views/cocoa/bridged_native_widget.h" +#import "ui/views/cocoa/native_widget_mac_nswindow.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/label.h" #include "ui/views/native_cursor.h" @@ -39,9 +44,68 @@ - (BOOL)_isTitleHidden; @end +// Test NSWindow that provides hooks via method overrides to verify behavior. +@interface NativeWidgetMacTestWindow : NativeWidgetMacNSWindow { + @private + int invalidateShadowCount_; +} +@property(readonly, nonatomic) int invalidateShadowCount; +@end + namespace views { namespace test { +// BridgedNativeWidget friend to access private members. +class BridgedNativeWidgetTestApi { + public: + explicit BridgedNativeWidgetTestApi(NSWindow* window) { + bridge_ = NativeWidgetMac::GetBridgeForNativeWindow(window); + } + + // Simulate a frame swap from the compositor. Assumes scale factor of 1.0f. + void SimulateFrameSwap(const gfx::Size& size) { + const float kScaleFactor = 1.0f; + SkBitmap bitmap; + bitmap.allocN32Pixels(size.width(), size.height()); + SkCanvas canvas(bitmap); + bridge_->compositor_widget_->GotSoftwareFrame(kScaleFactor, &canvas); + std::vector<ui::LatencyInfo> latency_info; + bridge_->AcceleratedWidgetSwapCompleted(latency_info); + } + + private: + BridgedNativeWidget* bridge_; + + DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidgetTestApi); +}; + +// Custom native_widget to create a NativeWidgetMacTestWindow. +class TestWindowNativeWidgetMac : public NativeWidgetMac { + public: + explicit TestWindowNativeWidgetMac(Widget* delegate) + : NativeWidgetMac(delegate) {} + + protected: + // NativeWidgetMac: + NativeWidgetMacNSWindow* CreateNSWindow( + const Widget::InitParams& params) override { + NSUInteger style_mask = NSBorderlessWindowMask; + if (params.type == Widget::InitParams::TYPE_WINDOW) { + style_mask = NSTexturedBackgroundWindowMask | NSTitledWindowMask | + NSClosableWindowMask | NSMiniaturizableWindowMask | + NSResizableWindowMask; + } + return [[[NativeWidgetMacTestWindow alloc] + initWithContentRect:ui::kWindowSizeDeterminedLater + styleMask:style_mask + backing:NSBackingStoreBuffered + defer:NO] autorelease]; + } + + private: + DISALLOW_COPY_AND_ASSIGN(TestWindowNativeWidgetMac); +}; + // Tests for parts of NativeWidgetMac not covered by BridgedNativeWidget, which // need access to Cocoa APIs. class NativeWidgetMacTest : public WidgetTest { @@ -51,18 +115,36 @@ class NativeWidgetMacTest : public WidgetTest { // The content size of NSWindows made by MakeNativeParent(). NSRect ParentRect() const { return NSMakeRect(100, 100, 300, 200); } - // Make a native NSWindow to use as a parent. - NSWindow* MakeNativeParent() { - native_parent_.reset( - [[NSWindow alloc] initWithContentRect:ParentRect() - styleMask:NSBorderlessWindowMask - backing:NSBackingStoreBuffered - defer:NO]); + // Make a native NSWindow with the given |style_mask| to use as a parent. + NSWindow* MakeNativeParentWithStyle(int style_mask) { + native_parent_.reset([[NSWindow alloc] + initWithContentRect:ParentRect() + styleMask:style_mask + backing:NSBackingStoreBuffered + defer:NO]); [native_parent_ setReleasedWhenClosed:NO]; // Owned by scoped_nsobject. [native_parent_ makeKeyAndOrderFront:nil]; return native_parent_; } + // Make a borderless, native NSWindow to use as a parent. + NSWindow* MakeNativeParent() { + return MakeNativeParentWithStyle(NSBorderlessWindowMask); + } + + // Create a Widget backed by the NativeWidgetMacTestWindow NSWindow subclass. + Widget* CreateWidgetWithTestWindow(Widget::InitParams params, + NativeWidgetMacTestWindow** window) { + Widget* widget = new Widget; + params.native_widget = new TestWindowNativeWidgetMac(widget); + widget->Init(params); + widget->Show(); + *window = base::mac::ObjCCastStrict<NativeWidgetMacTestWindow>( + widget->GetNativeWindow()); + EXPECT_TRUE(*window); + return widget; + } + private: base::scoped_nsobject<NSWindow> native_parent_; @@ -71,10 +153,22 @@ class NativeWidgetMacTest : public WidgetTest { class WidgetChangeObserver : public TestWidgetObserver { public: - WidgetChangeObserver(Widget* widget) - : TestWidgetObserver(widget), - gained_visible_count_(0), - lost_visible_count_(0) {} + WidgetChangeObserver(Widget* widget) : TestWidgetObserver(widget) {} + + void WaitForVisibleCounts(int gained, int lost) { + if (gained_visible_count_ >= gained && lost_visible_count_ >= lost) + return; + + target_gained_visible_count_ = gained; + target_lost_visible_count_ = lost; + + base::RunLoop run_loop; + run_loop_ = &run_loop; + base::MessageLoop::current()->task_runner()->PostDelayedTask( + FROM_HERE, run_loop.QuitClosure(), TestTimeouts::action_timeout()); + run_loop.Run(); + run_loop_ = nullptr; + } int gained_visible_count() const { return gained_visible_count_; } int lost_visible_count() const { return lost_visible_count_; } @@ -84,10 +178,16 @@ class WidgetChangeObserver : public TestWidgetObserver { void OnWidgetVisibilityChanged(Widget* widget, bool visible) override { ++(visible ? gained_visible_count_ : lost_visible_count_); + if (run_loop_ && gained_visible_count_ >= target_gained_visible_count_ && + lost_visible_count_ >= target_lost_visible_count_) + run_loop_->Quit(); } - int gained_visible_count_; - int lost_visible_count_; + int gained_visible_count_ = 0; + int lost_visible_count_ = 0; + int target_gained_visible_count_ = 0; + int target_lost_visible_count_ = 0; + base::RunLoop* run_loop_ = nullptr; DISALLOW_COPY_AND_ASSIGN(WidgetChangeObserver); }; @@ -140,16 +240,16 @@ TEST_F(NativeWidgetMacTest, HideAndShowExternally) { [NSApp hide:nil]; // When the activation policy is NSApplicationActivationPolicyRegular, the // calls via NSApp are asynchronous, and the run loop needs to be flushed. - // With NSApplicationActivationPolicyProhibited, the following RunUntilIdle - // calls are superfluous, but don't hurt. - base::RunLoop().RunUntilIdle(); + // With NSApplicationActivationPolicyProhibited, the following + // WaitForVisibleCounts calls are superfluous, but don't hurt. + observer.WaitForVisibleCounts(3, 3); EXPECT_FALSE(widget->IsVisible()); EXPECT_FALSE([ns_window isVisible]); EXPECT_EQ(3, observer.gained_visible_count()); EXPECT_EQ(3, observer.lost_visible_count()); [NSApp unhideWithoutActivation]; - base::RunLoop().RunUntilIdle(); + observer.WaitForVisibleCounts(4, 3); EXPECT_TRUE(widget->IsVisible()); EXPECT_TRUE([ns_window isVisible]); EXPECT_EQ(4, observer.gained_visible_count()); @@ -157,10 +257,10 @@ TEST_F(NativeWidgetMacTest, HideAndShowExternally) { // Hide again to test unhiding with an activation. [NSApp hide:nil]; - base::RunLoop().RunUntilIdle(); + observer.WaitForVisibleCounts(4, 4); EXPECT_EQ(4, observer.lost_visible_count()); [NSApp unhide:nil]; - base::RunLoop().RunUntilIdle(); + observer.WaitForVisibleCounts(5, 4); EXPECT_EQ(5, observer.gained_visible_count()); // Hide again to test makeKeyAndOrderFront:. @@ -185,20 +285,33 @@ TEST_F(NativeWidgetMacTest, HideAndShowExternally) { // A view that counts calls to OnPaint(). class PaintCountView : public View { public: - PaintCountView() : paint_count_(0) { - SetBounds(0, 0, 100, 100); - } + PaintCountView() { SetBounds(0, 0, 100, 100); } // View: void OnPaint(gfx::Canvas* canvas) override { EXPECT_TRUE(GetWidget()->IsVisible()); ++paint_count_; + if (run_loop_ && paint_count_ == target_paint_count_) + run_loop_->Quit(); + } + + void WaitForPaintCount(int target) { + if (paint_count_ == target) + return; + + target_paint_count_ = target; + base::RunLoop run_loop; + run_loop_ = &run_loop; + run_loop.Run(); + run_loop_ = nullptr; } int paint_count() { return paint_count_; } private: - int paint_count_; + int paint_count_ = 0; + int target_paint_count_ = 0; + base::RunLoop* run_loop_ = nullptr; DISALLOW_COPY_AND_ASSIGN(PaintCountView); }; @@ -208,9 +321,6 @@ class PaintCountView : public View { TEST_F(NativeWidgetMacTest, MiniaturizeExternally) { Widget* widget = new Widget; Widget::InitParams init_params(Widget::InitParams::TYPE_WINDOW); - // Make the layer not drawn, so that calls to paint can be observed - // synchronously. - init_params.layer_type = ui::LAYER_NOT_DRAWN; widget->Init(init_params); PaintCountView* view = new PaintCountView(); @@ -223,6 +333,7 @@ TEST_F(NativeWidgetMacTest, MiniaturizeExternally) { EXPECT_TRUE(view->IsDrawn()); EXPECT_EQ(0, view->paint_count()); widget->Show(); + base::RunLoop().RunUntilIdle(); EXPECT_EQ(1, observer.gained_visible_count()); EXPECT_EQ(0, observer.lost_visible_count()); @@ -232,12 +343,13 @@ TEST_F(NativeWidgetMacTest, MiniaturizeExternally) { EXPECT_TRUE(widget->IsVisible()); // Showing should paint. - EXPECT_EQ(1, view->paint_count()); + view->WaitForPaintCount(1); // First try performMiniaturize:, which requires a minimize button. Note that // Cocoa just blocks the UI thread during the animation, so no need to do // anything fancy to wait for it finish. [ns_window performMiniaturize:nil]; + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(widget->IsMinimized()); EXPECT_FALSE(widget->IsVisible()); // Minimizing also makes things invisible. @@ -252,6 +364,7 @@ TEST_F(NativeWidgetMacTest, MiniaturizeExternally) { EXPECT_EQ(1, view->paint_count()); [ns_window deminiaturize:nil]; + base::RunLoop().RunUntilIdle(); EXPECT_FALSE(widget->IsMinimized()); EXPECT_TRUE(widget->IsVisible()); @@ -259,10 +372,11 @@ TEST_F(NativeWidgetMacTest, MiniaturizeExternally) { EXPECT_EQ(1, observer.lost_visible_count()); EXPECT_EQ(restored_bounds, widget->GetRestoredBounds()); - EXPECT_EQ(2, view->paint_count()); // A single paint when deminiaturizing. + view->WaitForPaintCount(2); // A single paint when deminiaturizing. EXPECT_FALSE([ns_window isMiniaturized]); widget->Minimize(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(widget->IsMinimized()); EXPECT_TRUE([ns_window isMiniaturized]); @@ -272,15 +386,17 @@ TEST_F(NativeWidgetMacTest, MiniaturizeExternally) { EXPECT_EQ(2, view->paint_count()); // No paint when miniaturizing. widget->Restore(); // If miniaturized, should deminiaturize. + base::RunLoop().RunUntilIdle(); EXPECT_FALSE(widget->IsMinimized()); EXPECT_FALSE([ns_window isMiniaturized]); EXPECT_EQ(3, observer.gained_visible_count()); EXPECT_EQ(2, observer.lost_visible_count()); EXPECT_EQ(restored_bounds, widget->GetRestoredBounds()); - EXPECT_EQ(3, view->paint_count()); + view->WaitForPaintCount(3); widget->Restore(); // If not miniaturized, does nothing. + base::RunLoop().RunUntilIdle(); EXPECT_FALSE(widget->IsMinimized()); EXPECT_FALSE([ns_window isMiniaturized]); @@ -547,16 +663,19 @@ TEST_F(NativeWidgetMacTest, Tooltips) { namespace { -// Delegate to make Widgets of MODAL_TYPE_CHILD. -class ChildModalDialogDelegate : public DialogDelegateView { +// Delegate to make Widgets of a provided ui::ModalType. +class ModalDialogDelegate : public DialogDelegateView { public: - ChildModalDialogDelegate() {} + explicit ModalDialogDelegate(ui::ModalType modal_type) + : modal_type_(modal_type) {} // WidgetDelegate: - ui::ModalType GetModalType() const override { return ui::MODAL_TYPE_CHILD; } + ui::ModalType GetModalType() const override { return modal_type_; } private: - DISALLOW_COPY_AND_ASSIGN(ChildModalDialogDelegate); + const ui::ModalType modal_type_; + + DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate); }; // While in scope, waits for a call to a swizzled objective C method, then quits @@ -616,7 +735,8 @@ ScopedSwizzleWaiter* ScopedSwizzleWaiter::instance_ = nullptr; // animation). However, testing with overlapping swizzlers is tricky. Widget* ShowChildModalWidgetAndWait(NSWindow* native_parent) { Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget( - new ChildModalDialogDelegate, nullptr, [native_parent contentView]); + new ModalDialogDelegate(ui::MODAL_TYPE_CHILD), nullptr, + [native_parent contentView]); modal_dialog_widget->SetBounds(gfx::Rect(50, 50, 200, 150)); EXPECT_FALSE(modal_dialog_widget->IsVisible()); @@ -688,6 +808,118 @@ TEST_F(NativeWidgetMacTest, NativeWindowChildModalShowHide) { } } +// Tests behavior of window-modal dialogs, displayed as sheets. +TEST_F(NativeWidgetMacTest, WindowModalSheet) { + NSWindow* native_parent = + MakeNativeParentWithStyle(NSClosableWindowMask | NSTitledWindowMask); + + Widget* sheet_widget = views::DialogDelegate::CreateDialogWidget( + new ModalDialogDelegate(ui::MODAL_TYPE_WINDOW), nullptr, + [native_parent contentView]); + + // Retain, to run checks after the Widget is torn down. + base::scoped_nsobject<NSWindow> sheet_window( + [sheet_widget->GetNativeWindow() retain]); + + // Although there is no titlebar displayed, sheets need NSTitledWindowMask in + // order to properly engage window-modal behavior in AppKit. + EXPECT_EQ(NSTitledWindowMask, [sheet_window styleMask]); + + sheet_widget->SetBounds(gfx::Rect(50, 50, 200, 150)); + EXPECT_FALSE(sheet_widget->IsVisible()); + EXPECT_FALSE(sheet_widget->GetLayer()->IsDrawn()); + + NSButton* parent_close_button = + [native_parent standardWindowButton:NSWindowCloseButton]; + EXPECT_TRUE(parent_close_button); + EXPECT_TRUE([parent_close_button isEnabled]); + + bool did_observe = false; + bool* did_observe_ptr = &did_observe; + id observer = [[NSNotificationCenter defaultCenter] + addObserverForName:NSWindowWillBeginSheetNotification + object:native_parent + queue:nil + usingBlock:^(NSNotification* note) { + // Ensure that before the sheet runs, the window contents would + // be drawn. + EXPECT_TRUE(sheet_widget->IsVisible()); + EXPECT_TRUE(sheet_widget->GetLayer()->IsDrawn()); + *did_observe_ptr = true; + }]; + + sheet_widget->Show(); // Should run the above block, then animate the sheet. + EXPECT_TRUE(did_observe); + [[NSNotificationCenter defaultCenter] removeObserver:observer]; + + // Modal, so the close button in the parent window should get disabled. + EXPECT_FALSE([parent_close_button isEnabled]); + + // Trigger the close. Don't use CloseNow, since that tears down the UI before + // the close sheet animation gets a chance to run (so it's banned). + sheet_widget->Close(); + EXPECT_TRUE(sheet_widget->IsVisible()); + + did_observe = false; + + // Experimentally (on 10.10), this notification is posted from within the + // -[NSWindow orderOut:] call that is triggered from -[ViewsNSWindowDelegate + // sheetDidEnd:]. |sheet_widget| will be destroyed next, so it's still safe to + // use in the block. However, since the orderOut just happened, it's not very + // interesting. + observer = [[NSNotificationCenter defaultCenter] + addObserverForName:NSWindowDidEndSheetNotification + object:native_parent + queue:nil + usingBlock:^(NSNotification* note) { + EXPECT_TRUE([sheet_window delegate]); + EXPECT_FALSE(sheet_widget->IsVisible()); + EXPECT_FALSE(sheet_widget->GetLayer()->IsDrawn()); + *did_observe_ptr = true; + }]; + + // Pump in order to trigger -[NSWindow endSheet:..], which will block while + // the animation runs, then delete |sheet_widget|. + TestWidgetObserver widget_observer(sheet_widget); + EXPECT_TRUE([sheet_window delegate]); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE([sheet_window delegate]); + + EXPECT_TRUE(did_observe); // Also ensures the Close() actually uses sheets. + [[NSNotificationCenter defaultCenter] removeObserver:observer]; + + EXPECT_TRUE(widget_observer.widget_closed()); + EXPECT_TRUE([parent_close_button isEnabled]); +} + +// Test calls to Widget::ReparentNativeView() that result in a no-op on Mac. +// Tests with both native and non-native parents. +TEST_F(NativeWidgetMacTest, NoopReparentNativeView) { + NSWindow* parent = MakeNativeParent(); + Widget* dialog = views::DialogDelegate::CreateDialogWidget( + new DialogDelegateView, nullptr, [parent contentView]); + BridgedNativeWidget* bridge = + NativeWidgetMac::GetBridgeForNativeWindow(dialog->GetNativeWindow()); + + EXPECT_EQ(bridge->parent()->GetNSWindow(), parent); + Widget::ReparentNativeView(dialog->GetNativeView(), [parent contentView]); + EXPECT_EQ(bridge->parent()->GetNSWindow(), parent); + + [parent close]; + + Widget* parent_widget = CreateNativeDesktopWidget(); + parent = parent_widget->GetNativeWindow(); + dialog = views::DialogDelegate::CreateDialogWidget( + new DialogDelegateView, nullptr, [parent contentView]); + bridge = NativeWidgetMac::GetBridgeForNativeWindow(dialog->GetNativeWindow()); + + EXPECT_EQ(bridge->parent()->GetNSWindow(), parent); + Widget::ReparentNativeView(dialog->GetNativeView(), [parent contentView]); + EXPECT_EQ(bridge->parent()->GetNSWindow(), parent); + + parent_widget->CloseNow(); +} + // Tests Cocoa properties that should be given to particular widget types. TEST_F(NativeWidgetMacTest, NativeProperties) { // Create a regular widget (TYPE_WINDOW). @@ -702,7 +934,8 @@ TEST_F(NativeWidgetMacTest, NativeProperties) { // Create a dialog widget (also TYPE_WINDOW), but with a DialogDelegate. Widget* dialog_widget = views::DialogDelegate::CreateDialogWidget( - new ChildModalDialogDelegate, nullptr, regular_widget->GetNativeView()); + new ModalDialogDelegate(ui::MODAL_TYPE_CHILD), nullptr, + regular_widget->GetNativeView()); EXPECT_TRUE([dialog_widget->GetNativeWindow() canBecomeKeyWindow]); // Dialogs shouldn't take main status away from their parent. EXPECT_FALSE([dialog_widget->GetNativeWindow() canBecomeMainWindow]); @@ -796,6 +1029,68 @@ TEST_F(NativeWidgetMacTest, DoesHideTitle) { widget->CloseNow(); } +// Test calls to invalidate the shadow when composited frames arrive. +TEST_F(NativeWidgetMacTest, InvalidateShadow) { + NativeWidgetMacTestWindow* window; + const gfx::Rect rect(0, 0, 100, 200); + Widget::InitParams init_params = + CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); + init_params.bounds = rect; + Widget* widget = CreateWidgetWithTestWindow(init_params, &window); + + // Simulate the initial paint. + BridgedNativeWidgetTestApi(window).SimulateFrameSwap(rect.size()); + + // Default is an opaque window, so shadow doesn't need to be invalidated. + EXPECT_EQ(0, [window invalidateShadowCount]); + widget->CloseNow(); + + init_params.opacity = Widget::InitParams::TRANSLUCENT_WINDOW; + widget = CreateWidgetWithTestWindow(init_params, &window); + BridgedNativeWidgetTestApi test_api(window); + + // First paint on a translucent window needs to invalidate the shadow. Once. + EXPECT_EQ(0, [window invalidateShadowCount]); + test_api.SimulateFrameSwap(rect.size()); + EXPECT_EQ(1, [window invalidateShadowCount]); + test_api.SimulateFrameSwap(rect.size()); + EXPECT_EQ(1, [window invalidateShadowCount]); + + // Resizing the window also needs to trigger a shadow invalidation. + [window setContentSize:NSMakeSize(123, 456)]; + // A "late" frame swap at the old size should do nothing. + test_api.SimulateFrameSwap(rect.size()); + EXPECT_EQ(1, [window invalidateShadowCount]); + + test_api.SimulateFrameSwap(gfx::Size(123, 456)); + EXPECT_EQ(2, [window invalidateShadowCount]); + test_api.SimulateFrameSwap(gfx::Size(123, 456)); + EXPECT_EQ(2, [window invalidateShadowCount]); + + widget->CloseNow(); +} + +// Test the expected result of GetWorkAreaBoundsInScreen(). +TEST_F(NativeWidgetMacTest, GetWorkAreaBoundsInScreen) { + Widget widget; + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + + // This is relative to the top-left of the primary screen, so unless the bot's + // display is smaller than 400x300, the window will be wholly contained there. + params.bounds = gfx::Rect(100, 100, 300, 200); + widget.Init(params); + widget.Show(); + NSRect expected = [[[NSScreen screens] objectAtIndex:0] visibleFrame]; + NSRect actual = gfx::ScreenRectToNSRect(widget.GetWorkAreaBoundsInScreen()); + EXPECT_FALSE(NSIsEmptyRect(actual)); + EXPECT_NSEQ(expected, actual); + + [widget.GetNativeWindow() close]; + actual = gfx::ScreenRectToNSRect(widget.GetWorkAreaBoundsInScreen()); + EXPECT_TRUE(NSIsEmptyRect(actual)); +} + } // namespace test } // namespace views @@ -804,3 +1099,14 @@ TEST_F(NativeWidgetMacTest, DoesHideTitle) { views::test::ScopedSwizzleWaiter::GetMethodAndMarkCalled()(self, _cmd); } @end + +@implementation NativeWidgetMacTestWindow + +@synthesize invalidateShadowCount = invalidateShadowCount_; + +- (void)invalidateShadow { + ++invalidateShadowCount_; + [super invalidateShadow]; +} + +@end diff --git a/chromium/ui/views/widget/widget.cc b/chromium/ui/views/widget/widget.cc index 280969a84e5..e1fc508d798 100644 --- a/chromium/ui/views/widget/widget.cc +++ b/chromium/ui/views/widget/widget.cc @@ -21,6 +21,7 @@ #include "ui/gfx/image/image_skia.h" #include "ui/gfx/screen.h" #include "ui/views/controls/menu/menu_controller.h" +#include "ui/views/event_monitor.h" #include "ui/views/focus/focus_manager.h" #include "ui/views/focus/focus_manager_factory.h" #include "ui/views/focus/view_storage.h" @@ -122,7 +123,8 @@ Widget::InitParams::InitParams() desktop_window_tree_host(NULL), layer_type(ui::LAYER_TEXTURED), context(NULL), - force_show_in_taskbar(false) { + force_show_in_taskbar(false), + force_software_compositing(false) { } Widget::InitParams::InitParams(Type type) @@ -145,7 +147,8 @@ Widget::InitParams::InitParams(Type type) desktop_window_tree_host(NULL), layer_type(ui::LAYER_TEXTURED), context(NULL), - force_show_in_taskbar(false) { + force_show_in_taskbar(false), + force_software_compositing(false) { } Widget::InitParams::~InitParams() { @@ -988,9 +991,16 @@ gfx::Rect Widget::GetWorkAreaBoundsInScreen() const { } void Widget::SynthesizeMouseMoveEvent() { + // In screen coordinate. + gfx::Point mouse_location = EventMonitor::GetLastMouseLocation(); + if (!GetWindowBoundsInScreen().Contains(mouse_location)) + return; + + // Convert: screen coordinate -> widget coordinate. + View::ConvertPointFromScreen(root_view_.get(), &mouse_location); last_mouse_event_was_move_ = false; - ui::MouseEvent mouse_event(ui::ET_MOUSE_MOVED, last_mouse_event_position_, - last_mouse_event_position_, ui::EventTimeForNow(), + ui::MouseEvent mouse_event(ui::ET_MOUSE_MOVED, mouse_location, + mouse_location, ui::EventTimeForNow(), ui::EF_IS_SYNTHESIZED, 0); root_view_->OnMouseMoved(mouse_event); } diff --git a/chromium/ui/views/widget/widget.h b/chromium/ui/views/widget/widget.h index 320332bec24..301c0aff1a1 100644 --- a/chromium/ui/views/widget/widget.h +++ b/chromium/ui/views/widget/widget.h @@ -9,7 +9,6 @@ #include <stack> #include <vector> -#include "base/gtest_prod_util.h" #include "base/memory/scoped_ptr.h" #include "base/observer_list.h" #include "base/scoped_observer.h" @@ -266,6 +265,10 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, std::string wm_role_name; std::string wm_class_name; std::string wm_class_class; + + // If true then the widget uses software compositing. Defaults to false. + // Only used on Windows. + bool force_software_compositing; }; Widget(); diff --git a/chromium/ui/views/widget/widget_interactive_uitest.cc b/chromium/ui/views/widget/widget_interactive_uitest.cc index 4b5a01d754e..e1e0e49a071 100644 --- a/chromium/ui/views/widget/widget_interactive_uitest.cc +++ b/chromium/ui/views/widget/widget_interactive_uitest.cc @@ -18,7 +18,7 @@ #include "ui/events/event_utils.h" #include "ui/events/test/event_generator.h" #include "ui/gfx/native_widget_types.h" -#include "ui/gl/gl_surface.h" +#include "ui/gl/test/gl_surface_test_support.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/controls/textfield/textfield_test_api.h" #include "ui/views/focus/focus_manager.h" @@ -274,7 +274,7 @@ class WidgetTestInteractive : public WidgetTest { ~WidgetTestInteractive() override {} void SetUp() override { - gfx::GLSurface::InitializeOneOffForTests(); + gfx::GLSurfaceTestSupport::InitializeOneOff(); ui::RegisterPathProvider(); base::FilePath ui_test_pak_path; ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path)); @@ -1142,7 +1142,7 @@ class WidgetCaptureTest : public ViewsTestBase { ~WidgetCaptureTest() override {} void SetUp() override { - gfx::GLSurface::InitializeOneOffForTests(); + gfx::GLSurfaceTestSupport::InitializeOneOff(); ui::RegisterPathProvider(); base::FilePath ui_test_pak_path; ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path)); diff --git a/chromium/ui/views/widget/widget_unittest.cc b/chromium/ui/views/widget/widget_unittest.cc index ba0c03b2342..b6a18a94f67 100644 --- a/chromium/ui/views/widget/widget_unittest.cc +++ b/chromium/ui/views/widget/widget_unittest.cc @@ -41,6 +41,7 @@ #if defined(OS_MACOSX) #include "base/mac/mac_util.h" +#include "ui/base/test/scoped_fake_nswindow_fullscreen.h" #endif namespace views { @@ -68,68 +69,6 @@ bool IsTestingSnowLeopard() { } // 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) {} - - ~EventCountView() override {} - - 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: - 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: - void OnKeyEvent(ui::KeyEvent* event) override { RecordEvent(event); } - void OnMouseEvent(ui::MouseEvent* event) override { RecordEvent(event); } - void OnScrollEvent(ui::ScrollEvent* event) override { RecordEvent(event); } - 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 { @@ -207,67 +146,6 @@ class EventCountHandler : public ui::EventHandler { DISALLOW_COPY_AND_ASSIGN(EventCountHandler); }; -// A helper WidgetDelegate for tests that require hooks into WidgetDelegate -// calls, and removes some of the boilerplate for initializing a Widget. Calls -// Widget::CloseNow() when destroyed if it hasn't already been done. -class TestDesktopWidgetDelegate : public WidgetDelegate { - public: - TestDesktopWidgetDelegate() : widget_(new Widget) {} - - ~TestDesktopWidgetDelegate() override { - if (widget_) - widget_->CloseNow(); - EXPECT_FALSE(widget_); - } - - // Initialize the Widget, adding some meaningful default InitParams. - void InitWidget(Widget::InitParams init_params) { - init_params.delegate = this; -#if !defined(OS_CHROMEOS) - init_params.native_widget = new PlatformDesktopNativeWidget(widget_); -#endif - init_params.bounds = initial_bounds_; - widget_->Init(init_params); - } - - // Set the contents view to be used during Widget initialization. For Widgets - // that use non-client views, this will be the contents_view used to - // initialize the ClientView in WidgetDelegate::CreateClientView(). Otherwise, - // it is the ContentsView of the Widget's RootView. Ownership passes to the - // view hierarchy during InitWidget(). - void set_contents_view(View* contents_view) { - contents_view_ = contents_view; - } - - int window_closing_count() const { return window_closing_count_; } - const gfx::Rect& initial_bounds() { return initial_bounds_; } - - // WidgetDelegate overrides: - void WindowClosing() override { - window_closing_count_++; - widget_ = nullptr; - } - - Widget* GetWidget() override { return widget_; } - const Widget* GetWidget() const override { return widget_; } - - View* GetContentsView() override { - return contents_view_ ? contents_view_ : WidgetDelegate::GetContentsView(); - } - - bool ShouldAdvanceFocusToTopLevelWidget() const override { - return true; // Same default as DefaultWidgetDelegate in widget.cc. - } - - private: - Widget* widget_; - View* contents_view_ = nullptr; - int window_closing_count_ = 0; - gfx::Rect initial_bounds_ = gfx::Rect(100, 100, 200, 200); - - DISALLOW_COPY_AND_ASSIGN(TestDesktopWidgetDelegate); -}; - TEST_F(WidgetTest, WidgetInitParams) { // Widgets are not transparent by default. Widget::InitParams init1; @@ -1281,49 +1159,67 @@ TEST_F(WidgetTest, GetWindowBoundsInScreen) { widget->CloseNow(); } -// Before being enabled on Mac, this was #ifdef(false). -// TODO(tapted): Fix this for DesktopNativeWidgets on other platforms. +// Non-Desktop widgets need the shell to maximize/fullscreen window. +// Disable on Linux because windows restore to the wrong bounds. +// See http://crbug.com/515369. +#if defined(OS_CHROMEOS) || defined(OS_LINUX) +#define MAYBE_GetRestoredBounds DISABLED_GetRestoredBounds +#else +#define MAYBE_GetRestoredBounds GetRestoredBounds +#endif + +// Test that GetRestoredBounds() returns the original bounds of the window. +TEST_F(WidgetTest, MAYBE_GetRestoredBounds) { #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()); + // Fullscreen on Mac requires an interactive UI test, fake them here. + ui::test::ScopedFakeNSWindowFullscreen fake_fullscreen; +#endif + + Widget* toplevel = CreateNativeDesktopWidget(); toplevel->Show(); + // Initial restored bounds have non-zero size. + EXPECT_FALSE(toplevel->GetRestoredBounds().IsEmpty()); + + const gfx::Rect bounds(100, 100, 200, 200); + toplevel->SetBounds(bounds); + EXPECT_EQ(bounds, toplevel->GetWindowBoundsInScreen()); + EXPECT_EQ(bounds, toplevel->GetRestoredBounds()); + 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()); + EXPECT_EQ(toplevel->GetWindowBoundsInScreen(), toplevel->GetRestoredBounds()); #else - EXPECT_NE(toplevel->GetWindowBoundsInScreen().ToString(), - toplevel->GetRestoredBounds().ToString()); + EXPECT_NE(toplevel->GetWindowBoundsInScreen(), toplevel->GetRestoredBounds()); #endif - EXPECT_GT(toplevel->GetRestoredBounds().width(), 0); - EXPECT_GT(toplevel->GetRestoredBounds().height(), 0); + EXPECT_EQ(bounds, toplevel->GetRestoredBounds()); toplevel->Restore(); RunPendingMessages(); - EXPECT_EQ(toplevel->GetWindowBoundsInScreen().ToString(), - toplevel->GetRestoredBounds().ToString()); + EXPECT_EQ(bounds, toplevel->GetWindowBoundsInScreen()); + EXPECT_EQ(bounds, toplevel->GetRestoredBounds()); toplevel->SetFullscreen(true); RunPendingMessages(); if (IsTestingSnowLeopard()) { // Fullscreen not implemented for Snow Leopard. - EXPECT_EQ(toplevel->GetWindowBoundsInScreen().ToString(), - toplevel->GetRestoredBounds().ToString()); + EXPECT_EQ(toplevel->GetWindowBoundsInScreen(), + toplevel->GetRestoredBounds()); } else { - EXPECT_NE(toplevel->GetWindowBoundsInScreen().ToString(), - toplevel->GetRestoredBounds().ToString()); + EXPECT_NE(toplevel->GetWindowBoundsInScreen(), + toplevel->GetRestoredBounds()); } - EXPECT_GT(toplevel->GetRestoredBounds().width(), 0); - EXPECT_GT(toplevel->GetRestoredBounds().height(), 0); + EXPECT_EQ(bounds, toplevel->GetRestoredBounds()); + + toplevel->SetFullscreen(false); + RunPendingMessages(); + EXPECT_EQ(bounds, toplevel->GetWindowBoundsInScreen()); + EXPECT_EQ(bounds, toplevel->GetRestoredBounds()); + + toplevel->CloseNow(); } -#endif // 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 @@ -1801,28 +1697,44 @@ TEST_F(WidgetTest, EventHandlersOnRootView) { TEST_F(WidgetTest, SynthesizeMouseMoveEvent) { Widget* widget = CreateTopLevelNativeWidget(); View* root_view = widget->GetRootView(); + root_view->SetBoundsRect(gfx::Rect(0, 0, 500, 500)); EventCountView* v1 = new EventCountView(); - v1->SetBounds(0, 0, 10, 10); + v1->SetBounds(5, 5, 10, 10); root_view->AddChildView(v1); EventCountView* v2 = new EventCountView(); - v2->SetBounds(0, 10, 10, 10); + v2->SetBounds(5, 15, 10, 10); root_view->AddChildView(v2); + widget->Show(); + + // SynthesizeMouseMoveEvent does nothing until the mouse is entered. + widget->SynthesizeMouseMoveEvent(); + EXPECT_EQ(0, v1->GetEventCount(ui::ET_MOUSE_MOVED)); + EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_MOVED)); + gfx::Point cursor_location(5, 5); + View::ConvertPointToScreen(widget->GetRootView(), &cursor_location); ui::MouseEvent move(ui::ET_MOUSE_MOVED, cursor_location, cursor_location, ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE); - widget->OnMouseEvent(&move); + ui::EventDispatchDetails details = + WidgetTest::GetEventProcessor(widget)->OnEventFromSource(&move); + EXPECT_FALSE(details.dispatcher_destroyed); - EXPECT_EQ(1, v1->GetEventCount(ui::ET_MOUSE_ENTERED)); - EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); + EXPECT_EQ(1, v1->GetEventCount(ui::ET_MOUSE_MOVED)); + EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_MOVED)); + + // SynthesizeMouseMoveEvent dispatches an mousemove event. + widget->SynthesizeMouseMoveEvent(); + EXPECT_EQ(2, v1->GetEventCount(ui::ET_MOUSE_MOVED)); delete v1; - v2->SetBounds(0, 0, 10, 10); - EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); + EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_MOVED)); + v2->SetBounds(5, 5, 10, 10); + EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_MOVED)); widget->SynthesizeMouseMoveEvent(); - EXPECT_EQ(1, v2->GetEventCount(ui::ET_MOUSE_ENTERED)); + EXPECT_EQ(1, v2->GetEventCount(ui::ET_MOUSE_MOVED)); widget->CloseNow(); } @@ -2084,6 +1996,7 @@ class WidgetBoundsObserver : public WidgetObserver { // WidgetObserver: void OnWidgetDestroying(Widget* widget) override { + EXPECT_TRUE(widget->GetNativeWindow()); bounds_ = widget->GetWindowBoundsInScreen(); } @@ -2127,7 +2040,7 @@ TEST_F(WidgetTest, CloseWidgetWhileAnimating) { widget->Init(params); AnimationEndObserver animation_observer; WidgetBoundsObserver widget_observer; - gfx::Rect bounds(0, 0, 50, 50); + gfx::Rect bounds(100, 100, 50, 50); { // Normal animations for tests have ZERO_DURATION, make sure we are actually // animating the movement. @@ -2148,6 +2061,37 @@ TEST_F(WidgetTest, CloseWidgetWhileAnimating) { EXPECT_EQ(widget_observer.bounds(), bounds); } +// Test that the NativeWidget is still valid during OnNativeWidgetDestroying(), +// and properties that depend on it are valid, when closed via CloseNow(). +TEST_F(WidgetTest, ValidDuringOnNativeWidgetDestroyingFromCloseNow) { + Widget* widget = CreateNativeDesktopWidget(); + widget->Show(); + gfx::Rect screen_rect(50, 50, 100, 100); + widget->SetBounds(screen_rect); + WidgetBoundsObserver observer; + widget->AddObserver(&observer); + widget->CloseNow(); + EXPECT_EQ(screen_rect, observer.bounds()); +} + +// Test that the NativeWidget is still valid during OnNativeWidgetDestroying(), +// and properties that depend on it are valid, when closed via Close(). +TEST_F(WidgetTest, ValidDuringOnNativeWidgetDestroyingFromClose) { + Widget* widget = CreateNativeDesktopWidget(); + widget->Show(); + gfx::Rect screen_rect(50, 50, 100, 100); + widget->SetBounds(screen_rect); + WidgetBoundsObserver observer; + widget->AddObserver(&observer); + widget->Close(); + EXPECT_EQ(gfx::Rect(), observer.bounds()); + base::RunLoop().RunUntilIdle(); +// Broken on Linux. See http://crbug.com/515379. +#if !defined(OS_LINUX) || defined(OS_CHROMEOS) + EXPECT_EQ(screen_rect, observer.bounds()); +#endif +} + // Tests that we do not crash when a Widget is destroyed by going out of // scope (as opposed to being explicitly deleted by its NativeWidget). TEST_F(WidgetTest, NoCrashOnWidgetDelete) { @@ -3053,92 +2997,6 @@ 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() {} - ~ModalDialogDelegate() override {} - - // WidgetDelegate overrides. - 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::EventTimeForNow(), - 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::EventTimeForNow(), 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::EventTimeForNow(), - 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) { @@ -3169,45 +3027,6 @@ TEST_F(WidgetTest, FullscreenStatePropagated) { } #endif } -#if defined(OS_WIN) - -// Tests whether we can activate the top level widget when a modal dialog is -// active. -TEST_F(WidgetTest, WindowModalityActivationTest) { - TestDesktopWidgetDelegate widget_delegate; - widget_delegate.InitWidget(CreateParams(Widget::InitParams::TYPE_WINDOW)); - - Widget* top_level_widget = widget_delegate.GetWidget(); - 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->GetNativeView()); - 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(); -} -#endif // defined(OS_WIN) -#endif // !defined(OS_CHROMEOS) namespace { |