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.
8 #include "base/memory/ref_counted.h"
9 #include "base/memory/weak_ptr.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "chrome/app/chrome_command_ids.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/chrome_browser_main.h"
16 #include "chrome/browser/chrome_browser_main_extra_parts.h"
17 #include "chrome/browser/chrome_content_browser_client.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/download/download_browsertest.h"
20 #include "chrome/browser/extensions/api/web_navigation/web_navigation_api.h"
21 #include "chrome/browser/extensions/extension_apitest.h"
22 #include "chrome/browser/extensions/extension_service.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
25 #include "chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.h"
26 #include "chrome/browser/ui/browser.h"
27 #include "chrome/browser/ui/tabs/tab_strip_model.h"
28 #include "chrome/test/base/ui_test_utils.h"
29 #include "content/public/browser/browser_thread.h"
30 #include "content/public/browser/render_frame_host.h"
31 #include "content/public/browser/render_process_host.h"
32 #include "content/public/browser/render_view_host.h"
33 #include "content/public/browser/resource_controller.h"
34 #include "content/public/browser/resource_dispatcher_host.h"
35 #include "content/public/browser/resource_throttle.h"
36 #include "content/public/browser/web_contents.h"
37 #include "content/public/common/context_menu_params.h"
38 #include "content/public/common/url_constants.h"
39 #include "content/public/test/browser_test_utils.h"
40 #include "extensions/browser/extension_system.h"
41 #include "extensions/common/switches.h"
42 #include "net/dns/mock_host_resolver.h"
43 #include "net/test/embedded_test_server/embedded_test_server.h"
44 #include "third_party/WebKit/public/web/WebContextMenuData.h"
45 #include "third_party/WebKit/public/web/WebInputEvent.h"
46 #include "webkit/common/resource_type.h"
48 using content::WebContents
;
50 namespace extensions
{
54 // This class can defer requests for arbitrary URLs.
55 class TestNavigationListener
56 : public base::RefCountedThreadSafe
<TestNavigationListener
> {
58 TestNavigationListener() {}
60 // Add |url| to the set of URLs we should delay.
61 void DelayRequestsForURL(const GURL
& url
) {
62 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
)) {
63 content::BrowserThread::PostTask(
64 content::BrowserThread::IO
,
66 base::Bind(&TestNavigationListener::DelayRequestsForURL
, this, url
));
69 urls_to_delay_
.insert(url
);
72 // Resume all deferred requests.
74 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
)) {
75 content::BrowserThread::PostTask(
76 content::BrowserThread::IO
,
78 base::Bind(&TestNavigationListener::ResumeAll
, this));
81 WeakThrottleList::const_iterator it
;
82 for (it
= throttles_
.begin(); it
!= throttles_
.end(); ++it
) {
89 // Constructs a ResourceThrottle if the request for |url| should be held.
91 // Needs to be invoked on the IO thread.
92 content::ResourceThrottle
* CreateResourceThrottle(
94 ResourceType::Type resource_type
) {
95 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
96 if (urls_to_delay_
.find(url
) == urls_to_delay_
.end())
99 Throttle
* throttle
= new Throttle();
100 throttles_
.push_back(throttle
->AsWeakPtr());
105 friend class base::RefCountedThreadSafe
<TestNavigationListener
>;
107 virtual ~TestNavigationListener() {}
109 // Stores a throttle per URL request that we have delayed.
110 class Throttle
: public content::ResourceThrottle
,
111 public base::SupportsWeakPtr
<Throttle
> {
114 controller()->Resume();
117 // content::ResourceThrottle implementation.
118 virtual void WillStartRequest(bool* defer
) OVERRIDE
{
122 virtual const char* GetNameForLogging() const OVERRIDE
{
123 return "TestNavigationListener::Throttle";
126 typedef base::WeakPtr
<Throttle
> WeakThrottle
;
127 typedef std::list
<WeakThrottle
> WeakThrottleList
;
128 WeakThrottleList throttles_
;
130 // The set of URLs to be delayed.
131 std::set
<GURL
> urls_to_delay_
;
133 DISALLOW_COPY_AND_ASSIGN(TestNavigationListener
);
136 // Waits for a WC to be created. Once it starts loading |delay_url| (after at
137 // least the first navigation has committed), it delays the load, executes
138 // |script| in the last committed RVH and resumes the load when a URL ending in
139 // |until_url_suffix| commits. This class expects |script| to trigger the load
140 // of an URL ending in |until_url_suffix|.
141 class DelayLoadStartAndExecuteJavascript
142 : public content::NotificationObserver
,
143 public content::WebContentsObserver
{
145 DelayLoadStartAndExecuteJavascript(
146 TestNavigationListener
* test_navigation_listener
,
147 const GURL
& delay_url
,
148 const std::string
& script
,
149 const std::string
& until_url_suffix
)
150 : content::WebContentsObserver(),
151 test_navigation_listener_(test_navigation_listener
),
152 delay_url_(delay_url
),
153 until_url_suffix_(until_url_suffix
),
155 script_was_executed_(false),
158 chrome::NOTIFICATION_TAB_ADDED
,
159 content::NotificationService::AllSources());
160 test_navigation_listener_
->DelayRequestsForURL(delay_url_
);
162 virtual ~DelayLoadStartAndExecuteJavascript() {}
164 virtual void Observe(int type
,
165 const content::NotificationSource
& source
,
166 const content::NotificationDetails
& details
) OVERRIDE
{
167 if (type
!= chrome::NOTIFICATION_TAB_ADDED
) {
171 content::WebContentsObserver::Observe(
172 content::Details
<content::WebContents
>(details
).ptr());
173 registrar_
.RemoveAll();
176 virtual void DidStartProvisionalLoadForFrame(
178 int64 parent_frame_id
,
180 const GURL
& validated_url
,
182 bool is_iframe_srcdoc
,
183 content::RenderViewHost
* render_view_host
) OVERRIDE
{
184 if (validated_url
!= delay_url_
|| !rvh_
)
187 rvh_
->GetMainFrame()->ExecuteJavaScript(base::UTF8ToUTF16(script_
));
188 script_was_executed_
= true;
191 virtual void DidCommitProvisionalLoadForFrame(
193 const base::string16
& frame_unique_name
,
196 content::PageTransition transition_type
,
197 content::RenderViewHost
* render_view_host
) OVERRIDE
{
198 if (script_was_executed_
&& EndsWith(url
.spec(), until_url_suffix_
, true)) {
199 content::WebContentsObserver::Observe(NULL
);
200 test_navigation_listener_
->ResumeAll();
202 rvh_
= render_view_host
;
206 content::NotificationRegistrar registrar_
;
208 scoped_refptr
<TestNavigationListener
> test_navigation_listener_
;
211 std::string until_url_suffix_
;
213 bool script_was_executed_
;
214 content::RenderViewHost
* rvh_
;
216 DISALLOW_COPY_AND_ASSIGN(DelayLoadStartAndExecuteJavascript
);
219 // A ResourceDispatcherHostDelegate that adds a TestNavigationObserver.
220 class TestResourceDispatcherHostDelegate
221 : public ChromeResourceDispatcherHostDelegate
{
223 TestResourceDispatcherHostDelegate(
224 prerender::PrerenderTracker
* prerender_tracker
,
225 TestNavigationListener
* test_navigation_listener
)
226 : ChromeResourceDispatcherHostDelegate(prerender_tracker
),
227 test_navigation_listener_(test_navigation_listener
) {
229 virtual ~TestResourceDispatcherHostDelegate() {}
231 virtual void RequestBeginning(
232 net::URLRequest
* request
,
233 content::ResourceContext
* resource_context
,
234 appcache::AppCacheService
* appcache_service
,
235 ResourceType::Type resource_type
,
238 ScopedVector
<content::ResourceThrottle
>* throttles
) OVERRIDE
{
239 ChromeResourceDispatcherHostDelegate::RequestBeginning(
247 content::ResourceThrottle
* throttle
=
248 test_navigation_listener_
->CreateResourceThrottle(request
->url(),
251 throttles
->push_back(throttle
);
255 scoped_refptr
<TestNavigationListener
> test_navigation_listener_
;
257 DISALLOW_COPY_AND_ASSIGN(TestResourceDispatcherHostDelegate
);
262 class WebNavigationApiTest
: public ExtensionApiTest
{
264 WebNavigationApiTest() {}
265 virtual ~WebNavigationApiTest() {}
267 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE
{
268 ExtensionApiTest::SetUpInProcessBrowserTestFixture();
270 FrameNavigationState::set_allow_extension_scheme(true);
272 CommandLine::ForCurrentProcess()->AppendSwitch(
273 switches::kAllowLegacyExtensionManifests
);
275 host_resolver()->AddRule("*", "127.0.0.1");
278 virtual void SetUpOnMainThread() OVERRIDE
{
279 ExtensionApiTest::SetUpOnMainThread();
280 test_navigation_listener_
= new TestNavigationListener();
281 resource_dispatcher_host_delegate_
.reset(
282 new TestResourceDispatcherHostDelegate(
283 g_browser_process
->prerender_tracker(),
284 test_navigation_listener_
.get()));
285 content::ResourceDispatcherHost::Get()->SetDelegate(
286 resource_dispatcher_host_delegate_
.get());
289 TestNavigationListener
* test_navigation_listener() {
290 return test_navigation_listener_
.get();
294 scoped_refptr
<TestNavigationListener
> test_navigation_listener_
;
295 scoped_ptr
<TestResourceDispatcherHostDelegate
>
296 resource_dispatcher_host_delegate_
;
298 DISALLOW_COPY_AND_ASSIGN(WebNavigationApiTest
);
301 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, Api
) {
302 ASSERT_TRUE(StartEmbeddedTestServer());
303 ASSERT_TRUE(RunExtensionTest("webnavigation/api")) << message_
;
306 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, GetFrame
) {
307 ASSERT_TRUE(StartEmbeddedTestServer());
308 ASSERT_TRUE(RunExtensionTest("webnavigation/getFrame")) << message_
;
311 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, ClientRedirect
) {
312 ASSERT_TRUE(StartEmbeddedTestServer());
313 ASSERT_TRUE(RunExtensionTest("webnavigation/clientRedirect"))
317 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, ServerRedirect
) {
318 ASSERT_TRUE(StartEmbeddedTestServer());
319 ASSERT_TRUE(RunExtensionTest("webnavigation/serverRedirect"))
323 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, Download
) {
324 DownloadTestObserverNotInProgress
download_observer(
325 content::BrowserContext::GetDownloadManager(profile()), 1);
326 download_observer
.StartObserving();
327 ASSERT_TRUE(StartEmbeddedTestServer());
328 ASSERT_TRUE(RunExtensionTest("webnavigation/download"))
330 download_observer
.WaitForFinished();
333 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, ServerRedirectSingleProcess
) {
334 ASSERT_TRUE(StartEmbeddedTestServer());
336 // Set max renderers to 1 to force running out of processes.
337 content::RenderProcessHost::SetMaxRendererProcessCount(1);
339 // Wait for the extension to set itself up and return control to us.
341 RunExtensionTest("webnavigation/serverRedirectSingleProcess"))
344 WebContents
* tab
= browser()->tab_strip_model()->GetActiveWebContents();
345 content::WaitForLoadStop(tab
);
347 ResultCatcher catcher
;
348 GURL
url(base::StringPrintf(
349 "http://www.a.com:%d/"
350 "extensions/api_test/webnavigation/serverRedirectSingleProcess/a.html",
351 embedded_test_server()->port()));
353 ui_test_utils::NavigateToURL(browser(), url
);
355 url
= GURL(base::StringPrintf(
356 "http://www.b.com:%d/server-redirect?http://www.b.com:%d/",
357 embedded_test_server()->port(),
358 embedded_test_server()->port()));
360 ui_test_utils::NavigateToURL(browser(), url
);
362 ASSERT_TRUE(catcher
.GetNextResult()) << catcher
.message();
365 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, ForwardBack
) {
366 ASSERT_TRUE(StartEmbeddedTestServer());
367 ASSERT_TRUE(RunExtensionTest("webnavigation/forwardBack")) << message_
;
370 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, IFrame
) {
371 ASSERT_TRUE(StartEmbeddedTestServer());
372 ASSERT_TRUE(RunExtensionTest("webnavigation/iframe")) << message_
;
375 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, SrcDoc
) {
376 ASSERT_TRUE(StartEmbeddedTestServer());
377 ASSERT_TRUE(RunExtensionTest("webnavigation/srcdoc")) << message_
;
380 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, OpenTab
) {
381 ASSERT_TRUE(StartEmbeddedTestServer());
382 ASSERT_TRUE(RunExtensionTest("webnavigation/openTab")) << message_
;
385 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, ReferenceFragment
) {
386 ASSERT_TRUE(StartEmbeddedTestServer());
387 ASSERT_TRUE(RunExtensionTest("webnavigation/referenceFragment"))
391 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, SimpleLoad
) {
392 ASSERT_TRUE(StartEmbeddedTestServer());
393 ASSERT_TRUE(RunExtensionTest("webnavigation/simpleLoad")) << message_
;
396 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, Failures
) {
397 ASSERT_TRUE(StartEmbeddedTestServer());
398 ASSERT_TRUE(RunExtensionTest("webnavigation/failures")) << message_
;
401 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, FilteredTest
) {
402 ASSERT_TRUE(StartEmbeddedTestServer());
403 ASSERT_TRUE(RunExtensionTest("webnavigation/filtered")) << message_
;
406 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, UserAction
) {
407 ASSERT_TRUE(StartEmbeddedTestServer());
409 // Wait for the extension to set itself up and return control to us.
410 ASSERT_TRUE(RunExtensionTest("webnavigation/userAction")) << message_
;
412 WebContents
* tab
= browser()->tab_strip_model()->GetActiveWebContents();
413 content::WaitForLoadStop(tab
);
415 ResultCatcher catcher
;
417 ExtensionService
* service
= extensions::ExtensionSystem::Get(
418 browser()->profile())->extension_service();
419 const extensions::Extension
* extension
=
420 service
->GetExtensionById(last_loaded_extension_id(), false);
421 GURL url
= extension
->GetResourceURL("a.html");
423 ui_test_utils::NavigateToURL(browser(), url
);
425 // This corresponds to "Open link in new tab".
426 content::ContextMenuParams params
;
427 params
.is_editable
= false;
428 params
.media_type
= blink::WebContextMenuData::MediaTypeNone
;
429 params
.page_url
= url
;
430 params
.link_url
= extension
->GetResourceURL("b.html");
432 TestRenderViewContextMenu
menu(tab
->GetMainFrame(), params
);
434 menu
.ExecuteCommand(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB
, 0);
436 ASSERT_TRUE(catcher
.GetNextResult()) << catcher
.message();
439 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, RequestOpenTab
) {
440 ASSERT_TRUE(StartEmbeddedTestServer());
442 // Wait for the extension to set itself up and return control to us.
443 ASSERT_TRUE(RunExtensionTest("webnavigation/requestOpenTab"))
446 WebContents
* tab
= browser()->tab_strip_model()->GetActiveWebContents();
447 content::WaitForLoadStop(tab
);
449 ResultCatcher catcher
;
451 ExtensionService
* service
= extensions::ExtensionSystem::Get(
452 browser()->profile())->extension_service();
453 const extensions::Extension
* extension
=
454 service
->GetExtensionById(last_loaded_extension_id(), false);
455 GURL url
= extension
->GetResourceURL("a.html");
457 ui_test_utils::NavigateToURL(browser(), url
);
459 // There's a link on a.html. Middle-click on it to open it in a new tab.
460 blink::WebMouseEvent mouse_event
;
461 mouse_event
.type
= blink::WebInputEvent::MouseDown
;
462 mouse_event
.button
= blink::WebMouseEvent::ButtonMiddle
;
465 mouse_event
.clickCount
= 1;
466 tab
->GetRenderViewHost()->ForwardMouseEvent(mouse_event
);
467 mouse_event
.type
= blink::WebInputEvent::MouseUp
;
468 tab
->GetRenderViewHost()->ForwardMouseEvent(mouse_event
);
470 ASSERT_TRUE(catcher
.GetNextResult()) << catcher
.message();
473 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, TargetBlank
) {
474 ASSERT_TRUE(StartEmbeddedTestServer());
476 // Wait for the extension to set itself up and return control to us.
477 ASSERT_TRUE(RunExtensionTest("webnavigation/targetBlank")) << message_
;
479 WebContents
* tab
= browser()->tab_strip_model()->GetActiveWebContents();
480 content::WaitForLoadStop(tab
);
482 ResultCatcher catcher
;
484 GURL url
= embedded_test_server()->GetURL(
485 "/extensions/api_test/webnavigation/targetBlank/a.html");
487 chrome::NavigateParams
params(browser(), url
, content::PAGE_TRANSITION_LINK
);
488 ui_test_utils::NavigateToURL(¶ms
);
490 // There's a link with target=_blank on a.html. Click on it to open it in a
492 blink::WebMouseEvent mouse_event
;
493 mouse_event
.type
= blink::WebInputEvent::MouseDown
;
494 mouse_event
.button
= blink::WebMouseEvent::ButtonLeft
;
497 mouse_event
.clickCount
= 1;
498 tab
->GetRenderViewHost()->ForwardMouseEvent(mouse_event
);
499 mouse_event
.type
= blink::WebInputEvent::MouseUp
;
500 tab
->GetRenderViewHost()->ForwardMouseEvent(mouse_event
);
502 ASSERT_TRUE(catcher
.GetNextResult()) << catcher
.message();
505 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, TargetBlankIncognito
) {
506 ASSERT_TRUE(StartEmbeddedTestServer());
508 // Wait for the extension to set itself up and return control to us.
509 ASSERT_TRUE(RunExtensionTestIncognito("webnavigation/targetBlank"))
512 ResultCatcher catcher
;
514 GURL url
= embedded_test_server()->GetURL(
515 "/extensions/api_test/webnavigation/targetBlank/a.html");
517 Browser
* otr_browser
= ui_test_utils::OpenURLOffTheRecord(
518 browser()->profile(), url
);
519 WebContents
* tab
= otr_browser
->tab_strip_model()->GetActiveWebContents();
521 // There's a link with target=_blank on a.html. Click on it to open it in a
523 blink::WebMouseEvent mouse_event
;
524 mouse_event
.type
= blink::WebInputEvent::MouseDown
;
525 mouse_event
.button
= blink::WebMouseEvent::ButtonLeft
;
528 mouse_event
.clickCount
= 1;
529 tab
->GetRenderViewHost()->ForwardMouseEvent(mouse_event
);
530 mouse_event
.type
= blink::WebInputEvent::MouseUp
;
531 tab
->GetRenderViewHost()->ForwardMouseEvent(mouse_event
);
533 ASSERT_TRUE(catcher
.GetNextResult()) << catcher
.message();
536 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, History
) {
537 ASSERT_TRUE(StartEmbeddedTestServer());
538 ASSERT_TRUE(RunExtensionTest("webnavigation/history")) << message_
;
541 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, CrossProcess
) {
542 ASSERT_TRUE(StartEmbeddedTestServer());
544 LoadExtension(test_data_dir_
.AppendASCII("webnavigation").AppendASCII("app"));
546 // See crossProcess/d.html.
547 DelayLoadStartAndExecuteJavascript
call_script(
548 test_navigation_listener(),
549 embedded_test_server()->GetURL("/test1"),
553 ASSERT_TRUE(RunExtensionTest("webnavigation/crossProcess")) << message_
;
556 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, CrossProcessFragment
) {
557 ASSERT_TRUE(StartEmbeddedTestServer());
559 // See crossProcessFragment/f.html.
560 DelayLoadStartAndExecuteJavascript
call_script3(
561 test_navigation_listener(),
562 embedded_test_server()->GetURL("/test3"),
564 base::StringPrintf("f.html?%d#foo", embedded_test_server()->port()));
566 // See crossProcessFragment/g.html.
567 DelayLoadStartAndExecuteJavascript
call_script4(
568 test_navigation_listener(),
569 embedded_test_server()->GetURL("/test4"),
571 base::StringPrintf("g.html?%d#foo", embedded_test_server()->port()));
573 ASSERT_TRUE(RunExtensionTest("webnavigation/crossProcessFragment"))
577 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, CrossProcessHistory
) {
578 ASSERT_TRUE(StartEmbeddedTestServer());
580 // See crossProcessHistory/e.html.
581 DelayLoadStartAndExecuteJavascript
call_script2(
582 test_navigation_listener(),
583 embedded_test_server()->GetURL("/test2"),
587 // See crossProcessHistory/h.html.
588 DelayLoadStartAndExecuteJavascript
call_script5(
589 test_navigation_listener(),
590 embedded_test_server()->GetURL("/test5"),
594 // See crossProcessHistory/i.html.
595 DelayLoadStartAndExecuteJavascript
call_script6(
596 test_navigation_listener(),
597 embedded_test_server()->GetURL("/test6"),
601 ASSERT_TRUE(RunExtensionTest("webnavigation/crossProcessHistory"))
605 // TODO(jam): http://crbug.com/350550
606 #if !(defined(OS_CHROMEOS) && defined(ADDRESS_SANITIZER))
607 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest
, Crash
) {
608 ASSERT_TRUE(StartEmbeddedTestServer());
610 // Wait for the extension to set itself up and return control to us.
611 ASSERT_TRUE(RunExtensionTest("webnavigation/crash")) << message_
;
613 WebContents
* tab
= browser()->tab_strip_model()->GetActiveWebContents();
614 content::WaitForLoadStop(tab
);
616 ResultCatcher catcher
;
618 GURL
url(base::StringPrintf(
619 "http://www.a.com:%d/"
620 "extensions/api_test/webnavigation/crash/a.html",
621 embedded_test_server()->port()));
622 ui_test_utils::NavigateToURL(browser(), url
);
624 ui_test_utils::NavigateToURL(browser(), GURL(content::kChromeUICrashURL
));
626 url
= GURL(base::StringPrintf(
627 "http://www.a.com:%d/"
628 "extensions/api_test/webnavigation/crash/b.html",
629 embedded_test_server()->port()));
630 ui_test_utils::NavigateToURL(browser(), url
);
632 ASSERT_TRUE(catcher
.GetNextResult()) << catcher
.message();
637 } // namespace extensions