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/command_line.h"
8 #include "base/json/json_reader.h"
9 #include "base/path_service.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/utf_string_conversions.h"
14 #include "base/synchronization/waitable_event.h"
15 #include "base/test/test_timeouts.h"
16 #include "base/values.h"
17 #include "content/browser/renderer_host/render_widget_host_impl.h"
18 #include "content/browser/web_contents/web_contents_view.h"
19 #include "content/common/input/synthetic_web_input_event_builders.h"
20 #include "content/public/browser/browser_context.h"
21 #include "content/public/browser/dom_operation_notification_details.h"
22 #include "content/public/browser/notification_service.h"
23 #include "content/public/browser/notification_types.h"
24 #include "content/public/browser/render_frame_host.h"
25 #include "content/public/browser/render_process_host.h"
26 #include "content/public/browser/render_view_host.h"
27 #include "content/public/browser/web_contents.h"
28 #include "content/public/browser/web_contents_observer.h"
29 #include "content/public/test/test_utils.h"
30 #include "grit/webui_resources.h"
31 #include "net/base/filename_util.h"
32 #include "net/cookies/cookie_store.h"
33 #include "net/test/python_utils.h"
34 #include "net/url_request/url_request_context.h"
35 #include "net/url_request/url_request_context_getter.h"
36 #include "testing/gtest/include/gtest/gtest.h"
37 #include "ui/base/resource/resource_bundle.h"
38 #include "ui/compositor/test/draw_waiter_for_test.h"
39 #include "ui/events/gestures/gesture_configuration.h"
40 #include "ui/events/keycodes/dom4/keycode_converter.h"
43 #include "ui/aura/test/window_event_dispatcher_test_api.h"
44 #include "ui/aura/window.h"
45 #include "ui/aura/window_event_dispatcher.h"
46 #include "ui/aura/window_tree_host.h"
52 class DOMOperationObserver : public NotificationObserver,
53 public WebContentsObserver {
55 explicit DOMOperationObserver(RenderViewHost* rvh)
56 : WebContentsObserver(WebContents::FromRenderViewHost(rvh)),
58 registrar_.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE,
59 Source<WebContents>(web_contents()));
60 message_loop_runner_ = new MessageLoopRunner;
63 virtual void Observe(int type,
64 const NotificationSource& source,
65 const NotificationDetails& details) OVERRIDE {
66 DCHECK(type == NOTIFICATION_DOM_OPERATION_RESPONSE);
67 Details<DomOperationNotificationDetails> dom_op_details(details);
68 response_ = dom_op_details->json;
70 message_loop_runner_->Quit();
73 // Overridden from WebContentsObserver:
74 virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE {
75 message_loop_runner_->Quit();
78 bool WaitAndGetResponse(std::string* response) WARN_UNUSED_RESULT {
79 message_loop_runner_->Run();
80 *response = response_;
85 NotificationRegistrar registrar_;
86 std::string response_;
88 scoped_refptr<MessageLoopRunner> message_loop_runner_;
90 DISALLOW_COPY_AND_ASSIGN(DOMOperationObserver);
93 // Specifying a prototype so that we can add the WARN_UNUSED_RESULT attribute.
94 bool ExecuteScriptHelper(
95 RenderFrameHost* render_frame_host,
96 const std::string& original_script,
97 scoped_ptr<base::Value>* result) WARN_UNUSED_RESULT;
99 // Executes the passed |original_script| in the frame specified by
100 // |render_frame_host|. If |result| is not NULL, stores the value that the
101 // evaluation of the script in |result|. Returns true on success.
102 bool ExecuteScriptHelper(RenderFrameHost* render_frame_host,
103 const std::string& original_script,
104 scoped_ptr<base::Value>* result) {
105 // TODO(jcampan): we should make the domAutomationController not require an
108 "window.domAutomationController.setAutomationId(0);" + original_script;
109 DOMOperationObserver dom_op_observer(render_frame_host->GetRenderViewHost());
110 render_frame_host->ExecuteJavaScript(base::UTF8ToUTF16(script));
112 if (!dom_op_observer.WaitAndGetResponse(&json)) {
113 DLOG(ERROR) << "Cannot communicate with DOMOperationObserver.";
117 // Nothing more to do for callers that ignore the returned JS value.
121 base::JSONReader reader(base::JSON_ALLOW_TRAILING_COMMAS);
122 result->reset(reader.ReadToValue(json));
123 if (!result->get()) {
124 DLOG(ERROR) << reader.GetErrorMessage();
131 void BuildSimpleWebKeyEvent(blink::WebInputEvent::Type type,
132 ui::KeyboardCode key_code,
135 NativeWebKeyboardEvent* event) {
136 event->nativeKeyCode = native_key_code;
137 event->windowsKeyCode = key_code;
138 event->setKeyIdentifierFromWindowsKeyCode();
140 event->modifiers = modifiers;
141 event->isSystemKey = false;
142 event->timeStampSeconds = base::Time::Now().ToDoubleT();
143 event->skip_in_browser = true;
145 if (type == blink::WebInputEvent::Char ||
146 type == blink::WebInputEvent::RawKeyDown) {
147 event->text[0] = key_code;
148 event->unmodifiedText[0] = key_code;
152 void InjectRawKeyEvent(WebContents* web_contents,
153 blink::WebInputEvent::Type type,
154 ui::KeyboardCode key_code,
157 NativeWebKeyboardEvent event;
158 BuildSimpleWebKeyEvent(type, key_code, native_key_code, modifiers, &event);
159 web_contents->GetRenderViewHost()->ForwardKeyboardEvent(event);
162 void GetCookiesCallback(std::string* cookies_out,
163 base::WaitableEvent* event,
164 const std::string& cookies) {
165 *cookies_out = cookies;
169 void GetCookiesOnIOThread(const GURL& url,
170 net::URLRequestContextGetter* context_getter,
171 base::WaitableEvent* event,
172 std::string* cookies) {
173 net::CookieStore* cookie_store =
174 context_getter->GetURLRequestContext()->cookie_store();
175 cookie_store->GetCookiesWithOptionsAsync(
176 url, net::CookieOptions(),
177 base::Bind(&GetCookiesCallback, cookies, event));
180 void SetCookieCallback(bool* result,
181 base::WaitableEvent* event,
187 void SetCookieOnIOThread(const GURL& url,
188 const std::string& value,
189 net::URLRequestContextGetter* context_getter,
190 base::WaitableEvent* event,
192 net::CookieStore* cookie_store =
193 context_getter->GetURLRequestContext()->cookie_store();
194 cookie_store->SetCookieWithOptionsAsync(
195 url, value, net::CookieOptions(),
196 base::Bind(&SetCookieCallback, result, event));
202 GURL GetFileUrlWithQuery(const base::FilePath& path,
203 const std::string& query_string) {
204 GURL url = net::FilePathToFileURL(path);
205 if (!query_string.empty()) {
206 GURL::Replacements replacements;
207 replacements.SetQueryStr(query_string);
208 return url.ReplaceComponents(replacements);
213 void WaitForLoadStop(WebContents* web_contents) {
214 // In many cases, the load may have finished before we get here. Only wait if
215 // the tab still has a pending navigation.
216 if (web_contents->IsLoading()) {
217 WindowedNotificationObserver load_stop_observer(
218 NOTIFICATION_LOAD_STOP,
219 Source<NavigationController>(&web_contents->GetController()));
220 load_stop_observer.Wait();
224 void CrashTab(WebContents* web_contents) {
225 RenderProcessHost* rph = web_contents->GetRenderProcessHost();
226 RenderProcessHostWatcher watcher(
227 rph, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
228 base::KillProcess(rph->GetHandle(), 0, false);
232 #if defined(USE_AURA)
233 bool IsResizeComplete(aura::test::WindowEventDispatcherTestApi* dispatcher_test,
234 RenderWidgetHostImpl* widget_host) {
235 return !dispatcher_test->HoldingPointerMoves() &&
236 !widget_host->resize_ack_pending_for_testing();
239 void WaitForResizeComplete(WebContents* web_contents) {
240 aura::Window* content = web_contents->GetContentNativeView();
244 aura::WindowTreeHost* window_host = content->GetHost();
245 aura::WindowEventDispatcher* dispatcher = window_host->dispatcher();
246 aura::test::WindowEventDispatcherTestApi dispatcher_test(dispatcher);
247 RenderWidgetHostImpl* widget_host =
248 RenderWidgetHostImpl::From(web_contents->GetRenderViewHost());
249 if (!IsResizeComplete(&dispatcher_test, widget_host)) {
250 WindowedNotificationObserver resize_observer(
251 NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
252 base::Bind(IsResizeComplete, &dispatcher_test, widget_host));
253 resize_observer.Wait();
258 void SimulateMouseClick(WebContents* web_contents,
260 blink::WebMouseEvent::Button button) {
261 int x = web_contents->GetContainerBounds().width() / 2;
262 int y = web_contents->GetContainerBounds().height() / 2;
263 SimulateMouseClickAt(web_contents, modifiers, button, gfx::Point(x, y));
266 void SimulateMouseClickAt(WebContents* web_contents,
268 blink::WebMouseEvent::Button button,
269 const gfx::Point& point) {
270 blink::WebMouseEvent mouse_event;
271 mouse_event.type = blink::WebInputEvent::MouseDown;
272 mouse_event.button = button;
273 mouse_event.x = point.x();
274 mouse_event.y = point.y();
275 mouse_event.modifiers = modifiers;
276 // Mac needs globalX/globalY for events to plugins.
277 gfx::Rect offset = web_contents->GetContainerBounds();
278 mouse_event.globalX = point.x() + offset.x();
279 mouse_event.globalY = point.y() + offset.y();
280 mouse_event.clickCount = 1;
281 web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
282 mouse_event.type = blink::WebInputEvent::MouseUp;
283 web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
286 void SimulateMouseEvent(WebContents* web_contents,
287 blink::WebInputEvent::Type type,
288 const gfx::Point& point) {
289 blink::WebMouseEvent mouse_event;
290 mouse_event.type = type;
291 mouse_event.x = point.x();
292 mouse_event.y = point.y();
293 web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
296 void SimulateTapAt(WebContents* web_contents, const gfx::Point& point) {
297 blink::WebGestureEvent tap;
298 tap.type = blink::WebGestureEvent::GestureTap;
301 RenderWidgetHostImpl* widget_host =
302 RenderWidgetHostImpl::From(web_contents->GetRenderViewHost());
303 widget_host->ForwardGestureEvent(tap);
306 void SimulateKeyPress(WebContents* web_contents,
307 ui::KeyboardCode key_code,
312 SimulateKeyPressWithCode(
313 web_contents, key_code, NULL, control, shift, alt, command);
316 void SimulateKeyPressWithCode(WebContents* web_contents,
317 ui::KeyboardCode key_code,
323 ui::KeycodeConverter* key_converter = ui::KeycodeConverter::GetInstance();
324 int native_key_code = key_converter->CodeToNativeKeycode(code);
328 // The order of these key down events shouldn't matter for our simulation.
329 // For our simulation we can use either the left keys or the right keys.
331 modifiers |= blink::WebInputEvent::ControlKey;
334 blink::WebInputEvent::RawKeyDown,
336 key_converter->CodeToNativeKeycode("ControlLeft"),
341 modifiers |= blink::WebInputEvent::ShiftKey;
344 blink::WebInputEvent::RawKeyDown,
346 key_converter->CodeToNativeKeycode("ShiftLeft"),
351 modifiers |= blink::WebInputEvent::AltKey;
354 blink::WebInputEvent::RawKeyDown,
356 key_converter->CodeToNativeKeycode("AltLeft"),
361 modifiers |= blink::WebInputEvent::MetaKey;
364 blink::WebInputEvent::RawKeyDown,
366 key_converter->CodeToNativeKeycode("OSLeft"),
372 blink::WebInputEvent::RawKeyDown,
379 blink::WebInputEvent::Char,
386 blink::WebInputEvent::KeyUp,
391 // The order of these key releases shouldn't matter for our simulation.
393 modifiers &= ~blink::WebInputEvent::ControlKey;
396 blink::WebInputEvent::KeyUp,
398 key_converter->CodeToNativeKeycode("ControlLeft"),
403 modifiers &= ~blink::WebInputEvent::ShiftKey;
406 blink::WebInputEvent::KeyUp,
408 key_converter->CodeToNativeKeycode("ShiftLeft"),
413 modifiers &= ~blink::WebInputEvent::AltKey;
416 blink::WebInputEvent::KeyUp,
418 key_converter->CodeToNativeKeycode("AltLeft"),
423 modifiers &= ~blink::WebInputEvent::MetaKey;
426 blink::WebInputEvent::KeyUp,
428 key_converter->CodeToNativeKeycode("OSLeft"),
432 ASSERT_EQ(modifiers, 0);
437 ToRenderFrameHost::ToRenderFrameHost(WebContents* web_contents)
438 : render_frame_host_(web_contents->GetMainFrame()) {
441 ToRenderFrameHost::ToRenderFrameHost(RenderViewHost* render_view_host)
442 : render_frame_host_(render_view_host->GetMainFrame()) {
445 ToRenderFrameHost::ToRenderFrameHost(RenderFrameHost* render_frame_host)
446 : render_frame_host_(render_frame_host) {
449 } // namespace internal
451 bool ExecuteScript(const internal::ToRenderFrameHost& adapter,
452 const std::string& script) {
453 std::string new_script =
454 script + ";window.domAutomationController.send(0);";
455 return ExecuteScriptHelper(adapter.render_frame_host(), new_script, NULL);
458 bool ExecuteScriptAndExtractInt(const internal::ToRenderFrameHost& adapter,
459 const std::string& script, int* result) {
461 scoped_ptr<base::Value> value;
462 if (!ExecuteScriptHelper(adapter.render_frame_host(), script, &value) ||
467 return value->GetAsInteger(result);
470 bool ExecuteScriptAndExtractBool(const internal::ToRenderFrameHost& adapter,
471 const std::string& script, bool* result) {
473 scoped_ptr<base::Value> value;
474 if (!ExecuteScriptHelper(adapter.render_frame_host(), script, &value) ||
479 return value->GetAsBoolean(result);
482 bool ExecuteScriptAndExtractString(const internal::ToRenderFrameHost& adapter,
483 const std::string& script,
484 std::string* result) {
486 scoped_ptr<base::Value> value;
487 if (!ExecuteScriptHelper(adapter.render_frame_host(), script, &value) ||
492 return value->GetAsString(result);
496 void AddToSetIfFrameMatchesPredicate(
497 std::set<RenderFrameHost*>* frame_set,
498 const base::Callback<bool(RenderFrameHost*)>& predicate,
499 RenderFrameHost* host) {
500 if (predicate.Run(host))
501 frame_set->insert(host);
505 RenderFrameHost* FrameMatchingPredicate(
506 WebContents* web_contents,
507 const base::Callback<bool(RenderFrameHost*)>& predicate) {
508 std::set<RenderFrameHost*> frame_set;
509 web_contents->ForEachFrame(
510 base::Bind(&AddToSetIfFrameMatchesPredicate, &frame_set, predicate));
511 DCHECK_EQ(1U, frame_set.size());
512 return *frame_set.begin();
515 bool FrameMatchesName(const std::string& name, RenderFrameHost* frame) {
516 return frame->GetFrameName() == name;
519 bool FrameIsChildOfMainFrame(RenderFrameHost* frame) {
520 return frame->GetParent() && !frame->GetParent()->GetParent();
523 bool FrameHasSourceUrl(const GURL& url, RenderFrameHost* frame) {
524 return frame->GetLastCommittedURL() == url;
527 bool ExecuteWebUIResourceTest(WebContents* web_contents,
528 const std::vector<int>& js_resource_ids) {
529 // Inject WebUI test runner script first prior to other scripts required to
530 // run the test as scripts may depend on it being declared.
531 std::vector<int> ids;
532 ids.push_back(IDR_WEBUI_JS_WEBUI_RESOURCE_TEST);
533 ids.insert(ids.end(), js_resource_ids.begin(), js_resource_ids.end());
536 for (std::vector<int>::iterator iter = ids.begin();
539 ResourceBundle::GetSharedInstance().GetRawDataResource(*iter)
540 .AppendToString(&script);
543 if (!ExecuteScript(web_contents, script))
546 DOMMessageQueue message_queue;
547 if (!ExecuteScript(web_contents, "runTests()"))
552 if (!message_queue.WaitForMessage(&message))
554 } while (message.compare("\"PENDING\"") == 0);
556 return message.compare("\"SUCCESS\"") == 0;
559 std::string GetCookies(BrowserContext* browser_context, const GURL& url) {
561 base::WaitableEvent event(true, false);
562 net::URLRequestContextGetter* context_getter =
563 browser_context->GetRequestContext();
565 BrowserThread::PostTask(
566 BrowserThread::IO, FROM_HERE,
567 base::Bind(&GetCookiesOnIOThread, url,
568 make_scoped_refptr(context_getter), &event, &cookies));
573 bool SetCookie(BrowserContext* browser_context,
575 const std::string& value) {
577 base::WaitableEvent event(true, false);
578 net::URLRequestContextGetter* context_getter =
579 browser_context->GetRequestContext();
581 BrowserThread::PostTask(
582 BrowserThread::IO, FROM_HERE,
583 base::Bind(&SetCookieOnIOThread, url, value,
584 make_scoped_refptr(context_getter), &event, &result));
589 TitleWatcher::TitleWatcher(WebContents* web_contents,
590 const base::string16& expected_title)
591 : WebContentsObserver(web_contents),
592 message_loop_runner_(new MessageLoopRunner) {
593 EXPECT_TRUE(web_contents != NULL);
594 expected_titles_.push_back(expected_title);
597 void TitleWatcher::AlsoWaitForTitle(const base::string16& expected_title) {
598 expected_titles_.push_back(expected_title);
601 TitleWatcher::~TitleWatcher() {
604 const base::string16& TitleWatcher::WaitAndGetTitle() {
606 message_loop_runner_->Run();
607 return observed_title_;
610 void TitleWatcher::DidStopLoading(RenderViewHost* render_view_host) {
611 // When navigating through the history, the restored NavigationEntry's title
612 // will be used. If the entry ends up having the same title after we return
613 // to it, as will usually be the case, then WebContentsObserver::TitleSet
614 // will then be suppressed, since the NavigationEntry's title hasn't changed.
618 void TitleWatcher::TitleWasSet(NavigationEntry* entry, bool explicit_set) {
622 void TitleWatcher::TestTitle() {
623 std::vector<base::string16>::const_iterator it =
624 std::find(expected_titles_.begin(),
625 expected_titles_.end(),
626 web_contents()->GetTitle());
627 if (it == expected_titles_.end())
630 observed_title_ = *it;
631 message_loop_runner_->Quit();
634 WebContentsDestroyedWatcher::WebContentsDestroyedWatcher(
635 WebContents* web_contents)
636 : WebContentsObserver(web_contents),
637 message_loop_runner_(new MessageLoopRunner) {
638 EXPECT_TRUE(web_contents != NULL);
641 WebContentsDestroyedWatcher::~WebContentsDestroyedWatcher() {
644 void WebContentsDestroyedWatcher::Wait() {
645 message_loop_runner_->Run();
648 void WebContentsDestroyedWatcher::WebContentsDestroyed() {
649 message_loop_runner_->Quit();
652 RenderProcessHostWatcher::RenderProcessHostWatcher(
653 RenderProcessHost* render_process_host, WatchType type)
654 : render_process_host_(render_process_host),
656 message_loop_runner_(new MessageLoopRunner) {
657 render_process_host_->AddObserver(this);
660 RenderProcessHostWatcher::RenderProcessHostWatcher(
661 WebContents* web_contents, WatchType type)
662 : render_process_host_(web_contents->GetRenderProcessHost()),
664 message_loop_runner_(new MessageLoopRunner) {
665 render_process_host_->AddObserver(this);
668 RenderProcessHostWatcher::~RenderProcessHostWatcher() {
669 if (render_process_host_)
670 render_process_host_->RemoveObserver(this);
673 void RenderProcessHostWatcher::Wait() {
674 message_loop_runner_->Run();
677 void RenderProcessHostWatcher::RenderProcessExited(
678 RenderProcessHost* host,
679 base::ProcessHandle handle,
680 base::TerminationStatus status,
682 if (type_ == WATCH_FOR_PROCESS_EXIT)
683 message_loop_runner_->Quit();
686 void RenderProcessHostWatcher::RenderProcessHostDestroyed(
687 RenderProcessHost* host) {
688 render_process_host_ = NULL;
689 if (type_ == WATCH_FOR_HOST_DESTRUCTION)
690 message_loop_runner_->Quit();
693 DOMMessageQueue::DOMMessageQueue() {
694 registrar_.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE,
695 NotificationService::AllSources());
698 DOMMessageQueue::~DOMMessageQueue() {}
700 void DOMMessageQueue::Observe(int type,
701 const NotificationSource& source,
702 const NotificationDetails& details) {
703 Details<DomOperationNotificationDetails> dom_op_details(details);
704 message_queue_.push(dom_op_details->json);
705 if (message_loop_runner_)
706 message_loop_runner_->Quit();
709 void DOMMessageQueue::ClearQueue() {
710 message_queue_ = std::queue<std::string>();
713 bool DOMMessageQueue::WaitForMessage(std::string* message) {
715 if (message_queue_.empty()) {
716 // This will be quit when a new message comes in.
717 message_loop_runner_ = new MessageLoopRunner;
718 message_loop_runner_->Run();
720 // The queue should not be empty, unless we were quit because of a timeout.
721 if (message_queue_.empty())
723 *message = message_queue_.front();
724 message_queue_.pop();
728 } // namespace content