OLD | NEW |
| (Empty) |
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 #ifndef CHROME_FRAME_TEST_MOCK_IE_EVENT_SINK_ACTIONS_H_ | |
6 #define CHROME_FRAME_TEST_MOCK_IE_EVENT_SINK_ACTIONS_H_ | |
7 | |
8 #include <windows.h> | |
9 #include <algorithm> | |
10 #include <string> | |
11 | |
12 #include "base/basictypes.h" | |
13 #include "base/bind.h" | |
14 #include "base/strings/string16.h" | |
15 #include "base/strings/string_util.h" | |
16 #include "base/test/test_process_killer_win.h" | |
17 #include "base/threading/platform_thread.h" | |
18 #include "base/time/time.h" | |
19 #include "chrome/common/chrome_switches.h" | |
20 #include "chrome_frame/test/chrome_frame_test_utils.h" | |
21 #include "chrome_frame/test/ie_event_sink.h" | |
22 #include "chrome_frame/test/mock_ie_event_sink_test.h" | |
23 #include "chrome_frame/test/simulate_input.h" | |
24 #include "testing/gmock/include/gmock/gmock.h" | |
25 #include "ui/gfx/point.h" | |
26 #include "ui/gfx/rect.h" | |
27 | |
28 namespace chrome_frame_test { | |
29 | |
30 MATCHER_P(UrlPathEq, url, "equals the path and query portion of the url") { | |
31 return arg == chrome_frame_test::GetPathAndQueryFromUrl(url); | |
32 } | |
33 | |
34 MATCHER_P(AccSatisfies, matcher, "satisfies the given AccObjectMatcher") { | |
35 return matcher.DoesMatch(arg); | |
36 } | |
37 | |
38 // Returns true if the title of the page rendered in the window |arg| equals | |
39 // |the_url| or |the_title|. For pages rendered in Chrome, the title of the | |
40 // parent of |arg| is the page url or title. For pages rendered in IE, the title | |
41 // of the grandparent of |arg| begins with the page url or title. To handle both | |
42 // cases, attempt a prefix match on each window starting with the parent of | |
43 // |arg|. Both url and title are matched to account for a race between the test | |
44 // and Chrome when the window title is transitioned from the url to the title. | |
45 MATCHER_P2(TabContentsTitleEq, the_url, the_title, "") { | |
46 const base::string16 url(the_url); | |
47 DCHECK(!url.empty()); | |
48 const base::string16 title(the_title); | |
49 DCHECK(!title.empty()); | |
50 HWND parent = GetParent(arg); | |
51 if (parent != NULL) { | |
52 base::string16 parent_title(255, L'\0'); | |
53 std::ostringstream titles_found(std::string("titles found: ")); | |
54 base::string16 first_title; | |
55 do { | |
56 parent_title.resize(255, L'\0'); | |
57 parent_title.resize(GetWindowText(parent, &parent_title[0], | |
58 parent_title.size())); | |
59 if (parent_title.size() >= title.size() && | |
60 std::equal(title.begin(), title.end(), parent_title.begin()) || | |
61 parent_title.size() >= url.size() && | |
62 std::equal(url.begin(), url.end(), parent_title.begin())) { | |
63 return true; | |
64 } | |
65 titles_found << "\"" << UTF16ToASCII(parent_title) << "\" "; | |
66 parent = GetParent(parent); | |
67 } while(parent != NULL); | |
68 *result_listener << titles_found.str(); | |
69 } else { | |
70 *result_listener << "the window has no parent"; | |
71 } | |
72 return false; | |
73 } | |
74 | |
75 // IWebBrowser2 actions | |
76 | |
77 ACTION_P2(Navigate, mock, navigate_url) { | |
78 mock->event_sink()->Navigate(navigate_url); | |
79 } | |
80 | |
81 ACTION_P3(DelayNavigateToCurrentUrl, mock, loop, delay) { | |
82 loop->PostDelayedTask(FROM_HERE, | |
83 base::Bind(&NavigateToCurrentUrl, mock), delay); | |
84 } | |
85 | |
86 ACTION_P(CloseBrowserMock, mock) { | |
87 mock->event_sink()->CloseWebBrowser(); | |
88 } | |
89 | |
90 ACTION_P3(DelayCloseBrowserMock, loop, delay, mock) { | |
91 loop->PostDelayedTask( | |
92 FROM_HERE, | |
93 base::Bind(base::IgnoreResult(&IEEventSink::CloseWebBrowser), | |
94 mock->event_sink()), | |
95 delay); | |
96 } | |
97 | |
98 ACTION_P2(ConnectDocPropNotifySink, mock, sink) { | |
99 base::win::ScopedComPtr<IDispatch> document; | |
100 mock->event_sink()->web_browser2()->get_Document(document.Receive()); | |
101 EXPECT_TRUE(document != NULL); // NOLINT | |
102 if (document) { | |
103 sink->Attach(document); | |
104 } | |
105 } | |
106 | |
107 ACTION_P(DisconnectDocPropNotifySink, sink) { | |
108 sink->Detach(); | |
109 } | |
110 | |
111 ACTION_P8(DelayExecCommand, mock, loop, delay, cmd_group_guid, cmd_id, | |
112 cmd_exec_opt, in_args, out_args) { | |
113 loop->PostDelayedTask( | |
114 FROM_HERE, | |
115 base::Bind(&IEEventSink::Exec, mock->event_sink(), cmd_group_guid, cmd_id, | |
116 cmd_exec_opt, in_args, out_args), | |
117 delay); | |
118 } | |
119 | |
120 ACTION_P3(DelayGoBack, mock, loop, delay) { | |
121 loop->PostDelayedTask( | |
122 FROM_HERE, base::Bind(&IEEventSink::GoBack, mock->event_sink()), delay); | |
123 } | |
124 | |
125 ACTION_P3(DelayGoForward, mock, loop, delay) { | |
126 loop->PostDelayedTask( | |
127 FROM_HERE, base::Bind(&IEEventSink::GoForward, mock->event_sink()), | |
128 delay); | |
129 } | |
130 | |
131 ACTION_P3(DelayRefresh, mock, loop, delay) { | |
132 loop->PostDelayedTask( | |
133 FROM_HERE, base::Bind(&IEEventSink::Refresh, mock->event_sink()), delay); | |
134 } | |
135 | |
136 ACTION_P2(PostMessageToCF, mock, message) { | |
137 mock->event_sink()->PostMessageToCF(message, L"*"); | |
138 } | |
139 | |
140 // Accessibility-related actions | |
141 | |
142 ACTION_P(AccDoDefaultAction, matcher) { | |
143 scoped_refptr<AccObject> object; | |
144 if (FindAccObjectInWindow(arg0, matcher, &object)) { | |
145 EXPECT_TRUE(object->DoDefaultAction()); | |
146 } | |
147 } | |
148 | |
149 ACTION_P2(DelayAccDoDefaultAction, matcher, delay) { | |
150 SleepEx(delay, false); | |
151 scoped_refptr<AccObject> object; | |
152 if (FindAccObjectInWindow(arg0, matcher, &object)) { | |
153 EXPECT_TRUE(object->DoDefaultAction()); | |
154 } | |
155 } | |
156 | |
157 ACTION_P(AccLeftClick, matcher) { | |
158 scoped_refptr<AccObject> object; | |
159 if (FindAccObjectInWindow(arg0, matcher, &object)) { | |
160 EXPECT_TRUE(object->LeftClick()); | |
161 } | |
162 } | |
163 | |
164 ACTION_P(AccSendCommand, matcher) { | |
165 scoped_refptr<AccObject> object; | |
166 if (FindAccObjectInWindow(arg0, matcher, &object)) { | |
167 HWND window = NULL; | |
168 object->GetWindow(&window); | |
169 long window_id = GetWindowLong(window, GWL_ID); | |
170 ::SendMessage(arg0, WM_COMMAND, MAKEWPARAM(window_id, BN_CLICKED), | |
171 reinterpret_cast<LPARAM>(window)); | |
172 } | |
173 } | |
174 | |
175 ACTION_P(AccRightClick, matcher) { | |
176 scoped_refptr<AccObject> object; | |
177 if (FindAccObjectInWindow(arg0, matcher, &object)) { | |
178 EXPECT_TRUE(object->RightClick()); | |
179 } | |
180 } | |
181 | |
182 ACTION_P(AccFocus, matcher) { | |
183 scoped_refptr<AccObject> object; | |
184 if (FindAccObjectInWindow(arg0, matcher, &object)) { | |
185 EXPECT_TRUE(object->Focus()); | |
186 } | |
187 } | |
188 | |
189 ACTION_P(AccSelect, matcher) { | |
190 scoped_refptr<AccObject> object; | |
191 if (FindAccObjectInWindow(arg0, matcher, &object)) { | |
192 EXPECT_TRUE(object->Select()); | |
193 } | |
194 } | |
195 | |
196 ACTION_P2(AccSetValue, matcher, value) { | |
197 scoped_refptr<AccObject> object; | |
198 if (FindAccObjectInWindow(arg0, matcher, &object)) { | |
199 EXPECT_TRUE(object->SetValue(value)); | |
200 } | |
201 } | |
202 | |
203 namespace { // NOLINT | |
204 template<typename R> R AccInWindow(testing::Action<R(HWND)> action, HWND hwnd) { | |
205 return action.Perform(typename testing::Action<R(HWND)>::ArgumentTuple(hwnd)); | |
206 } | |
207 } | |
208 | |
209 ACTION_P2(AccDoDefaultActionInBrowser, mock, matcher) { | |
210 AccInWindow<void>(AccDoDefaultAction(matcher), | |
211 mock->event_sink()->GetBrowserWindow()); | |
212 } | |
213 | |
214 ACTION_P2(AccDoDefaultActionInRenderer, mock, matcher) { | |
215 AccInWindow<void>(AccDoDefaultAction(matcher), | |
216 mock->event_sink()->GetRendererWindow()); | |
217 } | |
218 | |
219 ACTION_P3(DelayAccDoDefaultActionInRenderer, mock, matcher, delay) { | |
220 SleepEx(delay, false); | |
221 AccInWindow<void>(AccDoDefaultAction(matcher), | |
222 mock->event_sink()->GetRendererWindow()); | |
223 } | |
224 | |
225 ACTION_P2(AccLeftClickInBrowser, mock, matcher) { | |
226 AccInWindow<void>(AccLeftClick(matcher), | |
227 mock->event_sink()->GetBrowserWindow()); | |
228 } | |
229 | |
230 ACTION_P2(AccLeftClickInRenderer, mock, matcher) { | |
231 AccInWindow<void>(AccLeftClick(matcher), | |
232 mock->event_sink()->GetRendererWindow()); | |
233 } | |
234 | |
235 ACTION_P3(AccSetValueInBrowser, mock, matcher, value) { | |
236 AccInWindow<void>(AccSetValue(matcher, value), | |
237 mock->event_sink()->GetBrowserWindow()); | |
238 } | |
239 | |
240 ACTION_P2(AccWatchForOneValueChange, observer, matcher) { | |
241 observer->WatchForOneValueChange(matcher); | |
242 } | |
243 | |
244 ACTION_P2(AccSendCharMessage, matcher, character_code) { | |
245 scoped_refptr<AccObject> object; | |
246 if (FindAccObjectInWindow(arg0, matcher, &object)) { | |
247 HWND window = NULL; | |
248 EXPECT_TRUE(object->GetWindow(&window)); | |
249 ::SendMessage(window, WM_CHAR, character_code, 0); | |
250 } | |
251 } | |
252 | |
253 // Various other actions | |
254 | |
255 ACTION(OpenContextMenuAsync) { | |
256 // Special case this implementation because the top-left of the window is | |
257 // much more likely to be empty than the center. | |
258 HWND hwnd = arg0; | |
259 LPARAM coordinates = (1 << 16) | 1; | |
260 // IE needs both messages in order to work. Chrome does not support | |
261 // WM_CONTEXTMENU in the renderer: http://crbug.com/51746. | |
262 ::PostMessage(hwnd, WM_RBUTTONDOWN, 0, coordinates); | |
263 ::PostMessage(hwnd, WM_RBUTTONUP, 0, coordinates); | |
264 } | |
265 | |
266 // Posts a WM_KEYDOWN and WM_KEYUP message to the renderer window. Modifiers are | |
267 // not supported, so |character_code| is limited to the regular expression | |
268 // [0-9a-z]. | |
269 ACTION_P2(PostKeyMessageToRenderer, mock, character_code) { | |
270 char character_codes[] = { character_code, '\0' }; | |
271 mock->event_sink()->SendKeys(character_codes); | |
272 } | |
273 | |
274 // Posts WM_KEYDOWN and WM_KEYUP messages to the renderer window. Modifiers are | |
275 // not supported, so |character_codes| is limited to the regular expression | |
276 // [0-9a-z]*. | |
277 ACTION_P2(PostKeyMessagesToRenderer, mock, character_codes) { | |
278 mock->event_sink()->SendKeys(std::string(character_codes).c_str()); | |
279 } | |
280 | |
281 ACTION_P3(WatchWindow, mock, caption, window_class) { | |
282 mock->WatchWindow(caption, window_class); | |
283 } | |
284 | |
285 ACTION_P(StopWindowWatching, mock) { | |
286 mock->StopWatching(); | |
287 } | |
288 | |
289 ACTION_P(WatchWindowProcess, mock_observer) { | |
290 mock_observer->WatchProcessForHwnd(arg0); | |
291 } | |
292 | |
293 ACTION_P2(WatchBrowserProcess, mock_observer, mock) { | |
294 AccInWindow<void>(WatchWindowProcess(mock_observer), | |
295 mock->event_sink()->GetBrowserWindow()); | |
296 } | |
297 | |
298 ACTION_P2(WatchRendererProcess, mock_observer, mock) { | |
299 AccInWindow<void>(WatchWindowProcess(mock_observer), | |
300 mock->event_sink()->GetRendererWindow()); | |
301 } | |
302 | |
303 namespace { // NOLINT | |
304 | |
305 void DoCloseWindowNow(HWND hwnd) { | |
306 ::PostMessage(hwnd, WM_CLOSE, 0, 0); | |
307 } | |
308 | |
309 } // namespace | |
310 | |
311 ACTION(DoCloseWindow) { | |
312 DoCloseWindowNow(arg0); | |
313 } | |
314 | |
315 ACTION_P(DelayDoCloseWindow, delay) { | |
316 DCHECK(base::MessageLoop::current()); | |
317 base::MessageLoop::current()->PostDelayedTask( | |
318 FROM_HERE, base::Bind(DoCloseWindowNow, arg0), | |
319 base::TimeDelta::FromMilliseconds(delay)); | |
320 } | |
321 | |
322 ACTION(KillChromeFrameProcesses) { | |
323 base::KillAllNamedProcessesWithArgument( | |
324 base::UTF8ToWide(chrome_frame_test::kChromeImageName), | |
325 base::UTF8ToWide(switches::kChromeFrame)); | |
326 } | |
327 | |
328 // Verifying actions | |
329 ACTION_P(AccExpect, matcher) { | |
330 scoped_refptr<AccObject> object; | |
331 EXPECT_TRUE(FindAccObjectInWindow(arg0, matcher, &object)); | |
332 } | |
333 | |
334 ACTION_P2(AccExpectInRenderer, mock, matcher) { | |
335 AccInWindow<void>(AccExpect(matcher), | |
336 mock->event_sink()->GetRendererWindow()); | |
337 } | |
338 | |
339 ACTION_P(ExpectRendererHasFocus, mock) { | |
340 mock->event_sink()->ExpectRendererWindowHasFocus(); | |
341 } | |
342 | |
343 ACTION_P(VerifyAddressBarUrl, mock) { | |
344 mock->event_sink()->ExpectAddressBarUrl(std::wstring(arg1)); | |
345 } | |
346 | |
347 ACTION_P3(VerifyPageLoad, mock, in_cf, url) { | |
348 EXPECT_TRUE(static_cast<bool>(in_cf) == mock->event_sink()->IsCFRendering()); | |
349 mock->event_sink()->ExpectAddressBarUrl(url); | |
350 } | |
351 | |
352 ACTION_P5(ValidateWindowSize, mock, left, top, width, height) { | |
353 int actual_left = 0; | |
354 int actual_top = 0; | |
355 int actual_width = 0; | |
356 int actual_height = 0; | |
357 | |
358 IWebBrowser2* web_browser2 = mock->event_sink()->web_browser2(); | |
359 web_browser2->get_Left(reinterpret_cast<long*>(&actual_left)); // NOLINT | |
360 web_browser2->get_Top(reinterpret_cast<long*>(&actual_top)); // NOLINT | |
361 web_browser2->get_Width(reinterpret_cast<long*>(&actual_width)); // NOLINT | |
362 web_browser2->get_Height(reinterpret_cast<long*>(&actual_height)); // NOLINT | |
363 | |
364 EXPECT_GE(actual_left, left); | |
365 EXPECT_GE(actual_top, top); | |
366 | |
367 EXPECT_GE(actual_width, width); | |
368 EXPECT_GE(actual_height, height); | |
369 } | |
370 | |
371 ACTION_P(VerifyAddressBarUrlWithGcf, mock) { | |
372 std::wstring expected_url = L"gcf:"; | |
373 expected_url += arg1; | |
374 mock->event_sink()->ExpectAddressBarUrl(expected_url); | |
375 } | |
376 | |
377 ACTION_P2(ExpectDocumentReadystate, mock, ready_state) { | |
378 mock->ExpectDocumentReadystate(ready_state); | |
379 } | |
380 | |
381 ACTION_P(VerifySelectedText, expected_text) { | |
382 std::wstring actual_text; | |
383 bool got_selection = arg1->GetSelectedText(&actual_text); | |
384 EXPECT_TRUE(got_selection); | |
385 if (got_selection) { | |
386 EXPECT_EQ(expected_text, actual_text); | |
387 } | |
388 } | |
389 | |
390 // Polling actions | |
391 | |
392 ACTION_P3(CloseWhenFileSaved, mock, file, timeout_ms) { | |
393 base::Time start = base::Time::Now(); | |
394 while (!base::PathExists(file)) { | |
395 if ((base::Time::Now() - start).InMilliseconds() > timeout_ms) { | |
396 ADD_FAILURE() << "File was not saved within timeout"; | |
397 TakeSnapshotAndLog(); | |
398 break; | |
399 } | |
400 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200)); | |
401 } | |
402 mock->event_sink()->CloseWebBrowser(); | |
403 } | |
404 | |
405 ACTION_P2(WaitForFileSave, file, timeout_ms) { | |
406 base::Time start = base::Time::Now(); | |
407 while (!base::PathExists(file)) { | |
408 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200)); | |
409 if ((base::Time::Now() - start).InMilliseconds() > timeout_ms) { | |
410 ADD_FAILURE() << "File was not saved within timeout"; | |
411 TakeSnapshotAndLog(); | |
412 break; | |
413 } | |
414 } | |
415 } | |
416 | |
417 // Flaky actions | |
418 | |
419 ACTION_P(SetFocusToRenderer, mock) { | |
420 simulate_input::SetKeyboardFocusToWindow( | |
421 mock->event_sink()->GetRendererWindow()); | |
422 } | |
423 | |
424 ACTION_P4(DelaySendChar, loop, delay, c, mod) { | |
425 loop->PostDelayedTask( | |
426 FROM_HERE, base::Bind(simulate_input::SendCharA, c, mod), delay); | |
427 } | |
428 | |
429 ACTION_P4(DelaySendScanCode, loop, delay, c, mod) { | |
430 loop->PostDelayedTask( | |
431 FROM_HERE, base::Bind(simulate_input::SendScanCode, c, mod), delay); | |
432 } | |
433 | |
434 // This function selects the address bar via the Alt+d shortcut. | |
435 ACTION_P3(TypeUrlInAddressBar, loop, url, delay) { | |
436 loop->PostDelayedTask( | |
437 FROM_HERE, | |
438 base::Bind(simulate_input::SendCharA, 'd', simulate_input::ALT), delay); | |
439 | |
440 const base::TimeDelta kInterval = base::TimeDelta::FromMilliseconds(500); | |
441 base::TimeDelta next_delay = delay + kInterval; | |
442 | |
443 loop->PostDelayedTask( | |
444 FROM_HERE, base::Bind(simulate_input::SendStringW, url), next_delay); | |
445 | |
446 next_delay = next_delay + kInterval; | |
447 | |
448 loop->PostDelayedTask( | |
449 FROM_HERE, | |
450 base::Bind(simulate_input::SendCharA, VK_RETURN, simulate_input::NONE), | |
451 next_delay); | |
452 } | |
453 | |
454 } // namespace chrome_frame_test | |
455 | |
456 #endif // CHROME_FRAME_TEST_MOCK_IE_EVENT_SINK_ACTIONS_H_ | |
OLD | NEW |