Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / public / test / browser_test_utils.cc
blobafb07ba6c226136d23bb0506370082e0035dccef
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.
5 #include "content/public/test/browser_test_utils.h"
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/json/json_reader.h"
10 #include "base/process/kill.h"
11 #include "base/rand_util.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_piece.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/synchronization/waitable_event.h"
16 #include "base/test/test_timeouts.h"
17 #include "base/values.h"
18 #include "content/browser/renderer_host/render_widget_host_impl.h"
19 #include "content/browser/web_contents/web_contents_impl.h"
20 #include "content/browser/web_contents/web_contents_view.h"
21 #include "content/common/input/synthetic_web_input_event_builders.h"
22 #include "content/public/browser/browser_context.h"
23 #include "content/public/browser/dom_operation_notification_details.h"
24 #include "content/public/browser/histogram_fetcher.h"
25 #include "content/public/browser/navigation_entry.h"
26 #include "content/public/browser/notification_service.h"
27 #include "content/public/browser/notification_types.h"
28 #include "content/public/browser/render_frame_host.h"
29 #include "content/public/browser/render_process_host.h"
30 #include "content/public/browser/render_view_host.h"
31 #include "content/public/browser/web_contents.h"
32 #include "content/public/browser/web_contents_observer.h"
33 #include "content/public/test/test_utils.h"
34 #include "net/base/filename_util.h"
35 #include "net/cookies/cookie_store.h"
36 #include "net/test/embedded_test_server/embedded_test_server.h"
37 #include "net/test/embedded_test_server/http_request.h"
38 #include "net/test/embedded_test_server/http_response.h"
39 #include "net/test/python_utils.h"
40 #include "net/url_request/url_request_context.h"
41 #include "net/url_request/url_request_context_getter.h"
42 #include "testing/gtest/include/gtest/gtest.h"
43 #include "ui/base/resource/resource_bundle.h"
44 #include "ui/compositor/test/draw_waiter_for_test.h"
45 #include "ui/events/gesture_detection/gesture_configuration.h"
46 #include "ui/events/keycodes/dom4/keycode_converter.h"
47 #include "ui/resources/grit/webui_resources.h"
49 #if defined(USE_AURA)
50 #include "ui/aura/test/window_event_dispatcher_test_api.h"
51 #include "ui/aura/window.h"
52 #include "ui/aura/window_event_dispatcher.h"
53 #include "ui/aura/window_tree_host.h"
54 #endif // USE_AURA
56 namespace content {
57 namespace {
59 class DOMOperationObserver : public NotificationObserver,
60 public WebContentsObserver {
61 public:
62 explicit DOMOperationObserver(RenderViewHost* rvh)
63 : WebContentsObserver(WebContents::FromRenderViewHost(rvh)),
64 did_respond_(false) {
65 registrar_.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE,
66 Source<WebContents>(web_contents()));
67 message_loop_runner_ = new MessageLoopRunner;
70 void Observe(int type,
71 const NotificationSource& source,
72 const NotificationDetails& details) override {
73 DCHECK(type == NOTIFICATION_DOM_OPERATION_RESPONSE);
74 Details<DomOperationNotificationDetails> dom_op_details(details);
75 response_ = dom_op_details->json;
76 did_respond_ = true;
77 message_loop_runner_->Quit();
80 // Overridden from WebContentsObserver:
81 void RenderProcessGone(base::TerminationStatus status) override {
82 message_loop_runner_->Quit();
85 bool WaitAndGetResponse(std::string* response) WARN_UNUSED_RESULT {
86 message_loop_runner_->Run();
87 *response = response_;
88 return did_respond_;
91 private:
92 NotificationRegistrar registrar_;
93 std::string response_;
94 bool did_respond_;
95 scoped_refptr<MessageLoopRunner> message_loop_runner_;
97 DISALLOW_COPY_AND_ASSIGN(DOMOperationObserver);
100 class InterstitialObserver : public content::WebContentsObserver {
101 public:
102 InterstitialObserver(content::WebContents* web_contents,
103 const base::Closure& attach_callback,
104 const base::Closure& detach_callback)
105 : WebContentsObserver(web_contents),
106 attach_callback_(attach_callback),
107 detach_callback_(detach_callback) {
109 ~InterstitialObserver() override {}
111 // WebContentsObserver methods:
112 void DidAttachInterstitialPage() override { attach_callback_.Run(); }
113 void DidDetachInterstitialPage() override { detach_callback_.Run(); }
115 private:
116 base::Closure attach_callback_;
117 base::Closure detach_callback_;
119 DISALLOW_COPY_AND_ASSIGN(InterstitialObserver);
122 // Specifying a prototype so that we can add the WARN_UNUSED_RESULT attribute.
123 bool ExecuteScriptHelper(
124 RenderFrameHost* render_frame_host,
125 const std::string& original_script,
126 scoped_ptr<base::Value>* result) WARN_UNUSED_RESULT;
128 // Executes the passed |original_script| in the frame specified by
129 // |render_frame_host|. If |result| is not NULL, stores the value that the
130 // evaluation of the script in |result|. Returns true on success.
131 bool ExecuteScriptHelper(RenderFrameHost* render_frame_host,
132 const std::string& original_script,
133 scoped_ptr<base::Value>* result) {
134 // TODO(jcampan): we should make the domAutomationController not require an
135 // automation id.
136 std::string script =
137 "window.domAutomationController.setAutomationId(0);" + original_script;
138 DOMOperationObserver dom_op_observer(render_frame_host->GetRenderViewHost());
139 render_frame_host->ExecuteJavaScriptForTests(base::UTF8ToUTF16(script));
140 std::string json;
141 if (!dom_op_observer.WaitAndGetResponse(&json)) {
142 DLOG(ERROR) << "Cannot communicate with DOMOperationObserver.";
143 return false;
146 // Nothing more to do for callers that ignore the returned JS value.
147 if (!result)
148 return true;
150 base::JSONReader reader(base::JSON_ALLOW_TRAILING_COMMAS);
151 result->reset(reader.ReadToValue(json));
152 if (!result->get()) {
153 DLOG(ERROR) << reader.GetErrorMessage();
154 return false;
157 return true;
160 void BuildSimpleWebKeyEvent(blink::WebInputEvent::Type type,
161 ui::KeyboardCode key_code,
162 int native_key_code,
163 int modifiers,
164 NativeWebKeyboardEvent* event) {
165 event->nativeKeyCode = native_key_code;
166 event->windowsKeyCode = key_code;
167 event->setKeyIdentifierFromWindowsKeyCode();
168 event->type = type;
169 event->modifiers = modifiers;
170 event->isSystemKey = false;
171 event->timeStampSeconds = base::Time::Now().ToDoubleT();
172 event->skip_in_browser = true;
174 if (type == blink::WebInputEvent::Char ||
175 type == blink::WebInputEvent::RawKeyDown) {
176 event->text[0] = key_code;
177 event->unmodifiedText[0] = key_code;
181 void InjectRawKeyEvent(WebContents* web_contents,
182 blink::WebInputEvent::Type type,
183 ui::KeyboardCode key_code,
184 int native_key_code,
185 int modifiers) {
186 NativeWebKeyboardEvent event;
187 BuildSimpleWebKeyEvent(type, key_code, native_key_code, modifiers, &event);
188 web_contents->GetRenderViewHost()->ForwardKeyboardEvent(event);
191 void GetCookiesCallback(std::string* cookies_out,
192 base::WaitableEvent* event,
193 const std::string& cookies) {
194 *cookies_out = cookies;
195 event->Signal();
198 void GetCookiesOnIOThread(const GURL& url,
199 net::URLRequestContextGetter* context_getter,
200 base::WaitableEvent* event,
201 std::string* cookies) {
202 net::CookieStore* cookie_store =
203 context_getter->GetURLRequestContext()->cookie_store();
204 cookie_store->GetCookiesWithOptionsAsync(
205 url, net::CookieOptions(),
206 base::Bind(&GetCookiesCallback, cookies, event));
209 void SetCookieCallback(bool* result,
210 base::WaitableEvent* event,
211 bool success) {
212 *result = success;
213 event->Signal();
216 void SetCookieOnIOThread(const GURL& url,
217 const std::string& value,
218 net::URLRequestContextGetter* context_getter,
219 base::WaitableEvent* event,
220 bool* result) {
221 net::CookieStore* cookie_store =
222 context_getter->GetURLRequestContext()->cookie_store();
223 cookie_store->SetCookieWithOptionsAsync(
224 url, value, net::CookieOptions(),
225 base::Bind(&SetCookieCallback, result, event));
228 scoped_ptr<net::test_server::HttpResponse> CrossSiteRedirectResponseHandler(
229 const GURL& server_base_url,
230 const net::test_server::HttpRequest& request) {
231 std::string prefix("/cross-site/");
232 if (!StartsWithASCII(request.relative_url, prefix, true))
233 return scoped_ptr<net::test_server::HttpResponse>();
235 std::string params = request.relative_url.substr(prefix.length());
237 // A hostname to redirect to must be included in the URL, therefore at least
238 // one '/' character is expected.
239 size_t slash = params.find('/');
240 if (slash == std::string::npos)
241 return scoped_ptr<net::test_server::HttpResponse>();
243 // Replace the host of the URL with the one passed in the URL.
244 GURL::Replacements replace_host;
245 replace_host.SetHostStr(base::StringPiece(params).substr(0, slash));
246 GURL redirect_server = server_base_url.ReplaceComponents(replace_host);
248 // Append the real part of the path to the new URL.
249 std::string path = params.substr(slash + 1);
250 GURL redirect_target(redirect_server.Resolve(path));
251 DCHECK(redirect_target.is_valid());
253 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
254 new net::test_server::BasicHttpResponse);
255 http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
256 http_response->AddCustomHeader("Location", redirect_target.spec());
257 return http_response.Pass();
260 } // namespace
262 bool NavigateIframeToURL(WebContents* web_contents,
263 std::string iframe_id,
264 const GURL& url) {
265 // TODO(creis): This should wait for LOAD_STOP, but cross-site subframe
266 // navigations generate extra DidStartLoading and DidStopLoading messages.
267 // Until we replace swappedout:// with frame proxies, we need to listen for
268 // something else. For now, we trigger NEW_SUBFRAME navigations and listen
269 // for commit. See https://crbug.com/436250.
270 std::string script = base::StringPrintf(
271 "setTimeout(\""
272 "var iframes = document.getElementById('%s');iframes.src='%s';"
273 "\",0)",
274 iframe_id.c_str(), url.spec().c_str());
275 WindowedNotificationObserver load_observer(
276 NOTIFICATION_NAV_ENTRY_COMMITTED,
277 Source<NavigationController>(&web_contents->GetController()));
278 bool result = ExecuteScript(web_contents, script);
279 load_observer.Wait();
280 return result;
283 GURL GetFileUrlWithQuery(const base::FilePath& path,
284 const std::string& query_string) {
285 GURL url = net::FilePathToFileURL(path);
286 if (!query_string.empty()) {
287 GURL::Replacements replacements;
288 replacements.SetQueryStr(query_string);
289 return url.ReplaceComponents(replacements);
291 return url;
294 void WaitForLoadStopWithoutSuccessCheck(WebContents* web_contents) {
295 // In many cases, the load may have finished before we get here. Only wait if
296 // the tab still has a pending navigation.
297 if (web_contents->IsLoading()) {
298 WindowedNotificationObserver load_stop_observer(
299 NOTIFICATION_LOAD_STOP,
300 Source<NavigationController>(&web_contents->GetController()));
301 load_stop_observer.Wait();
305 bool WaitForLoadStop(WebContents* web_contents) {
306 WaitForLoadStopWithoutSuccessCheck(web_contents);
307 return IsLastCommittedEntryOfPageType(web_contents, PAGE_TYPE_NORMAL);
310 bool IsLastCommittedEntryOfPageType(WebContents* web_contents,
311 content::PageType page_type) {
312 NavigationEntry* last_entry =
313 web_contents->GetController().GetLastCommittedEntry();
314 if (!last_entry)
315 return false;
316 return last_entry->GetPageType() == page_type;
319 void CrashTab(WebContents* web_contents) {
320 RenderProcessHost* rph = web_contents->GetRenderProcessHost();
321 RenderProcessHostWatcher watcher(
322 rph, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
323 rph->Shutdown(0, false);
324 watcher.Wait();
327 #if defined(USE_AURA)
328 bool IsResizeComplete(aura::test::WindowEventDispatcherTestApi* dispatcher_test,
329 RenderWidgetHostImpl* widget_host) {
330 return !dispatcher_test->HoldingPointerMoves() &&
331 !widget_host->resize_ack_pending_for_testing();
334 void WaitForResizeComplete(WebContents* web_contents) {
335 aura::Window* content = web_contents->GetContentNativeView();
336 if (!content)
337 return;
339 aura::WindowTreeHost* window_host = content->GetHost();
340 aura::WindowEventDispatcher* dispatcher = window_host->dispatcher();
341 aura::test::WindowEventDispatcherTestApi dispatcher_test(dispatcher);
342 RenderWidgetHostImpl* widget_host =
343 RenderWidgetHostImpl::From(web_contents->GetRenderViewHost());
344 if (!IsResizeComplete(&dispatcher_test, widget_host)) {
345 WindowedNotificationObserver resize_observer(
346 NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
347 base::Bind(IsResizeComplete, &dispatcher_test, widget_host));
348 resize_observer.Wait();
351 #elif defined(OS_ANDROID)
352 bool IsResizeComplete(RenderWidgetHostImpl* widget_host) {
353 return !widget_host->resize_ack_pending_for_testing();
356 void WaitForResizeComplete(WebContents* web_contents) {
357 RenderWidgetHostImpl* widget_host =
358 RenderWidgetHostImpl::From(web_contents->GetRenderViewHost());
359 if (!IsResizeComplete(widget_host)) {
360 WindowedNotificationObserver resize_observer(
361 NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
362 base::Bind(IsResizeComplete, widget_host));
363 resize_observer.Wait();
366 #endif
368 void SimulateMouseClick(WebContents* web_contents,
369 int modifiers,
370 blink::WebMouseEvent::Button button) {
371 int x = web_contents->GetContainerBounds().width() / 2;
372 int y = web_contents->GetContainerBounds().height() / 2;
373 SimulateMouseClickAt(web_contents, modifiers, button, gfx::Point(x, y));
376 void SimulateMouseClickAt(WebContents* web_contents,
377 int modifiers,
378 blink::WebMouseEvent::Button button,
379 const gfx::Point& point) {
380 blink::WebMouseEvent mouse_event;
381 mouse_event.type = blink::WebInputEvent::MouseDown;
382 mouse_event.button = button;
383 mouse_event.x = point.x();
384 mouse_event.y = point.y();
385 mouse_event.modifiers = modifiers;
386 // Mac needs globalX/globalY for events to plugins.
387 gfx::Rect offset = web_contents->GetContainerBounds();
388 mouse_event.globalX = point.x() + offset.x();
389 mouse_event.globalY = point.y() + offset.y();
390 mouse_event.clickCount = 1;
391 web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
392 mouse_event.type = blink::WebInputEvent::MouseUp;
393 web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
396 void SimulateMouseEvent(WebContents* web_contents,
397 blink::WebInputEvent::Type type,
398 const gfx::Point& point) {
399 blink::WebMouseEvent mouse_event;
400 mouse_event.type = type;
401 mouse_event.x = point.x();
402 mouse_event.y = point.y();
403 web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
406 void SimulateTapAt(WebContents* web_contents, const gfx::Point& point) {
407 blink::WebGestureEvent tap;
408 tap.type = blink::WebGestureEvent::GestureTap;
409 tap.x = point.x();
410 tap.y = point.y();
411 tap.modifiers = blink::WebInputEvent::ControlKey;
412 RenderWidgetHostImpl* widget_host =
413 RenderWidgetHostImpl::From(web_contents->GetRenderViewHost());
414 widget_host->ForwardGestureEvent(tap);
417 void SimulateTapWithModifiersAt(WebContents* web_contents,
418 unsigned modifiers,
419 const gfx::Point& point) {
420 blink::WebGestureEvent tap;
421 tap.type = blink::WebGestureEvent::GestureTap;
422 tap.x = point.x();
423 tap.y = point.y();
424 tap.modifiers = modifiers;
425 RenderWidgetHostImpl* widget_host =
426 RenderWidgetHostImpl::From(web_contents->GetRenderViewHost());
427 widget_host->ForwardGestureEvent(tap);
430 void SimulateKeyPress(WebContents* web_contents,
431 ui::KeyboardCode key_code,
432 bool control,
433 bool shift,
434 bool alt,
435 bool command) {
436 SimulateKeyPressWithCode(
437 web_contents, key_code, NULL, control, shift, alt, command);
440 void SimulateKeyPressWithCode(WebContents* web_contents,
441 ui::KeyboardCode key_code,
442 const char* code,
443 bool control,
444 bool shift,
445 bool alt,
446 bool command) {
447 int native_key_code = ui::KeycodeConverter::CodeToNativeKeycode(code);
449 int modifiers = 0;
451 // The order of these key down events shouldn't matter for our simulation.
452 // For our simulation we can use either the left keys or the right keys.
453 if (control) {
454 modifiers |= blink::WebInputEvent::ControlKey;
455 InjectRawKeyEvent(web_contents,
456 blink::WebInputEvent::RawKeyDown,
457 ui::VKEY_CONTROL,
458 ui::KeycodeConverter::CodeToNativeKeycode("ControlLeft"),
459 modifiers);
462 if (shift) {
463 modifiers |= blink::WebInputEvent::ShiftKey;
464 InjectRawKeyEvent(web_contents,
465 blink::WebInputEvent::RawKeyDown,
466 ui::VKEY_SHIFT,
467 ui::KeycodeConverter::CodeToNativeKeycode("ShiftLeft"),
468 modifiers);
471 if (alt) {
472 modifiers |= blink::WebInputEvent::AltKey;
473 InjectRawKeyEvent(web_contents,
474 blink::WebInputEvent::RawKeyDown,
475 ui::VKEY_MENU,
476 ui::KeycodeConverter::CodeToNativeKeycode("AltLeft"),
477 modifiers);
480 if (command) {
481 modifiers |= blink::WebInputEvent::MetaKey;
482 InjectRawKeyEvent(web_contents,
483 blink::WebInputEvent::RawKeyDown,
484 ui::VKEY_COMMAND,
485 ui::KeycodeConverter::CodeToNativeKeycode("OSLeft"),
486 modifiers);
489 InjectRawKeyEvent(
490 web_contents,
491 blink::WebInputEvent::RawKeyDown,
492 key_code,
493 native_key_code,
494 modifiers);
496 InjectRawKeyEvent(
497 web_contents,
498 blink::WebInputEvent::Char,
499 key_code,
500 native_key_code,
501 modifiers);
503 InjectRawKeyEvent(
504 web_contents,
505 blink::WebInputEvent::KeyUp,
506 key_code,
507 native_key_code,
508 modifiers);
510 // The order of these key releases shouldn't matter for our simulation.
511 if (control) {
512 modifiers &= ~blink::WebInputEvent::ControlKey;
513 InjectRawKeyEvent(web_contents,
514 blink::WebInputEvent::KeyUp,
515 ui::VKEY_CONTROL,
516 ui::KeycodeConverter::CodeToNativeKeycode("ControlLeft"),
517 modifiers);
520 if (shift) {
521 modifiers &= ~blink::WebInputEvent::ShiftKey;
522 InjectRawKeyEvent(web_contents,
523 blink::WebInputEvent::KeyUp,
524 ui::VKEY_SHIFT,
525 ui::KeycodeConverter::CodeToNativeKeycode("ShiftLeft"),
526 modifiers);
529 if (alt) {
530 modifiers &= ~blink::WebInputEvent::AltKey;
531 InjectRawKeyEvent(web_contents,
532 blink::WebInputEvent::KeyUp,
533 ui::VKEY_MENU,
534 ui::KeycodeConverter::CodeToNativeKeycode("AltLeft"),
535 modifiers);
538 if (command) {
539 modifiers &= ~blink::WebInputEvent::MetaKey;
540 InjectRawKeyEvent(web_contents,
541 blink::WebInputEvent::KeyUp,
542 ui::VKEY_COMMAND,
543 ui::KeycodeConverter::CodeToNativeKeycode("OSLeft"),
544 modifiers);
547 ASSERT_EQ(modifiers, 0);
550 namespace internal {
552 ToRenderFrameHost::ToRenderFrameHost(WebContents* web_contents)
553 : render_frame_host_(web_contents->GetMainFrame()) {
556 ToRenderFrameHost::ToRenderFrameHost(RenderViewHost* render_view_host)
557 : render_frame_host_(render_view_host->GetMainFrame()) {
560 ToRenderFrameHost::ToRenderFrameHost(RenderFrameHost* render_frame_host)
561 : render_frame_host_(render_frame_host) {
564 } // namespace internal
566 bool ExecuteScript(const internal::ToRenderFrameHost& adapter,
567 const std::string& script) {
568 std::string new_script =
569 script + ";window.domAutomationController.send(0);";
570 return ExecuteScriptHelper(adapter.render_frame_host(), new_script, NULL);
573 bool ExecuteScriptAndExtractInt(const internal::ToRenderFrameHost& adapter,
574 const std::string& script, int* result) {
575 DCHECK(result);
576 scoped_ptr<base::Value> value;
577 if (!ExecuteScriptHelper(adapter.render_frame_host(), script, &value) ||
578 !value.get()) {
579 return false;
582 return value->GetAsInteger(result);
585 bool ExecuteScriptAndExtractBool(const internal::ToRenderFrameHost& adapter,
586 const std::string& script, bool* result) {
587 DCHECK(result);
588 scoped_ptr<base::Value> value;
589 if (!ExecuteScriptHelper(adapter.render_frame_host(), script, &value) ||
590 !value.get()) {
591 return false;
594 return value->GetAsBoolean(result);
597 bool ExecuteScriptAndExtractString(const internal::ToRenderFrameHost& adapter,
598 const std::string& script,
599 std::string* result) {
600 DCHECK(result);
601 scoped_ptr<base::Value> value;
602 if (!ExecuteScriptHelper(adapter.render_frame_host(), script, &value) ||
603 !value.get()) {
604 return false;
607 return value->GetAsString(result);
610 namespace {
611 void AddToSetIfFrameMatchesPredicate(
612 std::set<RenderFrameHost*>* frame_set,
613 const base::Callback<bool(RenderFrameHost*)>& predicate,
614 RenderFrameHost* host) {
615 if (predicate.Run(host))
616 frame_set->insert(host);
620 RenderFrameHost* FrameMatchingPredicate(
621 WebContents* web_contents,
622 const base::Callback<bool(RenderFrameHost*)>& predicate) {
623 std::set<RenderFrameHost*> frame_set;
624 web_contents->ForEachFrame(
625 base::Bind(&AddToSetIfFrameMatchesPredicate, &frame_set, predicate));
626 DCHECK_EQ(1U, frame_set.size());
627 return *frame_set.begin();
630 bool FrameMatchesName(const std::string& name, RenderFrameHost* frame) {
631 return frame->GetFrameName() == name;
634 bool FrameIsChildOfMainFrame(RenderFrameHost* frame) {
635 return frame->GetParent() && !frame->GetParent()->GetParent();
638 bool FrameHasSourceUrl(const GURL& url, RenderFrameHost* frame) {
639 return frame->GetLastCommittedURL() == url;
642 bool ExecuteWebUIResourceTest(WebContents* web_contents,
643 const std::vector<int>& js_resource_ids) {
644 // Inject WebUI test runner script first prior to other scripts required to
645 // run the test as scripts may depend on it being declared.
646 std::vector<int> ids;
647 ids.push_back(IDR_WEBUI_JS_WEBUI_RESOURCE_TEST);
648 ids.insert(ids.end(), js_resource_ids.begin(), js_resource_ids.end());
650 std::string script;
651 for (std::vector<int>::iterator iter = ids.begin();
652 iter != ids.end();
653 ++iter) {
654 ResourceBundle::GetSharedInstance().GetRawDataResource(*iter)
655 .AppendToString(&script);
656 script.append("\n");
658 if (!ExecuteScript(web_contents, script))
659 return false;
661 DOMMessageQueue message_queue;
662 if (!ExecuteScript(web_contents, "runTests()"))
663 return false;
665 std::string message;
666 do {
667 if (!message_queue.WaitForMessage(&message))
668 return false;
669 } while (message.compare("\"PENDING\"") == 0);
671 return message.compare("\"SUCCESS\"") == 0;
674 std::string GetCookies(BrowserContext* browser_context, const GURL& url) {
675 std::string cookies;
676 base::WaitableEvent event(true, false);
677 net::URLRequestContextGetter* context_getter =
678 browser_context->GetRequestContext();
680 BrowserThread::PostTask(
681 BrowserThread::IO, FROM_HERE,
682 base::Bind(&GetCookiesOnIOThread, url,
683 make_scoped_refptr(context_getter), &event, &cookies));
684 event.Wait();
685 return cookies;
688 bool SetCookie(BrowserContext* browser_context,
689 const GURL& url,
690 const std::string& value) {
691 bool result = false;
692 base::WaitableEvent event(true, false);
693 net::URLRequestContextGetter* context_getter =
694 browser_context->GetRequestContext();
696 BrowserThread::PostTask(
697 BrowserThread::IO, FROM_HERE,
698 base::Bind(&SetCookieOnIOThread, url, value,
699 make_scoped_refptr(context_getter), &event, &result));
700 event.Wait();
701 return result;
704 void FetchHistogramsFromChildProcesses() {
705 scoped_refptr<content::MessageLoopRunner> runner = new MessageLoopRunner;
707 FetchHistogramsAsynchronously(
708 base::MessageLoop::current(),
709 runner->QuitClosure(),
710 // If this call times out, it means that a child process is not
711 // responding, which is something we should not ignore. The timeout is
712 // set to be longer than the normal browser test timeout so that it will
713 // be prempted by the normal timeout.
714 TestTimeouts::action_max_timeout());
715 runner->Run();
718 void SetupCrossSiteRedirector(
719 net::test_server::EmbeddedTestServer* embedded_test_server) {
720 embedded_test_server->RegisterRequestHandler(
721 base::Bind(&CrossSiteRedirectResponseHandler,
722 embedded_test_server->base_url()));
725 void WaitForInterstitialAttach(content::WebContents* web_contents) {
726 if (web_contents->ShowingInterstitialPage())
727 return;
728 scoped_refptr<content::MessageLoopRunner> loop_runner(
729 new content::MessageLoopRunner);
730 InterstitialObserver observer(web_contents,
731 loop_runner->QuitClosure(),
732 base::Closure());
733 loop_runner->Run();
736 void WaitForInterstitialDetach(content::WebContents* web_contents) {
737 RunTaskAndWaitForInterstitialDetach(web_contents, base::Closure());
740 void RunTaskAndWaitForInterstitialDetach(content::WebContents* web_contents,
741 const base::Closure& task) {
742 if (!web_contents || !web_contents->ShowingInterstitialPage())
743 return;
744 scoped_refptr<content::MessageLoopRunner> loop_runner(
745 new content::MessageLoopRunner);
746 InterstitialObserver observer(web_contents,
747 base::Closure(),
748 loop_runner->QuitClosure());
749 if (!task.is_null())
750 task.Run();
751 // At this point, web_contents may have been deleted.
752 loop_runner->Run();
755 bool WaitForRenderFrameReady(RenderFrameHost* rfh) {
756 if (!rfh)
757 return false;
758 std::string result;
759 EXPECT_TRUE(
760 content::ExecuteScriptAndExtractString(
761 rfh,
762 "(function() {"
763 " var done = false;"
764 " function checkState() {"
765 " if (!done && document.readyState == 'complete') {"
766 " done = true;"
767 " window.domAutomationController.send('pageLoadComplete');"
768 " }"
769 " }"
770 " checkState();"
771 " document.addEventListener('readystatechange', checkState);"
772 "})();",
773 &result));
774 return result == "pageLoadComplete";
777 TitleWatcher::TitleWatcher(WebContents* web_contents,
778 const base::string16& expected_title)
779 : WebContentsObserver(web_contents),
780 message_loop_runner_(new MessageLoopRunner) {
781 EXPECT_TRUE(web_contents != NULL);
782 expected_titles_.push_back(expected_title);
785 void TitleWatcher::AlsoWaitForTitle(const base::string16& expected_title) {
786 expected_titles_.push_back(expected_title);
789 TitleWatcher::~TitleWatcher() {
792 const base::string16& TitleWatcher::WaitAndGetTitle() {
793 TestTitle();
794 message_loop_runner_->Run();
795 return observed_title_;
798 void TitleWatcher::DidStopLoading() {
799 // When navigating through the history, the restored NavigationEntry's title
800 // will be used. If the entry ends up having the same title after we return
801 // to it, as will usually be the case, then WebContentsObserver::TitleSet
802 // will then be suppressed, since the NavigationEntry's title hasn't changed.
803 TestTitle();
806 void TitleWatcher::TitleWasSet(NavigationEntry* entry, bool explicit_set) {
807 TestTitle();
810 void TitleWatcher::TestTitle() {
811 std::vector<base::string16>::const_iterator it =
812 std::find(expected_titles_.begin(),
813 expected_titles_.end(),
814 web_contents()->GetTitle());
815 if (it == expected_titles_.end())
816 return;
818 observed_title_ = *it;
819 message_loop_runner_->Quit();
822 WebContentsDestroyedWatcher::WebContentsDestroyedWatcher(
823 WebContents* web_contents)
824 : WebContentsObserver(web_contents),
825 message_loop_runner_(new MessageLoopRunner) {
826 EXPECT_TRUE(web_contents != NULL);
829 WebContentsDestroyedWatcher::~WebContentsDestroyedWatcher() {
832 void WebContentsDestroyedWatcher::Wait() {
833 message_loop_runner_->Run();
836 void WebContentsDestroyedWatcher::WebContentsDestroyed() {
837 message_loop_runner_->Quit();
840 RenderProcessHostWatcher::RenderProcessHostWatcher(
841 RenderProcessHost* render_process_host, WatchType type)
842 : render_process_host_(render_process_host),
843 type_(type),
844 message_loop_runner_(new MessageLoopRunner) {
845 render_process_host_->AddObserver(this);
848 RenderProcessHostWatcher::RenderProcessHostWatcher(
849 WebContents* web_contents, WatchType type)
850 : render_process_host_(web_contents->GetRenderProcessHost()),
851 type_(type),
852 message_loop_runner_(new MessageLoopRunner) {
853 render_process_host_->AddObserver(this);
856 RenderProcessHostWatcher::~RenderProcessHostWatcher() {
857 if (render_process_host_)
858 render_process_host_->RemoveObserver(this);
861 void RenderProcessHostWatcher::Wait() {
862 message_loop_runner_->Run();
865 void RenderProcessHostWatcher::RenderProcessExited(
866 RenderProcessHost* host,
867 base::TerminationStatus status,
868 int exit_code) {
869 if (type_ == WATCH_FOR_PROCESS_EXIT)
870 message_loop_runner_->Quit();
873 void RenderProcessHostWatcher::RenderProcessHostDestroyed(
874 RenderProcessHost* host) {
875 render_process_host_ = NULL;
876 if (type_ == WATCH_FOR_HOST_DESTRUCTION)
877 message_loop_runner_->Quit();
880 DOMMessageQueue::DOMMessageQueue() {
881 registrar_.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE,
882 NotificationService::AllSources());
885 DOMMessageQueue::~DOMMessageQueue() {}
887 void DOMMessageQueue::Observe(int type,
888 const NotificationSource& source,
889 const NotificationDetails& details) {
890 Details<DomOperationNotificationDetails> dom_op_details(details);
891 message_queue_.push(dom_op_details->json);
892 if (message_loop_runner_.get())
893 message_loop_runner_->Quit();
896 void DOMMessageQueue::ClearQueue() {
897 message_queue_ = std::queue<std::string>();
900 bool DOMMessageQueue::WaitForMessage(std::string* message) {
901 DCHECK(message);
902 if (message_queue_.empty()) {
903 // This will be quit when a new message comes in.
904 message_loop_runner_ = new MessageLoopRunner;
905 message_loop_runner_->Run();
907 // The queue should not be empty, unless we were quit because of a timeout.
908 if (message_queue_.empty())
909 return false;
910 *message = message_queue_.front();
911 message_queue_.pop();
912 return true;
915 class WebContentsAddedObserver::RenderViewCreatedObserver
916 : public WebContentsObserver {
917 public:
918 explicit RenderViewCreatedObserver(WebContents* web_contents)
919 : WebContentsObserver(web_contents),
920 render_view_created_called_(false),
921 main_frame_created_called_(false) {}
923 // WebContentsObserver:
924 void RenderViewCreated(RenderViewHost* rvh) override {
925 render_view_created_called_ = true;
928 void RenderFrameCreated(RenderFrameHost* rfh) override {
929 if (rfh == web_contents()->GetMainFrame())
930 main_frame_created_called_ = true;
933 bool render_view_created_called_;
934 bool main_frame_created_called_;
937 WebContentsAddedObserver::WebContentsAddedObserver()
938 : web_contents_created_callback_(
939 base::Bind(&WebContentsAddedObserver::WebContentsCreated,
940 base::Unretained(this))),
941 web_contents_(NULL) {
942 WebContentsImpl::FriendZone::AddCreatedCallbackForTesting(
943 web_contents_created_callback_);
946 WebContentsAddedObserver::~WebContentsAddedObserver() {
947 WebContentsImpl::FriendZone::RemoveCreatedCallbackForTesting(
948 web_contents_created_callback_);
951 void WebContentsAddedObserver::WebContentsCreated(WebContents* web_contents) {
952 DCHECK(!web_contents_);
953 web_contents_ = web_contents;
954 child_observer_.reset(new RenderViewCreatedObserver(web_contents));
956 if (runner_.get())
957 runner_->QuitClosure().Run();
960 WebContents* WebContentsAddedObserver::GetWebContents() {
961 if (web_contents_)
962 return web_contents_;
964 runner_ = new MessageLoopRunner();
965 runner_->Run();
966 return web_contents_;
969 bool WebContentsAddedObserver::RenderViewCreatedCalled() {
970 if (child_observer_) {
971 return child_observer_->render_view_created_called_ &&
972 child_observer_->main_frame_created_called_;
974 return false;
977 } // namespace content