1 // Copyright (c) 2012 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 "chrome/test/base/interactive_test_utils.h"
6 
7 #include "base/logging.h"
8 #include "base/memory/scoped_refptr.h"
9 #include "build/build_config.h"
10 #include "chrome/browser/ui/browser.h"
11 #include "chrome/browser/ui/browser_finder.h"
12 #include "chrome/browser/ui/browser_list.h"
13 #include "chrome/browser/ui/browser_window.h"
14 #include "content/public/test/test_utils.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "ui/display/display.h"
17 #include "ui/display/screen.h"
18 #include "ui/events/keycodes/keyboard_codes.h"
19 
20 namespace ui_test_utils {
21 
22 namespace {
23 
GetNativeWindow(const Browser * browser,gfx::NativeWindow * native_window)24 bool GetNativeWindow(const Browser* browser, gfx::NativeWindow* native_window) {
25   BrowserWindow* window = browser->window();
26   if (!window)
27     return false;
28 
29   *native_window = window->GetNativeWindow();
30   return *native_window;
31 }
32 
33 }  // namespace
34 
BrowserActivationWaiter(const Browser * browser)35 BrowserActivationWaiter::BrowserActivationWaiter(const Browser* browser)
36     : browser_(browser) {
37   // When the active browser closes, the next "last active browser" in the
38   // BrowserList might not be immediately activated. So we need to wait for the
39   // "last active browser" to actually be active.
40   if (chrome::FindLastActive() == browser_ && browser_->window()->IsActive()) {
41     observed_ = true;
42     return;
43   }
44   BrowserList::AddObserver(this);
45 }
46 
WaitForActivation()47 void BrowserActivationWaiter::WaitForActivation() {
48   if (observed_)
49     return;
50   DCHECK(!run_loop_.running()) << "WaitForActivation() can be called at most "
51                                   "once. Construct a new "
52                                   "BrowserActivationWaiter instead.";
53   run_loop_.Run();
54 }
55 
OnBrowserSetLastActive(Browser * browser)56 void BrowserActivationWaiter::OnBrowserSetLastActive(Browser* browser) {
57   if (browser != browser_)
58     return;
59 
60 // On Mac, BrowserWindowCocoa::Show() sets the active browser before the
61 // window becomes the key window.
62 #if !defined(OS_MAC)
63   EXPECT_TRUE(browser->window()->IsActive());
64 #endif
65 
66   observed_ = true;
67   BrowserList::RemoveObserver(this);
68   if (run_loop_.running())
69     run_loop_.Quit();
70 }
71 
BrowserDeactivationWaiter(const Browser * browser)72 BrowserDeactivationWaiter::BrowserDeactivationWaiter(const Browser* browser)
73     : browser_(browser) {
74   if (chrome::FindLastActive() != browser_ && !browser->window()->IsActive()) {
75     observed_ = true;
76     return;
77   }
78   BrowserList::AddObserver(this);
79 }
80 
81 BrowserDeactivationWaiter::~BrowserDeactivationWaiter() = default;
82 
WaitForDeactivation()83 void BrowserDeactivationWaiter::WaitForDeactivation() {
84   if (observed_)
85     return;
86   DCHECK(!run_loop_.running()) << "WaitForDeactivation() can be called at most "
87                                   "once. Construct a new "
88                                   "BrowserDeactivationWaiter instead.";
89   run_loop_.Run();
90 }
91 
OnBrowserNoLongerActive(Browser * browser)92 void BrowserDeactivationWaiter::OnBrowserNoLongerActive(Browser* browser) {
93   if (browser != browser_)
94     return;
95 
96   observed_ = true;
97   BrowserList::RemoveObserver(this);
98   if (run_loop_.running())
99     run_loop_.Quit();
100 }
101 
BringBrowserWindowToFront(const Browser * browser)102 bool BringBrowserWindowToFront(const Browser* browser) {
103   gfx::NativeWindow window = NULL;
104   if (!GetNativeWindow(browser, &window))
105     return false;
106 
107   if (!ShowAndFocusNativeWindow(window))
108     return false;
109 
110   BrowserActivationWaiter waiter(browser);
111   waiter.WaitForActivation();
112   return true;
113 }
114 
SendKeyPressSync(const Browser * browser,ui::KeyboardCode key,bool control,bool shift,bool alt,bool command)115 bool SendKeyPressSync(const Browser* browser,
116                       ui::KeyboardCode key,
117                       bool control,
118                       bool shift,
119                       bool alt,
120                       bool command) {
121   gfx::NativeWindow window = NULL;
122   if (!GetNativeWindow(browser, &window))
123     return false;
124   return SendKeyPressToWindowSync(window, key, control, shift, alt, command);
125 }
126 
SendKeyPressToWindowSync(const gfx::NativeWindow window,ui::KeyboardCode key,bool control,bool shift,bool alt,bool command)127 bool SendKeyPressToWindowSync(const gfx::NativeWindow window,
128                               ui::KeyboardCode key,
129                               bool control,
130                               bool shift,
131                               bool alt,
132                               bool command) {
133 #if defined(OS_WIN)
134   DCHECK(key != ui::VKEY_ESCAPE || !control)
135       << "'ctrl + esc' opens start menu on Windows. Start menu on windows "
136          "2012 is a full-screen always on top window. It breaks all "
137          "interactive tests.";
138 #endif
139 
140   scoped_refptr<content::MessageLoopRunner> runner =
141       new content::MessageLoopRunner;
142   bool result;
143   result = ui_controls::SendKeyPressNotifyWhenDone(
144       window, key, control, shift, alt, command, runner->QuitClosure());
145 #if defined(OS_WIN)
146   if (!result && ui_test_utils::ShowAndFocusNativeWindow(window)) {
147     result = ui_controls::SendKeyPressNotifyWhenDone(
148         window, key, control, shift, alt, command, runner->QuitClosure());
149   }
150 #endif
151   if (!result) {
152     LOG(ERROR) << "ui_controls::SendKeyPressNotifyWhenDone failed";
153     return false;
154   }
155 
156   // Run the message loop. It'll stop running when either the key was received
157   // or the test timed out (in which case testing::Test::HasFatalFailure should
158   // be set).
159   runner->Run();
160   return !testing::Test::HasFatalFailure();
161 }
162 
SendMouseMoveSync(const gfx::Point & location)163 bool SendMouseMoveSync(const gfx::Point& location) {
164   scoped_refptr<content::MessageLoopRunner> runner =
165       new content::MessageLoopRunner;
166   if (!ui_controls::SendMouseMoveNotifyWhenDone(
167           location.x(), location.y(), runner->QuitClosure())) {
168     return false;
169   }
170   runner->Run();
171   return !testing::Test::HasFatalFailure();
172 }
173 
SendMouseEventsSync(ui_controls::MouseButton type,int button_state)174 bool SendMouseEventsSync(ui_controls::MouseButton type, int button_state) {
175   scoped_refptr<content::MessageLoopRunner> runner =
176       new content::MessageLoopRunner;
177   if (!ui_controls::SendMouseEventsNotifyWhenDone(type, button_state,
178                                                   runner->QuitClosure())) {
179     return false;
180   }
181   runner->Run();
182   return !testing::Test::HasFatalFailure();
183 }
184 
185 namespace internal {
186 
ClickTask(ui_controls::MouseButton button,int button_state,base::OnceClosure followup,int accelerator_state)187 void ClickTask(ui_controls::MouseButton button,
188                int button_state,
189                base::OnceClosure followup,
190                int accelerator_state) {
191   if (!followup.is_null()) {
192     ui_controls::SendMouseEventsNotifyWhenDone(
193         button, button_state, std::move(followup), accelerator_state);
194   } else {
195     ui_controls::SendMouseEvents(button, button_state, accelerator_state);
196   }
197 }
198 
199 }  // namespace internal
200 
GetSecondaryDisplay(display::Screen * screen)201 display::Display GetSecondaryDisplay(display::Screen* screen) {
202   for (const auto& iter : screen->GetAllDisplays()) {
203     if (iter.id() != screen->GetPrimaryDisplay().id())
204       return iter;
205   }
206   NOTREACHED();
207   return display::Display();
208 }
209 
GetDisplays(display::Screen * screen)210 std::pair<display::Display, display::Display> GetDisplays(
211     display::Screen* screen) {
212   return std::make_pair(screen->GetPrimaryDisplay(),
213                         GetSecondaryDisplay(screen));
214 }
215 
216 }  // namespace ui_test_utils
217