summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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,