summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlazyboy@chromium.org <lazyboy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-03 09:01:54 +0000
committerlazyboy@chromium.org <lazyboy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-03 09:01:54 +0000
commitf85f5037bcba6e52fedae883add00b39f824edc3 (patch)
tree60a6c3fd2830a90d7c18fc6700f1d144bba04ef3
parent090374df3d55fbe4e173d78e43684aa93675063d (diff)
downloadchromium_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
-rw-r--r--android_webview/native/aw_web_contents_delegate.cc10
-rw-r--r--android_webview/native/aw_web_contents_delegate.h6
-rw-r--r--chrome/browser/android/chrome_web_contents_delegate_android.cc9
-rw-r--r--chrome/browser/android/chrome_web_contents_delegate_android.h5
-rw-r--r--chrome/browser/download/download_request_limiter.cc57
-rw-r--r--chrome/browser/download/download_request_limiter.h12
-rw-r--r--chrome/browser/extensions/web_view_browsertest.cc113
-rw-r--r--chrome/browser/prerender/prerender_contents.cc10
-rw-r--r--chrome/browser/ui/search/instant_loader.cc7
-rw-r--r--chrome/browser/ui/search/instant_loader.h5
-rw-r--r--chrome/browser/ui/views/external_tab_container_win.cc10
-rw-r--r--chrome/browser/ui/views/external_tab_container_win.h5
-rw-r--r--chrome/renderer/resources/extensions/web_view_experimental.js3
-rw-r--r--chrome/test/data/extensions/platform_apps/web_view/download/embedder.html11
-rw-r--r--chrome/test/data/extensions/platform_apps/web_view/download/embedder.js43
-rw-r--r--chrome/test/data/extensions/platform_apps/web_view/download/guest.html26
-rw-r--r--chrome/test/data/extensions/platform_apps/web_view/download/manifest.json13
-rw-r--r--chrome/test/data/extensions/platform_apps/web_view/download/test.js7
-rw-r--r--content/browser/browser_plugin/browser_plugin_guest.cc90
-rw-r--r--content/browser/browser_plugin/browser_plugin_guest.h18
-rw-r--r--content/browser/browser_plugin/browser_plugin_message_filter.h2
-rw-r--r--content/browser/web_contents/web_contents_view_guest.cc2
-rw-r--r--content/common/browser_plugin/browser_plugin_constants.cc2
-rw-r--r--content/common/browser_plugin/browser_plugin_constants.h2
-rw-r--r--content/common/browser_plugin/browser_plugin_message_enums.h20
-rw-r--r--content/public/browser/web_contents_delegate.cc10
-rw-r--r--content/public/browser/web_contents_delegate.h6
-rw-r--r--content/renderer/browser_plugin/browser_plugin.cc2
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: