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