| 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 |