diff options
author | lazyboy@chromium.org <lazyboy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-03 09:01:54 +0000 |
---|---|---|
committer | lazyboy@chromium.org <lazyboy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-03 09:01:54 +0000 |
commit | f85f5037bcba6e52fedae883add00b39f824edc3 (patch) | |
tree | 60a6c3fd2830a90d7c18fc6700f1d144bba04ef3 | |
parent | 090374df3d55fbe4e173d78e43684aa93675063d (diff) | |
download | chromium_src-f85f5037bcba6e52fedae883add00b39f824edc3.zip chromium_src-f85f5037bcba6e52fedae883add00b39f824edc3.tar.gz chromium_src-f85f5037bcba6e52fedae883add00b39f824edc3.tar.bz2 |
permissionrequest API for guest Download.
Exposed event:
event.type = 'download'
event.requestMethod = 'GET'/'POST'...
event.url = url
BUG=141204
TEST=Pending: osx + win (b/c there is a *ViewGuest change).
Added browser_tests:WebViewTest.Download, ran unit_tests:DownloadRequestLimiterTest*
Review URL: https://chromiumcodereview.appspot.com/13037003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@192029 0039d316-1c4b-4281-b951-d872f2087c98
28 files changed, 435 insertions, 71 deletions
diff --git a/android_webview/native/aw_web_contents_delegate.cc b/android_webview/native/aw_web_contents_delegate.cc index ef93550..427ffa3 100644 --- a/android_webview/native/aw_web_contents_delegate.cc +++ b/android_webview/native/aw_web_contents_delegate.cc @@ -52,13 +52,15 @@ void AwWebContentsDelegate::FindReply(WebContents* web_contents, final_update); } -bool AwWebContentsDelegate::CanDownload(content::RenderViewHost* source, - int request_id, - const std::string& request_method) { +void AwWebContentsDelegate::CanDownload( + content::RenderViewHost* source, + int request_id, + const std::string& request_method, + const base::Callback<void(bool)>& callback) { // Android webview intercepts download in its resource dispatcher host // delegate, so should not reach here. NOTREACHED(); - return false; + callback.Run(false); } void AwWebContentsDelegate::AddNewContents(content::WebContents* source, diff --git a/android_webview/native/aw_web_contents_delegate.h b/android_webview/native/aw_web_contents_delegate.h index 500097e..56fed6b 100644 --- a/android_webview/native/aw_web_contents_delegate.h +++ b/android_webview/native/aw_web_contents_delegate.h @@ -27,9 +27,11 @@ class AwWebContentsDelegate const gfx::Rect& selection_rect, int active_match_ordinal, bool final_update) OVERRIDE; - virtual bool CanDownload(content::RenderViewHost* source, + virtual void CanDownload(content::RenderViewHost* source, int request_id, - const std::string& request_method) OVERRIDE; + const std::string& request_method, + const base::Callback<void(bool)>& callback) OVERRIDE; + virtual void AddNewContents(content::WebContents* source, content::WebContents* new_contents, WindowOpenDisposition disposition, diff --git a/chrome/browser/android/chrome_web_contents_delegate_android.cc b/chrome/browser/android/chrome_web_contents_delegate_android.cc index 56f993d..0f6ccd6 100644 --- a/chrome/browser/android/chrome_web_contents_delegate_android.cc +++ b/chrome/browser/android/chrome_web_contents_delegate_android.cc @@ -228,19 +228,20 @@ ChromeWebContentsDelegateAndroid::GetJavaScriptDialogManager() { return GetJavaScriptDialogManagerInstance(); } -bool ChromeWebContentsDelegateAndroid::CanDownload( +void ChromeWebContentsDelegateAndroid::CanDownload( content::RenderViewHost* source, int request_id, - const std::string& request_method) { + const std::string& request_method, + const base::Callback<void(bool)>& callback) { if (request_method == net::HttpRequestHeaders::kGetMethod) { content::DownloadControllerAndroid::Get()->CreateGETDownload( source, request_id); - return false; + callback.Run(false); } // DownloadControllerAndroid::OnPostDownloadStarted() is called for the // started download by the default DownloadUIController::Delegate // implementation. - return true; + callback.Run(true); } void ChromeWebContentsDelegateAndroid::DidNavigateToPendingEntry( diff --git a/chrome/browser/android/chrome_web_contents_delegate_android.h b/chrome/browser/android/chrome_web_contents_delegate_android.h index 867db32..94ee39f 100644 --- a/chrome/browser/android/chrome_web_contents_delegate_android.h +++ b/chrome/browser/android/chrome_web_contents_delegate_android.h @@ -54,9 +54,10 @@ class ChromeWebContentsDelegateAndroid const gfx::RectF& active_rect) OVERRIDE; virtual content::JavaScriptDialogManager* GetJavaScriptDialogManager() OVERRIDE; - virtual bool CanDownload(content::RenderViewHost* source, + virtual void CanDownload(content::RenderViewHost* source, int request_id, - const std::string& request_method) OVERRIDE; + const std::string& request_method, + const base::Callback<void(bool)>& callback) OVERRIDE; virtual void DidNavigateToPendingEntry(content::WebContents* source) OVERRIDE; virtual void DidNavigateMainFramePostCommit( content::WebContents* source) OVERRIDE; diff --git a/chrome/browser/download/download_request_limiter.cc b/chrome/browser/download/download_request_limiter.cc index f09ff68..5f62cdf 100644 --- a/chrome/browser/download/download_request_limiter.cc +++ b/chrome/browser/download/download_request_limiter.cc @@ -16,6 +16,7 @@ #include "content/public/browser/navigation_entry.h" #include "content/public/browser/notification_source.h" #include "content/public/browser/notification_types.h" +#include "content/public/browser/render_process_host.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_delegate.h" @@ -208,7 +209,8 @@ void DownloadRequestLimiter::TabDownloadState::NotifyCallbacks(bool allow) { // DownloadRequestLimiter ------------------------------------------------------ -DownloadRequestLimiter::DownloadRequestLimiter() { +DownloadRequestLimiter::DownloadRequestLimiter() + : factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { } DownloadRequestLimiter::~DownloadRequestLimiter() { @@ -273,11 +275,48 @@ void DownloadRequestLimiter::CanDownload(int render_process_host_id, return; } - CanDownloadImpl( - originating_contents, + if (!originating_contents->GetDelegate()) { + ScheduleNotification(callback, false); + return; + } + + // Note that because |originating_contents| might go away before + // OnCanDownloadDecided is invoked, we look it up by |render_process_host_id| + // and |render_view_id|. + base::Callback<void(bool)> can_download_callback = base::Bind( + &DownloadRequestLimiter::OnCanDownloadDecided, + factory_.GetWeakPtr(), + render_process_host_id, + render_view_id, request_id, request_method, callback); + + originating_contents->GetDelegate()->CanDownload( + originating_contents->GetRenderViewHost(), + request_id, + request_method, + can_download_callback); +} + +void DownloadRequestLimiter::OnCanDownloadDecided( + int render_process_host_id, + int render_view_id, + int request_id, + const std::string& request_method, + const Callback& orig_callback, bool allow) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + WebContents* originating_contents = + tab_util::GetWebContentsByID(render_process_host_id, render_view_id); + if (!originating_contents || !allow) { + ScheduleNotification(orig_callback, false); + return; + } + + CanDownloadImpl(originating_contents, + request_id, + request_method, + orig_callback); } void DownloadRequestLimiter::CanDownloadImpl(WebContents* originating_contents, @@ -286,18 +325,6 @@ void DownloadRequestLimiter::CanDownloadImpl(WebContents* originating_contents, const Callback& callback) { DCHECK(originating_contents); - // FYI: Chrome Frame overrides CanDownload in ExternalTabContainer in order - // to cancel the download operation in chrome and let the host browser - // take care of it. - if (originating_contents->GetDelegate() && - !originating_contents->GetDelegate()->CanDownload( - originating_contents->GetRenderViewHost(), - request_id, - request_method)) { - ScheduleNotification(callback, false); - return; - } - // If the tab requesting the download is a constrained popup that is not // shown, treat the request as if it came from the parent. WebContents* effective_contents = originating_contents; diff --git a/chrome/browser/download/download_request_limiter.h b/chrome/browser/download/download_request_limiter.h index 16ebfaf..05b3b73 100644 --- a/chrome/browser/download/download_request_limiter.h +++ b/chrome/browser/download/download_request_limiter.h @@ -218,6 +218,14 @@ class DownloadRequestLimiter const std::string& request_method, const Callback& callback); + // Invoked when decision to download has been made. + void OnCanDownloadDecided(int render_process_host_id, + int render_view_id, + int request_id, + const std::string& request_method, + const Callback& orig_callback, + bool allow); + // Invoked on the UI thread. Schedules a call to NotifyCallback on the io // thread. void ScheduleNotification(const Callback& callback, bool allow); @@ -234,6 +242,10 @@ class DownloadRequestLimiter typedef std::map<content::WebContents*, TabDownloadState*> StateMap; StateMap state_map_; + // Weak ptr factory used when |CanDownload| asks the delegate asynchronously + // about the download. + base::WeakPtrFactory<DownloadRequestLimiter> factory_; + DISALLOW_COPY_AND_ASSIGN(DownloadRequestLimiter); }; diff --git a/chrome/browser/extensions/web_view_browsertest.cc b/chrome/browser/extensions/web_view_browsertest.cc index df713f7..b4004de 100644 --- a/chrome/browser/extensions/web_view_browsertest.cc +++ b/chrome/browser/extensions/web_view_browsertest.cc @@ -56,6 +56,74 @@ class MockWebContentsDelegate : public content::WebContentsDelegate { private: bool requested_; scoped_refptr<content::MessageLoopRunner> message_loop_runner_; + + DISALLOW_COPY_AND_ASSIGN(MockWebContentsDelegate); +}; + +// This class intercepts download request from the guest. +class MockDownloadWebContentsDelegate : public content::WebContentsDelegate { + public: + explicit MockDownloadWebContentsDelegate( + content::WebContentsDelegate* orig_delegate) + : orig_delegate_(orig_delegate), + waiting_for_decision_(false), + expect_allow_(false), + decision_made_(false), + last_download_allowed_(false) {} + virtual ~MockDownloadWebContentsDelegate() {} + + virtual void CanDownload( + content::RenderViewHost* render_view_host, + int request_id, + const std::string& request_method, + const base::Callback<void(bool)>& callback) OVERRIDE { + orig_delegate_->CanDownload( + render_view_host, request_id, request_method, + base::Bind(&MockDownloadWebContentsDelegate::DownloadDecided, + base::Unretained(this))); + } + + void WaitForCanDownload(bool expect_allow) { + EXPECT_FALSE(waiting_for_decision_); + waiting_for_decision_ = true; + + if (decision_made_) { + EXPECT_EQ(expect_allow, last_download_allowed_); + return; + } + + expect_allow_ = expect_allow; + message_loop_runner_ = new content::MessageLoopRunner; + message_loop_runner_->Run(); + } + + void DownloadDecided(bool allow) { + EXPECT_FALSE(decision_made_); + decision_made_ = true; + + if (waiting_for_decision_) { + EXPECT_EQ(expect_allow_, allow); + if (message_loop_runner_) + message_loop_runner_->Quit(); + return; + } + last_download_allowed_ = allow; + } + + void Reset() { + waiting_for_decision_ = false; + decision_made_ = false; + } + + private: + content::WebContentsDelegate* orig_delegate_; + bool waiting_for_decision_; + bool expect_allow_; + bool decision_made_; + bool last_download_allowed_; + scoped_refptr<content::MessageLoopRunner> message_loop_runner_; + + DISALLOW_COPY_AND_ASSIGN(MockDownloadWebContentsDelegate); }; class WebViewTest : public extensions::PlatformAppBrowserTest { @@ -860,3 +928,48 @@ IN_PROC_BROWSER_TEST_F(WebViewTest, MAYBE_NewWindow) { ASSERT_TRUE(RunPlatformAppTest("platform_apps/web_view/newwindow")) << message_; } + +IN_PROC_BROWSER_TEST_F(WebViewTest, DownloadPermission) { + ASSERT_TRUE(StartTestServer()); // For serving guest pages. + LoadAndLaunchPlatformApp("web_view/download"); + + GURL::Replacements replace_host; + std::string host_str("localhost"); // Must stay in scope with replace_host. + replace_host.SetHostStr(host_str); + + // Grab the guest's WebContents. + GURL guest_url = test_server()->GetURL( + "files/extensions/platform_apps/web_view/download/guest.html"); + guest_url = guest_url.ReplaceComponents(replace_host); + ui_test_utils::UrlLoadObserver observer( + guest_url, content::NotificationService::AllSources()); + observer.Wait(); + content::Source<content::NavigationController> source = observer.source(); + EXPECT_TRUE(source->GetWebContents()->GetRenderProcessHost()->IsGuest()); + content::WebContents* guest_web_contents = source->GetWebContents(); + + // Replace WebContentsDelegate with mock version so we can intercept download + // requests. + content::WebContentsDelegate* delegate = guest_web_contents->GetDelegate(); + MockDownloadWebContentsDelegate* mock_delegate = + new MockDownloadWebContentsDelegate(delegate); + guest_web_contents->SetDelegate(mock_delegate); + + // Start test. + // 1. Guest requests a download that its embedder denies. + EXPECT_TRUE(content::ExecuteScript(guest_web_contents, + "startDownload('download-link-1')")); + mock_delegate->WaitForCanDownload(false); // Expect to not allow. + mock_delegate->Reset(); + + // 2. Guest requests a download that its embedder allows. + EXPECT_TRUE(content::ExecuteScript(guest_web_contents, + "startDownload('download-link-2')")); + mock_delegate->WaitForCanDownload(true); // Expect to allow. + mock_delegate->Reset(); + + // 3. Guest requests a download that its embedder ignores, this implies deny. + EXPECT_TRUE(content::ExecuteScript(guest_web_contents, + "startDownload('download-link-3')")); + mock_delegate->WaitForCanDownload(false); // Expect to not allow. +} diff --git a/chrome/browser/prerender/prerender_contents.cc b/chrome/browser/prerender/prerender_contents.cc index e82ad17..ff32646 100644 --- a/chrome/browser/prerender/prerender_contents.cc +++ b/chrome/browser/prerender/prerender_contents.cc @@ -78,12 +78,14 @@ class PrerenderContents::WebContentsDelegateImpl return NULL; } - virtual bool CanDownload(RenderViewHost* render_view_host, - int request_id, - const std::string& request_method) OVERRIDE { + virtual void CanDownload( + RenderViewHost* render_view_host, + int request_id, + const std::string& request_method, + const base::Callback<void(bool)>& callback) OVERRIDE { prerender_contents_->Destroy(FINAL_STATUS_DOWNLOAD); // Cancel the download. - return false; + callback.Run(false); } virtual bool ShouldCreateWebContents( diff --git a/chrome/browser/ui/search/instant_loader.cc b/chrome/browser/ui/search/instant_loader.cc index 092c835..29f4b74 100644 --- a/chrome/browser/ui/search/instant_loader.cc +++ b/chrome/browser/ui/search/instant_loader.cc @@ -195,11 +195,12 @@ void InstantLoader::WebContentsFocused(content::WebContents* /* contents */) { delegate_->OnFocus(); } -bool InstantLoader::CanDownload(content::RenderViewHost* /* render_view_host */, +void InstantLoader::CanDownload(content::RenderViewHost* /* render_view_host */, int /* request_id */, - const std::string& /* request_method */) { + const std::string& /* request_method */, + const base::Callback<void(bool)>& callback) { // Downloads are disabled. - return false; + callback.Run(false); } void InstantLoader::HandleMouseDown() { diff --git a/chrome/browser/ui/search/instant_loader.h b/chrome/browser/ui/search/instant_loader.h index 1d5c693..251c43b 100644 --- a/chrome/browser/ui/search/instant_loader.h +++ b/chrome/browser/ui/search/instant_loader.h @@ -103,9 +103,10 @@ class InstantLoader : public content::NotificationObserver, virtual bool ShouldFocusPageAfterCrash() OVERRIDE; virtual void LostCapture() OVERRIDE; virtual void WebContentsFocused(content::WebContents* contents) OVERRIDE; - virtual bool CanDownload(content::RenderViewHost* render_view_host, + virtual void CanDownload(content::RenderViewHost* render_view_host, int request_id, - const std::string& request_method) OVERRIDE; + const std::string& request_method, + const base::Callback<void(bool)>& callback) OVERRIDE; virtual void HandleMouseDown() OVERRIDE; virtual void HandleMouseUp() OVERRIDE; virtual void HandlePointerActivate() OVERRIDE; diff --git a/chrome/browser/ui/views/external_tab_container_win.cc b/chrome/browser/ui/views/external_tab_container_win.cc index 8530638..e39b66c 100644 --- a/chrome/browser/ui/views/external_tab_container_win.cc +++ b/chrome/browser/ui/views/external_tab_container_win.cc @@ -664,9 +664,11 @@ bool ExternalTabContainerWin::TakeFocus(content::WebContents* source, return true; } -bool ExternalTabContainerWin::CanDownload(RenderViewHost* render_view_host, - int request_id, - const std::string& request_method) { +void ExternalTabContainerWin::CanDownload( + RenderViewHost* render_view_host, + int request_id, + const std::string& request_method, + const base::Callback<void(bool)>& callback) { if (load_requests_via_automation_) { if (automation_) { // In case the host needs to show UI that needs to take the focus. @@ -686,7 +688,7 @@ bool ExternalTabContainerWin::CanDownload(RenderViewHost* render_view_host, } // Never allow downloads. - return false; + callback.Run(false); } void ExternalTabContainerWin::RegisterRenderViewHostForAutomation( diff --git a/chrome/browser/ui/views/external_tab_container_win.h b/chrome/browser/ui/views/external_tab_container_win.h index 185f458..b9d695cc 100644 --- a/chrome/browser/ui/views/external_tab_container_win.h +++ b/chrome/browser/ui/views/external_tab_container_win.h @@ -126,9 +126,10 @@ class ExternalTabContainerWin : public ExternalTabContainer, content::WebContents* source, const content::NativeWebKeyboardEvent& event) OVERRIDE; virtual bool TakeFocus(content::WebContents* source, bool reverse) OVERRIDE; - virtual bool CanDownload(content::RenderViewHost* render_view_host, + virtual void CanDownload(content::RenderViewHost* render_view_host, int request_id, - const std::string& request_method) OVERRIDE; + const std::string& request_method, + const base::Callback<void(bool)>& callback) OVERRIDE; virtual bool OnGoToEntryOffset(int offset) OVERRIDE; virtual bool HandleContextMenu( const content::ContextMenuParams& params) OVERRIDE; diff --git a/chrome/renderer/resources/extensions/web_view_experimental.js b/chrome/renderer/resources/extensions/web_view_experimental.js index 4f1e201..7487489 100644 --- a/chrome/renderer/resources/extensions/web_view_experimental.js +++ b/chrome/renderer/resources/extensions/web_view_experimental.js @@ -16,7 +16,7 @@ var WebView = require('webView').WebView; var forEach = require('utils').forEach; /** @type {Array.<string>} */ -var PERMISSION_TYPES = ['media', 'geolocation', 'pointerLock']; +var PERMISSION_TYPES = ['download', 'media', 'geolocation', 'pointerLock']; /** @type {string} */ var REQUEST_TYPE_NEW_WINDOW = 'newwindow'; @@ -28,6 +28,7 @@ var ERROR_MSG_PERMISSION_ALREADY_DECIDED = '<webview>: ' + var EXPOSED_PERMISSION_EVENT_ATTRIBS = [ 'lastUnlockedBySelf', 'permission', + 'requestMethod', 'url', 'userGesture' ]; diff --git a/chrome/test/data/extensions/platform_apps/web_view/download/embedder.html b/chrome/test/data/extensions/platform_apps/web_view/download/embedder.html new file mode 100644 index 0000000..d350e46 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/web_view/download/embedder.html @@ -0,0 +1,11 @@ +<!-- + * Copyright 2013 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. +--> +<html> +<body> + <div id="webview-tag-container"></div> + <script src="embedder.js"></script> +</body> +</html> diff --git a/chrome/test/data/extensions/platform_apps/web_view/download/embedder.js b/chrome/test/data/extensions/platform_apps/web_view/download/embedder.js new file mode 100644 index 0000000..ec4e9b7 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/web_view/download/embedder.js @@ -0,0 +1,43 @@ +// Copyright 2013 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. + +var embedder = {}; +embedder.baseGuestURL = ''; +embedder.guestURL = ''; + +/** @private */ +embedder.setUpGuest_ = function() { + document.querySelector('#webview-tag-container').innerHTML = + '<webview style="width: 100px; height: 100px;"' + + ' src="' + embedder.guestURL + '"' + + '></webview>'; + var webview = document.querySelector('webview'); + if (!webview) { + chrome.test.fail('No <webview> element created'); + return null; + } + + webview.addEventListener('permissionrequest', function(e) { + if (e.permission == 'download') { + var url = e.url; + if (url.indexOf('expect-deny.zip') != -1) { + e.request.deny(); + } else if (url.indexOf('expect-allow.zip') != -1) { + e.request.allow(); + } else { + // Ignore. + } + } + }); + return webview; +}; + +onload = function() { + chrome.test.getConfig(function(config) { + embedder.baseGuestURL = 'http://localhost:' + config.testServer.port; + embedder.guestURL = embedder.baseGuestURL + + '/files/extensions/platform_apps/web_view/download/guest.html'; + embedder.setUpGuest_(); + }); +}; diff --git a/chrome/test/data/extensions/platform_apps/web_view/download/guest.html b/chrome/test/data/extensions/platform_apps/web_view/download/guest.html new file mode 100644 index 0000000..87565a1 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/web_view/download/guest.html @@ -0,0 +1,26 @@ +<!-- + * Copyright 2013 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. +--> +<html> + <head> + <script type="text/javascript"> + function startDownload(elementId) { + document.getElementById(elementId).click(); + } + </script> + </head> + <body> + <div>This is guest that initiates download.</div> + <a href="expect-deny.zip" + download="download" + id="download-link-1">Embedder is expected to deny this download link.</a> + <a href="expect-allow.zip" + download="download" + id="download-link-2">Embedder is expected to allow this download link.</a> + <a href="expect-ignore.zip" + download="download" + id="download-link-3">Embedder is expected to ignore this download link (which is equivalent of denying).</a> + </body> +</html> diff --git a/chrome/test/data/extensions/platform_apps/web_view/download/manifest.json b/chrome/test/data/extensions/platform_apps/web_view/download/manifest.json new file mode 100644 index 0000000..d639fe3 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/web_view/download/manifest.json @@ -0,0 +1,13 @@ +{ + "name": "Platform App Test: <webview> download permission API", + "description": "Loads a guest which initiates download", + "version": "1", + "permissions": [ + "webview" + ], + "app": { + "background": { + "scripts": ["test.js"] + } + } +} diff --git a/chrome/test/data/extensions/platform_apps/web_view/download/test.js b/chrome/test/data/extensions/platform_apps/web_view/download/test.js new file mode 100644 index 0000000..1886432 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/web_view/download/test.js @@ -0,0 +1,7 @@ +// Copyright 2013 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. + +chrome.app.runtime.onLaunched.addListener(function() { + chrome.app.window.create('embedder.html', {}, function () {}); +}); diff --git a/content/browser/browser_plugin/browser_plugin_guest.cc b/content/browser/browser_plugin/browser_plugin_guest.cc index 4d7dfd5..6240790 100644 --- a/content/browser/browser_plugin/browser_plugin_guest.cc +++ b/content/browser/browser_plugin/browser_plugin_guest.cc @@ -13,6 +13,8 @@ #include "content/browser/browser_plugin/browser_plugin_guest_helper.h" #include "content/browser/browser_plugin/browser_plugin_guest_manager.h" #include "content/browser/browser_plugin/browser_plugin_host_factory.h" +#include "content/browser/browser_thread_impl.h" +#include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" @@ -37,6 +39,7 @@ #include "content/public/common/media_stream_request.h" #include "content/public/common/result_codes.h" #include "net/base/net_errors.h" +#include "net/url_request/url_request.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebCursorInfo.h" #include "ui/surface/transport_dib.h" #include "webkit/glue/resource_type.h" @@ -77,6 +80,21 @@ static std::string WindowOpenDispositionToString( } } +// Called on IO thread. +static std::string RetrieveDownloadURLFromRequestId( + RenderViewHost* render_view_host, + int url_request_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + int render_process_id = render_view_host->GetProcess()->GetID(); + GlobalRequestID global_id(render_process_id, url_request_id); + net::URLRequest* url_request = + ResourceDispatcherHostImpl::Get()->GetURLRequest(global_id); + if (url_request) + return url_request->url().possibly_invalid_spec(); + return ""; +} + } class BrowserPluginGuest::EmbedderRenderViewHostObserver @@ -309,14 +327,32 @@ void BrowserPluginGuest::AddNewContents(WebContents* source, disposition, initial_pos, user_gesture); } -bool BrowserPluginGuest::CanDownload(RenderViewHost* render_view_host, - int request_id, - const std::string& request_method) { - // TODO(fsamuel): We disable downloads in guests for now, but we will later - // expose API to allow embedders to handle them. - // Note: it seems content_shell ignores this. This should be fixed - // for debugging and test purposes. - return false; +void BrowserPluginGuest::CanDownload( + RenderViewHost* render_view_host, + int request_id, + const std::string& request_method, + const base::Callback<void(bool)>& callback) { + if (download_request_callback_map_.size() >= + kNumMaxOutstandingPermissionRequests) { + // Deny the download request. + callback.Run(false); + return; + } + + // TODO(lazyboy): Remove download specific map + // |download_request_callback_map_| once we have generalized request items for + // all permission types. + int permission_request_id = next_permission_request_id_++; + download_request_callback_map_[permission_request_id] = callback; + + BrowserThread::PostTaskAndReplyWithResult( + BrowserThread::IO, FROM_HERE, + base::Bind(&RetrieveDownloadURLFromRequestId, + render_view_host, request_id), + base::Bind(&BrowserPluginGuest::DidRetrieveDownloadURLFromRequestId, + weak_ptr_factory_.GetWeakPtr(), + request_method, + permission_request_id)); } bool BrowserPluginGuest::HandleContextMenu( @@ -958,6 +994,9 @@ void BrowserPluginGuest::OnRespondPermission( int request_id, bool should_allow) { switch (permission_type) { + case BrowserPluginPermissionTypeDownload: + OnRespondPermissionDownload(request_id, should_allow); + break; case BrowserPluginPermissionTypeGeolocation: OnRespondPermissionGeolocation(request_id, should_allow); break; @@ -1170,6 +1209,20 @@ void BrowserPluginGuest::OnUpdateRect( new BrowserPluginMsg_UpdateRect(instance_id(), relay_params)); } +void BrowserPluginGuest::OnRespondPermissionDownload(int request_id, + bool should_allow) { + DownloadRequestMap::iterator download_request_iter = + download_request_callback_map_.find(request_id); + if (download_request_iter == download_request_callback_map_.end()) { + LOG(INFO) << "Not a valid request ID."; + return; + } + + const base::Callback<void(bool)>& can_download_callback = + download_request_iter->second; + can_download_callback.Run(should_allow); +} + void BrowserPluginGuest::OnRespondPermissionGeolocation( int request_id, bool should_allow) { if (should_allow && embedder_web_contents_) { @@ -1249,4 +1302,25 @@ void BrowserPluginGuest::OnRespondPermissionNewWindow( new_window_request_map_.erase(new_window_request_iter); } +void BrowserPluginGuest::DidRetrieveDownloadURLFromRequestId( + const std::string& request_method, + int permission_request_id, + const std::string& url) { + if (url.empty()) { + OnRespondPermissionDownload(permission_request_id, + false /* should_allow */); + return; + } + + base::DictionaryValue request_info; + request_info.Set(browser_plugin::kRequestMethod, + base::Value::CreateStringValue(request_method)); + request_info.Set(browser_plugin::kURL, base::Value::CreateStringValue(url)); + + SendMessageToEmbedder( + new BrowserPluginMsg_RequestPermission(instance_id(), + BrowserPluginPermissionTypeDownload, permission_request_id, + request_info)); +} + } // namespace content diff --git a/content/browser/browser_plugin/browser_plugin_guest.h b/content/browser/browser_plugin/browser_plugin_guest.h index aaa2cc9..605cafc 100644 --- a/content/browser/browser_plugin/browser_plugin_guest.h +++ b/content/browser/browser_plugin/browser_plugin_guest.h @@ -172,9 +172,10 @@ class CONTENT_EXPORT BrowserPluginGuest : public NotificationObserver, const gfx::Rect& initial_pos, bool user_gesture, bool* was_blocked) OVERRIDE; - virtual bool CanDownload(RenderViewHost* render_view_host, + virtual void CanDownload(RenderViewHost* render_view_host, int request_id, - const std::string& request_method) OVERRIDE; + const std::string& request_method, + const base::Callback<void(bool)>& callback) OVERRIDE; virtual bool HandleContextMenu(const ContextMenuParams& params) OVERRIDE; virtual void WebContentsCreated(WebContents* source_contents, int64 source_frame_id, @@ -219,7 +220,7 @@ class CONTENT_EXPORT BrowserPluginGuest : public NotificationObserver, void Attach(WebContentsImpl* embedder_web_contents, BrowserPluginHostMsg_CreateGuest_Params params); - // Requests geolocation permission through embedder js api. + // Requests geolocation permission through Embedder JavaScript API. void AskEmbedderForGeolocationPermission(int bridge_id, const GURL& requesting_frame, const GeolocationCallback& callback); @@ -228,6 +229,7 @@ class CONTENT_EXPORT BrowserPluginGuest : public NotificationObserver, // Embedder sets permission to allow or deny geolocation request. void SetGeolocationPermission(int request_id, bool allowed); + // Allow the embedder to call this for unhandled messages when // BrowserPluginGuest is already destroyed. static void AcknowledgeBufferPresent(int route_id, @@ -388,10 +390,17 @@ class CONTENT_EXPORT BrowserPluginGuest : public NotificationObserver, void OnUpdateRect(const ViewHostMsg_UpdateRect_Params& params); // Helpers for |OnRespondPermission|. + void OnRespondPermissionDownload(int request_id, bool should_allow); void OnRespondPermissionGeolocation(int request_id, bool should_allow); void OnRespondPermissionMedia(int request_id, bool should_allow); void OnRespondPermissionNewWindow(int request_id, bool should_allow); + // Requests download permission through embedder JavaScript API after + // retrieving url information from IO thread. + void DidRetrieveDownloadURLFromRequestId(const std::string& request_method, + int permission_request_id, + const std::string& url); + // Weak pointer used to ask GeolocationPermissionContext about geolocation // permission. base::WeakPtrFactory<BrowserPluginGuest> weak_ptr_factory_; @@ -441,6 +450,9 @@ class CONTENT_EXPORT BrowserPluginGuest : public NotificationObserver, typedef std::map<int, int> NewWindowRequestMap; NewWindowRequestMap new_window_request_map_; + typedef std::map<int, base::Callback<void(bool)> > DownloadRequestMap; + DownloadRequestMap download_request_callback_map_; + DISALLOW_COPY_AND_ASSIGN(BrowserPluginGuest); }; diff --git a/content/browser/browser_plugin/browser_plugin_message_filter.h b/content/browser/browser_plugin/browser_plugin_message_filter.h index 327d2f8..829c1ba 100644 --- a/content/browser/browser_plugin/browser_plugin_message_filter.h +++ b/content/browser/browser_plugin/browser_plugin_message_filter.h @@ -7,8 +7,6 @@ #include "content/public/browser/browser_message_filter.h" -struct ViewHostMsg_CreateWindow_Params; - namespace content { class BrowserContext; diff --git a/content/browser/web_contents/web_contents_view_guest.cc b/content/browser/web_contents/web_contents_view_guest.cc index 088ebfd..876f684 100644 --- a/content/browser/web_contents/web_contents_view_guest.cc +++ b/content/browser/web_contents/web_contents_view_guest.cc @@ -36,7 +36,7 @@ WebContentsViewGuest::~WebContentsViewGuest() { } gfx::NativeView WebContentsViewGuest::GetNativeView() const { - return NULL; + return platform_view_->GetNativeView(); } gfx::NativeView WebContentsViewGuest::GetContentNativeView() const { diff --git a/content/common/browser_plugin/browser_plugin_constants.cc b/content/common/browser_plugin/browser_plugin_constants.cc index f486917..dab2cab 100644 --- a/content/common/browser_plugin/browser_plugin_constants.cc +++ b/content/common/browser_plugin/browser_plugin_constants.cc @@ -62,6 +62,7 @@ const char kOldURL[] = "oldUrl"; const char kOldHeight[] = "oldHeight"; const char kOldWidth[] = "oldWidth"; const char kPermission[] = "permission"; +const char kPermissionTypeDownload[] = "download"; const char kPermissionTypeGeolocation[] = "geolocation"; const char kPermissionTypeMedia[] = "media"; const char kPermissionTypeNewWindow[] = "newwindow"; @@ -70,6 +71,7 @@ const char kPersistPrefix[] = "persist:"; const char kProcessId[] = "processId"; const char kReason[] = "reason"; const char kRequestId[] = "requestId"; +const char kRequestMethod[] = "requestMethod"; const char kTargetURL[] = "targetUrl"; const char kURL[] = "url"; const char kWindowID[] = "windowId"; diff --git a/content/common/browser_plugin/browser_plugin_constants.h b/content/common/browser_plugin/browser_plugin_constants.h index 3dc489d..ce96acb 100644 --- a/content/common/browser_plugin/browser_plugin_constants.h +++ b/content/common/browser_plugin/browser_plugin_constants.h @@ -63,6 +63,7 @@ extern const char kOldURL[]; extern const char kOldHeight[]; extern const char kOldWidth[]; extern const char kPermission[]; +extern const char kPermissionTypeDownload[]; extern const char kPermissionTypeGeolocation[]; extern const char kPermissionTypeMedia[]; extern const char kPermissionTypeNewWindow[]; @@ -71,6 +72,7 @@ extern const char kPersistPrefix[]; extern const char kProcessId[]; extern const char kReason[]; extern const char kRequestId[]; +extern const char kRequestMethod[]; extern const char kTargetURL[]; extern const char kURL[]; extern const char kUserGesture[]; diff --git a/content/common/browser_plugin/browser_plugin_message_enums.h b/content/common/browser_plugin/browser_plugin_message_enums.h index c4ea346..9fe045a 100644 --- a/content/common/browser_plugin/browser_plugin_message_enums.h +++ b/content/common/browser_plugin/browser_plugin_message_enums.h @@ -9,20 +9,24 @@ enum BrowserPluginPermissionType { // Unknown type of permission request. BrowserPluginPermissionTypeUnknown, - // New window requests. - // Note: Even though new windows don't use the permission API, the new window - // API is sufficiently similar that it's convenient to consider it a - // permission type for code reuse. - BrowserPluginPermissionTypeNewWindow, - - // Media access (audio/video) permission request type. - BrowserPluginPermissionTypeMedia, + // Download. + BrowserPluginPermissionTypeDownload, // Geolocation. BrowserPluginPermissionTypeGeolocation, + // Media access (audio/video) permission request type. + BrowserPluginPermissionTypeMedia, + // PointerLock BrowserPluginPermissionTypePointerLock, + + + // New window requests. + // Note: Even though new windows don't use the permission API, the new window + // API is sufficiently similar that it's convenient to consider it a + // permission type for code reuse. + BrowserPluginPermissionTypeNewWindow, }; #endif // CONTENT_COMMON_BROWSER_PLUGIN_BROWSER_PLUGIN_MESSAGE_ENUMS_H_ diff --git a/content/public/browser/web_contents_delegate.cc b/content/public/browser/web_contents_delegate.cc index 7a4409d..db56c7e 100644 --- a/content/public/browser/web_contents_delegate.cc +++ b/content/public/browser/web_contents_delegate.cc @@ -69,10 +69,12 @@ int WebContentsDelegate::GetExtraRenderViewHeight() const { return 0; } -bool WebContentsDelegate::CanDownload(RenderViewHost* render_view_host, - int request_id, - const std::string& request_method) { - return true; +void WebContentsDelegate::CanDownload( + RenderViewHost* render_view_host, + int request_id, + const std::string& request_method, + const base::Callback<void(bool)>& callback) { + callback.Run(true); } bool WebContentsDelegate::HandleContextMenu( diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h index 6705514..7371bda 100644 --- a/content/public/browser/web_contents_delegate.h +++ b/content/public/browser/web_contents_delegate.h @@ -220,9 +220,11 @@ class CONTENT_EXPORT WebContentsDelegate { virtual void WebContentsFocused(WebContents* contents) {} // Asks the delegate if the given tab can download. - virtual bool CanDownload(RenderViewHost* render_view_host, + // Invoking the |callback| synchronously is OK. + virtual void CanDownload(RenderViewHost* render_view_host, int request_id, - const std::string& request_method); + const std::string& request_method, + const base::Callback<void(bool)>& callback); // Return much extra vertical space should be allotted to the // render view widget during various animations (e.g. infobar closing). diff --git a/content/renderer/browser_plugin/browser_plugin.cc b/content/renderer/browser_plugin/browser_plugin.cc index 636d2f4..f5b0f37 100644 --- a/content/renderer/browser_plugin/browser_plugin.cc +++ b/content/renderer/browser_plugin/browser_plugin.cc @@ -95,6 +95,8 @@ static std::string GetInternalEventName(const char* event_name) { static std::string PermissionTypeToString(BrowserPluginPermissionType type) { switch (type) { + case BrowserPluginPermissionTypeDownload: + return browser_plugin::kPermissionTypeDownload; case BrowserPluginPermissionTypeGeolocation: return browser_plugin::kPermissionTypeGeolocation; case BrowserPluginPermissionTypeMedia: |