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/web_contents/web_contents_view.h"
18 #include "content/public/browser/browser_context.h"
19 #include "content/public/browser/dom_operation_notification_details.h"
20 #include "content/public/browser/notification_service.h"
21 #include "content/public/browser/notification_types.h"
22 #include "content/public/browser/render_frame_host.h"
23 #include "content/public/browser/render_process_host.h"
24 #include "content/public/browser/render_view_host.h"
25 #include "content/public/browser/web_contents.h"
26 #include "content/public/browser/web_contents_observer.h"
27 #include "content/public/test/test_utils.h"
28 #include "grit/webui_resources.h"
29 #include "net/base/filename_util.h"
30 #include "net/cookies/cookie_store.h"
31 #include "net/test/python_utils.h"
32 #include "net/url_request/url_request_context.h"
33 #include "net/url_request/url_request_context_getter.h"
34 #include "testing/gtest/include/gtest/gtest.h"
35 #include "ui/base/resource/resource_bundle.h"
36 #include "ui/events/keycodes/dom4/keycode_converter.h"
41 class DOMOperationObserver
: public NotificationObserver
,
42 public WebContentsObserver
{
44 explicit DOMOperationObserver(RenderViewHost
* rvh
)
45 : WebContentsObserver(WebContents::FromRenderViewHost(rvh
)),
47 registrar_
.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE
,
48 Source
<WebContents
>(web_contents()));
49 message_loop_runner_
= new MessageLoopRunner
;
52 virtual void Observe(int type
,
53 const NotificationSource
& source
,
54 const NotificationDetails
& details
) OVERRIDE
{
55 DCHECK(type
== NOTIFICATION_DOM_OPERATION_RESPONSE
);
56 Details
<DomOperationNotificationDetails
> dom_op_details(details
);
57 response_
= dom_op_details
->json
;
59 message_loop_runner_
->Quit();
62 // Overridden from WebContentsObserver:
63 virtual void RenderProcessGone(base::TerminationStatus status
) OVERRIDE
{
64 message_loop_runner_
->Quit();
67 bool WaitAndGetResponse(std::string
* response
) WARN_UNUSED_RESULT
{
68 message_loop_runner_
->Run();
69 *response
= response_
;
74 NotificationRegistrar registrar_
;
75 std::string response_
;
77 scoped_refptr
<MessageLoopRunner
> message_loop_runner_
;
79 DISALLOW_COPY_AND_ASSIGN(DOMOperationObserver
);
82 // Specifying a prototype so that we can add the WARN_UNUSED_RESULT attribute.
83 bool ExecuteScriptHelper(
84 RenderFrameHost
* render_frame_host
,
85 const std::string
& original_script
,
86 scoped_ptr
<base::Value
>* result
) WARN_UNUSED_RESULT
;
88 // Executes the passed |original_script| in the frame specified by
89 // |render_frame_host|. If |result| is not NULL, stores the value that the
90 // evaluation of the script in |result|. Returns true on success.
91 bool ExecuteScriptHelper(RenderFrameHost
* render_frame_host
,
92 const std::string
& original_script
,
93 scoped_ptr
<base::Value
>* result
) {
94 // TODO(jcampan): we should make the domAutomationController not require an
97 "window.domAutomationController.setAutomationId(0);" + original_script
;
98 DOMOperationObserver
dom_op_observer(render_frame_host
->GetRenderViewHost());
99 render_frame_host
->ExecuteJavaScript(base::UTF8ToUTF16(script
));
101 if (!dom_op_observer
.WaitAndGetResponse(&json
)) {
102 DLOG(ERROR
) << "Cannot communicate with DOMOperationObserver.";
106 // Nothing more to do for callers that ignore the returned JS value.
110 base::JSONReader
reader(base::JSON_ALLOW_TRAILING_COMMAS
);
111 result
->reset(reader
.ReadToValue(json
));
112 if (!result
->get()) {
113 DLOG(ERROR
) << reader
.GetErrorMessage();
120 void BuildSimpleWebKeyEvent(blink::WebInputEvent::Type type
,
121 ui::KeyboardCode key_code
,
124 NativeWebKeyboardEvent
* event
) {
125 event
->nativeKeyCode
= native_key_code
;
126 event
->windowsKeyCode
= key_code
;
127 event
->setKeyIdentifierFromWindowsKeyCode();
129 event
->modifiers
= modifiers
;
130 event
->isSystemKey
= false;
131 event
->timeStampSeconds
= base::Time::Now().ToDoubleT();
132 event
->skip_in_browser
= true;
134 if (type
== blink::WebInputEvent::Char
||
135 type
== blink::WebInputEvent::RawKeyDown
) {
136 event
->text
[0] = key_code
;
137 event
->unmodifiedText
[0] = key_code
;
141 void InjectRawKeyEvent(WebContents
* web_contents
,
142 blink::WebInputEvent::Type type
,
143 ui::KeyboardCode key_code
,
146 NativeWebKeyboardEvent event
;
147 BuildSimpleWebKeyEvent(type
, key_code
, native_key_code
, modifiers
, &event
);
148 web_contents
->GetRenderViewHost()->ForwardKeyboardEvent(event
);
151 void GetCookiesCallback(std::string
* cookies_out
,
152 base::WaitableEvent
* event
,
153 const std::string
& cookies
) {
154 *cookies_out
= cookies
;
158 void GetCookiesOnIOThread(const GURL
& url
,
159 net::URLRequestContextGetter
* context_getter
,
160 base::WaitableEvent
* event
,
161 std::string
* cookies
) {
162 net::CookieStore
* cookie_store
=
163 context_getter
->GetURLRequestContext()->cookie_store();
164 cookie_store
->GetCookiesWithOptionsAsync(
165 url
, net::CookieOptions(),
166 base::Bind(&GetCookiesCallback
, cookies
, event
));
169 void SetCookieCallback(bool* result
,
170 base::WaitableEvent
* event
,
176 void SetCookieOnIOThread(const GURL
& url
,
177 const std::string
& value
,
178 net::URLRequestContextGetter
* context_getter
,
179 base::WaitableEvent
* event
,
181 net::CookieStore
* cookie_store
=
182 context_getter
->GetURLRequestContext()->cookie_store();
183 cookie_store
->SetCookieWithOptionsAsync(
184 url
, value
, net::CookieOptions(),
185 base::Bind(&SetCookieCallback
, result
, event
));
191 GURL
GetFileUrlWithQuery(const base::FilePath
& path
,
192 const std::string
& query_string
) {
193 GURL url
= net::FilePathToFileURL(path
);
194 if (!query_string
.empty()) {
195 GURL::Replacements replacements
;
196 replacements
.SetQueryStr(query_string
);
197 return url
.ReplaceComponents(replacements
);
202 void WaitForLoadStop(WebContents
* web_contents
) {
203 WindowedNotificationObserver
load_stop_observer(
204 NOTIFICATION_LOAD_STOP
,
205 Source
<NavigationController
>(&web_contents
->GetController()));
206 // In many cases, the load may have finished before we get here. Only wait if
207 // the tab still has a pending navigation.
208 if (!web_contents
->IsLoading())
210 load_stop_observer
.Wait();
213 void CrashTab(WebContents
* web_contents
) {
214 RenderProcessHost
* rph
= web_contents
->GetRenderProcessHost();
215 RenderProcessHostWatcher
watcher(
216 rph
, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT
);
217 base::KillProcess(rph
->GetHandle(), 0, false);
221 void SimulateMouseClick(WebContents
* web_contents
,
223 blink::WebMouseEvent::Button button
) {
224 int x
= web_contents
->GetContainerBounds().width() / 2;
225 int y
= web_contents
->GetContainerBounds().height() / 2;
226 SimulateMouseClickAt(web_contents
, modifiers
, button
, gfx::Point(x
, y
));
229 void SimulateMouseClickAt(WebContents
* web_contents
,
231 blink::WebMouseEvent::Button button
,
232 const gfx::Point
& point
) {
233 blink::WebMouseEvent mouse_event
;
234 mouse_event
.type
= blink::WebInputEvent::MouseDown
;
235 mouse_event
.button
= button
;
236 mouse_event
.x
= point
.x();
237 mouse_event
.y
= point
.y();
238 mouse_event
.modifiers
= modifiers
;
239 // Mac needs globalX/globalY for events to plugins.
240 gfx::Rect offset
= web_contents
->GetContainerBounds();
241 mouse_event
.globalX
= point
.x() + offset
.x();
242 mouse_event
.globalY
= point
.y() + offset
.y();
243 mouse_event
.clickCount
= 1;
244 web_contents
->GetRenderViewHost()->ForwardMouseEvent(mouse_event
);
245 mouse_event
.type
= blink::WebInputEvent::MouseUp
;
246 web_contents
->GetRenderViewHost()->ForwardMouseEvent(mouse_event
);
249 void SimulateMouseEvent(WebContents
* web_contents
,
250 blink::WebInputEvent::Type type
,
251 const gfx::Point
& point
) {
252 blink::WebMouseEvent mouse_event
;
253 mouse_event
.type
= type
;
254 mouse_event
.x
= point
.x();
255 mouse_event
.y
= point
.y();
256 web_contents
->GetRenderViewHost()->ForwardMouseEvent(mouse_event
);
259 void SimulateKeyPress(WebContents
* web_contents
,
260 ui::KeyboardCode key_code
,
265 SimulateKeyPressWithCode(
266 web_contents
, key_code
, NULL
, control
, shift
, alt
, command
);
269 void SimulateKeyPressWithCode(WebContents
* web_contents
,
270 ui::KeyboardCode key_code
,
276 ui::KeycodeConverter
* key_converter
= ui::KeycodeConverter::GetInstance();
277 int native_key_code
= key_converter
->CodeToNativeKeycode(code
);
281 // The order of these key down events shouldn't matter for our simulation.
282 // For our simulation we can use either the left keys or the right keys.
284 modifiers
|= blink::WebInputEvent::ControlKey
;
287 blink::WebInputEvent::RawKeyDown
,
289 key_converter
->CodeToNativeKeycode("ControlLeft"),
294 modifiers
|= blink::WebInputEvent::ShiftKey
;
297 blink::WebInputEvent::RawKeyDown
,
299 key_converter
->CodeToNativeKeycode("ShiftLeft"),
304 modifiers
|= blink::WebInputEvent::AltKey
;
307 blink::WebInputEvent::RawKeyDown
,
309 key_converter
->CodeToNativeKeycode("AltLeft"),
314 modifiers
|= blink::WebInputEvent::MetaKey
;
317 blink::WebInputEvent::RawKeyDown
,
319 key_converter
->CodeToNativeKeycode("OSLeft"),
325 blink::WebInputEvent::RawKeyDown
,
332 blink::WebInputEvent::Char
,
339 blink::WebInputEvent::KeyUp
,
344 // The order of these key releases shouldn't matter for our simulation.
346 modifiers
&= ~blink::WebInputEvent::ControlKey
;
349 blink::WebInputEvent::KeyUp
,
351 key_converter
->CodeToNativeKeycode("ControlLeft"),
356 modifiers
&= ~blink::WebInputEvent::ShiftKey
;
359 blink::WebInputEvent::KeyUp
,
361 key_converter
->CodeToNativeKeycode("ShiftLeft"),
366 modifiers
&= ~blink::WebInputEvent::AltKey
;
369 blink::WebInputEvent::KeyUp
,
371 key_converter
->CodeToNativeKeycode("AltLeft"),
376 modifiers
&= ~blink::WebInputEvent::MetaKey
;
379 blink::WebInputEvent::KeyUp
,
381 key_converter
->CodeToNativeKeycode("OSLeft"),
385 ASSERT_EQ(modifiers
, 0);
390 ToRenderFrameHost::ToRenderFrameHost(WebContents
* web_contents
)
391 : render_frame_host_(web_contents
->GetMainFrame()) {
394 ToRenderFrameHost::ToRenderFrameHost(RenderViewHost
* render_view_host
)
395 : render_frame_host_(render_view_host
->GetMainFrame()) {
398 ToRenderFrameHost::ToRenderFrameHost(RenderFrameHost
* render_frame_host
)
399 : render_frame_host_(render_frame_host
) {
402 } // namespace internal
404 bool ExecuteScript(const internal::ToRenderFrameHost
& adapter
,
405 const std::string
& script
) {
406 std::string new_script
=
407 script
+ ";window.domAutomationController.send(0);";
408 return ExecuteScriptHelper(adapter
.render_frame_host(), new_script
, NULL
);
411 bool ExecuteScriptAndExtractInt(const internal::ToRenderFrameHost
& adapter
,
412 const std::string
& script
, int* result
) {
414 scoped_ptr
<base::Value
> value
;
415 if (!ExecuteScriptHelper(adapter
.render_frame_host(), script
, &value
) ||
420 return value
->GetAsInteger(result
);
423 bool ExecuteScriptAndExtractBool(const internal::ToRenderFrameHost
& adapter
,
424 const std::string
& script
, bool* result
) {
426 scoped_ptr
<base::Value
> value
;
427 if (!ExecuteScriptHelper(adapter
.render_frame_host(), script
, &value
) ||
432 return value
->GetAsBoolean(result
);
435 bool ExecuteScriptAndExtractString(const internal::ToRenderFrameHost
& adapter
,
436 const std::string
& script
,
437 std::string
* result
) {
439 scoped_ptr
<base::Value
> value
;
440 if (!ExecuteScriptHelper(adapter
.render_frame_host(), script
, &value
) ||
445 return value
->GetAsString(result
);
449 void AddToSetIfFrameMatchesPredicate(
450 std::set
<RenderFrameHost
*>* frame_set
,
451 const base::Callback
<bool(RenderFrameHost
*)>& predicate
,
452 RenderFrameHost
* host
) {
453 if (predicate
.Run(host
))
454 frame_set
->insert(host
);
458 RenderFrameHost
* FrameMatchingPredicate(
459 WebContents
* web_contents
,
460 const base::Callback
<bool(RenderFrameHost
*)>& predicate
) {
461 std::set
<RenderFrameHost
*> frame_set
;
462 web_contents
->ForEachFrame(
463 base::Bind(&AddToSetIfFrameMatchesPredicate
, &frame_set
, predicate
));
464 DCHECK_EQ(1U, frame_set
.size());
465 return *frame_set
.begin();
468 bool FrameMatchesName(const std::string
& name
, RenderFrameHost
* frame
) {
469 return frame
->GetFrameName() == name
;
472 bool FrameIsChildOfMainFrame(RenderFrameHost
* frame
) {
473 return frame
->GetParent() && !frame
->GetParent()->GetParent();
476 bool FrameHasSourceUrl(const GURL
& url
, RenderFrameHost
* frame
) {
477 return frame
->GetLastCommittedURL() == url
;
480 bool ExecuteWebUIResourceTest(WebContents
* web_contents
,
481 const std::vector
<int>& js_resource_ids
) {
482 // Inject WebUI test runner script first prior to other scripts required to
483 // run the test as scripts may depend on it being declared.
484 std::vector
<int> ids
;
485 ids
.push_back(IDR_WEBUI_JS_WEBUI_RESOURCE_TEST
);
486 ids
.insert(ids
.end(), js_resource_ids
.begin(), js_resource_ids
.end());
489 for (std::vector
<int>::iterator iter
= ids
.begin();
492 ResourceBundle::GetSharedInstance().GetRawDataResource(*iter
)
493 .AppendToString(&script
);
496 if (!ExecuteScript(web_contents
, script
))
499 DOMMessageQueue message_queue
;
500 if (!ExecuteScript(web_contents
, "runTests()"))
505 if (!message_queue
.WaitForMessage(&message
))
507 } while (message
.compare("\"PENDING\"") == 0);
509 return message
.compare("\"SUCCESS\"") == 0;
512 std::string
GetCookies(BrowserContext
* browser_context
, const GURL
& url
) {
514 base::WaitableEvent
event(true, false);
515 net::URLRequestContextGetter
* context_getter
=
516 browser_context
->GetRequestContext();
518 BrowserThread::PostTask(
519 BrowserThread::IO
, FROM_HERE
,
520 base::Bind(&GetCookiesOnIOThread
, url
,
521 make_scoped_refptr(context_getter
), &event
, &cookies
));
526 bool SetCookie(BrowserContext
* browser_context
,
528 const std::string
& value
) {
530 base::WaitableEvent
event(true, false);
531 net::URLRequestContextGetter
* context_getter
=
532 browser_context
->GetRequestContext();
534 BrowserThread::PostTask(
535 BrowserThread::IO
, FROM_HERE
,
536 base::Bind(&SetCookieOnIOThread
, url
, value
,
537 make_scoped_refptr(context_getter
), &event
, &result
));
542 TitleWatcher::TitleWatcher(WebContents
* web_contents
,
543 const base::string16
& expected_title
)
544 : WebContentsObserver(web_contents
),
545 message_loop_runner_(new MessageLoopRunner
) {
546 EXPECT_TRUE(web_contents
!= NULL
);
547 expected_titles_
.push_back(expected_title
);
550 void TitleWatcher::AlsoWaitForTitle(const base::string16
& expected_title
) {
551 expected_titles_
.push_back(expected_title
);
554 TitleWatcher::~TitleWatcher() {
557 const base::string16
& TitleWatcher::WaitAndGetTitle() {
559 message_loop_runner_
->Run();
560 return observed_title_
;
563 void TitleWatcher::DidStopLoading(RenderViewHost
* render_view_host
) {
564 // When navigating through the history, the restored NavigationEntry's title
565 // will be used. If the entry ends up having the same title after we return
566 // to it, as will usually be the case, then WebContentsObserver::TitleSet
567 // will then be suppressed, since the NavigationEntry's title hasn't changed.
571 void TitleWatcher::TitleWasSet(NavigationEntry
* entry
, bool explicit_set
) {
575 void TitleWatcher::TestTitle() {
576 std::vector
<base::string16
>::const_iterator it
=
577 std::find(expected_titles_
.begin(),
578 expected_titles_
.end(),
579 web_contents()->GetTitle());
580 if (it
== expected_titles_
.end())
583 observed_title_
= *it
;
584 message_loop_runner_
->Quit();
587 WebContentsDestroyedWatcher::WebContentsDestroyedWatcher(
588 WebContents
* web_contents
)
589 : WebContentsObserver(web_contents
),
590 message_loop_runner_(new MessageLoopRunner
) {
591 EXPECT_TRUE(web_contents
!= NULL
);
594 WebContentsDestroyedWatcher::~WebContentsDestroyedWatcher() {
597 void WebContentsDestroyedWatcher::Wait() {
598 message_loop_runner_
->Run();
601 void WebContentsDestroyedWatcher::WebContentsDestroyed(
602 WebContents
* web_contents
) {
603 message_loop_runner_
->Quit();
606 RenderProcessHostWatcher::RenderProcessHostWatcher(
607 RenderProcessHost
* render_process_host
, WatchType type
)
608 : render_process_host_(render_process_host
),
610 message_loop_runner_(new MessageLoopRunner
) {
611 render_process_host_
->AddObserver(this);
614 RenderProcessHostWatcher::RenderProcessHostWatcher(
615 WebContents
* web_contents
, WatchType type
)
616 : render_process_host_(web_contents
->GetRenderProcessHost()),
618 message_loop_runner_(new MessageLoopRunner
) {
619 render_process_host_
->AddObserver(this);
622 RenderProcessHostWatcher::~RenderProcessHostWatcher() {
623 if (render_process_host_
)
624 render_process_host_
->RemoveObserver(this);
627 void RenderProcessHostWatcher::Wait() {
628 message_loop_runner_
->Run();
631 void RenderProcessHostWatcher::RenderProcessExited(
632 RenderProcessHost
* host
,
633 base::ProcessHandle handle
,
634 base::TerminationStatus status
,
636 if (type_
== WATCH_FOR_PROCESS_EXIT
)
637 message_loop_runner_
->Quit();
640 void RenderProcessHostWatcher::RenderProcessHostDestroyed(
641 RenderProcessHost
* host
) {
642 render_process_host_
= NULL
;
643 if (type_
== WATCH_FOR_HOST_DESTRUCTION
)
644 message_loop_runner_
->Quit();
647 DOMMessageQueue::DOMMessageQueue() {
648 registrar_
.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE
,
649 NotificationService::AllSources());
652 DOMMessageQueue::~DOMMessageQueue() {}
654 void DOMMessageQueue::Observe(int type
,
655 const NotificationSource
& source
,
656 const NotificationDetails
& details
) {
657 Details
<DomOperationNotificationDetails
> dom_op_details(details
);
658 message_queue_
.push(dom_op_details
->json
);
659 if (message_loop_runner_
)
660 message_loop_runner_
->Quit();
663 void DOMMessageQueue::ClearQueue() {
664 message_queue_
= std::queue
<std::string
>();
667 bool DOMMessageQueue::WaitForMessage(std::string
* message
) {
669 if (message_queue_
.empty()) {
670 // This will be quit when a new message comes in.
671 message_loop_runner_
= new MessageLoopRunner
;
672 message_loop_runner_
->Run();
674 // The queue should not be empty, unless we were quit because of a timeout.
675 if (message_queue_
.empty())
677 *message
= message_queue_
.front();
678 message_queue_
.pop();
682 } // namespace content