Home | History | Annotate | Download | only in widget
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/basictypes.h"
      6 #include "base/bind.h"
      7 #include "base/command_line.h"
      8 #include "base/path_service.h"
      9 #include "base/run_loop.h"
     10 #include "base/strings/stringprintf.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "ui/aura/client/focus_client.h"
     13 #include "ui/aura/env.h"
     14 #include "ui/aura/test/event_generator.h"
     15 #include "ui/aura/window.h"
     16 #include "ui/aura/window_tree_host.h"
     17 #include "ui/base/resource/resource_bundle.h"
     18 #include "ui/base/ui_base_paths.h"
     19 #include "ui/base/ui_base_switches.h"
     20 #include "ui/events/event_processor.h"
     21 #include "ui/gfx/native_widget_types.h"
     22 #include "ui/gl/gl_surface.h"
     23 #include "ui/views/controls/textfield/textfield.h"
     24 #include "ui/views/controls/textfield/textfield_test_api.h"
     25 #include "ui/views/test/widget_test.h"
     26 #include "ui/views/touchui/touch_selection_controller_impl.h"
     27 #include "ui/views/widget/widget.h"
     28 #include "ui/views/window/dialog_delegate.h"
     29 #include "ui/wm/public/activation_client.h"
     30 
     31 #if !defined(OS_CHROMEOS)
     32 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
     33 #endif
     34 
     35 #if defined(OS_WIN)
     36 #include "ui/views/win/hwnd_util.h"
     37 #endif
     38 
     39 namespace views {
     40 namespace test {
     41 
     42 namespace {
     43 
     44 // A View that closes the Widget and exits the current message-loop when it
     45 // receives a mouse-release event.
     46 class ExitLoopOnRelease : public View {
     47  public:
     48   ExitLoopOnRelease() {}
     49   virtual ~ExitLoopOnRelease() {}
     50 
     51  private:
     52   // Overridden from View:
     53   virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE {
     54     GetWidget()->Close();
     55     base::MessageLoop::current()->QuitNow();
     56   }
     57 
     58   DISALLOW_COPY_AND_ASSIGN(ExitLoopOnRelease);
     59 };
     60 
     61 // A view that does a capture on gesture-begin events.
     62 class GestureCaptureView : public View {
     63  public:
     64   GestureCaptureView() {}
     65   virtual ~GestureCaptureView() {}
     66 
     67  private:
     68   // Overridden from View:
     69   virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
     70     if (event->type() == ui::ET_GESTURE_BEGIN) {
     71       GetWidget()->SetCapture(this);
     72       event->StopPropagation();
     73     }
     74   }
     75 
     76   DISALLOW_COPY_AND_ASSIGN(GestureCaptureView);
     77 };
     78 
     79 // A view that always processes all mouse events.
     80 class MouseView : public View {
     81  public:
     82   MouseView()
     83       : View(),
     84         entered_(0),
     85         exited_(0),
     86         pressed_(0) {
     87   }
     88   virtual ~MouseView() {}
     89 
     90   virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE {
     91     pressed_++;
     92     return true;
     93   }
     94 
     95   virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE {
     96     entered_++;
     97   }
     98 
     99   virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE {
    100     exited_++;
    101   }
    102 
    103   // Return the number of OnMouseEntered calls and reset the counter.
    104   int EnteredCalls() {
    105     int i = entered_;
    106     entered_ = 0;
    107     return i;
    108   }
    109 
    110   // Return the number of OnMouseExited calls and reset the counter.
    111   int ExitedCalls() {
    112     int i = exited_;
    113     exited_ = 0;
    114     return i;
    115   }
    116 
    117   int pressed() const { return pressed_; }
    118 
    119  private:
    120   int entered_;
    121   int exited_;
    122 
    123   int pressed_;
    124 
    125   DISALLOW_COPY_AND_ASSIGN(MouseView);
    126 };
    127 
    128 // A View that shows a different widget, sets capture on that widget, and
    129 // initiates a nested message-loop when it receives a mouse-press event.
    130 class NestedLoopCaptureView : public View {
    131  public:
    132   explicit NestedLoopCaptureView(Widget* widget) : widget_(widget) {}
    133   virtual ~NestedLoopCaptureView() {}
    134 
    135  private:
    136   // Overridden from View:
    137   virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE {
    138     // Start a nested loop.
    139     widget_->Show();
    140     widget_->SetCapture(widget_->GetContentsView());
    141     EXPECT_TRUE(widget_->HasCapture());
    142 
    143     base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
    144     base::MessageLoop::ScopedNestableTaskAllower allow(loop);
    145 
    146     base::RunLoop run_loop;
    147     run_loop.Run();
    148     return true;
    149   }
    150 
    151   Widget* widget_;
    152 
    153   DISALLOW_COPY_AND_ASSIGN(NestedLoopCaptureView);
    154 };
    155 
    156 }  // namespace
    157 
    158 class WidgetTestInteractive : public WidgetTest {
    159  public:
    160   WidgetTestInteractive() {}
    161   virtual ~WidgetTestInteractive() {}
    162 
    163   virtual void SetUp() OVERRIDE {
    164     gfx::GLSurface::InitializeOneOffForTests();
    165     base::FilePath pak_dir;
    166     PathService::Get(base::DIR_MODULE, &pak_dir);
    167     base::FilePath pak_file;
    168     pak_file = pak_dir.Append(FILE_PATH_LITERAL("ui_test.pak"));
    169     ui::ResourceBundle::InitSharedInstanceWithPakPath(pak_file);
    170     WidgetTest::SetUp();
    171   }
    172 
    173  protected:
    174   static void ShowQuickMenuImmediately(
    175       TouchSelectionControllerImpl* controller) {
    176     DCHECK(controller);
    177     if (controller->context_menu_timer_.IsRunning()) {
    178       controller->context_menu_timer_.Stop();
    179       controller->ContextMenuTimerFired();
    180     }
    181   }
    182 
    183   static bool IsQuickMenuVisible(TouchSelectionControllerImpl* controller) {
    184     DCHECK(controller);
    185     return controller->context_menu_ && controller->context_menu_->visible();
    186   }
    187 };
    188 
    189 #if defined(OS_WIN)
    190 // Tests whether activation and focus change works correctly in Windows.
    191 // We test the following:-
    192 // 1. If the active aura window is correctly set when a top level widget is
    193 //    created.
    194 // 2. If the active aura window in widget 1 created above, is set to NULL when
    195 //    another top level widget is created and focused.
    196 // 3. On focusing the native platform window for widget 1, the active aura
    197 //    window for widget 1 should be set and that for widget 2 should reset.
    198 // TODO(ananta): Discuss with erg on how to write this test for linux x11 aura.
    199 TEST_F(WidgetTestInteractive, DesktopNativeWidgetAuraActivationAndFocusTest) {
    200   // Create widget 1 and expect the active window to be its window.
    201   View* contents_view1 = new View;
    202   contents_view1->SetFocusable(true);
    203   Widget widget1;
    204   Widget::InitParams init_params =
    205       CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
    206   init_params.bounds = gfx::Rect(0, 0, 200, 200);
    207   init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    208   init_params.native_widget = new DesktopNativeWidgetAura(&widget1);
    209   widget1.Init(init_params);
    210   widget1.SetContentsView(contents_view1);
    211   widget1.Show();
    212   aura::Window* root_window1= widget1.GetNativeView()->GetRootWindow();
    213   contents_view1->RequestFocus();
    214 
    215   EXPECT_TRUE(root_window1 != NULL);
    216   aura::client::ActivationClient* activation_client1 =
    217       aura::client::GetActivationClient(root_window1);
    218   EXPECT_TRUE(activation_client1 != NULL);
    219   EXPECT_EQ(activation_client1->GetActiveWindow(), widget1.GetNativeView());
    220 
    221   // Create widget 2 and expect the active window to be its window.
    222   View* contents_view2 = new View;
    223   Widget widget2;
    224   Widget::InitParams init_params2 =
    225       CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
    226   init_params2.bounds = gfx::Rect(0, 0, 200, 200);
    227   init_params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    228   init_params2.native_widget = new DesktopNativeWidgetAura(&widget2);
    229   widget2.Init(init_params2);
    230   widget2.SetContentsView(contents_view2);
    231   widget2.Show();
    232   aura::Window* root_window2 = widget2.GetNativeView()->GetRootWindow();
    233   contents_view2->RequestFocus();
    234   ::SetActiveWindow(
    235       root_window2->GetHost()->GetAcceleratedWidget());
    236 
    237   aura::client::ActivationClient* activation_client2 =
    238       aura::client::GetActivationClient(root_window2);
    239   EXPECT_TRUE(activation_client2 != NULL);
    240   EXPECT_EQ(activation_client2->GetActiveWindow(), widget2.GetNativeView());
    241   EXPECT_EQ(activation_client1->GetActiveWindow(),
    242             reinterpret_cast<aura::Window*>(NULL));
    243 
    244   // Now set focus back to widget 1 and expect the active window to be its
    245   // window.
    246   contents_view1->RequestFocus();
    247   ::SetActiveWindow(
    248       root_window1->GetHost()->GetAcceleratedWidget());
    249   EXPECT_EQ(activation_client2->GetActiveWindow(),
    250             reinterpret_cast<aura::Window*>(NULL));
    251   EXPECT_EQ(activation_client1->GetActiveWindow(), widget1.GetNativeView());
    252 }
    253 #endif
    254 
    255 TEST_F(WidgetTestInteractive, CaptureAutoReset) {
    256   Widget* toplevel = CreateTopLevelFramelessPlatformWidget();
    257   View* container = new View;
    258   toplevel->SetContentsView(container);
    259 
    260   EXPECT_FALSE(toplevel->HasCapture());
    261   toplevel->SetCapture(NULL);
    262   EXPECT_TRUE(toplevel->HasCapture());
    263 
    264   // By default, mouse release removes capture.
    265   gfx::Point click_location(45, 15);
    266   ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
    267                          ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
    268   toplevel->OnMouseEvent(&release);
    269   EXPECT_FALSE(toplevel->HasCapture());
    270 
    271   // Now a mouse release shouldn't remove capture.
    272   toplevel->set_auto_release_capture(false);
    273   toplevel->SetCapture(NULL);
    274   EXPECT_TRUE(toplevel->HasCapture());
    275   toplevel->OnMouseEvent(&release);
    276   EXPECT_TRUE(toplevel->HasCapture());
    277   toplevel->ReleaseCapture();
    278   EXPECT_FALSE(toplevel->HasCapture());
    279 
    280   toplevel->Close();
    281   RunPendingMessages();
    282 }
    283 
    284 TEST_F(WidgetTestInteractive, ResetCaptureOnGestureEnd) {
    285   Widget* toplevel = CreateTopLevelFramelessPlatformWidget();
    286   View* container = new View;
    287   toplevel->SetContentsView(container);
    288 
    289   View* gesture = new GestureCaptureView;
    290   gesture->SetBounds(0, 0, 30, 30);
    291   container->AddChildView(gesture);
    292 
    293   MouseView* mouse = new MouseView;
    294   mouse->SetBounds(30, 0, 30, 30);
    295   container->AddChildView(mouse);
    296 
    297   toplevel->SetSize(gfx::Size(100, 100));
    298   toplevel->Show();
    299 
    300   // Start a gesture on |gesture|.
    301   ui::GestureEvent begin(ui::ET_GESTURE_BEGIN,
    302       15, 15, 0, base::TimeDelta(),
    303       ui::GestureEventDetails(ui::ET_GESTURE_BEGIN, 0, 0), 1);
    304   ui::GestureEvent end(ui::ET_GESTURE_END,
    305       15, 15, 0, base::TimeDelta(),
    306       ui::GestureEventDetails(ui::ET_GESTURE_END, 0, 0), 1);
    307   toplevel->OnGestureEvent(&begin);
    308 
    309   // Now try to click on |mouse|. Since |gesture| will have capture, |mouse|
    310   // will not receive the event.
    311   gfx::Point click_location(45, 15);
    312 
    313   ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location,
    314                        ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
    315   ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
    316                          ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
    317 
    318   EXPECT_TRUE(toplevel->HasCapture());
    319 
    320   toplevel->OnMouseEvent(&press);
    321   toplevel->OnMouseEvent(&release);
    322   EXPECT_EQ(0, mouse->pressed());
    323 
    324   EXPECT_FALSE(toplevel->HasCapture());
    325 
    326   // The end of the gesture should release the capture, and pressing on |mouse|
    327   // should now reach |mouse|.
    328   toplevel->OnGestureEvent(&end);
    329   toplevel->OnMouseEvent(&press);
    330   toplevel->OnMouseEvent(&release);
    331   EXPECT_EQ(1, mouse->pressed());
    332 
    333   toplevel->Close();
    334   RunPendingMessages();
    335 }
    336 
    337 // Checks that if a mouse-press triggers a capture on a different widget (which
    338 // consumes the mouse-release event), then the target of the press does not have
    339 // capture.
    340 TEST_F(WidgetTestInteractive, DisableCaptureWidgetFromMousePress) {
    341   // The test creates two widgets: |first| and |second|.
    342   // The View in |first| makes |second| visible, sets capture on it, and starts
    343   // a nested loop (like a menu does). The View in |second| terminates the
    344   // nested loop and closes the widget.
    345   // The test sends a mouse-press event to |first|, and posts a task to send a
    346   // release event to |second|, to make sure that the release event is
    347   // dispatched after the nested loop starts.
    348 
    349   Widget* first = CreateTopLevelFramelessPlatformWidget();
    350   Widget* second = CreateTopLevelFramelessPlatformWidget();
    351 
    352   View* container = new NestedLoopCaptureView(second);
    353   first->SetContentsView(container);
    354 
    355   second->SetContentsView(new ExitLoopOnRelease());
    356 
    357   first->SetSize(gfx::Size(100, 100));
    358   first->Show();
    359 
    360   gfx::Point location(20, 20);
    361   base::MessageLoop::current()->PostTask(FROM_HERE,
    362       base::Bind(&Widget::OnMouseEvent,
    363                  base::Unretained(second),
    364                  base::Owned(new ui::MouseEvent(ui::ET_MOUSE_RELEASED,
    365                                                 location,
    366                                                 location,
    367                                                 ui::EF_LEFT_MOUSE_BUTTON,
    368                                                 ui::EF_LEFT_MOUSE_BUTTON))));
    369   ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location,
    370                        ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
    371   first->OnMouseEvent(&press);
    372   EXPECT_FALSE(first->HasCapture());
    373   first->Close();
    374   RunPendingMessages();
    375 }
    376 
    377 // Tests some grab/ungrab events.
    378 // TODO(estade): can this be enabled now that this is an interactive ui test?
    379 TEST_F(WidgetTestInteractive, DISABLED_GrabUngrab) {
    380   Widget* toplevel = CreateTopLevelPlatformWidget();
    381   Widget* child1 = CreateChildNativeWidgetWithParent(toplevel);
    382   Widget* child2 = CreateChildNativeWidgetWithParent(toplevel);
    383 
    384   toplevel->SetBounds(gfx::Rect(0, 0, 500, 500));
    385 
    386   child1->SetBounds(gfx::Rect(10, 10, 300, 300));
    387   View* view = new MouseView();
    388   view->SetBounds(0, 0, 300, 300);
    389   child1->GetRootView()->AddChildView(view);
    390 
    391   child2->SetBounds(gfx::Rect(200, 10, 200, 200));
    392   view = new MouseView();
    393   view->SetBounds(0, 0, 200, 200);
    394   child2->GetRootView()->AddChildView(view);
    395 
    396   toplevel->Show();
    397   RunPendingMessages();
    398 
    399   // Click on child1
    400   gfx::Point p1(45, 45);
    401   ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1,
    402                          ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
    403   toplevel->OnMouseEvent(&pressed);
    404 
    405   EXPECT_TRUE(toplevel->HasCapture());
    406   EXPECT_TRUE(child1->HasCapture());
    407   EXPECT_FALSE(child2->HasCapture());
    408 
    409   ui::MouseEvent released(ui::ET_MOUSE_RELEASED, p1, p1,
    410                           ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
    411   toplevel->OnMouseEvent(&released);
    412 
    413   EXPECT_FALSE(toplevel->HasCapture());
    414   EXPECT_FALSE(child1->HasCapture());
    415   EXPECT_FALSE(child2->HasCapture());
    416 
    417   RunPendingMessages();
    418 
    419   // Click on child2
    420   gfx::Point p2(315, 45);
    421   ui::MouseEvent pressed2(ui::ET_MOUSE_PRESSED, p2, p2,
    422                           ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
    423   toplevel->OnMouseEvent(&pressed2);
    424   EXPECT_TRUE(pressed2.handled());
    425   EXPECT_TRUE(toplevel->HasCapture());
    426   EXPECT_TRUE(child2->HasCapture());
    427   EXPECT_FALSE(child1->HasCapture());
    428 
    429   ui::MouseEvent released2(ui::ET_MOUSE_RELEASED, p2, p2,
    430                            ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
    431   toplevel->OnMouseEvent(&released2);
    432   EXPECT_FALSE(toplevel->HasCapture());
    433   EXPECT_FALSE(child1->HasCapture());
    434   EXPECT_FALSE(child2->HasCapture());
    435 
    436   toplevel->CloseNow();
    437 }
    438 
    439 // Tests mouse move outside of the window into the "resize controller" and back
    440 // will still generate an OnMouseEntered and OnMouseExited event..
    441 TEST_F(WidgetTestInteractive, CheckResizeControllerEvents) {
    442   Widget* toplevel = CreateTopLevelPlatformWidget();
    443 
    444   toplevel->SetBounds(gfx::Rect(0, 0, 100, 100));
    445 
    446   MouseView* view = new MouseView();
    447   view->SetBounds(90, 90, 10, 10);
    448   toplevel->GetRootView()->AddChildView(view);
    449 
    450   toplevel->Show();
    451   RunPendingMessages();
    452 
    453   // Move to an outside position.
    454   gfx::Point p1(200, 200);
    455   ui::MouseEvent moved_out(ui::ET_MOUSE_MOVED, p1, p1, ui::EF_NONE,
    456                            ui::EF_NONE);
    457   toplevel->OnMouseEvent(&moved_out);
    458   EXPECT_EQ(0, view->EnteredCalls());
    459   EXPECT_EQ(0, view->ExitedCalls());
    460 
    461   // Move onto the active view.
    462   gfx::Point p2(95, 95);
    463   ui::MouseEvent moved_over(ui::ET_MOUSE_MOVED, p2, p2, ui::EF_NONE,
    464                             ui::EF_NONE);
    465   toplevel->OnMouseEvent(&moved_over);
    466   EXPECT_EQ(1, view->EnteredCalls());
    467   EXPECT_EQ(0, view->ExitedCalls());
    468 
    469   // Move onto the outer resizing border.
    470   gfx::Point p3(102, 95);
    471   ui::MouseEvent moved_resizer(ui::ET_MOUSE_MOVED, p3, p3, ui::EF_NONE,
    472                                ui::EF_NONE);
    473   toplevel->OnMouseEvent(&moved_resizer);
    474   EXPECT_EQ(0, view->EnteredCalls());
    475   EXPECT_EQ(1, view->ExitedCalls());
    476 
    477   // Move onto the view again.
    478   toplevel->OnMouseEvent(&moved_over);
    479   EXPECT_EQ(1, view->EnteredCalls());
    480   EXPECT_EQ(0, view->ExitedCalls());
    481 
    482   RunPendingMessages();
    483 
    484   toplevel->CloseNow();
    485 }
    486 
    487 // Test view focus restoration when a widget is deactivated and re-activated.
    488 TEST_F(WidgetTestInteractive, ViewFocusOnWidgetActivationChanges) {
    489   Widget* widget1 = CreateTopLevelPlatformWidget();
    490   View* view1 = new View;
    491   view1->SetFocusable(true);
    492   widget1->GetContentsView()->AddChildView(view1);
    493 
    494   Widget* widget2 = CreateTopLevelPlatformWidget();
    495   View* view2a = new View;
    496   View* view2b = new View;
    497   view2a->SetFocusable(true);
    498   view2b->SetFocusable(true);
    499   widget2->GetContentsView()->AddChildView(view2a);
    500   widget2->GetContentsView()->AddChildView(view2b);
    501 
    502   widget1->Show();
    503   EXPECT_TRUE(widget1->IsActive());
    504   view1->RequestFocus();
    505   EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView());
    506 
    507   widget2->Show();
    508   EXPECT_TRUE(widget2->IsActive());
    509   EXPECT_FALSE(widget1->IsActive());
    510   EXPECT_EQ(NULL, widget1->GetFocusManager()->GetFocusedView());
    511   view2a->RequestFocus();
    512   EXPECT_EQ(view2a, widget2->GetFocusManager()->GetFocusedView());
    513   view2b->RequestFocus();
    514   EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView());
    515 
    516   widget1->Activate();
    517   EXPECT_TRUE(widget1->IsActive());
    518   EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView());
    519   EXPECT_FALSE(widget2->IsActive());
    520   EXPECT_EQ(NULL, widget2->GetFocusManager()->GetFocusedView());
    521 
    522   widget2->Activate();
    523   EXPECT_TRUE(widget2->IsActive());
    524   EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView());
    525   EXPECT_FALSE(widget1->IsActive());
    526   EXPECT_EQ(NULL, widget1->GetFocusManager()->GetFocusedView());
    527 
    528   widget1->CloseNow();
    529   widget2->CloseNow();
    530 }
    531 
    532 #if defined(OS_WIN)
    533 
    534 // Test view focus retention when a widget's HWND is disabled and re-enabled.
    535 TEST_F(WidgetTestInteractive, ViewFocusOnHWNDEnabledChanges) {
    536   Widget* widget = CreateTopLevelFramelessPlatformWidget();
    537   widget->SetContentsView(new View);
    538   for (size_t i = 0; i < 2; ++i) {
    539     widget->GetContentsView()->AddChildView(new View);
    540     widget->GetContentsView()->child_at(i)->SetFocusable(true);
    541   }
    542 
    543   widget->Show();
    544   const HWND hwnd = HWNDForWidget(widget);
    545   EXPECT_TRUE(::IsWindow(hwnd));
    546   EXPECT_TRUE(::IsWindowEnabled(hwnd));
    547   EXPECT_EQ(hwnd, ::GetActiveWindow());
    548 
    549   for (int i = 0; i < widget->GetContentsView()->child_count(); ++i) {
    550     SCOPED_TRACE(base::StringPrintf("Child view %d", i));
    551     View* view = widget->GetContentsView()->child_at(i);
    552 
    553     view->RequestFocus();
    554     EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
    555     EXPECT_FALSE(::EnableWindow(hwnd, FALSE));
    556     EXPECT_FALSE(::IsWindowEnabled(hwnd));
    557 
    558     // Oddly, disabling the HWND leaves it active with the focus unchanged.
    559     EXPECT_EQ(hwnd, ::GetActiveWindow());
    560     EXPECT_TRUE(widget->IsActive());
    561     EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
    562 
    563     EXPECT_TRUE(::EnableWindow(hwnd, TRUE));
    564     EXPECT_TRUE(::IsWindowEnabled(hwnd));
    565     EXPECT_EQ(hwnd, ::GetActiveWindow());
    566     EXPECT_TRUE(widget->IsActive());
    567     EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
    568   }
    569 
    570   widget->CloseNow();
    571 }
    572 
    573 // This class subclasses the Widget class to listen for activation change
    574 // notifications and provides accessors to return information as to whether
    575 // the widget is active. We need this to ensure that users of the widget
    576 // class activate the widget only when the underlying window becomes really
    577 // active. Previously we would activate the widget in the WM_NCACTIVATE
    578 // message which is incorrect because APIs like FlashWindowEx flash the
    579 // window caption by sending fake WM_NCACTIVATE messages.
    580 class WidgetActivationTest : public Widget {
    581  public:
    582   WidgetActivationTest()
    583       : active_(false) {}
    584 
    585   virtual ~WidgetActivationTest() {}
    586 
    587   virtual void OnNativeWidgetActivationChanged(bool active) OVERRIDE {
    588     active_ = active;
    589   }
    590 
    591   bool active() const { return active_; }
    592 
    593  private:
    594   bool active_;
    595 
    596   DISALLOW_COPY_AND_ASSIGN(WidgetActivationTest);
    597 };
    598 
    599 // Tests whether the widget only becomes active when the underlying window
    600 // is really active.
    601 TEST_F(WidgetTestInteractive, WidgetNotActivatedOnFakeActivationMessages) {
    602   WidgetActivationTest widget1;
    603   Widget::InitParams init_params =
    604       CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
    605   init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    606   init_params.native_widget = new DesktopNativeWidgetAura(&widget1);
    607   init_params.bounds = gfx::Rect(0, 0, 200, 200);
    608   widget1.Init(init_params);
    609   widget1.Show();
    610   EXPECT_EQ(true, widget1.active());
    611 
    612   WidgetActivationTest widget2;
    613   init_params.native_widget = new DesktopNativeWidgetAura(&widget2);
    614   widget2.Init(init_params);
    615   widget2.Show();
    616   EXPECT_EQ(true, widget2.active());
    617   EXPECT_EQ(false, widget1.active());
    618 
    619   HWND win32_native_window1 = HWNDForWidget(&widget1);
    620   EXPECT_TRUE(::IsWindow(win32_native_window1));
    621 
    622   ::SendMessage(win32_native_window1, WM_NCACTIVATE, 1, 0);
    623   EXPECT_EQ(false, widget1.active());
    624   EXPECT_EQ(true, widget2.active());
    625 
    626   ::SetActiveWindow(win32_native_window1);
    627   EXPECT_EQ(true, widget1.active());
    628   EXPECT_EQ(false, widget2.active());
    629 }
    630 #endif
    631 
    632 #if !defined(OS_CHROMEOS)
    633 // Provides functionality to create a window modal dialog.
    634 class ModalDialogDelegate : public DialogDelegateView {
    635  public:
    636   explicit ModalDialogDelegate(ui::ModalType type) : type_(type) {}
    637   virtual ~ModalDialogDelegate() {}
    638 
    639   // WidgetDelegate overrides.
    640   virtual ui::ModalType GetModalType() const OVERRIDE {
    641     return type_;
    642   }
    643 
    644  private:
    645   ui::ModalType type_;
    646 
    647   DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate);
    648 };
    649 
    650 // Tests whether the focused window is set correctly when a modal window is
    651 // created and destroyed. When it is destroyed it should focus the owner window.
    652 TEST_F(WidgetTestInteractive, WindowModalWindowDestroyedActivationTest) {
    653   // Create a top level widget.
    654   Widget top_level_widget;
    655   Widget::InitParams init_params =
    656       CreateParams(Widget::InitParams::TYPE_WINDOW);
    657   init_params.show_state = ui::SHOW_STATE_NORMAL;
    658   gfx::Rect initial_bounds(0, 0, 500, 500);
    659   init_params.bounds = initial_bounds;
    660   init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    661   init_params.native_widget = new DesktopNativeWidgetAura(&top_level_widget);
    662   top_level_widget.Init(init_params);
    663   top_level_widget.Show();
    664 
    665   aura::Window* top_level_window = top_level_widget.GetNativeWindow();
    666   EXPECT_EQ(top_level_window, aura::client::GetFocusClient(
    667                 top_level_window)->GetFocusedWindow());
    668 
    669   // Create a modal dialog.
    670   // This instance will be destroyed when the dialog is destroyed.
    671   ModalDialogDelegate* dialog_delegate =
    672       new ModalDialogDelegate(ui::MODAL_TYPE_WINDOW);
    673 
    674   Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
    675       dialog_delegate, NULL, top_level_widget.GetNativeWindow());
    676   modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
    677   modal_dialog_widget->Show();
    678   aura::Window* dialog_window = modal_dialog_widget->GetNativeWindow();
    679   EXPECT_EQ(dialog_window, aura::client::GetFocusClient(
    680                 top_level_window)->GetFocusedWindow());
    681 
    682   modal_dialog_widget->CloseNow();
    683   EXPECT_EQ(top_level_window, aura::client::GetFocusClient(
    684                 top_level_window)->GetFocusedWindow());
    685   top_level_widget.CloseNow();
    686 }
    687 
    688 // Test that when opening a system-modal window, capture is released.
    689 TEST_F(WidgetTestInteractive, SystemModalWindowReleasesCapture) {
    690   // Create a top level widget.
    691   Widget top_level_widget;
    692   Widget::InitParams init_params =
    693       CreateParams(Widget::InitParams::TYPE_WINDOW);
    694   init_params.show_state = ui::SHOW_STATE_NORMAL;
    695   gfx::Rect initial_bounds(0, 0, 500, 500);
    696   init_params.bounds = initial_bounds;
    697   init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    698   init_params.native_widget = new DesktopNativeWidgetAura(&top_level_widget);
    699   top_level_widget.Init(init_params);
    700   top_level_widget.Show();
    701 
    702   aura::Window* top_level_window = top_level_widget.GetNativeWindow();
    703   EXPECT_EQ(top_level_window, aura::client::GetFocusClient(
    704                 top_level_window)->GetFocusedWindow());
    705 
    706   EXPECT_FALSE(top_level_window->HasCapture());
    707   top_level_window->SetCapture();
    708   EXPECT_TRUE(top_level_window->HasCapture());
    709 
    710   // Create a modal dialog.
    711   ModalDialogDelegate* dialog_delegate =
    712       new ModalDialogDelegate(ui::MODAL_TYPE_SYSTEM);
    713 
    714   Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
    715       dialog_delegate, NULL, top_level_widget.GetNativeWindow());
    716   modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
    717   modal_dialog_widget->Show();
    718 
    719   EXPECT_FALSE(top_level_window->HasCapture());
    720 
    721   modal_dialog_widget->CloseNow();
    722   top_level_widget.CloseNow();
    723 }
    724 
    725 #endif
    726 
    727 TEST_F(WidgetTestInteractive, CanActivateFlagIsHonored) {
    728   Widget widget;
    729   Widget::InitParams init_params =
    730       CreateParams(Widget::InitParams::TYPE_WINDOW);
    731   init_params.bounds = gfx::Rect(0, 0, 200, 200);
    732   init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    733   init_params.activatable = Widget::InitParams::ACTIVATABLE_NO;
    734 #if !defined(OS_CHROMEOS)
    735   init_params.native_widget = new DesktopNativeWidgetAura(&widget);
    736 #endif  // !defined(OS_CHROMEOS)
    737   widget.Init(init_params);
    738 
    739   widget.Show();
    740   EXPECT_FALSE(widget.IsActive());
    741 }
    742 
    743 // Test that touch selection quick menu is not activated when opened.
    744 TEST_F(WidgetTestInteractive, TouchSelectionQuickMenuIsNotActivated) {
    745   CommandLine::ForCurrentProcess()->AppendSwitch(switches::kEnableTouchEditing);
    746 #if defined(OS_WIN)
    747   views_delegate().set_use_desktop_native_widgets(true);
    748 #endif  // !defined(OS_WIN)
    749 
    750   Widget widget;
    751   Widget::InitParams init_params =
    752       CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
    753   init_params.bounds = gfx::Rect(0, 0, 200, 200);
    754   init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    755   widget.Init(init_params);
    756 
    757   Textfield* textfield = new Textfield;
    758   textfield->SetBounds(0, 0, 200, 20);
    759   textfield->SetText(base::ASCIIToUTF16("some text"));
    760   widget.GetRootView()->AddChildView(textfield);
    761 
    762   widget.Show();
    763   textfield->RequestFocus();
    764   textfield->SelectAll(true);
    765   TextfieldTestApi textfield_test_api(textfield);
    766 
    767   RunPendingMessages();
    768 
    769   aura::test::EventGenerator generator(widget.GetNativeView()->GetRootWindow());
    770   generator.GestureTapAt(gfx::Point(10, 10));
    771   ShowQuickMenuImmediately(static_cast<TouchSelectionControllerImpl*>(
    772       textfield_test_api.touch_selection_controller()));
    773 
    774   EXPECT_TRUE(textfield->HasFocus());
    775   EXPECT_TRUE(widget.IsActive());
    776   EXPECT_TRUE(IsQuickMenuVisible(static_cast<TouchSelectionControllerImpl*>(
    777       textfield_test_api.touch_selection_controller())));
    778 }
    779 
    780 namespace {
    781 
    782 // Used to veirfy OnMouseCaptureLost() has been invoked.
    783 class CaptureLostTrackingWidget : public Widget {
    784  public:
    785   CaptureLostTrackingWidget() : got_capture_lost_(false) {}
    786   virtual ~CaptureLostTrackingWidget() {}
    787 
    788   bool GetAndClearGotCaptureLost() {
    789     bool value = got_capture_lost_;
    790     got_capture_lost_ = false;
    791     return value;
    792   }
    793 
    794   // Widget:
    795   virtual void OnMouseCaptureLost() OVERRIDE {
    796     got_capture_lost_ = true;
    797     Widget::OnMouseCaptureLost();
    798   }
    799 
    800  private:
    801   bool got_capture_lost_;
    802 
    803   DISALLOW_COPY_AND_ASSIGN(CaptureLostTrackingWidget);
    804 };
    805 
    806 }  // namespace
    807 
    808 class WidgetCaptureTest : public ViewsTestBase {
    809  public:
    810   WidgetCaptureTest() {
    811   }
    812 
    813   virtual ~WidgetCaptureTest() {
    814   }
    815 
    816   virtual void SetUp() OVERRIDE {
    817     gfx::GLSurface::InitializeOneOffForTests();
    818     base::FilePath pak_dir;
    819     PathService::Get(base::DIR_MODULE, &pak_dir);
    820     base::FilePath pak_file;
    821     pak_file = pak_dir.Append(FILE_PATH_LITERAL("ui_test.pak"));
    822     ui::ResourceBundle::InitSharedInstanceWithPakPath(pak_file);
    823     ViewsTestBase::SetUp();
    824   }
    825 
    826   // Verifies Widget::SetCapture() results in updating native capture along with
    827   // invoking the right Widget function.
    828   void TestCapture(bool use_desktop_native_widget) {
    829     CaptureLostTrackingWidget widget1;
    830     Widget::InitParams params1 =
    831         CreateParams(views::Widget::InitParams::TYPE_WINDOW);
    832     params1.native_widget = CreateNativeWidget(use_desktop_native_widget,
    833                                                &widget1);
    834     params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    835     widget1.Init(params1);
    836     widget1.Show();
    837 
    838     CaptureLostTrackingWidget widget2;
    839     Widget::InitParams params2 =
    840         CreateParams(views::Widget::InitParams::TYPE_WINDOW);
    841     params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    842     params2.native_widget = CreateNativeWidget(use_desktop_native_widget,
    843                                                &widget2);
    844     widget2.Init(params2);
    845     widget2.Show();
    846 
    847     // Set capture to widget2 and verity it gets it.
    848     widget2.SetCapture(widget2.GetRootView());
    849     EXPECT_FALSE(widget1.HasCapture());
    850     EXPECT_TRUE(widget2.HasCapture());
    851     EXPECT_FALSE(widget1.GetAndClearGotCaptureLost());
    852     EXPECT_FALSE(widget2.GetAndClearGotCaptureLost());
    853 
    854     // Set capture to widget1 and verify it gets it.
    855     widget1.SetCapture(widget1.GetRootView());
    856     EXPECT_TRUE(widget1.HasCapture());
    857     EXPECT_FALSE(widget2.HasCapture());
    858     EXPECT_FALSE(widget1.GetAndClearGotCaptureLost());
    859     EXPECT_TRUE(widget2.GetAndClearGotCaptureLost());
    860 
    861     // Release and verify no one has it.
    862     widget1.ReleaseCapture();
    863     EXPECT_FALSE(widget1.HasCapture());
    864     EXPECT_FALSE(widget2.HasCapture());
    865     EXPECT_TRUE(widget1.GetAndClearGotCaptureLost());
    866     EXPECT_FALSE(widget2.GetAndClearGotCaptureLost());
    867   }
    868 
    869  private:
    870   NativeWidget* CreateNativeWidget(bool create_desktop_native_widget,
    871                                    Widget* widget) {
    872 #if !defined(OS_CHROMEOS)
    873     if (create_desktop_native_widget)
    874       return new DesktopNativeWidgetAura(widget);
    875 #endif
    876     return NULL;
    877   }
    878 
    879   DISALLOW_COPY_AND_ASSIGN(WidgetCaptureTest);
    880 };
    881 
    882 // See description in TestCapture().
    883 TEST_F(WidgetCaptureTest, Capture) {
    884   TestCapture(false);
    885 }
    886 
    887 #if !defined(OS_LINUX)
    888 // See description in TestCapture(). Creates DesktopNativeWidget.
    889 TEST_F(WidgetCaptureTest, CaptureDesktopNativeWidget) {
    890   TestCapture(true);
    891 }
    892 #endif
    893 
    894 #if !defined(OS_CHROMEOS)
    895 namespace {
    896 
    897 // Used to veirfy OnMouseEvent() has been invoked.
    898 class MouseEventTrackingWidget : public Widget {
    899  public:
    900   MouseEventTrackingWidget() : got_mouse_event_(false) {}
    901   virtual ~MouseEventTrackingWidget() {}
    902 
    903   bool GetAndClearGotMouseEvent() {
    904     bool value = got_mouse_event_;
    905     got_mouse_event_ = false;
    906     return value;
    907   }
    908 
    909   // Widget:
    910   virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
    911     got_mouse_event_ = true;
    912     Widget::OnMouseEvent(event);
    913   }
    914 
    915  private:
    916   bool got_mouse_event_;
    917 
    918   DISALLOW_COPY_AND_ASSIGN(MouseEventTrackingWidget);
    919 };
    920 
    921 }  // namespace
    922 
    923 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
    924 // TODO(erg): linux_aura bringup: http://crbug.com/163931
    925 #define MAYBE_MouseEventDispatchedToRightWindow \
    926   DISABLED_MouseEventDispatchedToRightWindow
    927 #else
    928 #define MAYBE_MouseEventDispatchedToRightWindow \
    929   MouseEventDispatchedToRightWindow
    930 #endif
    931 
    932 // Verifies if a mouse event is received on a widget that doesn't have capture
    933 // it is correctly processed by the widget that doesn't have capture.
    934 TEST_F(WidgetCaptureTest, MAYBE_MouseEventDispatchedToRightWindow) {
    935   MouseEventTrackingWidget widget1;
    936   Widget::InitParams params1 =
    937       CreateParams(views::Widget::InitParams::TYPE_WINDOW);
    938   params1.native_widget = new DesktopNativeWidgetAura(&widget1);
    939   params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    940   widget1.Init(params1);
    941   widget1.Show();
    942 
    943   MouseEventTrackingWidget widget2;
    944   Widget::InitParams params2 =
    945       CreateParams(views::Widget::InitParams::TYPE_WINDOW);
    946   params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    947   params2.native_widget = new DesktopNativeWidgetAura(&widget2);
    948   widget2.Init(params2);
    949   widget2.Show();
    950 
    951   // Set capture to widget2 and verity it gets it.
    952   widget2.SetCapture(widget2.GetRootView());
    953   EXPECT_FALSE(widget1.HasCapture());
    954   EXPECT_TRUE(widget2.HasCapture());
    955 
    956   widget1.GetAndClearGotMouseEvent();
    957   widget2.GetAndClearGotMouseEvent();
    958   // Send a mouse event to the RootWindow associated with |widget1|. Even though
    959   // |widget2| has capture, |widget1| should still get the event.
    960   ui::MouseEvent mouse_event(ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(),
    961                              ui::EF_NONE, ui::EF_NONE);
    962   ui::EventDispatchDetails details = widget1.GetNativeWindow()->
    963       GetHost()->event_processor()->OnEventFromSource(&mouse_event);
    964   ASSERT_FALSE(details.dispatcher_destroyed);
    965   EXPECT_TRUE(widget1.GetAndClearGotMouseEvent());
    966   EXPECT_FALSE(widget2.GetAndClearGotMouseEvent());
    967 }
    968 #endif
    969 
    970 }  // namespace test
    971 }  // namespace views
    972