// Copyright (c) 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 "base/basictypes.h"
#include "base/bind.h"
#include "base/run_loop.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/views/test/widget_test.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/dialog_delegate.h"
#if defined(USE_AURA)
#include "ui/aura/client/activation_client.h"
#include "ui/aura/client/focus_client.h"
#include "ui/aura/env.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#endif
#if defined(USE_AURA) && !defined(OS_CHROMEOS)
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
#endif
#if defined(OS_WIN)
#include "ui/views/win/hwnd_util.h"
#endif
namespace views {
namespace test {
namespace {
// A View that closes the Widget and exits the current message-loop when it
// receives a mouse-release event.
class ExitLoopOnRelease : public View {
public:
ExitLoopOnRelease() {}
virtual ~ExitLoopOnRelease() {}
private:
// Overridden from View:
virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE {
GetWidget()->Close();
base::MessageLoop::current()->QuitNow();
}
DISALLOW_COPY_AND_ASSIGN(ExitLoopOnRelease);
};
// A view that does a capture on gesture-begin events.
class GestureCaptureView : public View {
public:
GestureCaptureView() {}
virtual ~GestureCaptureView() {}
private:
// Overridden from View:
virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
if (event->type() == ui::ET_GESTURE_BEGIN) {
GetWidget()->SetCapture(this);
event->StopPropagation();
}
}
DISALLOW_COPY_AND_ASSIGN(GestureCaptureView);
};
// A view that always processes all mouse events.
class MouseView : public View {
public:
MouseView()
: View(),
entered_(0),
exited_(0),
pressed_(0) {
}
virtual ~MouseView() {}
virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE {
pressed_++;
return true;
}
virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE {
entered_++;
}
virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE {
exited_++;
}
// Return the number of OnMouseEntered calls and reset the counter.
int EnteredCalls() {
int i = entered_;
entered_ = 0;
return i;
}
// Return the number of OnMouseExited calls and reset the counter.
int ExitedCalls() {
int i = exited_;
exited_ = 0;
return i;
}
int pressed() const { return pressed_; }
private:
int entered_;
int exited_;
int pressed_;
DISALLOW_COPY_AND_ASSIGN(MouseView);
};
// A View that shows a different widget, sets capture on that widget, and
// initiates a nested message-loop when it receives a mouse-press event.
class NestedLoopCaptureView : public View {
public:
explicit NestedLoopCaptureView(Widget* widget) : widget_(widget) {}
virtual ~NestedLoopCaptureView() {}
private:
// Overridden from View:
virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE {
// Start a nested loop.
widget_->Show();
widget_->SetCapture(widget_->GetContentsView());
EXPECT_TRUE(widget_->HasCapture());
base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
base::MessageLoop::ScopedNestableTaskAllower allow(loop);
base::RunLoop run_loop;
#if defined(USE_AURA)
run_loop.set_dispatcher(aura::Env::GetInstance()->GetDispatcher());
#endif
run_loop.Run();
return true;
}
Widget* widget_;
DISALLOW_COPY_AND_ASSIGN(NestedLoopCaptureView);
};
} // namespace
#if defined(OS_WIN) && defined(USE_AURA)
// Tests whether activation and focus change works correctly in Windows AURA.
// We test the following:-
// 1. If the active aura window is correctly set when a top level widget is
// created.
// 2. If the active aura window in widget 1 created above, is set to NULL when
// another top level widget is created and focused.
// 3. On focusing the native platform window for widget 1, the active aura
// window for widget 1 should be set and that for widget 2 should reset.
// TODO(ananta)
// Discuss with erg on how to write this test for linux x11 aura.
TEST_F(WidgetTest, DesktopNativeWidgetAuraActivationAndFocusTest) {
// Create widget 1 and expect the active window to be its window.
View* contents_view1 = new View;
contents_view1->SetFocusable(true);
Widget widget1;
Widget::InitParams init_params =
CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
init_params.bounds = gfx::Rect(0, 0, 200, 200);
init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
init_params.native_widget = new DesktopNativeWidgetAura(&widget1);
widget1.Init(init_params);
widget1.SetContentsView(contents_view1);
widget1.Show();
aura::Window* root_window1= widget1.GetNativeView()->GetRootWindow();
contents_view1->RequestFocus();
EXPECT_TRUE(root_window1 != NULL);
aura::client::ActivationClient* activation_client1 =
aura::client::GetActivationClient(root_window1);
EXPECT_TRUE(activation_client1 != NULL);
EXPECT_EQ(activation_client1->GetActiveWindow(), widget1.GetNativeView());
// Create widget 2 and expect the active window to be its window.
View* contents_view2 = new View;
Widget widget2;
Widget::InitParams init_params2 =
CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
init_params2.bounds = gfx::Rect(0, 0, 200, 200);
init_params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
init_params2.native_widget = new DesktopNativeWidgetAura(&widget2);
widget2.Init(init_params2);
widget2.SetContentsView(contents_view2);
widget2.Show();
aura::Window* root_window2 = widget2.GetNativeView()->GetRootWindow();
contents_view2->RequestFocus();
::SetActiveWindow(
root_window2->GetDispatcher()->host()->GetAcceleratedWidget());
aura::client::ActivationClient* activation_client2 =
aura::client::GetActivationClient(root_window2);
EXPECT_TRUE(activation_client2 != NULL);
EXPECT_EQ(activation_client2->GetActiveWindow(), widget2.GetNativeView());
EXPECT_EQ(activation_client1->GetActiveWindow(),
reinterpret_cast<aura::Window*>(NULL));
// Now set focus back to widget 1 and expect the active window to be its
// window.
contents_view1->RequestFocus();
::SetActiveWindow(
root_window1->GetDispatcher()->host()->GetAcceleratedWidget());
EXPECT_EQ(activation_client2->GetActiveWindow(),
reinterpret_cast<aura::Window*>(NULL));
EXPECT_EQ(activation_client1->GetActiveWindow(), widget1.GetNativeView());
}
#endif
TEST_F(WidgetTest, CaptureAutoReset) {
Widget* toplevel = CreateTopLevelFramelessPlatformWidget();
View* container = new View;
toplevel->SetContentsView(container);
EXPECT_FALSE(toplevel->HasCapture());
toplevel->SetCapture(NULL);
EXPECT_TRUE(toplevel->HasCapture());
// By default, mouse release removes capture.
gfx::Point click_location(45, 15);
ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
ui::EF_LEFT_MOUSE_BUTTON);
toplevel->OnMouseEvent(&release);
EXPECT_FALSE(toplevel->HasCapture());
// Now a mouse release shouldn't remove capture.
toplevel->set_auto_release_capture(false);
toplevel->SetCapture(NULL);
EXPECT_TRUE(toplevel->HasCapture());
toplevel->OnMouseEvent(&release);
EXPECT_TRUE(toplevel->HasCapture());
toplevel->ReleaseCapture();
EXPECT_FALSE(toplevel->HasCapture());
toplevel->Close();
RunPendingMessages();
}
TEST_F(WidgetTest, ResetCaptureOnGestureEnd) {
Widget* toplevel = CreateTopLevelFramelessPlatformWidget();
View* container = new View;
toplevel->SetContentsView(container);
View* gesture = new GestureCaptureView;
gesture->SetBounds(0, 0, 30, 30);
container->AddChildView(gesture);
MouseView* mouse = new MouseView;
mouse->SetBounds(30, 0, 30, 30);
container->AddChildView(mouse);
toplevel->SetSize(gfx::Size(100, 100));
toplevel->Show();
// Start a gesture on |gesture|.
ui::GestureEvent begin(ui::ET_GESTURE_BEGIN,
15, 15, 0, base::TimeDelta(),
ui::GestureEventDetails(ui::ET_GESTURE_BEGIN, 0, 0), 1);
ui::GestureEvent end(ui::ET_GESTURE_END,
15, 15, 0, base::TimeDelta(),
ui::GestureEventDetails(ui::ET_GESTURE_END, 0, 0), 1);
toplevel->OnGestureEvent(&begin);
// Now try to click on |mouse|. Since |gesture| will have capture, |mouse|
// will not receive the event.
gfx::Point click_location(45, 15);
ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location,
ui::EF_LEFT_MOUSE_BUTTON);
ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
ui::EF_LEFT_MOUSE_BUTTON);
EXPECT_TRUE(toplevel->HasCapture());
toplevel->OnMouseEvent(&press);
toplevel->OnMouseEvent(&release);
EXPECT_EQ(0, mouse->pressed());
EXPECT_FALSE(toplevel->HasCapture());
// The end of the gesture should release the capture, and pressing on |mouse|
// should now reach |mouse|.
toplevel->OnGestureEvent(&end);
toplevel->OnMouseEvent(&press);
toplevel->OnMouseEvent(&release);
EXPECT_EQ(1, mouse->pressed());
toplevel->Close();
RunPendingMessages();
}
// Checks that if a mouse-press triggers a capture on a different widget (which
// consumes the mouse-release event), then the target of the press does not have
// capture.
TEST_F(WidgetTest, DisableCaptureWidgetFromMousePress) {
// The test creates two widgets: |first| and |second|.
// The View in |first| makes |second| visible, sets capture on it, and starts
// a nested loop (like a menu does). The View in |second| terminates the
// nested loop and closes the widget.
// The test sends a mouse-press event to |first|, and posts a task to send a
// release event to |second|, to make sure that the release event is
// dispatched after the nested loop starts.
Widget* first = CreateTopLevelFramelessPlatformWidget();
Widget* second = CreateTopLevelFramelessPlatformWidget();
View* container = new NestedLoopCaptureView(second);
first->SetContentsView(container);
second->SetContentsView(new ExitLoopOnRelease());
first->SetSize(gfx::Size(100, 100));
first->Show();
gfx::Point location(20, 20);
base::MessageLoop::current()->PostTask(FROM_HERE,
base::Bind(&Widget::OnMouseEvent,
base::Unretained(second),
base::Owned(new ui::MouseEvent(ui::ET_MOUSE_RELEASED,
location,
location,
ui::EF_LEFT_MOUSE_BUTTON))));
ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location,
ui::EF_LEFT_MOUSE_BUTTON);
first->OnMouseEvent(&press);
EXPECT_FALSE(first->HasCapture());
first->Close();
RunPendingMessages();
}
// Tests some grab/ungrab events.
// TODO(estade): can this be enabled now that this is an interactive ui test?
TEST_F(WidgetTest, DISABLED_GrabUngrab) {
Widget* toplevel = CreateTopLevelPlatformWidget();
Widget* child1 = CreateChildNativeWidgetWithParent(toplevel);
Widget* child2 = CreateChildNativeWidgetWithParent(toplevel);
toplevel->SetBounds(gfx::Rect(0, 0, 500, 500));
child1->SetBounds(gfx::Rect(10, 10, 300, 300));
View* view = new MouseView();
view->SetBounds(0, 0, 300, 300);
child1->GetRootView()->AddChildView(view);
child2->SetBounds(gfx::Rect(200, 10, 200, 200));
view = new MouseView();
view->SetBounds(0, 0, 200, 200);
child2->GetRootView()->AddChildView(view);
toplevel->Show();
RunPendingMessages();
// Click on child1
gfx::Point p1(45, 45);
ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1,
ui::EF_LEFT_MOUSE_BUTTON);
toplevel->OnMouseEvent(&pressed);
EXPECT_TRUE(toplevel->HasCapture());
EXPECT_TRUE(child1->HasCapture());
EXPECT_FALSE(child2->HasCapture());
ui::MouseEvent released(ui::ET_MOUSE_RELEASED, p1, p1,
ui::EF_LEFT_MOUSE_BUTTON);
toplevel->OnMouseEvent(&released);
EXPECT_FALSE(toplevel->HasCapture());
EXPECT_FALSE(child1->HasCapture());
EXPECT_FALSE(child2->HasCapture());
RunPendingMessages();
// Click on child2
gfx::Point p2(315, 45);
ui::MouseEvent pressed2(ui::ET_MOUSE_PRESSED, p2, p2,
ui::EF_LEFT_MOUSE_BUTTON);
toplevel->OnMouseEvent(&pressed2);
EXPECT_TRUE(pressed2.handled());
EXPECT_TRUE(toplevel->HasCapture());
EXPECT_TRUE(child2->HasCapture());
EXPECT_FALSE(child1->HasCapture());
ui::MouseEvent released2(ui::ET_MOUSE_RELEASED, p2, p2,
ui::EF_LEFT_MOUSE_BUTTON);
toplevel->OnMouseEvent(&released2);
EXPECT_FALSE(toplevel->HasCapture());
EXPECT_FALSE(child1->HasCapture());
EXPECT_FALSE(child2->HasCapture());
toplevel->CloseNow();
}
// Tests mouse move outside of the window into the "resize controller" and back
// will still generate an OnMouseEntered and OnMouseExited event..
TEST_F(WidgetTest, CheckResizeControllerEvents) {
Widget* toplevel = CreateTopLevelPlatformWidget();
toplevel->SetBounds(gfx::Rect(0, 0, 100, 100));
MouseView* view = new MouseView();
view->SetBounds(90, 90, 10, 10);
toplevel->GetRootView()->AddChildView(view);
toplevel->Show();
RunPendingMessages();
// Move to an outside position.
gfx::Point p1(200, 200);
ui::MouseEvent moved_out(ui::ET_MOUSE_MOVED, p1, p1, ui::EF_NONE);
toplevel->OnMouseEvent(&moved_out);
EXPECT_EQ(0, view->EnteredCalls());
EXPECT_EQ(0, view->ExitedCalls());
// Move onto the active view.
gfx::Point p2(95, 95);
ui::MouseEvent moved_over(ui::ET_MOUSE_MOVED, p2, p2, ui::EF_NONE);
toplevel->OnMouseEvent(&moved_over);
EXPECT_EQ(1, view->EnteredCalls());
EXPECT_EQ(0, view->ExitedCalls());
// Move onto the outer resizing border.
gfx::Point p3(102, 95);
ui::MouseEvent moved_resizer(ui::ET_MOUSE_MOVED, p3, p3, ui::EF_NONE);
toplevel->OnMouseEvent(&moved_resizer);
EXPECT_EQ(0, view->EnteredCalls());
EXPECT_EQ(1, view->ExitedCalls());
// Move onto the view again.
toplevel->OnMouseEvent(&moved_over);
EXPECT_EQ(1, view->EnteredCalls());
EXPECT_EQ(0, view->ExitedCalls());
RunPendingMessages();
toplevel->CloseNow();
}
#if defined(OS_WIN)
// This class subclasses the Widget class to listen for activation change
// notifications and provides accessors to return information as to whether
// the widget is active. We need this to ensure that users of the widget
// class activate the widget only when the underlying window becomes really
// active. Previously we would activate the widget in the WM_NCACTIVATE
// message which is incorrect because APIs like FlashWindowEx flash the
// window caption by sending fake WM_NCACTIVATE messages.
class WidgetActivationTest : public Widget {
public:
WidgetActivationTest()
: active_(false) {}
virtual ~WidgetActivationTest() {}
virtual void OnNativeWidgetActivationChanged(bool active) OVERRIDE {
active_ = active;
}
bool active() const { return active_; }
private:
bool active_;
DISALLOW_COPY_AND_ASSIGN(WidgetActivationTest);
};
// Tests whether the widget only becomes active when the underlying window
// is really active.
TEST_F(WidgetTest, WidgetNotActivatedOnFakeActivationMessages) {
WidgetActivationTest widget1;
Widget::InitParams init_params =
CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
#if defined(USE_AURA)
init_params.native_widget = new DesktopNativeWidgetAura(&widget1);
#endif
init_params.bounds = gfx::Rect(0, 0, 200, 200);
widget1.Init(init_params);
widget1.Show();
EXPECT_EQ(true, widget1.active());
WidgetActivationTest widget2;
#if defined(USE_AURA)
init_params.native_widget = new DesktopNativeWidgetAura(&widget2);
#endif
widget2.Init(init_params);
widget2.Show();
EXPECT_EQ(true, widget2.active());
EXPECT_EQ(false, widget1.active());
HWND win32_native_window1 = HWNDForWidget(&widget1);
EXPECT_TRUE(::IsWindow(win32_native_window1));
::SendMessage(win32_native_window1, WM_NCACTIVATE, 1, 0);
EXPECT_EQ(false, widget1.active());
EXPECT_EQ(true, widget2.active());
::SetActiveWindow(win32_native_window1);
EXPECT_EQ(true, widget1.active());
EXPECT_EQ(false, widget2.active());
}
#endif
#if defined(USE_AURA) && !defined(OS_CHROMEOS)
// Provides functionality to create a window modal dialog.
class ModalDialogDelegate : public DialogDelegateView {
public:
explicit ModalDialogDelegate(ui::ModalType type) : type_(type) {}
virtual ~ModalDialogDelegate() {}
// WidgetDelegate overrides.
virtual ui::ModalType GetModalType() const OVERRIDE {
return type_;
}
private:
ui::ModalType type_;
DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate);
};
// Tests whether the focused window is set correctly when a modal window is
// created and destroyed. When it is destroyed it should focus the owner
// window.
TEST_F(WidgetTest, WindowModalWindowDestroyedActivationTest) {
// Create a top level widget.
Widget top_level_widget;
Widget::InitParams init_params =
CreateParams(Widget::InitParams::TYPE_WINDOW);
init_params.show_state = ui::SHOW_STATE_NORMAL;
gfx::Rect initial_bounds(0, 0, 500, 500);
init_params.bounds = initial_bounds;
init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
init_params.native_widget = new DesktopNativeWidgetAura(&top_level_widget);
top_level_widget.Init(init_params);
top_level_widget.Show();
aura::Window* top_level_window = top_level_widget.GetNativeWindow();
EXPECT_EQ(top_level_window, aura::client::GetFocusClient(
top_level_window)->GetFocusedWindow());
// Create a modal dialog.
// This instance will be destroyed when the dialog is destroyed.
ModalDialogDelegate* dialog_delegate =
new ModalDialogDelegate(ui::MODAL_TYPE_WINDOW);
Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
dialog_delegate, NULL, top_level_widget.GetNativeWindow());
modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
modal_dialog_widget->Show();
aura::Window* dialog_window = modal_dialog_widget->GetNativeWindow();
EXPECT_EQ(dialog_window, aura::client::GetFocusClient(
top_level_window)->GetFocusedWindow());
modal_dialog_widget->CloseNow();
EXPECT_EQ(top_level_window, aura::client::GetFocusClient(
top_level_window)->GetFocusedWindow());
top_level_widget.CloseNow();
}
// Test that when opening a system-modal window, capture is released.
TEST_F(WidgetTest, SystemModalWindowReleasesCapture) {
// Create a top level widget.
Widget top_level_widget;
Widget::InitParams init_params =
CreateParams(Widget::InitParams::TYPE_WINDOW);
init_params.show_state = ui::SHOW_STATE_NORMAL;
gfx::Rect initial_bounds(0, 0, 500, 500);
init_params.bounds = initial_bounds;
init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
init_params.native_widget = new DesktopNativeWidgetAura(&top_level_widget);
top_level_widget.Init(init_params);
top_level_widget.Show();
aura::Window* top_level_window = top_level_widget.GetNativeWindow();
EXPECT_EQ(top_level_window, aura::client::GetFocusClient(
top_level_window)->GetFocusedWindow());
EXPECT_FALSE(top_level_window->HasCapture());
top_level_window->SetCapture();
EXPECT_TRUE(top_level_window->HasCapture());
// Create a modal dialog.
ModalDialogDelegate* dialog_delegate =
new ModalDialogDelegate(ui::MODAL_TYPE_SYSTEM);
Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
dialog_delegate, NULL, top_level_widget.GetNativeWindow());
modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
modal_dialog_widget->Show();
EXPECT_FALSE(top_level_window->HasCapture());
modal_dialog_widget->CloseNow();
top_level_widget.CloseNow();
}
#endif
namespace {
// Used to veirfy OnMouseCaptureLost() has been invoked.
class CaptureLostTrackingWidget : public Widget {
public:
CaptureLostTrackingWidget() : got_capture_lost_(false) {}
virtual ~CaptureLostTrackingWidget() {}
bool GetAndClearGotCaptureLost() {
bool value = got_capture_lost_;
got_capture_lost_ = false;
return value;
}
// Widget:
virtual void OnMouseCaptureLost() OVERRIDE {
got_capture_lost_ = true;
Widget::OnMouseCaptureLost();
}
private:
bool got_capture_lost_;
DISALLOW_COPY_AND_ASSIGN(CaptureLostTrackingWidget);
};
} // namespace
class WidgetCaptureTest : public ViewsTestBase {
public:
WidgetCaptureTest() {
}
virtual ~WidgetCaptureTest() {
}
// Verifies Widget::SetCapture() results in updating native capture along with
// invoking the right Widget function.
void TestCapture(bool use_desktop_native_widget) {
CaptureLostTrackingWidget widget1;
Widget::InitParams params1 =
CreateParams(views::Widget::InitParams::TYPE_WINDOW);
params1.native_widget = CreateNativeWidget(use_desktop_native_widget,
&widget1);
params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
widget1.Init(params1);
widget1.Show();
CaptureLostTrackingWidget widget2;
Widget::InitParams params2 =
CreateParams(views::Widget::InitParams::TYPE_WINDOW);
params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params2.native_widget = CreateNativeWidget(use_desktop_native_widget,
&widget2);
widget2.Init(params2);
widget2.Show();
// Set capture to widget2 and verity it gets it.
widget2.SetCapture(widget2.GetRootView());
EXPECT_FALSE(widget1.HasCapture());
EXPECT_TRUE(widget2.HasCapture());
EXPECT_FALSE(widget1.GetAndClearGotCaptureLost());
EXPECT_FALSE(widget2.GetAndClearGotCaptureLost());
// Set capture to widget1 and verify it gets it.
widget1.SetCapture(widget1.GetRootView());
EXPECT_TRUE(widget1.HasCapture());
EXPECT_FALSE(widget2.HasCapture());
EXPECT_FALSE(widget1.GetAndClearGotCaptureLost());
EXPECT_TRUE(widget2.GetAndClearGotCaptureLost());
// Release and verify no one has it.
widget1.ReleaseCapture();
EXPECT_FALSE(widget1.HasCapture());
EXPECT_FALSE(widget2.HasCapture());
EXPECT_TRUE(widget1.GetAndClearGotCaptureLost());
EXPECT_FALSE(widget2.GetAndClearGotCaptureLost());
}
private:
NativeWidget* CreateNativeWidget(bool create_desktop_native_widget,
Widget* widget) {
#if defined(USE_AURA) && !defined(OS_CHROMEOS)
if (create_desktop_native_widget)
return new DesktopNativeWidgetAura(widget);
#endif
return NULL;
}
DISALLOW_COPY_AND_ASSIGN(WidgetCaptureTest);
};
// See description in TestCapture().
TEST_F(WidgetCaptureTest, Capture) {
TestCapture(false);
}
#if defined(USE_AURA) && !defined(OS_LINUX)
// See description in TestCapture(). Creates DesktopNativeWidget.
TEST_F(WidgetCaptureTest, CaptureDesktopNativeWidget) {
TestCapture(true);
}
#endif
#if defined(USE_AURA) && !defined(OS_CHROMEOS)
namespace {
// Used to veirfy OnMouseEvent() has been invoked.
class MouseEventTrackingWidget : public Widget {
public:
MouseEventTrackingWidget() : got_mouse_event_(false) {}
virtual ~MouseEventTrackingWidget() {}
bool GetAndClearGotMouseEvent() {
bool value = got_mouse_event_;
got_mouse_event_ = false;
return value;
}
// Widget:
virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
got_mouse_event_ = true;
Widget::OnMouseEvent(event);
}
private:
bool got_mouse_event_;
DISALLOW_COPY_AND_ASSIGN(MouseEventTrackingWidget);
};
} // namespace
#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA)
// TODO(erg): linux_aura bringup: http://crbug.com/163931
#define MAYBE_MouseEventDispatchedToRightWindow \
DISABLED_MouseEventDispatchedToRightWindow
#else
#define MAYBE_MouseEventDispatchedToRightWindow \
MouseEventDispatchedToRightWindow
#endif
// Verifies if a mouse event is received on a widget that doesn't have capture
// it is correctly processed by the widget that doesn't have capture.
TEST_F(WidgetCaptureTest, MAYBE_MouseEventDispatchedToRightWindow) {
MouseEventTrackingWidget widget1;
Widget::InitParams params1 =
CreateParams(views::Widget::InitParams::TYPE_WINDOW);
params1.native_widget = new DesktopNativeWidgetAura(&widget1);
params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
widget1.Init(params1);
widget1.Show();
MouseEventTrackingWidget widget2;
Widget::InitParams params2 =
CreateParams(views::Widget::InitParams::TYPE_WINDOW);
params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params2.native_widget = new DesktopNativeWidgetAura(&widget2);
widget2.Init(params2);
widget2.Show();
// Set capture to widget2 and verity it gets it.
widget2.SetCapture(widget2.GetRootView());
EXPECT_FALSE(widget1.HasCapture());
EXPECT_TRUE(widget2.HasCapture());
widget1.GetAndClearGotMouseEvent();
widget2.GetAndClearGotMouseEvent();
// Send a mouse event to the RootWindow associated with |widget1|. Even though
// |widget2| has capture, |widget1| should still get the event.
ui::MouseEvent mouse_event(ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(),
ui::EF_NONE);
widget1.GetNativeWindow()->GetDispatcher()->AsRootWindowHostDelegate()->
OnHostMouseEvent(&mouse_event);
EXPECT_TRUE(widget1.GetAndClearGotMouseEvent());
EXPECT_FALSE(widget2.GetAndClearGotMouseEvent());
}
#endif
} // namespace test
} // namespace views