blob: b1027cf4eeeec0c8a53c5358af94d7998a6a2a38 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
#include "base/bind.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/client/window_parenting_client.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/display/screen.h"
#include "ui/events/event_processor.h"
#include "ui/events/event_utils.h"
#include "ui/events/test/event_generator.h"
#include "ui/views/test/native_widget_factory.h"
#include "ui/views/test/test_views.h"
#include "ui/views/test/test_views_delegate.h"
#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"
#if defined(OS_WIN)
#include "ui/base/view_prop.h"
#include "ui/base/win/window_event_target.h"
#include "ui/views/win/hwnd_util.h"
#endif
namespace views {
namespace test {
typedef ViewsTestBase DesktopNativeWidgetAuraTest;
// Verifies creating a Widget with a parent that is not in a RootWindow doesn't
// crash.
TEST_F(DesktopNativeWidgetAuraTest, CreateWithParentNotInRootWindow) {
std::unique_ptr<aura::Window> window(new aura::Window(NULL));
window->Init(ui::LAYER_NOT_DRAWN);
Widget widget;
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
params.bounds = gfx::Rect(0, 0, 200, 200);
params.parent = window.get();
params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.native_widget = new DesktopNativeWidgetAura(&widget);
widget.Init(params);
}
// Verifies that the Aura windows making up a widget instance have the correct
// bounds after the widget is resized.
TEST_F(DesktopNativeWidgetAuraTest, DesktopAuraWindowSizeTest) {
Widget widget;
// On Linux we test this with popup windows because the WM may ignore the size
// suggestion for normal windows.
#if defined(OS_LINUX)
Widget::InitParams init_params =
CreateParams(Widget::InitParams::TYPE_POPUP);
#else
Widget::InitParams init_params =
CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
#endif
init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
init_params.native_widget = new DesktopNativeWidgetAura(&widget);
widget.Init(init_params);
gfx::Rect bounds(0, 0, 100, 100);
widget.SetBounds(bounds);
widget.Show();
EXPECT_EQ(bounds.ToString(),
widget.GetNativeView()->GetRootWindow()->bounds().ToString());
EXPECT_EQ(bounds.ToString(), widget.GetNativeView()->bounds().ToString());
EXPECT_EQ(bounds.ToString(),
widget.GetNativeView()->parent()->bounds().ToString());
gfx::Rect new_bounds(0, 0, 200, 200);
widget.SetBounds(new_bounds);
EXPECT_EQ(new_bounds.ToString(),
widget.GetNativeView()->GetRootWindow()->bounds().ToString());
EXPECT_EQ(new_bounds.ToString(), widget.GetNativeView()->bounds().ToString());
EXPECT_EQ(new_bounds.ToString(),
widget.GetNativeView()->parent()->bounds().ToString());
}
// Verifies GetNativeView() is initially hidden. If the native view is initially
// shown then animations can not be disabled.
TEST_F(DesktopNativeWidgetAuraTest, NativeViewInitiallyHidden) {
Widget widget;
Widget::InitParams init_params =
CreateParams(Widget::InitParams::TYPE_WINDOW);
init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
init_params.native_widget = new DesktopNativeWidgetAura(&widget);
widget.Init(init_params);
EXPECT_FALSE(widget.GetNativeView()->IsVisible());
}
// Verifies that if the DesktopWindowTreeHost is already shown, the native view
// still reports not visible as we haven't shown the content window.
TEST_F(DesktopNativeWidgetAuraTest, WidgetNotVisibleOnlyWindowTreeHostShown) {
Widget widget;
Widget::InitParams init_params =
CreateParams(Widget::InitParams::TYPE_WINDOW);
init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
init_params.native_widget = new DesktopNativeWidgetAura(&widget);
widget.Init(init_params);
DesktopNativeWidgetAura* desktop_native_widget_aura =
static_cast<DesktopNativeWidgetAura*>(widget.native_widget());
desktop_native_widget_aura->host()->Show();
EXPECT_FALSE(widget.IsVisible());
}
// Verify that the cursor state is shared between two native widgets.
TEST_F(DesktopNativeWidgetAuraTest, GlobalCursorState) {
// Create two native widgets, each owning different root windows.
Widget widget_a;
Widget::InitParams init_params_a =
CreateParams(Widget::InitParams::TYPE_WINDOW);
init_params_a.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
DesktopNativeWidgetAura* desktop_native_widget_aura_a =
new DesktopNativeWidgetAura(&widget_a);
init_params_a.native_widget = desktop_native_widget_aura_a;
widget_a.Init(init_params_a);
Widget widget_b;
Widget::InitParams init_params_b =
CreateParams(Widget::InitParams::TYPE_WINDOW);
init_params_b.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
DesktopNativeWidgetAura* desktop_native_widget_aura_b =
new DesktopNativeWidgetAura(&widget_b);
init_params_b.native_widget = desktop_native_widget_aura_b;
widget_b.Init(init_params_b);
aura::client::CursorClient* cursor_client_a = aura::client::GetCursorClient(
desktop_native_widget_aura_a->host()->window());
aura::client::CursorClient* cursor_client_b = aura::client::GetCursorClient(
desktop_native_widget_aura_b->host()->window());
// Verify the cursor can be locked using one client and unlocked using
// another.
EXPECT_FALSE(cursor_client_a->IsCursorLocked());
EXPECT_FALSE(cursor_client_b->IsCursorLocked());
cursor_client_a->LockCursor();
EXPECT_TRUE(cursor_client_a->IsCursorLocked());
EXPECT_TRUE(cursor_client_b->IsCursorLocked());
cursor_client_b->UnlockCursor();
EXPECT_FALSE(cursor_client_a->IsCursorLocked());
EXPECT_FALSE(cursor_client_b->IsCursorLocked());
// Verify that mouse events can be disabled using one client and then
// re-enabled using another. Note that disabling mouse events should also
// have the side effect of making the cursor invisible.
EXPECT_TRUE(cursor_client_a->IsCursorVisible());
EXPECT_TRUE(cursor_client_b->IsCursorVisible());
EXPECT_TRUE(cursor_client_a->IsMouseEventsEnabled());
EXPECT_TRUE(cursor_client_b->IsMouseEventsEnabled());
cursor_client_b->DisableMouseEvents();
EXPECT_FALSE(cursor_client_a->IsCursorVisible());
EXPECT_FALSE(cursor_client_b->IsCursorVisible());
EXPECT_FALSE(cursor_client_a->IsMouseEventsEnabled());
EXPECT_FALSE(cursor_client_b->IsMouseEventsEnabled());
cursor_client_a->EnableMouseEvents();
EXPECT_TRUE(cursor_client_a->IsCursorVisible());
EXPECT_TRUE(cursor_client_b->IsCursorVisible());
EXPECT_TRUE(cursor_client_a->IsMouseEventsEnabled());
EXPECT_TRUE(cursor_client_b->IsMouseEventsEnabled());
// Verify that setting the cursor using one cursor client
// will set it for all root windows.
EXPECT_EQ(ui::CursorType::kNone, cursor_client_a->GetCursor().native_type());
EXPECT_EQ(ui::CursorType::kNone, cursor_client_b->GetCursor().native_type());
cursor_client_b->SetCursor(ui::CursorType::kPointer);
EXPECT_EQ(ui::CursorType::kPointer,
cursor_client_a->GetCursor().native_type());
EXPECT_EQ(ui::CursorType::kPointer,
cursor_client_b->GetCursor().native_type());
// Verify that hiding the cursor using one cursor client will
// hide it for all root windows. Note that hiding the cursor
// should not disable mouse events.
cursor_client_a->HideCursor();
EXPECT_FALSE(cursor_client_a->IsCursorVisible());
EXPECT_FALSE(cursor_client_b->IsCursorVisible());
EXPECT_TRUE(cursor_client_a->IsMouseEventsEnabled());
EXPECT_TRUE(cursor_client_b->IsMouseEventsEnabled());
// Verify that the visibility state cannot be changed using one
// cursor client when the cursor was locked using another.
cursor_client_b->LockCursor();
cursor_client_a->ShowCursor();
EXPECT_FALSE(cursor_client_a->IsCursorVisible());
EXPECT_FALSE(cursor_client_b->IsCursorVisible());
// Verify the cursor becomes visible on unlock (since a request
// to make it visible was queued up while the cursor was locked).
cursor_client_b->UnlockCursor();
EXPECT_TRUE(cursor_client_a->IsCursorVisible());
EXPECT_TRUE(cursor_client_b->IsCursorVisible());
}
// Verifies FocusController doesn't attempt to access |content_window_| during
// destruction. Previously the FocusController was destroyed after the window.
// This could be problematic as FocusController references |content_window_| and
// could attempt to use it after |content_window_| was destroyed. This test
// verifies this doesn't happen. Note that this test only failed under ASAN.
TEST_F(DesktopNativeWidgetAuraTest, DontAccessContentWindowDuringDestruction) {
aura::test::TestWindowDelegate delegate;
{
Widget widget;
Widget::InitParams init_params =
CreateParams(Widget::InitParams::TYPE_WINDOW);
init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
DesktopNativeWidgetAura* desktop_native_widget_aura =
new DesktopNativeWidgetAura(&widget);
init_params.native_widget = desktop_native_widget_aura;
widget.Init(init_params);
// Owned by |widget|.
aura::Window* window = new aura::Window(&delegate);
window->Init(ui::LAYER_NOT_DRAWN);
window->Show();
widget.GetNativeWindow()->parent()->AddChild(window);
widget.Show();
}
}
void QuitNestedLoopAndCloseWidget(std::unique_ptr<Widget> widget,
base::Closure* quit_runloop) {
quit_runloop->Run();
}
// Verifies that a widget can be destroyed when running a nested message-loop.
TEST_F(DesktopNativeWidgetAuraTest, WidgetCanBeDestroyedFromNestedLoop) {
std::unique_ptr<Widget> widget(new Widget);
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
params.bounds = gfx::Rect(0, 0, 200, 200);
params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.native_widget = new DesktopNativeWidgetAura(widget.get());
widget->Init(params);
widget->Show();
// Post a task that terminates the nested loop and destroyes the widget. This
// task will be executed from the nested loop initiated with the call to
// |RunWithDispatcher()| below.
base::RunLoop run_loop;
base::Closure quit_runloop = run_loop.QuitClosure();
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&QuitNestedLoopAndCloseWidget, base::Passed(&widget),
base::Unretained(&quit_runloop)));
run_loop.Run();
}
// This class provides functionality to create fullscreen and top level popup
// windows. It additionally tests whether the destruction of these windows
// occurs correctly in desktop AURA without crashing.
// It provides facilities to test the following cases:-
// 1. Child window destroyed which should lead to the destruction of the
// parent.
// 2. Parent window destroyed which should lead to the child being destroyed.
class DesktopAuraTopLevelWindowTest : public aura::WindowObserver {
public:
DesktopAuraTopLevelWindowTest()
: top_level_widget_(NULL),
owned_window_(NULL),
owner_destroyed_(false),
owned_window_destroyed_(false),
use_async_mode_(true) {}
~DesktopAuraTopLevelWindowTest() override {
EXPECT_TRUE(owner_destroyed_);
EXPECT_TRUE(owned_window_destroyed_);
top_level_widget_ = NULL;
owned_window_ = NULL;
}
void CreateTopLevelWindow(const gfx::Rect& bounds, bool fullscreen) {
Widget::InitParams init_params;
init_params.type = Widget::InitParams::TYPE_WINDOW;
init_params.bounds = bounds;
init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
init_params.layer_type = ui::LAYER_NOT_DRAWN;
init_params.accept_events = fullscreen;
widget_.Init(init_params);
owned_window_ = new aura::Window(&child_window_delegate_);
owned_window_->SetType(ui::wm::WINDOW_TYPE_NORMAL);
owned_window_->SetName("TestTopLevelWindow");
if (fullscreen) {
owned_window_->SetProperty(aura::client::kShowStateKey,
ui::SHOW_STATE_FULLSCREEN);
} else {
owned_window_->SetType(ui::wm::WINDOW_TYPE_MENU);
}
owned_window_->Init(ui::LAYER_TEXTURED);
aura::client::ParentWindowWithContext(
owned_window_,
widget_.GetNativeView()->GetRootWindow(),
gfx::Rect(0, 0, 1900, 1600));
owned_window_->Show();
owned_window_->AddObserver(this);
ASSERT_TRUE(owned_window_->parent() != NULL);
owned_window_->parent()->AddObserver(this);
top_level_widget_ =
views::Widget::GetWidgetForNativeView(owned_window_->parent());
ASSERT_TRUE(top_level_widget_ != NULL);
}
void DestroyOwnedWindow() {
ASSERT_TRUE(owned_window_ != NULL);
// If async mode is off then clean up state here.
if (!use_async_mode_) {
owned_window_->RemoveObserver(this);
owned_window_->parent()->RemoveObserver(this);
owner_destroyed_ = true;
owned_window_destroyed_ = true;
}
delete owned_window_;
}
void DestroyOwnerWindow() {
ASSERT_TRUE(top_level_widget_ != NULL);
top_level_widget_->CloseNow();
}
void OnWindowDestroying(aura::Window* window) override {
window->RemoveObserver(this);
if (window == owned_window_) {
owned_window_destroyed_ = true;
} else if (window == top_level_widget_->GetNativeView()) {
owner_destroyed_ = true;
} else {
ADD_FAILURE() << "Unexpected window destroyed callback: " << window;
}
}
aura::Window* owned_window() {
return owned_window_;
}
views::Widget* top_level_widget() {
return top_level_widget_;
}
void set_use_async_mode(bool async_mode) {
use_async_mode_ = async_mode;
}
private:
views::Widget widget_;
views::Widget* top_level_widget_;
aura::Window* owned_window_;
bool owner_destroyed_;
bool owned_window_destroyed_;
aura::test::TestWindowDelegate child_window_delegate_;
// This flag controls whether we need to wait for the destruction to complete
// before finishing the test. Defaults to true.
bool use_async_mode_;
DISALLOW_COPY_AND_ASSIGN(DesktopAuraTopLevelWindowTest);
};
class DesktopAuraWidgetTest : public WidgetTest {
public:
DesktopAuraWidgetTest() {}
void SetUp() override {
ViewsTestBase::SetUp();
test_views_delegate()->set_use_desktop_native_widgets(true);
}
private:
DISALLOW_COPY_AND_ASSIGN(DesktopAuraWidgetTest);
};
TEST_F(DesktopAuraWidgetTest, FullscreenWindowDestroyedBeforeOwnerTest) {
DesktopAuraTopLevelWindowTest fullscreen_window;
ASSERT_NO_FATAL_FAILURE(fullscreen_window.CreateTopLevelWindow(
gfx::Rect(0, 0, 200, 200), true));
RunPendingMessages();
ASSERT_NO_FATAL_FAILURE(fullscreen_window.DestroyOwnedWindow());
RunPendingMessages();
}
TEST_F(DesktopAuraWidgetTest, FullscreenWindowOwnerDestroyed) {
DesktopAuraTopLevelWindowTest fullscreen_window;
ASSERT_NO_FATAL_FAILURE(fullscreen_window.CreateTopLevelWindow(
gfx::Rect(0, 0, 200, 200), true));
RunPendingMessages();
ASSERT_NO_FATAL_FAILURE(fullscreen_window.DestroyOwnerWindow());
RunPendingMessages();
}
TEST_F(DesktopAuraWidgetTest, TopLevelOwnedPopupTest) {
DesktopAuraTopLevelWindowTest popup_window;
ASSERT_NO_FATAL_FAILURE(popup_window.CreateTopLevelWindow(
gfx::Rect(0, 0, 200, 200), false));
RunPendingMessages();
ASSERT_NO_FATAL_FAILURE(popup_window.DestroyOwnedWindow());
RunPendingMessages();
}
// This test validates that when a top level owned popup Aura window is
// resized, the widget is resized as well.
TEST_F(DesktopAuraWidgetTest, TopLevelOwnedPopupResizeTest) {
DesktopAuraTopLevelWindowTest popup_window;
popup_window.set_use_async_mode(false);
ASSERT_NO_FATAL_FAILURE(popup_window.CreateTopLevelWindow(
gfx::Rect(0, 0, 200, 200), false));
gfx::Rect new_size(0, 0, 400, 400);
popup_window.owned_window()->SetBounds(new_size);
EXPECT_EQ(popup_window.top_level_widget()->GetNativeView()->bounds().size(),
new_size.size());
ASSERT_NO_FATAL_FAILURE(popup_window.DestroyOwnedWindow());
}
// This test validates that when a top level owned popup Aura window is
// repositioned, the widget is repositioned as well.
TEST_F(DesktopAuraWidgetTest, TopLevelOwnedPopupRepositionTest) {
DesktopAuraTopLevelWindowTest popup_window;
popup_window.set_use_async_mode(false);
ASSERT_NO_FATAL_FAILURE(popup_window.CreateTopLevelWindow(
gfx::Rect(0, 0, 200, 200), false));
gfx::Rect new_pos(10, 10, 400, 400);
popup_window.owned_window()->SetBoundsInScreen(
new_pos,
display::Screen::GetScreen()->GetDisplayNearestPoint(gfx::Point()));
EXPECT_EQ(new_pos,
popup_window.top_level_widget()->GetWindowBoundsInScreen());
ASSERT_NO_FATAL_FAILURE(popup_window.DestroyOwnedWindow());
}
// The following code verifies we can correctly destroy a Widget from a mouse
// enter/exit. We could test move/drag/enter/exit but in general we don't run
// nested run loops from such events, nor has the code ever really dealt
// with this situation.
// Generates two moves (first generates enter, second real move), a press, drag
// and release stopping at |last_event_type|.
void GenerateMouseEvents(Widget* widget, ui::EventType last_event_type) {
const gfx::Rect screen_bounds(widget->GetWindowBoundsInScreen());
ui::MouseEvent move_event(ui::ET_MOUSE_MOVED, screen_bounds.CenterPoint(),
screen_bounds.CenterPoint(), ui::EventTimeForNow(),
0, 0);
ui::EventSink* sink = WidgetTest::GetEventSink(widget);
ui::EventDispatchDetails details = sink->OnEventFromSource(&move_event);
if (last_event_type == ui::ET_MOUSE_ENTERED || details.dispatcher_destroyed)
return;
details = sink->OnEventFromSource(&move_event);
if (last_event_type == ui::ET_MOUSE_MOVED || details.dispatcher_destroyed)
return;
ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, screen_bounds.CenterPoint(),
screen_bounds.CenterPoint(), ui::EventTimeForNow(),
0, 0);
details = sink->OnEventFromSource(&press_event);
if (last_event_type == ui::ET_MOUSE_PRESSED || details.dispatcher_destroyed)
return;
gfx::Point end_point(screen_bounds.CenterPoint());
end_point.Offset(1, 1);
ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, end_point, end_point,
ui::EventTimeForNow(), 0, 0);
details = sink->OnEventFromSource(&drag_event);
if (last_event_type == ui::ET_MOUSE_DRAGGED || details.dispatcher_destroyed)
return;
ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, end_point, end_point,
ui::EventTimeForNow(), 0, 0);
details = sink->OnEventFromSource(&release_event);
if (details.dispatcher_destroyed)
return;
}
// Creates a widget and invokes GenerateMouseEvents() with |last_event_type|.
void RunCloseWidgetDuringDispatchTest(WidgetTest* test,
ui::EventType last_event_type) {
// |widget| is deleted by CloseWidgetView.
Widget* widget = new Widget;
Widget::InitParams params =
test->CreateParams(Widget::InitParams::TYPE_POPUP);
params.native_widget =
CreatePlatformDesktopNativeWidgetImpl(params, widget, nullptr);
params.bounds = gfx::Rect(0, 0, 50, 100);
widget->Init(params);
widget->SetContentsView(new CloseWidgetView(last_event_type));
widget->Show();
GenerateMouseEvents(widget, last_event_type);
}
// Verifies deleting the widget from a mouse pressed event doesn't crash.
TEST_F(DesktopAuraWidgetTest, CloseWidgetDuringMousePress) {
RunCloseWidgetDuringDispatchTest(this, ui::ET_MOUSE_PRESSED);
}
// Verifies deleting the widget from a mouse released event doesn't crash.
TEST_F(DesktopAuraWidgetTest, CloseWidgetDuringMouseReleased) {
RunCloseWidgetDuringDispatchTest(this, ui::ET_MOUSE_RELEASED);
}
namespace {
// 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);
};
} // namespace
// 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 = CreatePlatformDesktopNativeWidgetImpl(
init_params, &top_level_widget, nullptr);
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 =
GetEventSink(&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 =
GetEventSink(&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 =
GetEventSink(&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.
// Flaky: crbug.com/613428
TEST_F(WidgetTest, DISABLED_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();
}
// This test validates that sending WM_CHAR/WM_SYSCHAR/WM_SYSDEADCHAR
// messages via the WindowEventTarget interface implemented by the
// HWNDMessageHandler class does not cause a crash due to an unprocessed
// event
TEST_F(WidgetTest, CharMessagesAsKeyboardMessagesDoesNotCrash) {
Widget widget;
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
params.native_widget =
CreatePlatformDesktopNativeWidgetImpl(params, &widget, nullptr);
params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
widget.Init(params);
widget.Show();
ui::WindowEventTarget* target =
reinterpret_cast<ui::WindowEventTarget*>(ui::ViewProp::GetValue(
widget.GetNativeWindow()->GetHost()->GetAcceleratedWidget(),
ui::WindowEventTarget::kWin32InputEventTarget));
ASSERT_NE(nullptr, target);
bool handled = false;
target->HandleKeyboardMessage(WM_CHAR, 0, 0, &handled);
target->HandleKeyboardMessage(WM_SYSCHAR, 0, 0, &handled);
target->HandleKeyboardMessage(WM_SYSDEADCHAR, 0, 0, &handled);
widget.CloseNow();
}
#endif // defined(OS_WIN)
} // namespace test
} // namespace views