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