summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortbarzic@chromium.org <tbarzic@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-18 10:35:21 +0000
committertbarzic@chromium.org <tbarzic@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-18 10:35:21 +0000
commit3ca004f7fbcf3a9a9816def839a1e02ee1a3e868 (patch)
tree9d43d46fdd5a3a6114d0cc7214ff2abad6e79019
parentf5feb768de61eac3e7d983220cb7c5caeba605e9 (diff)
downloadchromium_src-3ca004f7fbcf3a9a9816def839a1e02ee1a3e868.zip
chromium_src-3ca004f7fbcf3a9a9816def839a1e02ee1a3e868.tar.gz
chromium_src-3ca004f7fbcf3a9a9816def839a1e02ee1a3e868.tar.bz2
Add file browser handler resource throttler for intercepting downloads.
The throttle will intercept requests with MIME types handleable by quick office extension, cancel them and send an event to the quick office extension to handle the request. BUG=162736 TEST=unit_tests:FileBrowserResourceThrotlleTest; browser_tests:FileBrowserResourceThrottleExtensionApiTest Review URL: https://chromiumcodereview.appspot.com/11280264 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@173713 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc1
-rw-r--r--android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.h1
-rw-r--r--chrome/browser/chromeos/extensions/file_browser_resource_throttle.cc207
-rw-r--r--chrome/browser/chromeos/extensions/file_browser_resource_throttle.h122
-rw-r--r--chrome/browser/chromeos/extensions/file_browser_resource_throttle_browsertest.cc394
-rw-r--r--chrome/browser/chromeos/extensions/file_browser_resource_throttle_unittest.cc327
-rw-r--r--chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.cc14
-rw-r--r--chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.h1
-rw-r--r--chrome/chrome_browser_chromeos.gypi2
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/chrome_tests_unit.gypi1
-rw-r--r--chrome/common/extensions/file_browser_handler.cc14
-rw-r--r--chrome/common/extensions/file_browser_handler.h13
-rw-r--r--chrome/test/data/extensions/api_test/file_browser/handle_mime_type/background.js36
-rw-r--r--chrome/test/data/extensions/api_test/file_browser/handle_mime_type/manifest.json20
-rw-r--r--content/browser/loader/buffered_resource_handler.cc4
-rw-r--r--content/browser/loader/resource_dispatcher_host_impl.cc6
-rw-r--r--content/browser/loader/resource_dispatcher_host_impl.h1
-rw-r--r--content/public/browser/resource_dispatcher_host_delegate.cc1
-rw-r--r--content/public/browser/resource_dispatcher_host_delegate.h7
20 files changed, 1164 insertions, 9 deletions
diff --git a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc
index db605c3..1c3054c 100644
--- a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc
+++ b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc
@@ -237,6 +237,7 @@ void AwResourceDispatcherHostDelegate::DownloadStarting(
int route_id,
int request_id,
bool is_content_initiated,
+ bool must_download,
ScopedVector<content::ResourceThrottle>* throttles) {
GURL url(request->url());
std::string user_agent;
diff --git a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.h b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.h
index 3a80a37..1741a89 100644
--- a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.h
+++ b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.h
@@ -41,6 +41,7 @@ class AwResourceDispatcherHostDelegate
int route_id,
int request_id,
bool is_content_initiated,
+ bool must_download,
ScopedVector<content::ResourceThrottle>* throttles) OVERRIDE;
virtual bool AcceptAuthRequest(net::URLRequest* request,
diff --git a/chrome/browser/chromeos/extensions/file_browser_resource_throttle.cc b/chrome/browser/chromeos/extensions/file_browser_resource_throttle.cc
new file mode 100644
index 0000000..7649929
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_browser_resource_throttle.cc
@@ -0,0 +1,207 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/extensions/file_browser_resource_throttle.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/values.h"
+#include "chrome/browser/extensions/event_router.h"
+#include "chrome/browser/extensions/extension_info_map.h"
+#include "chrome/browser/extensions/extension_system.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/extensions/extension_set.h"
+#include "chrome/common/extensions/file_browser_handler.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/resource_controller.h"
+#include "content/public/browser/web_contents.h"
+#include "net/url_request/url_request.h"
+
+using extensions::Event;
+using extensions::Extension;
+using extensions::ExtensionSystem;
+
+namespace {
+
+const char* const kOnExecuteContentHandlerEvent =
+ "fileBrowserHandler.onExecuteContentHandler";
+
+// Goes through the extension's file browser handlers and checks it there is one
+// that can handle the |mime_type|.
+// |extension| must not be NULL.
+bool CanHandleMimeType(const Extension* extension,
+ const std::string& mime_type) {
+ if (!extension->file_browser_handlers())
+ return false;
+
+ for (Extension::FileBrowserHandlerList::const_iterator handler =
+ extension->file_browser_handlers()->begin();
+ handler != extension->file_browser_handlers()->end();
+ ++handler) {
+ if ((*handler)->CanHandleMIMEType(mime_type)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Retrieves Profile for a render view host specified by |render_process_id| and
+// |render_view_id|.
+Profile* GetProfile(int render_process_id, int render_view_id) {
+ content::RenderViewHost* render_view_host =
+ content::RenderViewHost::FromID(render_process_id, render_view_id);
+ if (!render_view_host)
+ return NULL;
+
+ content::WebContents* web_contents =
+ content::WebContents::FromRenderViewHost(render_view_host);
+ if (!web_contents)
+ return NULL;
+
+ content::BrowserContext* browser_context = web_contents->GetBrowserContext();
+ if (!browser_context)
+ return NULL;
+
+ return Profile::FromBrowserContext(browser_context);
+}
+
+void DispatchEventOnUIThread(const std::string& mime_type,
+ const GURL& request_url,
+ int render_process_id,
+ int render_view_id,
+ const std::string& extension_id) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ Profile* profile = GetProfile(render_process_id, render_view_id);
+ if (!profile)
+ return;
+
+ // Create the event's arguments value.
+ scoped_ptr<ListValue> event_args(new ListValue());
+ event_args->Append(new base::StringValue(mime_type));
+ event_args->Append(new base::StringValue(request_url.spec()));
+
+ scoped_ptr<Event> event(new Event(kOnExecuteContentHandlerEvent,
+ event_args.Pass()));
+
+ ExtensionSystem::Get(profile)->event_router()->DispatchEventToExtension(
+ extension_id, event.Pass());
+}
+
+// Default implementation of FileBrowserHandlerEventRouter.
+class FileBrowserHandlerEventRouterImpl
+ : public FileBrowserResourceThrottle::FileBrowserHandlerEventRouter {
+ public:
+ FileBrowserHandlerEventRouterImpl() {}
+ virtual ~FileBrowserHandlerEventRouterImpl() {}
+
+ virtual void DispatchMimeTypeHandlerEvent(
+ int render_process_id,
+ int render_view_id,
+ const std::string& mime_type,
+ const GURL& request_url,
+ const std::string& extension_id) OVERRIDE {
+ // The event must be dispatched on the UI thread.
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI, FROM_HERE,
+ base::Bind(&DispatchEventOnUIThread, mime_type, request_url,
+ render_process_id, render_view_id, extension_id));
+ }
+};
+
+} // namespace
+
+// static
+FileBrowserResourceThrottle* FileBrowserResourceThrottle::Create(
+ int render_process_id,
+ int render_view_id,
+ net::URLRequest* request,
+ bool profile_is_incognito,
+ const ExtensionInfoMap* extension_info_map) {
+ std::string mime_type;
+ request->GetMimeType(&mime_type);
+ scoped_ptr<FileBrowserHandlerEventRouter> event_router(
+ new FileBrowserHandlerEventRouterImpl());
+ return new FileBrowserResourceThrottle(render_process_id, render_view_id,
+ mime_type, request->url(), profile_is_incognito, extension_info_map,
+ event_router.Pass());
+}
+
+// static
+FileBrowserResourceThrottle* FileBrowserResourceThrottle::CreateForTest(
+ int render_process_id,
+ int render_view_id,
+ const std::string& mime_type,
+ const GURL& request_url,
+ bool profile_is_incognito,
+ const ExtensionInfoMap* extension_info_map,
+ scoped_ptr<FileBrowserHandlerEventRouter> event_router_in) {
+ scoped_ptr<FileBrowserHandlerEventRouter> event_router =
+ event_router_in.Pass();
+ if (!event_router)
+ event_router.reset(new FileBrowserHandlerEventRouterImpl());
+ return new FileBrowserResourceThrottle(render_process_id, render_view_id,
+ mime_type, request_url, profile_is_incognito, extension_info_map,
+ event_router.Pass());
+}
+
+FileBrowserResourceThrottle::FileBrowserResourceThrottle(
+ int render_process_id,
+ int render_view_id,
+ const std::string& mime_type,
+ const GURL& request_url,
+ bool profile_is_incognito,
+ const ExtensionInfoMap* extension_info_map,
+ scoped_ptr<FileBrowserHandlerEventRouter> event_router)
+ : render_process_id_(render_process_id),
+ render_view_id_(render_view_id),
+ mime_type_(mime_type),
+ request_url_(request_url),
+ profile_is_incognito_(profile_is_incognito),
+ extension_info_map_(extension_info_map),
+ event_router_(event_router.Pass()) {
+}
+
+FileBrowserResourceThrottle::~FileBrowserResourceThrottle() {}
+
+void FileBrowserResourceThrottle::WillProcessResponse(bool* defer) {
+ std::vector<std::string> whitelist =
+ FileBrowserHandler::GetMIMETypeWhitelist();
+ // Go through the white-listed extensions and try to use them to intercept
+ // the URL request.
+ for (size_t i = 0; i < whitelist.size(); ++i) {
+ if (MaybeInterceptWithExtension(whitelist[i]))
+ return;
+ }
+}
+
+bool FileBrowserResourceThrottle::MaybeInterceptWithExtension(
+ const std::string& extension_id) {
+ const Extension* extension =
+ extension_info_map_->extensions().GetByID(extension_id);
+ // The white-listed extension may not be installed, so we have to NULL check
+ // |extension|.
+ if (!extension)
+ return false;
+
+ // If in incognito mode, skip the extensions that are not incognito enabled.
+ if (profile_is_incognito_ &&
+ !extension_info_map_->IsIncognitoEnabled(extension_id)) {
+ return false;
+ }
+
+ if (CanHandleMimeType(extension, mime_type_)) {
+ event_router_->DispatchMimeTypeHandlerEvent(render_process_id_,
+ render_view_id_, mime_type_, request_url_, extension->id());
+ controller()->CancelAndIgnore();
+ return true;
+ }
+ return false;
+}
diff --git a/chrome/browser/chromeos/extensions/file_browser_resource_throttle.h b/chrome/browser/chromeos/extensions/file_browser_resource_throttle.h
new file mode 100644
index 0000000..cf20a16
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_browser_resource_throttle.h
@@ -0,0 +1,122 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_BROWSER_RESOURCE_THROTTLE_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_BROWSER_RESOURCE_THROTTLE_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/public/browser/resource_throttle.h"
+#include "googleurl/src/gurl.h"
+
+class ExtensionInfoMap;
+class ExtensionSet;
+
+namespace net {
+class URLRequest;
+}
+
+// Resource throttle that intercepts URL requests that can be handled by an
+// installed, white-listed file browser handler extension.
+// The requests are intercepted before processing the response because the
+// request's MIME type is needed to determine if it can be handled by a file
+// browser handler. It the request can be handled by a file browser handler
+// extension, it gets canceled and the extension is notified to handle
+// the requested URL.
+class FileBrowserResourceThrottle : public content::ResourceThrottle {
+ public:
+ // Used to dispatch fileBrowserHandler events to notify an extension it should
+ // handle an URL request.
+ // Can be used on any thread. The actual dispatch tasks will be posted to the
+ // UI thread.
+ class FileBrowserHandlerEventRouter {
+ public:
+ virtual ~FileBrowserHandlerEventRouter() {}
+
+ // Used to dispatch fileBrowserHandler.onExecuteContentHandler to the
+ // extension with id |extension_id|. |mime_type| and |request_url| are the
+ // URL request's parameters that should be handed to the extension.
+ // |render_process_id| and |render_view_id| are used to determine the
+ // profile from which the URL request came and in which the event should be
+ // dispatched.
+ virtual void DispatchMimeTypeHandlerEvent(
+ int render_process_id,
+ int render_view_id,
+ const std::string& mime_type,
+ const GURL& request_url,
+ const std::string& extension_id) = 0;
+ };
+
+ // Creates a FileBrowserResourceThrottle for the |request|.
+ // The throtlle's |mime_type_| and |url_request_| members are extracted from
+ // the request.
+ // The throttle's |event_router_| is created and set (it's a
+ // FileBrowserHandlerEventRouterImpl instance; see the .cc file).
+ static FileBrowserResourceThrottle* Create(
+ int render_process_id,
+ int render_view_id,
+ net::URLRequest* request,
+ bool profile_is_incognito,
+ const ExtensionInfoMap* extension_info_map);
+
+ // Creates a FileBrowserResourceThrottle to be used in a test.
+ // |event_router| can hold NULL, in which case the throttle's |event_router_|
+ // member is set to a FileBrowserHandlerEventRouterImpl instance, just like in
+ // FileBrowserResourceThrottle::Create.
+ static FileBrowserResourceThrottle* CreateForTest(
+ int render_process_id,
+ int renser_view_id,
+ const std::string& mime_type,
+ const GURL& request_url,
+ bool profile_is_incognito,
+ const ExtensionInfoMap* extension_info_map,
+ scoped_ptr<FileBrowserHandlerEventRouter> event_router);
+
+ virtual ~FileBrowserResourceThrottle();
+
+ // content::ResourceThrottle implementation.
+ // Calls |MaybeInterceptWithExtension| for all extension_id's white-listed to
+ // use file browser handler MIME type filters.
+ virtual void WillProcessResponse(bool* defer) OVERRIDE;
+
+ private:
+ // Use Create* methods to create a FileBrowserResourceThrottle instance.
+ FileBrowserResourceThrottle(
+ int render_process_id,
+ int render_view_id,
+ const std::string& mime_type,
+ const GURL& request_url,
+ bool profile_is_incognito,
+ const ExtensionInfoMap* extension_info_map,
+ scoped_ptr<FileBrowserHandlerEventRouter> event_router);
+
+ // Checks if the extension has a registered file browser handler that can
+ // handle the request's mime_type. If this is the case, the request is
+ // canceled and fileBrowserHandler.onExtecuteContentHandler event is
+ // dispatched to the extension.
+ // Returns whether the URL request has been intercepted.
+ bool MaybeInterceptWithExtension(const std::string& extension_id);
+
+ // Render process id from which the request came.
+ int render_process_id_;
+ // Render view id from which the request came.
+ int render_view_id_;
+ // The request's MIME type.
+ std::string mime_type_;
+ // The request's URL.
+ GURL request_url_;
+ // Whether the request came from an incognito profile.
+ bool profile_is_incognito_;
+ // Map holding list of installed extensions. Must be used exclusively on IO
+ // thread.
+ const scoped_refptr<const ExtensionInfoMap> extension_info_map_;
+
+ // Event router to be used to dispatch the fileBrowserHandler events.
+ scoped_ptr<FileBrowserHandlerEventRouter> event_router_;
+};
+
+#endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_BROWSER_RESOURCE_THROTTLE_H_
diff --git a/chrome/browser/chromeos/extensions/file_browser_resource_throttle_browsertest.cc b/chrome/browser/chromeos/extensions/file_browser_resource_throttle_browsertest.cc
new file mode 100644
index 0000000..bd24d9f
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_browser_resource_throttle_browsertest.cc
@@ -0,0 +1,394 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/message_loop.h"
+#include "chrome/browser/chromeos/extensions/file_browser_resource_throttle.h"
+#include "chrome/browser/download/download_prefs.h"
+#include "chrome/browser/extensions/event_router.h"
+#include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/extensions/extension_info_map.h"
+#include "chrome/browser/extensions/extension_system.h"
+#include "chrome/browser/google_apis/test_server/http_server.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_tabstrip.cc"
+#include "chrome/common/extensions/file_browser_handler.h"
+#include "chrome/common/pref_names.cc"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/download_item.h"
+#include "content/public/browser/download_manager.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/resource_controller.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/download_test_observer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using content::BrowserContext;
+using content::BrowserThread;
+using content::DownloadItem;
+using content::DownloadManager;
+using content::DownloadUrlParameters;
+using content::ResourceController;
+using content::WebContents;
+using extensions::Event;
+using extensions::ExtensionSystem;
+using google_apis::test_server::HttpRequest;
+using google_apis::test_server::HttpResponse;
+using google_apis::test_server::HttpServer;
+using testing::_;
+
+namespace {
+
+// Used in FileBrowserResourceThrottleExtensionApiTest.Basic test to mock the
+// resource controller bound to the test resource throttle.
+class MockResourceController : public ResourceController {
+ public:
+ virtual ~MockResourceController() {}
+ MOCK_METHOD0(Cancel, void());
+ MOCK_METHOD0(CancelAndIgnore, void());
+ MOCK_METHOD1(CancelWithError, void(int error_code));
+ MOCK_METHOD0(Resume, void());
+};
+
+// Creates and triggers a test FileBrowserResourceThrottle for the listed
+// request parameters (child and routing id from which the request came,
+// request's MIME type and URL), with extension info map |info_map| and resource
+// controller |controller|.
+// The created resource throttle is started by calling |WillProcessResponse|.
+// Used in FileBrowserResourceThrottleExtensionApiTest.Basic test.
+//
+// Must be called on the IO thread.
+void CreateAndTriggerThrottle(int child_id,
+ int routing_id,
+ const std::string& mime_type,
+ const GURL& url,
+ bool is_incognito,
+ scoped_refptr<ExtensionInfoMap> info_map,
+ ResourceController* controller) {
+ typedef FileBrowserResourceThrottle::FileBrowserHandlerEventRouter
+ HandlerEventRouter;
+ scoped_ptr<FileBrowserResourceThrottle> throttle(
+ FileBrowserResourceThrottle::CreateForTest(child_id, routing_id,
+ mime_type, url, is_incognito, info_map,
+ scoped_ptr<HandlerEventRouter>()));
+
+ throttle->set_controller_for_testing(controller);
+
+ bool defer;
+ throttle->WillProcessResponse(&defer);
+ EXPECT_FALSE(defer);
+}
+
+// Test server's request handler.
+// Returns response that should be sent by the test server.
+scoped_ptr<HttpResponse> HandleRequest(const HttpRequest& request) {
+ scoped_ptr<HttpResponse> response(new HttpResponse());
+
+ // For relative path "/doc_path.doc", return success response with MIME type
+ // "application/msword".
+ if (request.relative_url == "/doc_path.doc") {
+ response->set_code(google_apis::test_server::SUCCESS);
+ response->set_content_type("application/msword");
+ return response.Pass();
+ }
+
+ // For relative path "/test_path_attch.txt", return success response with
+ // MIME type "plain/text" and content "txt content". Also, set content
+ // disposition to be attachment.
+ if (request.relative_url == "/text_path_attch.txt") {
+ response->set_code(google_apis::test_server::SUCCESS);
+ response->set_content("txt content");
+ response->set_content_type("plain/text");
+ response->AddCustomHeader("Content-Disposition",
+ "attachment; filename=test_path.txt");
+ return response.Pass();
+ }
+ // For relative path "/test_path_attch.txt", return success response with
+ // MIME type "plain/text" and content "txt content".
+ if (request.relative_url == "/text_path.txt") {
+ response->set_code(google_apis::test_server::SUCCESS);
+ response->set_content("txt content");
+ response->set_content_type("plain/text");
+ return response.Pass();
+ }
+
+ // No other requests should be handled in the tests.
+ EXPECT_TRUE(false) << "NOTREACHED!";
+ response->set_code(google_apis::test_server::NOT_FOUND);
+ return response.Pass();
+}
+
+// Tests to verify that resources are correctly intercepted by
+// FileBrowserResourceThrottle.
+// The test extension expects the resources that should be handed to the
+// extension to have MIME type 'application/msword' and the resources that
+// should be downloaded by the browser to have MIME type 'plain/text'.
+class FileBrowserResourceThrottleExtensionApiTest : public ExtensionApiTest {
+ public:
+ FileBrowserResourceThrottleExtensionApiTest() {}
+
+ virtual ~FileBrowserResourceThrottleExtensionApiTest() {
+ // Clear the test extension from the white-list.
+ FileBrowserHandler::set_extension_whitelisted_for_test(NULL);
+ }
+
+ virtual void SetUpOnMainThread() {
+ // Init test server.
+ test_server_.reset(new HttpServer());
+ test_server_->InitializeAndWaitUntilReady();
+ test_server_->RegisterRequestHandler(base::Bind(&HandleRequest));
+
+ ExtensionApiTest::SetUpOnMainThread();
+ }
+
+ virtual void CleanUpOnMainThread() {
+ // Tear down the test server.
+ test_server_->ShutdownAndWaitUntilComplete();
+ test_server_.reset();
+ ExtensionApiTest::CleanUpOnMainThread();
+ }
+
+ void InitializeDownloadSettings() {
+ ASSERT_TRUE(browser());
+ ASSERT_TRUE(downloads_dir_.CreateUniqueTempDir());
+
+ // Setup default downloads directory to the scoped tmp directory created for
+ // the test.
+ browser()->profile()->GetPrefs()->SetFilePath(
+ prefs::kDownloadDefaultDirectory, downloads_dir_.path());
+ // Ensure there are no prompts for download during the test.
+ browser()->profile()->GetPrefs()->SetBoolean(
+ prefs::kPromptForDownload, false);
+
+ DownloadManager* manager = GetDownloadManager();
+ DownloadPrefs::FromDownloadManager(manager)->ResetAutoOpen();
+ manager->RemoveAllDownloads();
+ }
+
+ // Sends onExecuteContentHandler event with the MIME type "test/done" to the
+ // test extension.
+ // The test extension calls 'chrome.test.notifySuccess' when it receives the
+ // event with the "test/done" MIME type (unless the 'chrome.test.notifyFail'
+ // has already been called).
+ void SendDoneEvent() {
+ scoped_ptr<ListValue> event_args(new ListValue());
+ event_args->Append(new base::StringValue("test/done"));
+ event_args->Append(new base::StringValue("http://foo"));
+
+ scoped_ptr<Event> event(new Event(
+ "fileBrowserHandler.onExecuteContentHandler", event_args.Pass()));
+
+ ExtensionSystem::Get(browser()->profile())->event_router()->
+ DispatchEventToExtension(test_extension_id_, event.Pass());
+ }
+
+ // Loads the test extension and set's up its file_browser_handler to handle
+ // 'application/msword' and 'plain/text' MIME types.
+ // The extension will notify success when it detects an event with the MIME
+ // type 'application/msword' and notify fail when it detects an event with the
+ // MIME type 'plain/text'.
+ const extensions::Extension* LoadTestExtension() {
+ const extensions::Extension* extension = LoadExtension(
+ test_data_dir_.AppendASCII("file_browser/handle_mime_type"));
+ if (!extension)
+ return NULL;
+
+ if (!extension->file_browser_handlers() ||
+ extension->file_browser_handlers()->size() == 0u) {
+ message_ = "No file browser handlers defined.";
+ return NULL;
+ }
+
+ test_extension_id_ = extension->id();
+
+ FileBrowserHandler::set_extension_whitelisted_for_test(&test_extension_id_);
+
+ // The MIME type filters cannot be defined directly in the extension's
+ // manifest because the extension installation would fail for the extension
+ // that is not white-listed to handle MIME types with its file browser
+ // handlers.
+ FileBrowserHandler* file_browser_handler = const_cast<FileBrowserHandler*>(
+ extension->file_browser_handlers()->at(0).get());
+ file_browser_handler->AddMIMEType("application/msword");
+ file_browser_handler->AddMIMEType("plain/text");
+
+ return extension;
+ }
+
+ // Returns the download manager for the current browser.
+ DownloadManager* GetDownloadManager() const {
+ DownloadManager* download_manager =
+ BrowserContext::GetDownloadManager(browser()->profile());
+ EXPECT_TRUE(download_manager);
+ return download_manager;
+ }
+
+ // Deletes the download and waits until it's flushed.
+ // The |manager| should have |download| in its list of downloads.
+ void DeleteDownloadAndWaitForFlush(DownloadItem* download,
+ DownloadManager* manager) {
+ scoped_refptr<content::DownloadTestFlushObserver> flush_observer(
+ new content::DownloadTestFlushObserver(manager));
+ download->Delete(DownloadItem::DELETE_DUE_TO_USER_DISCARD);
+ flush_observer->WaitForFlush();
+ }
+
+ protected:
+ std::string test_extension_id_;
+ // The HTTP server used in the tests.
+ scoped_ptr<HttpServer> test_server_;
+ base::ScopedTempDir downloads_dir_;
+};
+
+// Tests that invoking FileBrowserResourceThrottle with handleable MIME type
+// actually invokes the fileBrowserHandler.onExecuteContnentHandler event.
+IN_PROC_BROWSER_TEST_F(FileBrowserResourceThrottleExtensionApiTest, Basic) {
+ ASSERT_TRUE(LoadTestExtension()) << message_;
+
+ ResultCatcher catcher;
+
+ MockResourceController mock_resource_controller;
+ EXPECT_CALL(mock_resource_controller, Cancel()).Times(0);
+ EXPECT_CALL(mock_resource_controller, CancelAndIgnore()).Times(1);
+ EXPECT_CALL(mock_resource_controller, CancelWithError(_)).Times(0);
+ EXPECT_CALL(mock_resource_controller, Resume()).Times(0);
+
+ // Get child and routing id from the current web contents (the real values
+ // should be used so the FileBrowserHandlerEventRouter can correctly extract
+ // profile from them).
+ WebContents* web_contents = chrome::GetActiveWebContents(browser());
+ ASSERT_TRUE(web_contents);
+ int child_id = web_contents->GetRenderProcessHost()->GetID();
+ int routing_id = web_contents->GetRoutingID();
+
+ scoped_refptr<ExtensionInfoMap> info_map =
+ extensions::ExtensionSystem::Get(browser()->profile())->info_map();
+
+ // The resource throttle must be created and invoked on IO thread.
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&CreateAndTriggerThrottle, child_id, routing_id,
+ "application/msword", GURL("http://foo"), false, info_map,
+ &mock_resource_controller));
+
+ // Wait for the test extension to received the onExecuteContentHandler event.
+ EXPECT_TRUE(catcher.GetNextResult());
+}
+
+// Tests that navigating to a resource with a MIME type handleable by an
+// installed, white-listed extension invokes the extension's
+// onExecuteContentHandler event (and does not start a download).
+IN_PROC_BROWSER_TEST_F(FileBrowserResourceThrottleExtensionApiTest, Navigate) {
+ ASSERT_TRUE(LoadTestExtension()) << message_;
+
+ ResultCatcher catcher;
+
+ ui_test_utils::NavigateToURL(browser(),
+ test_server_->GetURL("/doc_path.doc"));
+
+ // Wait for the response from the test server.
+ MessageLoop::current()->RunUntilIdle();
+
+ // There should be no downloads started by the navigation.
+ DownloadManager* download_manager = GetDownloadManager();
+ std::vector<DownloadItem*> downloads;
+ download_manager->GetAllDownloads(&downloads);
+ ASSERT_EQ(0u, downloads.size());
+
+ // The test extension should receive onExecuteContentHandler event with MIME
+ // type 'application/msword' (and call chrome.test.notifySuccess).
+ EXPECT_TRUE(catcher.GetNextResult());
+}
+
+// Tests that navigation to an attachment starts a download, even if there is an
+// extension with a file browser handler that can handle the attachment's MIME
+// type.
+IN_PROC_BROWSER_TEST_F(FileBrowserResourceThrottleExtensionApiTest,
+ NavigateToAnAttachment) {
+ InitializeDownloadSettings();
+
+ ASSERT_TRUE(LoadTestExtension()) << message_;
+
+ ResultCatcher catcher;
+
+ // The test should start a downloadm.
+ DownloadManager* download_manager = GetDownloadManager();
+ scoped_ptr<content::DownloadTestObserver> download_observer(
+ new content::DownloadTestObserverInProgress(download_manager, 1));
+
+ ui_test_utils::NavigateToURL(browser(),
+ test_server_->GetURL("/text_path_attch.txt"));
+
+ // Wait for the download to start.
+ download_observer->WaitForFinished();
+
+ // There should be one download started by the navigation.
+ DownloadManager::DownloadVector downloads;
+ download_manager->GetAllDownloads(&downloads);
+ ASSERT_EQ(1u, downloads.size());
+
+ // Cancel and delete the download started in the test.
+ DeleteDownloadAndWaitForFlush(downloads[0], download_manager);
+
+ // The test extension should not receive any events by now. Send it an event
+ // with MIME type "test/done", so it stops waiting for the events. (If there
+ // was an event with MIME type 'plain/text', |catcher.GetNextResult()| will
+ // fail regardless of the sent event; chrome.test.notifySuccess will not be
+ // called by the extension).
+ SendDoneEvent();
+ EXPECT_TRUE(catcher.GetNextResult());
+}
+
+// Tests that direct download requests don't get intercepted by
+// FileBrowserResourceThrottle, even if there is an extension with a file
+// browser handler that can handle the download's MIME type.
+IN_PROC_BROWSER_TEST_F(FileBrowserResourceThrottleExtensionApiTest,
+ DirectDownload) {
+ InitializeDownloadSettings();
+
+ ASSERT_TRUE(LoadTestExtension()) << message_;
+
+ ResultCatcher catcher;
+
+ DownloadManager* download_manager = GetDownloadManager();
+ scoped_ptr<content::DownloadTestObserver> download_observer(
+ new content::DownloadTestObserverInProgress(download_manager, 1));
+
+ // The resource's URL on the test server.
+ GURL url = test_server_->GetURL("/text_path.txt");
+
+ // The download's target file path.
+ FilePath target_path =
+ downloads_dir_.path().Append(FILE_PATH_LITERAL("download_target.txt"));
+
+ // Set the downloads parameters.
+ content::WebContents* web_contents = chrome::GetActiveWebContents(browser());
+ ASSERT_TRUE(web_contents);
+ scoped_ptr<DownloadUrlParameters> params(
+ DownloadUrlParameters::FromWebContents(web_contents, url));
+ params->set_file_path(target_path);
+
+ // Start download of the URL with a path "/text_path.txt" on the test server.
+ download_manager->DownloadUrl(params.Pass());
+
+ // Wait for the download to start.
+ download_observer->WaitForFinished();
+
+ // There should have been one download.
+ std::vector<DownloadItem*> downloads;
+ download_manager->GetAllDownloads(&downloads);
+ ASSERT_EQ(1u, downloads.size());
+
+ // Cancel and delete the download statred in the test.
+ DeleteDownloadAndWaitForFlush(downloads[0], download_manager);
+
+ // The test extension should not receive any events by now. Send it an event
+ // with MIME type "test/done", so it stops waiting for the events. (If there
+ // was an event with MIME type 'plain/text', |catcher.GetNextResult()| will
+ // fail regardless of the sent event; chrome.test.notifySuccess will not be
+ // called by the extension).
+ SendDoneEvent();
+ EXPECT_TRUE(catcher.GetNextResult());
+}
+
+} // namespace
diff --git a/chrome/browser/chromeos/extensions/file_browser_resource_throttle_unittest.cc b/chrome/browser/chromeos/extensions/file_browser_resource_throttle_unittest.cc
new file mode 100644
index 0000000..7456c46
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_browser_resource_throttle_unittest.cc
@@ -0,0 +1,327 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/message_loop.h"
+#include "chrome/browser/chromeos/extensions/file_browser_resource_throttle.h"
+#include "chrome/browser/extensions/extension_info_map.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_builder.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/extensions/file_browser_handler.h"
+#include "chrome/common/extensions/value_builder.h"
+#include "content/public/browser/resource_controller.h"
+#include "content/public/test/test_browser_thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::BrowserThread;
+using extensions::DictionaryBuilder;
+using extensions::Extension;
+using extensions::ExtensionBuilder;
+using extensions::ListBuilder;
+using testing::_;
+
+namespace {
+
+// Mock file browser handler event router to be used in the test.
+class MockFileBrowserHandlerEventRouter
+ : public FileBrowserResourceThrottle::FileBrowserHandlerEventRouter {
+ public:
+ virtual ~MockFileBrowserHandlerEventRouter() {}
+
+ MOCK_METHOD5(DispatchMimeTypeHandlerEvent,
+ void(int render_process_id,
+ int render_process_view,
+ const std::string& mime_type,
+ const GURL& request_url,
+ const std::string& extension_id));
+};
+
+// Resource controller to be used in the tests.
+class MockResourceController : public content::ResourceController {
+ public:
+ virtual ~MockResourceController() {}
+ MOCK_METHOD0(Cancel, void());
+ MOCK_METHOD0(CancelAndIgnore, void());
+ MOCK_METHOD1(CancelWithError, void(int error_code));
+ MOCK_METHOD0(Resume, void());
+};
+
+class FileBrowserResourceThrottleTest : public testing::Test {
+ public:
+ typedef FileBrowserResourceThrottle::FileBrowserHandlerEventRouter
+ HandlerEventRouter;
+
+ FileBrowserResourceThrottleTest()
+ : test_extension_id_("test_extension_id"),
+ test_render_process_id_(2),
+ test_render_view_id_(12),
+ test_request_url_("http://some_url/file.txt"),
+ ui_thread_(content::BrowserThread::UI, &message_loop_),
+ io_thread_(content::BrowserThread::IO, &message_loop_) {
+ }
+
+ virtual ~FileBrowserResourceThrottleTest() {}
+
+ virtual void SetUp() {
+ // Extension info map must be created before |CreateAndIstallTestExtension|
+ // is called (the method will add created extension to the info map).
+ extension_info_map_ = new ExtensionInfoMap();
+ CreateAndInstallTestExtension();
+ InitResourceController();
+ }
+
+ virtual void TearDown() {
+ FileBrowserHandler::set_extension_whitelisted_for_test(NULL);
+ }
+
+ protected:
+ // Creates the test extension, and adds it to the |extension_info_map_|.
+ // The extension has separate file browser handlers that can handle
+ // 'plain/html' and 'plain/text' MIME types.
+ void CreateAndInstallTestExtension() {
+ // The extension must be white-listed in order to be successfully created.
+ FileBrowserHandler::set_extension_whitelisted_for_test(
+ &test_extension_id_);
+
+ extension_ =
+ ExtensionBuilder()
+ .SetManifest(DictionaryBuilder()
+ .Set("name", "file browser handler test")
+ .Set("version", "1.0.0")
+ .Set("manifest_version", 2)
+ .Set("file_browser_handlers", ListBuilder()
+ .Append(DictionaryBuilder()
+ // Handler that handles 'plain/html', among others.
+ .Set("id", "ID_handle_html")
+ .Set("default_title", "Default title")
+ .Set("default_icon", "icon.png")
+ // file_filters_field is mandatory, even though
+ // it's not used in the tests.
+ .Set("file_filters", ListBuilder()
+ .Append("filesystem:*.html"))
+ .Set("mime_types", ListBuilder()
+ .Append("random/mime1")
+ .Append("random/mime2")
+ .Append("plain/html")))
+ .Append(DictionaryBuilder()
+ // Handler that handles only 'plain/text'.
+ .Set("id", "ID_handle_text")
+ .Set("default_title", "Default title")
+ .Set("default_icon", "icon.png")
+ // file_filters_field is mandatory, even though
+ // it's not used in the tests.
+ .Set("file_filters", ListBuilder()
+ .Append("filesystem:*.txt"))
+ .Set("mime_types", ListBuilder()
+ .Append("plain/text")))))
+ .SetID(test_extension_id_)
+ .Build();
+
+ // 'Install' the extension.
+ extension_info_map_->AddExtension(extension_,
+ base::Time(), // install time
+ true); // enable_incognito
+ }
+
+ // Initiates the default mock_resource_controller_ expectations.
+ // By the default, resource controller should not be called at all.
+ void InitResourceController() {
+ EXPECT_CALL(mock_resource_controller_, Cancel()).Times(0);
+ EXPECT_CALL(mock_resource_controller_, CancelAndIgnore()).Times(0);
+ EXPECT_CALL(mock_resource_controller_, CancelWithError(_)).Times(0);
+ EXPECT_CALL(mock_resource_controller_, Resume()).Times(0);
+ }
+
+ // Removes the test extension to |extension_info_map_|'s disabled extensions.
+ void DisableTestExtension() {
+ extension_info_map_->RemoveExtension(test_extension_id_,
+ extension_misc::UNLOAD_REASON_DISABLE);
+ }
+
+ void ReloadTestExtensionIncognitoDisabled() {
+ extension_info_map_->RemoveExtension(
+ test_extension_id_, extension_misc::UNLOAD_REASON_UNINSTALL);
+ extension_info_map_->AddExtension(extension_,
+ base::Time(), // install_time
+ false); // enable incognito
+ }
+
+ // Creates the resource throttle that should be tested.
+ // It's setup with |mock_event_router| passed to the method and
+ // |mock_resource_throttle_|.
+ // |mock_event_router|'s expectations must be set before calling this method.
+ scoped_ptr<FileBrowserResourceThrottle> CreateThrottleToTest(
+ bool is_incognito,
+ scoped_ptr<MockFileBrowserHandlerEventRouter> mock_event_router,
+ const std::string& mime_type) {
+ scoped_ptr<HandlerEventRouter> event_router(mock_event_router.release());
+ scoped_ptr<FileBrowserResourceThrottle> test_throttle(
+ FileBrowserResourceThrottle::CreateForTest(test_render_process_id_,
+ test_render_view_id_,
+ mime_type,
+ test_request_url_,
+ is_incognito,
+ extension_info_map_.get(),
+ event_router.Pass()));
+
+ test_throttle->set_controller_for_testing(&mock_resource_controller_);
+
+ return test_throttle.Pass();
+ }
+
+ // The test extension's id.
+ std::string test_extension_id_;
+ // The test extension.
+ scoped_refptr<const Extension> extension_;
+
+ // The extension info map used in the test. The extensions are considered
+ // installed/disabled depending on the extension_info_map_ state.
+ scoped_refptr<ExtensionInfoMap> extension_info_map_;
+
+ MockResourceController mock_resource_controller_;
+
+ // Parameters used to create the test resource throttle. Unlike, mime_type
+ // these can be selected at random.
+ int test_render_process_id_;
+ int test_render_view_id_;
+ GURL test_request_url_;
+
+ private:
+ // ExtensionInfoMap needs IO thread.
+ MessageLoop message_loop_;
+ content::TestBrowserThread ui_thread_;
+ content::TestBrowserThread io_thread_;
+};
+
+// Tests that the request gets canceled (mock_resource_controller_.Cancel() is
+// called) and the event_router is invoked when a white-listed extension has a
+// file browser handler that can handle the request's MIME type.
+TEST_F(FileBrowserResourceThrottleTest, HandlerWhiteListed) {
+ EXPECT_CALL(mock_resource_controller_, CancelAndIgnore()).Times(1);
+
+ scoped_ptr<MockFileBrowserHandlerEventRouter> mock_event_router(
+ new MockFileBrowserHandlerEventRouter());
+ EXPECT_CALL(*mock_event_router,
+ DispatchMimeTypeHandlerEvent(test_render_process_id_,
+ test_render_view_id_,
+ "plain/html",
+ test_request_url_,
+ test_extension_id_))
+ .Times(1);
+
+ scoped_ptr<FileBrowserResourceThrottle> throttle(
+ CreateThrottleToTest(false, mock_event_router.Pass(), "plain/html"));
+
+ bool defer = false;
+ throttle->WillProcessResponse(&defer);
+ EXPECT_FALSE(defer);
+}
+
+// Tests that the request gets canceled (mock_resource_controller_.Cancel() is
+// called) and the event_router is invoked when a white-listed extension has a
+// file browser handler that can handle the request's MIME type, even when the
+// file browser handler is not first in the extension's file browser handler
+// list.
+TEST_F(FileBrowserResourceThrottleTest, SecondHandlerWhiteListed) {
+ EXPECT_CALL(mock_resource_controller_, CancelAndIgnore()).Times(1);
+
+ scoped_ptr<MockFileBrowserHandlerEventRouter> mock_event_router(
+ new MockFileBrowserHandlerEventRouter());
+ EXPECT_CALL(*mock_event_router,
+ DispatchMimeTypeHandlerEvent(test_render_process_id_,
+ test_render_view_id_,
+ "plain/text",
+ test_request_url_,
+ test_extension_id_))
+ .Times(1);
+
+ scoped_ptr<FileBrowserResourceThrottle> throttle(
+ CreateThrottleToTest(false, mock_event_router.Pass(), "plain/text"));
+
+ bool defer = false;
+ throttle->WillProcessResponse(&defer);
+ EXPECT_FALSE(defer);
+}
+
+// Tests that the request is not canceled and the event router is not invoked
+// if there is no file browser handlers registered for the request's MIME type.
+TEST_F(FileBrowserResourceThrottleTest, NoWhiteListedHandler) {
+ scoped_ptr<MockFileBrowserHandlerEventRouter> mock_event_router(
+ new MockFileBrowserHandlerEventRouter());
+ EXPECT_CALL(*mock_event_router, DispatchMimeTypeHandlerEvent(_, _, _, _, _))
+ .Times(0);
+
+ scoped_ptr<FileBrowserResourceThrottle> throttle(
+ CreateThrottleToTest(false, mock_event_router.Pass(),
+ "random_mime_type"));
+
+ bool defer = false;
+ throttle->WillProcessResponse(&defer);
+ EXPECT_FALSE(defer);
+}
+
+// Tests that the request is not canceled and the event router is not invoked
+// if there is an extension with the file browser handler that can handle the
+// request's MIME type, but the extension is disabled.
+TEST_F(FileBrowserResourceThrottleTest, HandlerWhiteListedAndDisabled) {
+ DisableTestExtension();
+
+ scoped_ptr<MockFileBrowserHandlerEventRouter> mock_event_router(
+ new MockFileBrowserHandlerEventRouter());
+ EXPECT_CALL(*mock_event_router, DispatchMimeTypeHandlerEvent(_, _, _, _, _))
+ .Times(0);
+
+ scoped_ptr<FileBrowserResourceThrottle> throttle(
+ CreateThrottleToTest(false, mock_event_router.Pass(), "plain/text"));
+
+ bool defer = false;
+ throttle->WillProcessResponse(&defer);
+ EXPECT_FALSE(defer);
+}
+
+// Tests that the request is not canceled and the event router is not invoked
+// in incognito mode if the extension that handles the request's MIME type is
+// not incognito enabled.
+TEST_F(FileBrowserResourceThrottleTest, IncognitoExtensionNotEnabled) {
+ ReloadTestExtensionIncognitoDisabled();
+
+ scoped_ptr<MockFileBrowserHandlerEventRouter> mock_event_router(
+ new MockFileBrowserHandlerEventRouter());
+ EXPECT_CALL(*mock_event_router, DispatchMimeTypeHandlerEvent(_, _, _, _, _))
+ .Times(0);
+
+ scoped_ptr<FileBrowserResourceThrottle> throttle(
+ CreateThrottleToTest(true, mock_event_router.Pass(), "plain/text"));
+
+ bool defer = false;
+ throttle->WillProcessResponse(&defer);
+ EXPECT_FALSE(defer);
+}
+
+// Tests that the request gets canceled (mock_resource_controller_.Cancel() is
+// called) and the event_router is invoked in incognito when a white-listed
+// extension that handles request's MIME type is incognito enabled.
+TEST_F(FileBrowserResourceThrottleTest, IncognitoExtensionEnabled) {
+ EXPECT_CALL(mock_resource_controller_, CancelAndIgnore()).Times(1);
+
+ scoped_ptr<MockFileBrowserHandlerEventRouter> mock_event_router(
+ new MockFileBrowserHandlerEventRouter());
+ EXPECT_CALL(*mock_event_router,
+ DispatchMimeTypeHandlerEvent(test_render_process_id_,
+ test_render_view_id_,
+ "plain/html",
+ test_request_url_,
+ test_extension_id_))
+ .Times(1);
+
+ scoped_ptr<FileBrowserResourceThrottle> throttle(
+ CreateThrottleToTest(true, mock_event_router.Pass(), "plain/html"));
+
+ bool defer = false;
+ throttle->WillProcessResponse(&defer);
+ EXPECT_FALSE(defer);
+}
+
+} // namespace
diff --git a/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.cc b/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.cc
index 02912d2..3be4456 100644
--- a/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.cc
+++ b/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.cc
@@ -57,8 +57,9 @@
#include "chrome/browser/managed_mode/managed_mode_resource_throttle.h"
#endif
-// TODO(oshima): Enable this for other platforms.
#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/extensions/file_browser_resource_throttle.h"
+// TODO(oshima): Enable this for other platforms.
#include "chrome/browser/renderer_host/offline_resource_throttle.h"
#endif
@@ -192,6 +193,7 @@ void ChromeResourceDispatcherHostDelegate::DownloadStarting(
int route_id,
int request_id,
bool is_content_initiated,
+ bool must_download,
ScopedVector<content::ResourceThrottle>* throttles) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
@@ -199,6 +201,16 @@ void ChromeResourceDispatcherHostDelegate::DownloadStarting(
// If it's from the web, we don't trust it, so we push the throttle on.
if (is_content_initiated) {
+#if defined(OS_CHROMEOS)
+ if (!must_download) {
+ ProfileIOData* io_data =
+ ProfileIOData::FromResourceContext(resource_context);
+ throttles->push_back(FileBrowserResourceThrottle::Create(
+ child_id, route_id, request, io_data->is_incognito(),
+ io_data->GetExtensionInfoMap()));
+ }
+#endif // defined(OS_CHROMEOS)
+
throttles->push_back(new DownloadResourceThrottle(
download_request_limiter_, child_id, route_id, request_id,
request->method()));
diff --git a/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.h b/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.h
index 22e66b80..7d82d5a 100644
--- a/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.h
+++ b/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.h
@@ -63,6 +63,7 @@ class ChromeResourceDispatcherHostDelegate
int route_id,
int request_id,
bool is_content_initiated,
+ bool must_download,
ScopedVector<content::ResourceThrottle>* throttles) OVERRIDE;
virtual bool AcceptSSLClientCertificateRequest(
net::URLRequest* request,
diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi
index 4abc9bb..6f228bf 100644
--- a/chrome/chrome_browser_chromeos.gypi
+++ b/chrome/chrome_browser_chromeos.gypi
@@ -274,6 +274,8 @@
'browser/chromeos/extensions/file_browser_event_router.h',
'browser/chromeos/extensions/file_browser_notifications.cc',
'browser/chromeos/extensions/file_browser_notifications.h',
+ 'browser/chromeos/extensions/file_browser_resource_throttle.cc',
+ 'browser/chromeos/extensions/file_browser_resource_throttle.h',
'browser/chromeos/extensions/file_handler_util.cc',
'browser/chromeos/extensions/file_handler_util.h',
'browser/chromeos/extensions/file_manager_util.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 524c88b..61098bd 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -883,6 +883,7 @@
'browser/chromeos/extensions/file_browser_handler_api_test.cc',
'browser/chromeos/extensions/file_browser_notifications_browsertest.cc',
'browser/chromeos/extensions/file_browser_private_apitest.cc',
+ 'browser/chromeos/extensions/file_browser_resource_throttle_browsertest.cc',
'browser/chromeos/extensions/info_private_apitest.cc',
'browser/chromeos/extensions/input_method_apitest_chromeos.cc',
'browser/chromeos/extensions/power/power_api_browsertest.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 3bbf8da..3ada3ac 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -558,6 +558,7 @@
'browser/chromeos/drive/stale_cache_files_remover_unittest.cc',
'browser/chromeos/extensions/default_app_order_unittest.cc',
'browser/chromeos/extensions/file_browser_notifications_unittest.cc',
+ 'browser/chromeos/extensions/file_browser_resource_throttle_unittest.cc',
'browser/chromeos/extensions/wallpaper_private_api_unittest.cc',
'browser/chromeos/external_metrics_unittest.cc',
'browser/chromeos/imageburner/burn_manager_unittest.cc',
diff --git a/chrome/common/extensions/file_browser_handler.cc b/chrome/common/extensions/file_browser_handler.cc
index 8d77497..1587935 100644
--- a/chrome/common/extensions/file_browser_handler.cc
+++ b/chrome/common/extensions/file_browser_handler.cc
@@ -41,6 +41,8 @@ const char* const kMIMETypeHandlersWhitelist[] = {
// static
bool FileBrowserHandler::ExtensionWhitelistedForMIMETypes(
const std::string& extension_id) {
+ if (g_test_extension_id_ && extension_id == *g_test_extension_id_)
+ return true;
for (size_t i = 0; i < arraysize(kMIMETypeHandlersWhitelist); ++i) {
if (extension_id == kMIMETypeHandlersWhitelist[i])
return true;
@@ -48,6 +50,16 @@ bool FileBrowserHandler::ExtensionWhitelistedForMIMETypes(
return false;
}
+// static
+std::vector<std::string> FileBrowserHandler::GetMIMETypeWhitelist() {
+ std::vector<std::string> whitelist;
+ if (g_test_extension_id_)
+ whitelist.push_back(*g_test_extension_id_);
+ for (size_t i = 0; i < arraysize(kMIMETypeHandlersWhitelist); ++i)
+ whitelist.push_back(kMIMETypeHandlersWhitelist[i]);
+ return whitelist;
+}
+
FileBrowserHandler::FileBrowserHandler()
: file_access_permission_flags_(kPermissionsNotDefined) {
}
@@ -111,3 +123,5 @@ bool FileBrowserHandler::HasCreateAccessPermission() const {
DCHECK(!(file_access_permission_flags_ & kInvalidPermission));
return (file_access_permission_flags_ & kCreatePermission) != 0;
}
+
+std::string* FileBrowserHandler::g_test_extension_id_ = NULL;
diff --git a/chrome/common/extensions/file_browser_handler.h b/chrome/common/extensions/file_browser_handler.h
index 06d568d..90e2384 100644
--- a/chrome/common/extensions/file_browser_handler.h
+++ b/chrome/common/extensions/file_browser_handler.h
@@ -23,6 +23,15 @@ class FileBrowserHandler {
// MIME type filters.
static bool ExtensionWhitelistedForMIMETypes(const std::string& extension_id);
+ // Returns list of extensions' ids that are allowed to use MIME type filters.
+ static std::vector<std::string> GetMIMETypeWhitelist();
+
+ // Whitelists the extension to use MIME type filters for a test.
+ // |extension_id| should be owned by the test code.
+ static void set_extension_whitelisted_for_test(std::string* extension_id) {
+ g_test_extension_id_ = extension_id;
+ }
+
FileBrowserHandler();
~FileBrowserHandler();
@@ -75,6 +84,10 @@ class FileBrowserHandler {
bool HasCreateAccessPermission() const;
private:
+ // The id of the extension that will be whitelisted to use MIME type filters
+ // during tests.
+ static std::string* g_test_extension_id_;
+
// The id for the extension this action belongs to (as defined in the
// extension manifest).
std::string extension_id_;
diff --git a/chrome/test/data/extensions/api_test/file_browser/handle_mime_type/background.js b/chrome/test/data/extensions/api_test/file_browser/handle_mime_type/background.js
new file mode 100644
index 0000000..16864b3
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/file_browser/handle_mime_type/background.js
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// True iff the notifyFail has alerady been called.
+var hasFailed = false;
+
+chrome.fileBrowserHandler.onExecuteContentHandler.addListener(
+ function(mime_type, content_url) {
+ // The tests are setup so resources with MIME type 'application/msword' are
+ // meant to be handled by the extension. The extension getting an event with
+ // the MIME type 'application/msword' means the test has succeeded.
+ if (mime_type == 'application/msword') {
+ chrome.test.notifyPass();
+ return;
+ }
+
+ // The tests are setup so resources with MIME type 'plain/text' are meant to
+ // be handled by the browser (i.e. downloaded). The extension getting event
+ // with MIME type 'plain/text' is thus a failure.
+ if (mime_type == 'plain/text') {
+ chrome.test.notifyFail(
+ 'Unexpected request to handle "plain/text" MIME type.');
+ // Set |hasFailed| so notifyPass doesn't get called later (when event with
+ // MIME type 'test/done' is received).
+ hasFailed = true;
+ return;
+ }
+
+ // MIME type 'test/done' is received only when tests for which no events
+ // should be raised to notify the extension it's job is done. If the extension
+ // receives the 'test/done' and there were no previous failures, notify that
+ // the test has succeeded.
+ if (!hasFailed && mime_type == 'test/done')
+ chrome.test.notifyPass();
+});
diff --git a/chrome/test/data/extensions/api_test/file_browser/handle_mime_type/manifest.json b/chrome/test/data/extensions/api_test/file_browser/handle_mime_type/manifest.json
new file mode 100644
index 0000000..7b85ce1
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/file_browser/handle_mime_type/manifest.json
@@ -0,0 +1,20 @@
+{
+ "name": "Test Download redirect",
+ "version": "1",
+ "manifest_version": 2,
+ "background": {
+ "scripts": ["background.js"]
+ },
+ "file_browser_handlers": [
+ {
+ "id": "Some ID",
+ "default_title": "Some title",
+ "file_filters": [
+ "filesystem:*.doc"
+ ]
+ }
+ ],
+ "permissions": [
+ "fileBrowserHandler"
+ ]
+}
diff --git a/content/browser/loader/buffered_resource_handler.cc b/content/browser/loader/buffered_resource_handler.cc
index 47d00df..44d7b22 100644
--- a/content/browser/loader/buffered_resource_handler.cc
+++ b/content/browser/loader/buffered_resource_handler.cc
@@ -315,7 +315,8 @@ bool BufferedResourceHandler::SelectNextHandler(bool* defer) {
if (!info->allow_download())
return true;
- if (!MustDownload()) {
+ bool must_download = MustDownload();
+ if (!must_download) {
if (net::IsSupportedMimeType(mime_type))
return true;
@@ -339,6 +340,7 @@ bool BufferedResourceHandler::SelectNextHandler(bool* defer) {
host_->CreateResourceHandlerForDownload(
request_,
true, // is_content_initiated
+ must_download,
scoped_ptr<DownloadSaveInfo>(new DownloadSaveInfo()),
DownloadResourceHandler::OnStartedCallback()));
return UseAlternateNextHandler(handler.Pass());
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc
index 420b087..fd945c7 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -550,7 +550,8 @@ net::Error ResourceDispatcherHostImpl::BeginDownload(
// |started_callback|.
scoped_ptr<ResourceHandler> handler(
CreateResourceHandlerForDownload(request.get(), is_content_initiated,
- save_info.Pass(), started_callback));
+ true, save_info.Pass(),
+ started_callback));
BeginRequestInternal(request.Pass(), handler.Pass());
@@ -579,6 +580,7 @@ scoped_ptr<ResourceHandler>
ResourceDispatcherHostImpl::CreateResourceHandlerForDownload(
net::URLRequest* request,
bool is_content_initiated,
+ bool must_download,
scoped_ptr<DownloadSaveInfo> save_info,
const DownloadResourceHandler::OnStartedCallback& started_cb) {
scoped_ptr<ResourceHandler> handler(
@@ -591,7 +593,7 @@ ResourceDispatcherHostImpl::CreateResourceHandlerForDownload(
delegate_->DownloadStarting(
request, request_info->GetContext(), request_info->GetChildID(),
request_info->GetRouteID(), request_info->GetRequestID(),
- is_content_initiated, &throttles);
+ is_content_initiated, must_download, &throttles);
if (!throttles.empty()) {
handler.reset(
new ThrottlingResourceHandler(
diff --git a/content/browser/loader/resource_dispatcher_host_impl.h b/content/browser/loader/resource_dispatcher_host_impl.h
index 20b3d55..0b88ecd 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.h
+++ b/content/browser/loader/resource_dispatcher_host_impl.h
@@ -204,6 +204,7 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl
scoped_ptr<ResourceHandler> CreateResourceHandlerForDownload(
net::URLRequest* request,
bool is_content_initiated,
+ bool must_download,
scoped_ptr<DownloadSaveInfo> save_info,
const DownloadResourceHandler::OnStartedCallback& started_cb);
diff --git a/content/public/browser/resource_dispatcher_host_delegate.cc b/content/public/browser/resource_dispatcher_host_delegate.cc
index 0739a11..ccca655 100644
--- a/content/public/browser/resource_dispatcher_host_delegate.cc
+++ b/content/public/browser/resource_dispatcher_host_delegate.cc
@@ -35,6 +35,7 @@ void ResourceDispatcherHostDelegate::DownloadStarting(
int route_id,
int request_id,
bool is_content_initiated,
+ bool must_download,
ScopedVector<ResourceThrottle>* throttles) {
}
diff --git a/content/public/browser/resource_dispatcher_host_delegate.h b/content/public/browser/resource_dispatcher_host_delegate.h
index bee58fb..890f1c7 100644
--- a/content/public/browser/resource_dispatcher_host_delegate.h
+++ b/content/public/browser/resource_dispatcher_host_delegate.h
@@ -66,11 +66,7 @@ class CONTENT_EXPORT ResourceDispatcherHostDelegate {
ScopedVector<ResourceThrottle>* throttles);
// Allows an embedder to add additional resource handlers for a download.
- // |is_new_request| is true if this is a request that is just starting, i.e.
- // the content layer has just added its own resource handlers; it's false if
- // this was originally a non-download request that had some resource handlers
- // applied already and now we found out it's a download.
- // |in_complete| is true if this is invoked from |OnResponseCompleted|.
+ // |must_download| is set if the request must be handled as a download.
virtual void DownloadStarting(
net::URLRequest* request,
ResourceContext* resource_context,
@@ -78,6 +74,7 @@ class CONTENT_EXPORT ResourceDispatcherHostDelegate {
int route_id,
int request_id,
bool is_content_initiated,
+ bool must_download,
ScopedVector<ResourceThrottle>* throttles);
// Called when an SSL Client Certificate is requested. If false is returned,