Index: ui/views/widget/widget_interactive_uitest.cc |
diff --git a/ui/views/widget/widget_interactive_uitest.cc b/ui/views/widget/widget_interactive_uitest.cc |
index 1ae760055ca74c6fccddd4efaf5321fd14716c26..b05a0bc8be620c5e34a14e8d60ba42fa71ae0efd 100644 |
--- a/ui/views/widget/widget_interactive_uitest.cc |
+++ b/ui/views/widget/widget_interactive_uitest.cc |
@@ -3,19 +3,142 @@ |
// 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" |
#if defined(USE_AURA) |
#include "ui/aura/client/activation_client.h" |
+#include "ui/aura/env.h" |
#include "ui/aura/root_window.h" |
-#include "ui/views/test/views_test_base.h" |
#if !defined(OS_CHROMEOS) |
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" |
#endif |
#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. |
@@ -28,7 +151,7 @@ namespace views { |
// 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(ViewsTestBase, DesktopNativeWidgetAuraActivationAndFocusTest) { |
+TEST_F(WidgetTest, DesktopNativeWidgetAuraActivationAndFocusTest) { |
// Create widget 1 and expect the active window to be its window. |
View* contents_view1 = new View; |
contents_view1->set_focusable(true); |
@@ -82,4 +205,241 @@ TEST_F(ViewsTestBase, DesktopNativeWidgetAuraActivationAndFocusTest) { |
} |
#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. |
+// Fails on chromium.webkit Windows bot, see crbug.com/264872. |
+#if defined(OS_WIN) |
+#define MAYBE_DisableCaptureWidgetFromMousePress\ |
+ DISABLED_CaptureWidgetFromMousePress |
+#else |
+#define MAYBE_DisableCaptureWidgetFromMousePress\ |
+ CaptureWidgetFromMousePress |
+#endif |
+TEST_F(WidgetTest, MAYBE_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(); |
+} |
+ |
+} // namespace test |
} // namespace views |