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 "base/command_line.h"
6 #include "base/message_loop/message_loop.h"
7 #include "base/prefs/pref_service.h"
8 #include "chrome/browser/download/download_prefs.h"
9 #include "chrome/browser/extensions/extension_apitest.h"
10 #include "chrome/browser/extensions/extension_system.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/tabs/tab_strip_model.h"
14 #include "chrome/common/extensions/mime_types_handler.h"
15 #include "chrome/common/pref_names.h"
16 #include "chrome/test/base/test_switches.h"
17 #include "chrome/test/base/ui_test_utils.h"
18 #include "content/public/browser/download_item.h"
19 #include "content/public/browser/download_manager.h"
20 #include "content/public/browser/render_process_host.h"
21 #include "content/public/browser/resource_controller.h"
22 #include "content/public/browser/web_contents.h"
23 #include "content/public/test/download_test_observer.h"
24 #include "extensions/browser/event_router.h"
25 #include "net/test/embedded_test_server/embedded_test_server.h"
26 #include "net/test/embedded_test_server/http_request.h"
27 #include "net/test/embedded_test_server/http_response.h"
28 #include "testing/gmock/include/gmock/gmock.h"
30 using content::BrowserContext
;
31 using content::BrowserThread
;
32 using content::DownloadItem
;
33 using content::DownloadManager
;
34 using content::DownloadUrlParameters
;
35 using content::ResourceController
;
36 using content::WebContents
;
37 using extensions::Event
;
38 using extensions::ExtensionSystem
;
39 using net::test_server::BasicHttpResponse
;
40 using net::test_server::HttpRequest
;
41 using net::test_server::HttpResponse
;
42 using net::test_server::EmbeddedTestServer
;
47 // Test server's request handler.
48 // Returns response that should be sent by the test server.
49 scoped_ptr
<HttpResponse
> HandleRequest(const HttpRequest
& request
) {
50 scoped_ptr
<BasicHttpResponse
> response(new BasicHttpResponse());
52 // For relative path "/doc_path.doc", return success response with MIME type
53 // "application/msword".
54 if (request
.relative_url
== "/doc_path.doc") {
55 response
->set_code(net::HTTP_OK
);
56 response
->set_content_type("application/msword");
57 return response
.PassAs
<HttpResponse
>();
60 // For relative path "/test_path_attch.txt", return success response with
61 // MIME type "plain/text" and content "txt content". Also, set content
62 // disposition to be attachment.
63 if (request
.relative_url
== "/text_path_attch.txt") {
64 response
->set_code(net::HTTP_OK
);
65 response
->set_content("txt content");
66 response
->set_content_type("plain/text");
67 response
->AddCustomHeader("Content-Disposition",
68 "attachment; filename=test_path.txt");
69 return response
.PassAs
<HttpResponse
>();
71 // For relative path "/test_path_attch.txt", return success response with
72 // MIME type "plain/text" and content "txt content".
73 if (request
.relative_url
== "/text_path.txt") {
74 response
->set_code(net::HTTP_OK
);
75 response
->set_content("txt content");
76 response
->set_content_type("plain/text");
77 return response
.PassAs
<HttpResponse
>();
80 // No other requests should be handled in the tests.
81 EXPECT_TRUE(false) << "NOTREACHED!";
82 response
->set_code(net::HTTP_NOT_FOUND
);
83 return response
.PassAs
<HttpResponse
>();
86 // Tests to verify that resources are correctly intercepted by
87 // StreamsResourceThrottle.
88 // The test extension expects the resources that should be handed to the
89 // extension to have MIME type 'application/msword' and the resources that
90 // should be downloaded by the browser to have MIME type 'plain/text'.
91 class StreamsPrivateApiTest
: public ExtensionApiTest
{
93 StreamsPrivateApiTest() {}
95 virtual ~StreamsPrivateApiTest() {}
97 virtual void SetUpOnMainThread() OVERRIDE
{
99 test_server_
.reset(new EmbeddedTestServer
);
100 ASSERT_TRUE(test_server_
->InitializeAndWaitUntilReady());
101 test_server_
->RegisterRequestHandler(base::Bind(&HandleRequest
));
103 ExtensionApiTest::SetUpOnMainThread();
106 virtual void CleanUpOnMainThread() OVERRIDE
{
107 // Tear down the test server.
108 EXPECT_TRUE(test_server_
->ShutdownAndWaitUntilComplete());
109 test_server_
.reset();
110 ExtensionApiTest::CleanUpOnMainThread();
113 void InitializeDownloadSettings() {
114 ASSERT_TRUE(browser());
115 ASSERT_TRUE(downloads_dir_
.CreateUniqueTempDir());
117 // Setup default downloads directory to the scoped tmp directory created for
119 browser()->profile()->GetPrefs()->SetFilePath(
120 prefs::kDownloadDefaultDirectory
, downloads_dir_
.path());
121 // Ensure there are no prompts for download during the test.
122 browser()->profile()->GetPrefs()->SetBoolean(
123 prefs::kPromptForDownload
, false);
125 DownloadManager
* manager
= GetDownloadManager();
126 DownloadPrefs::FromDownloadManager(manager
)->ResetAutoOpen();
127 manager
->RemoveAllDownloads();
130 // Sends onExecuteContentHandler event with the MIME type "test/done" to the
132 // The test extension calls 'chrome.test.notifySuccess' when it receives the
133 // event with the "test/done" MIME type (unless the 'chrome.test.notifyFail'
134 // has already been called).
135 void SendDoneEvent() {
136 scoped_ptr
<base::ListValue
> event_args(new base::ListValue());
137 event_args
->Append(new base::StringValue("test/done"));
138 event_args
->Append(new base::StringValue("http://foo"));
139 event_args
->Append(new base::StringValue("blob://bar"));
140 event_args
->Append(new base::FundamentalValue(10));
141 event_args
->Append(new base::FundamentalValue(20));
143 scoped_ptr
<Event
> event(new Event(
144 "streamsPrivate.onExecuteMimeTypeHandler", event_args
.Pass()));
146 ExtensionSystem::Get(browser()->profile())->event_router()->
147 DispatchEventToExtension(test_extension_id_
, event
.Pass());
150 // Loads the test extension and set's up its file_browser_handler to handle
151 // 'application/msword' and 'plain/text' MIME types.
152 // The extension will notify success when it detects an event with the MIME
153 // type 'application/msword' and notify fail when it detects an event with the
154 // MIME type 'plain/text'.
155 const extensions::Extension
* LoadTestExtension() {
156 // The test extension id is set by the key value in the manifest.
157 test_extension_id_
= "oickdpebdnfbgkcaoklfcdhjniefkcji";
159 const extensions::Extension
* extension
= LoadExtension(
160 test_data_dir_
.AppendASCII("streams_private/handle_mime_type"));
164 MimeTypesHandler
* handler
= MimeTypesHandler::GetHandler(extension
);
166 message_
= "No mime type handlers defined.";
170 DCHECK_EQ(test_extension_id_
, extension
->id());
175 // Returns the download manager for the current browser.
176 DownloadManager
* GetDownloadManager() const {
177 DownloadManager
* download_manager
=
178 BrowserContext::GetDownloadManager(browser()->profile());
179 EXPECT_TRUE(download_manager
);
180 return download_manager
;
183 // Deletes the download and waits until it's flushed.
184 // The |manager| should have |download| in its list of downloads.
185 void DeleteDownloadAndWaitForFlush(DownloadItem
* download
,
186 DownloadManager
* manager
) {
187 scoped_refptr
<content::DownloadTestFlushObserver
> flush_observer(
188 new content::DownloadTestFlushObserver(manager
));
190 flush_observer
->WaitForFlush();
194 std::string test_extension_id_
;
195 // The HTTP server used in the tests.
196 scoped_ptr
<EmbeddedTestServer
> test_server_
;
197 base::ScopedTempDir downloads_dir_
;
200 // Tests that navigating to a resource with a MIME type handleable by an
201 // installed, white-listed extension invokes the extension's
202 // onExecuteContentHandler event (and does not start a download).
203 IN_PROC_BROWSER_TEST_F(StreamsPrivateApiTest
, Navigate
) {
204 #if defined(OS_WIN) && defined(USE_ASH)
205 // Disable this test in Metro+Ash for now (http://crbug.com/262796).
206 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests
))
210 ASSERT_TRUE(LoadTestExtension()) << message_
;
212 ResultCatcher catcher
;
214 ui_test_utils::NavigateToURL(browser(),
215 test_server_
->GetURL("/doc_path.doc"));
217 // Wait for the response from the test server.
218 base::MessageLoop::current()->RunUntilIdle();
220 // There should be no downloads started by the navigation.
221 DownloadManager
* download_manager
= GetDownloadManager();
222 std::vector
<DownloadItem
*> downloads
;
223 download_manager
->GetAllDownloads(&downloads
);
224 ASSERT_EQ(0u, downloads
.size());
226 // The test extension should receive onExecuteContentHandler event with MIME
227 // type 'application/msword' (and call chrome.test.notifySuccess).
228 EXPECT_TRUE(catcher
.GetNextResult());
231 // Tests that navigation to an attachment starts a download, even if there is an
232 // extension with a file browser handler that can handle the attachment's MIME
234 IN_PROC_BROWSER_TEST_F(StreamsPrivateApiTest
, NavigateToAnAttachment
) {
235 InitializeDownloadSettings();
237 ASSERT_TRUE(LoadTestExtension()) << message_
;
239 ResultCatcher catcher
;
241 // The test should start a downloadm.
242 DownloadManager
* download_manager
= GetDownloadManager();
243 scoped_ptr
<content::DownloadTestObserver
> download_observer(
244 new content::DownloadTestObserverInProgress(download_manager
, 1));
246 ui_test_utils::NavigateToURL(browser(),
247 test_server_
->GetURL("/text_path_attch.txt"));
249 // Wait for the download to start.
250 download_observer
->WaitForFinished();
252 // There should be one download started by the navigation.
253 DownloadManager::DownloadVector downloads
;
254 download_manager
->GetAllDownloads(&downloads
);
255 ASSERT_EQ(1u, downloads
.size());
257 // Cancel and delete the download started in the test.
258 DeleteDownloadAndWaitForFlush(downloads
[0], download_manager
);
260 // The test extension should not receive any events by now. Send it an event
261 // with MIME type "test/done", so it stops waiting for the events. (If there
262 // was an event with MIME type 'plain/text', |catcher.GetNextResult()| will
263 // fail regardless of the sent event; chrome.test.notifySuccess will not be
264 // called by the extension).
266 EXPECT_TRUE(catcher
.GetNextResult());
269 // Tests that direct download requests don't get intercepted by
270 // StreamsResourceThrottle, even if there is an extension with a file
271 // browser handler that can handle the download's MIME type.
272 IN_PROC_BROWSER_TEST_F(StreamsPrivateApiTest
, DirectDownload
) {
273 InitializeDownloadSettings();
275 ASSERT_TRUE(LoadTestExtension()) << message_
;
277 ResultCatcher catcher
;
279 DownloadManager
* download_manager
= GetDownloadManager();
280 scoped_ptr
<content::DownloadTestObserver
> download_observer(
281 new content::DownloadTestObserverInProgress(download_manager
, 1));
283 // The resource's URL on the test server.
284 GURL url
= test_server_
->GetURL("/text_path.txt");
286 // The download's target file path.
287 base::FilePath target_path
=
288 downloads_dir_
.path().Append(FILE_PATH_LITERAL("download_target.txt"));
290 // Set the downloads parameters.
291 content::WebContents
* web_contents
=
292 browser()->tab_strip_model()->GetActiveWebContents();
293 ASSERT_TRUE(web_contents
);
294 scoped_ptr
<DownloadUrlParameters
> params(
295 DownloadUrlParameters::FromWebContents(web_contents
, url
));
296 params
->set_file_path(target_path
);
298 // Start download of the URL with a path "/text_path.txt" on the test server.
299 download_manager
->DownloadUrl(params
.Pass());
301 // Wait for the download to start.
302 download_observer
->WaitForFinished();
304 // There should have been one download.
305 std::vector
<DownloadItem
*> downloads
;
306 download_manager
->GetAllDownloads(&downloads
);
307 ASSERT_EQ(1u, downloads
.size());
309 // Cancel and delete the download statred in the test.
310 DeleteDownloadAndWaitForFlush(downloads
[0], download_manager
);
312 // The test extension should not receive any events by now. Send it an event
313 // with MIME type "test/done", so it stops waiting for the events. (If there
314 // was an event with MIME type 'plain/text', |catcher.GetNextResult()| will
315 // fail regardless of the sent event; chrome.test.notifySuccess will not be
316 // called by the extension).
318 EXPECT_TRUE(catcher
.GetNextResult());