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