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