diff options
author | lazyboy@chromium.org <lazyboy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-09 01:32:49 +0000 |
---|---|---|
committer | lazyboy@chromium.org <lazyboy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-09 01:32:49 +0000 |
commit | e4a55a597c2d3ad0e7b1fad022c510452171a248 (patch) | |
tree | f6f68d9a59401c2cdcfa6fa51ed2f8a72502a4e7 | |
parent | eba66b0c2f522302638803d3c04827b901c95ba5 (diff) | |
download | chromium_src-e4a55a597c2d3ad0e7b1fad022c510452171a248.zip chromium_src-e4a55a597c2d3ad0e7b1fad022c510452171a248.tar.gz chromium_src-e4a55a597c2d3ad0e7b1fad022c510452171a248.tar.bz2 |
<browser>: permissionRequest API for geolocation.
BUG=141195
TEST=Added browsertests: WebViewTest.GeolocationAPI*
Review URL: https://chromiumcodereview.appspot.com/11142012
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@187099 0039d316-1c4b-4281-b951-d872f2087c98
21 files changed, 624 insertions, 10 deletions
diff --git a/chrome/browser/extensions/web_view_browsertest.cc b/chrome/browser/extensions/web_view_browsertest.cc index fb4e900..93fe133 100644 --- a/chrome/browser/extensions/web_view_browsertest.cc +++ b/chrome/browser/extensions/web_view_browsertest.cc @@ -97,6 +97,16 @@ class WebViewTest : public extensions::PlatformAppBrowserTest { extensions::PlatformAppBrowserTest::TearDown(); } + virtual void SetUpOnMainThread() OVERRIDE { + const testing::TestInfo* const test_info = + testing::UnitTest::GetInstance()->current_test_info(); + // Mock out geolocation for geolocation specific tests. + if (!strncmp(test_info->name(), "GeolocationAPI", + strlen("GeolocationAPI"))) { + ui_test_utils::OverrideGeolocation(10, 20); + } + } + // This method is responsible for initializing a packaged app, which contains // multiple webview tags. The tags have different partition identifiers and // their WebContent objects are returned as output. The method also verifies @@ -817,3 +827,22 @@ IN_PROC_BROWSER_TEST_F(WebViewTest, TearDownTest) { LoadAndLaunchPlatformApp("web_view/teardown"); ASSERT_TRUE(second_loaded_listener.WaitUntilSatisfied()); } + +// Embedder does not have geolocation permission for this test. +// No matter what the API does, geolocation permission would be denied. +// Note that the test name prefix must be "GeolocationAPI". +IN_PROC_BROWSER_TEST_F(WebViewTest, GeolocationAPIEmbedderHasNoAccess) { + ASSERT_TRUE(StartTestServer()); // For serving guest pages. + ASSERT_TRUE(RunPlatformAppTest( + "platform_apps/web_view/geolocation/embedder_has_no_permission")) + << message_; +} + +// Embedder has geolocation permission for this test. +// Note that the test name prefix must be "GeolocationAPI". +IN_PROC_BROWSER_TEST_F(WebViewTest, GeolocationAPIEmbedderHasAccess) { + ASSERT_TRUE(StartTestServer()); // For serving guest pages. + ASSERT_TRUE(RunPlatformAppTest( + "platform_apps/web_view/geolocation/embedder_has_permission")) + << message_; +} diff --git a/chrome/renderer/resources/extensions/web_view_experimental.js b/chrome/renderer/resources/extensions/web_view_experimental.js index 4681e24..2b75e38 100644 --- a/chrome/renderer/resources/extensions/web_view_experimental.js +++ b/chrome/renderer/resources/extensions/web_view_experimental.js @@ -14,7 +14,7 @@ var WebView = require('webview').WebView; /** @type {Array.<string>} */ -var PERMISSION_TYPES = ['media']; +var PERMISSION_TYPES = ['media', 'geolocation']; /** @type {string} */ var ERROR_MSG_PERMISSION_ALREADY_DECIDED = '<webview>: ' + diff --git a/chrome/test/data/extensions/platform_apps/web_view/geolocation/embedder_has_no_permission/embedder.html b/chrome/test/data/extensions/platform_apps/web_view/geolocation/embedder_has_no_permission/embedder.html new file mode 100644 index 0000000..d350e46 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/web_view/geolocation/embedder_has_no_permission/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/geolocation/embedder_has_no_permission/embedder.js b/chrome/test/data/extensions/platform_apps/web_view/geolocation/embedder_has_no_permission/embedder.js new file mode 100644 index 0000000..9b24fd3 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/web_view/geolocation/embedder_has_no_permission/embedder.js @@ -0,0 +1,107 @@ +// 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.tests = {}; +embedder.baseGuestURL = ''; +embedder.guestURL = ''; + +embedder.setUp = function(config) { + embedder.baseGuestURL = 'http://localhost:' + config.testServer.port; + embedder.guestURL = embedder.baseGuestURL + + '/files/extensions/platform_apps/web_view/geolocation' + + '/geolocation_access_guest.html'; + chrome.test.log('Guest url is: ' + 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 webview; +}; + +/** @private */ +embedder.setUpLoadStop_ = function(webview, testName) { + var onWebViewLoadStop = function(e) { + // Send post message to <webview> when it's ready to receive them. + webview.contentWindow.postMessage( + JSON.stringify(['check-geolocation-permission', '' + testName]), '*'); + }; + webview.addEventListener('loadstop', onWebViewLoadStop); +}; + +/** @private */ +embedder.registerAndWaitForPostMessage_ = function( + webview, expectedData) { + var testName = expectedData[0]; + var onPostMessageReceived = function(e) { + var data = JSON.parse(e.data); + if (data[0] == '' + testName) { + chrome.test.assertEq(expectedData, data); + chrome.test.succeed(); + } + }; + window.addEventListener('message', onPostMessageReceived); +}; + +/** @private */ +embedder.assertCorrectEvent_ = function(e) { + chrome.test.assertEq('geolocation', e.permission); + chrome.test.assertTrue(!!e.url); + chrome.test.assertTrue(e.url.indexOf(embedder.baseGuestURL) == 0); +}; + +// Tests begin. + +// Embedder does not have geolocation permission, so geolocation access is +// always denied for these tests. + +// Calling deny() results in deny. +embedder.tests.testDenyDenies = function testDenyDenies() { + var webview = embedder.setUpGuest_(); + + var onPermissionRequest = function(e) { + chrome.test.log('Embedder notified on permissionRequest'); + embedder.assertCorrectEvent_(e); + e.request.deny(); + }; + webview.addEventListener('permissionrequest', onPermissionRequest); + + embedder.setUpLoadStop_(webview, 'test1'); + embedder.registerAndWaitForPostMessage_( + webview, ['test1', 'access-denied']); +}; + +// Calling allow() results in deny too. +embedder.tests.testAllowDenies = function testAllowDenies() { + var webview = embedder.setUpGuest_(); + + var onPermissionRequest = function(e) { + chrome.test.log('Embedder notified on permissionRequest'); + embedder.assertCorrectEvent_(e); + e.request.allow(); + }; + webview.addEventListener('permissionrequest', onPermissionRequest); + + embedder.setUpLoadStop_(webview, 'test2'); + embedder.registerAndWaitForPostMessage_( + webview, ['test2', 'access-denied']); +}; + +onload = function() { + chrome.test.getConfig(function(config) { + embedder.setUp(config); + chrome.test.runTests([ + embedder.tests.testDenyDenies, + embedder.tests.testAllowDenies + ]); + }); +}; diff --git a/chrome/test/data/extensions/platform_apps/web_view/geolocation/embedder_has_no_permission/manifest.json b/chrome/test/data/extensions/platform_apps/web_view/geolocation/embedder_has_no_permission/manifest.json new file mode 100644 index 0000000..c30ec57 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/web_view/geolocation/embedder_has_no_permission/manifest.json @@ -0,0 +1,13 @@ +{ + "name": "Platform App Test: <webview> geolocation", + "description": "Loads a guest which requests geolocation", + "version": "1", + "permissions": [ + "webview" + ], + "app": { + "background": { + "scripts": ["test.js"] + } + } +} diff --git a/chrome/test/data/extensions/platform_apps/web_view/geolocation/embedder_has_no_permission/test.js b/chrome/test/data/extensions/platform_apps/web_view/geolocation/embedder_has_no_permission/test.js new file mode 100644 index 0000000..1886432 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/web_view/geolocation/embedder_has_no_permission/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/chrome/test/data/extensions/platform_apps/web_view/geolocation/embedder_has_permission/embedder.html b/chrome/test/data/extensions/platform_apps/web_view/geolocation/embedder_has_permission/embedder.html new file mode 100644 index 0000000..d350e46 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/web_view/geolocation/embedder_has_permission/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/geolocation/embedder_has_permission/embedder.js b/chrome/test/data/extensions/platform_apps/web_view/geolocation/embedder_has_permission/embedder.js new file mode 100644 index 0000000..5fa91c4 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/web_view/geolocation/embedder_has_permission/embedder.js @@ -0,0 +1,112 @@ +// 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.tests = {}; +embedder.baseGuestURL = ''; +embedder.guestURL = ''; + +embedder.setUp = function(config) { + embedder.baseGuestURL = 'http://localhost:' + config.testServer.port; + embedder.guestURL = embedder.baseGuestURL + + '/files/extensions/platform_apps/web_view/geolocation' + + '/geolocation_access_guest.html'; + chrome.test.log('Guest url is: ' + 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 webview; +}; + +/** @private */ +embedder.setUpLoadStop_ = function(webview, testName) { + var onWebViewLoadStop = function(e) { + // Send post message to <webview> when it's ready to receive them. + webview.contentWindow.postMessage( + JSON.stringify(['check-geolocation-permission', '' + testName]), '*'); + }; + webview.addEventListener('loadstop', onWebViewLoadStop); +}; + +/** @private */ +embedder.registerAndWaitForPostMessage_ = function( + webview, expectedData) { + var testName = expectedData[0]; + var onPostMessageReceived = function(e) { + var data = JSON.parse(e.data); + if (data[0] == '' + testName) { + chrome.test.assertEq(expectedData, data); + chrome.test.succeed(); + } + }; + window.addEventListener('message', onPostMessageReceived); +}; + +/** @private */ +embedder.assertCorrectEvent_ = function(e) { + chrome.test.assertEq('geolocation', e.permission); + chrome.test.assertTrue(!!e.url); + chrome.test.assertTrue(e.url.indexOf(embedder.baseGuestURL) == 0); +}; + +// Tests begin. + +// Once the guest is allowed or denied geolocation, the guest notifies the +// embedder about the fact via post message. +// The embedder has to initiate a post message so that the guest can get a +// reference to embedder to send the reply back. + +// Loads a guest which requests geolocation. The embedder (platform app) has +// acccess to geolocation and allows geolocation for the guest. +embedder.tests.testAllow = function testAllow() { + var webview = embedder.setUpGuest_(); + + var onPermissionRequest = function(e) { + chrome.test.log('Embedder notified on permissionRequest'); + embedder.assertCorrectEvent_(e); + e.request.allow(); + }; + webview.addEventListener('permissionrequest', onPermissionRequest); + + embedder.setUpLoadStop_(webview, 'test1'); + // WebViewTest sets (mock) lat, lng to 10, 20. + embedder.registerAndWaitForPostMessage_( + webview, ['test1', 'access-granted', 10, 20]); +}; + +// Loads a guest which requests geolocation. The embedder (platform app) has +// acccess to geolocation, but denies geolocation for the guest. +embedder.tests.testDeny = function testDeny() { + var webview = embedder.setUpGuest_(); + + var onPermissionRequest = function(e) { + chrome.test.log('Embedder notified on permissionRequest'); + embedder.assertCorrectEvent_(e); + e.request.deny(); + }; + webview.addEventListener('permissionrequest', onPermissionRequest); + + embedder.setUpLoadStop_(webview, 'test2'); + embedder.registerAndWaitForPostMessage_( + webview, ['test2', 'access-denied']); +}; + +onload = function() { + chrome.test.getConfig(function(config) { + embedder.setUp(config); + chrome.test.runTests([ + embedder.tests.testAllow, + embedder.tests.testDeny + ]); + }); +}; diff --git a/chrome/test/data/extensions/platform_apps/web_view/geolocation/embedder_has_permission/manifest.json b/chrome/test/data/extensions/platform_apps/web_view/geolocation/embedder_has_permission/manifest.json new file mode 100644 index 0000000..74ad74a --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/web_view/geolocation/embedder_has_permission/manifest.json @@ -0,0 +1,14 @@ +{ + "name": "Platform App Test: <webview> geolocation", + "description": "Loads a guest which requests geolocation", + "version": "1", + "permissions": [ + "geolocation", + "webview" + ], + "app": { + "background": { + "scripts": ["test.js"] + } + } +} diff --git a/chrome/test/data/extensions/platform_apps/web_view/geolocation/embedder_has_permission/test.js b/chrome/test/data/extensions/platform_apps/web_view/geolocation/embedder_has_permission/test.js new file mode 100644 index 0000000..1886432 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/web_view/geolocation/embedder_has_permission/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/chrome/test/data/extensions/platform_apps/web_view/geolocation/geolocation_access_guest.html b/chrome/test/data/extensions/platform_apps/web_view/geolocation/geolocation_access_guest.html new file mode 100644 index 0000000..05c42bd --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/web_view/geolocation/geolocation_access_guest.html @@ -0,0 +1,55 @@ +<!-- + * 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"> + // A guest that requests geolocation. + // Notifies the embedder about the result of the request (success/fail) + // via post message. Note that the embedder has to initiate a postMessage + // first so that guest has a reference to the embedder's window. + + // The window reference of the embedder to send post message reply. + var embedderWindowChannel = null; + + var notifyEmbedder = function(msg_array) { + embedderWindowChannel.postMessage(JSON.stringify(msg_array), '*'); + }; + + var onGeolocationSuccess = function(testName, position) { + window.console.log('onGeolocationSuccess callback.'); + var latitude = position.coords.latitude; + var longitude = position.coords.longitude; + notifyEmbedder([testName, 'access-granted', latitude, longitude]); + }; + + var onGeolocationFailure = function(testName, err) { + window.console.log('onGeolocationFailure callback.'); + notifyEmbedder([testName, 'access-denied']); + }; + + var startTest = function(testName) { + navigator.geolocation.getCurrentPosition( + onGeolocationSuccess.bind(this, testName), + onGeolocationFailure.bind(this, testName)); + }; + + var onPostMessageReceived = function(e) { + var data = JSON.parse(e.data); + if (data[0] == 'check-geolocation-permission') { + var testName = data[1]; + embedderWindowChannel = e.source; + // Start the test once we have |embedderWindowChannel|. + startTest(testName); + } + }; + + window.addEventListener('message', onPostMessageReceived, false); + </script> + </head> + <body> + <div>This is a guest that requests geolocation.</div> + </body> +</html> diff --git a/content/browser/browser_plugin/browser_plugin_geolocation_permission_context.cc b/content/browser/browser_plugin/browser_plugin_geolocation_permission_context.cc new file mode 100644 index 0000000..59a23b5 --- /dev/null +++ b/content/browser/browser_plugin/browser_plugin_geolocation_permission_context.cc @@ -0,0 +1,95 @@ +// 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. + +#include "content/browser/browser_plugin/browser_plugin_geolocation_permission_context.h" + +#include "base/bind.h" +#include "content/browser/browser_plugin/browser_plugin_guest.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_view_host.h" + +namespace content { + +BrowserPluginGeolocationPermissionContext:: + BrowserPluginGeolocationPermissionContext() { +} + +BrowserPluginGeolocationPermissionContext:: + ~BrowserPluginGeolocationPermissionContext() { +} + +void BrowserPluginGeolocationPermissionContext::RequestGeolocationPermission( + int render_process_id, + int render_view_id, + int bridge_id, + const GURL& requesting_frame, + base::Callback<void(bool)> callback) { + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind( + &BrowserPluginGeolocationPermissionContext:: + RequestGeolocationPermission, + this, + render_process_id, + render_view_id, + bridge_id, + requesting_frame, + callback)); + return; + } + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // Note that callback.Run(true) allows geolocation access, callback.Run(false) + // denies geolocation access. + // We need to go to the renderer to ask embedder's js if we are allowed to + // have geolocation access. + RenderViewHost* rvh = RenderViewHost::FromID(render_process_id, + render_view_id); + DCHECK(rvh); + if (rvh) { + DCHECK(rvh->GetProcess()->IsGuest()); + WebContentsImpl* guest_web_contents = + static_cast<WebContentsImpl*>(rvh->GetDelegate()->GetAsWebContents()); + BrowserPluginGuest* guest = guest_web_contents->GetBrowserPluginGuest(); + guest->AskEmbedderForGeolocationPermission(bridge_id, + requesting_frame, + callback); + } +} + +void BrowserPluginGeolocationPermissionContext:: + CancelGeolocationPermissionRequest(int render_process_id, + int render_view_id, + int bridge_id, + const GURL& requesting_frame) { + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind( + &BrowserPluginGeolocationPermissionContext:: + CancelGeolocationPermissionRequest, + this, + render_process_id, + render_view_id, + bridge_id, + requesting_frame)); + return; + } + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + RenderViewHost* rvh = RenderViewHost::FromID(render_process_id, + render_view_id); + if (rvh) { + DCHECK(rvh->GetProcess()->IsGuest()); + WebContentsImpl* guest_web_contents = + static_cast<WebContentsImpl*>(rvh->GetDelegate()->GetAsWebContents()); + BrowserPluginGuest* guest = guest_web_contents->GetBrowserPluginGuest(); + if (guest) + guest->CancelGeolocationRequest(bridge_id); + } +} + +} // namespace content diff --git a/content/browser/browser_plugin/browser_plugin_geolocation_permission_context.h b/content/browser/browser_plugin/browser_plugin_geolocation_permission_context.h new file mode 100644 index 0000000..27cc9e3 --- /dev/null +++ b/content/browser/browser_plugin/browser_plugin_geolocation_permission_context.h @@ -0,0 +1,44 @@ +// 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. + +#ifndef CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GEOLOCATION_PERMISSION_CONTEXT_H_ +#define CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GEOLOCATION_PERMISSION_CONTEXT_H_ + +#include "content/public/browser/geolocation_permission_context.h" + +namespace content { + +// Browser plugin specific implementation of GeolocationPermissionContext. +// It manages Geolocation permissions flow for BrowserPluginGuest. When a guest +// requests gelocation permission, it delegates the request to embedder though +// embedder's javascript api. +// This runs on the I/O thread. We have to return to UI thread to talk to a +// BrowserPluginGuest. +class BrowserPluginGeolocationPermissionContext : + public GeolocationPermissionContext { + public: + BrowserPluginGeolocationPermissionContext(); + + // GeolocationPermissionContext implementation: + virtual void RequestGeolocationPermission( + int render_process_id, + int render_view_id, + int bridge_id, + const GURL& requesting_frame, + base::Callback<void(bool)> callback) OVERRIDE; + virtual void CancelGeolocationPermissionRequest( + int render_process_id, + int render_view_id, + int bridge_id, + const GURL& requesting_frame) OVERRIDE; + + private: + virtual ~BrowserPluginGeolocationPermissionContext(); + + DISALLOW_COPY_AND_ASSIGN(BrowserPluginGeolocationPermissionContext); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GEOLOCATION_PERMISSION_CONTEXT_H_ diff --git a/content/browser/browser_plugin/browser_plugin_guest.cc b/content/browser/browser_plugin/browser_plugin_guest.cc index 962a931..8a9b3e1 100644 --- a/content/browser/browser_plugin/browser_plugin_guest.cc +++ b/content/browser/browser_plugin/browser_plugin_guest.cc @@ -25,6 +25,7 @@ #include "content/port/browser/render_view_host_delegate_view.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/content_browser_client.h" +#include "content/public/browser/geolocation_permission_context.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" #include "content/public/browser/render_process_host.h" @@ -50,7 +51,7 @@ namespace content { BrowserPluginHostFactory* BrowserPluginGuest::factory_ = NULL; namespace { -const size_t kNumMaxOutstandingMediaRequests = 1024; +const size_t kNumMaxOutstandingPermissionRequests = 1024; } BrowserPluginGuest::BrowserPluginGuest( @@ -58,6 +59,7 @@ BrowserPluginGuest::BrowserPluginGuest( WebContentsImpl* web_contents, const BrowserPluginHostMsg_CreateGuest_Params& params) : WebContentsObserver(web_contents), + ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)), embedder_web_contents_(NULL), instance_id_(instance_id), damage_buffer_sequence_id_(0), @@ -376,6 +378,45 @@ void BrowserPluginGuest::LoadRedirect( is_top_level)); } +void BrowserPluginGuest::AskEmbedderForGeolocationPermission( + int bridge_id, + const GURL& requesting_frame, + GeolocationCallback callback) { + if (geolocation_request_callback_map_.size() >= + kNumMaxOutstandingPermissionRequests) { + // Deny the geolocation request. + callback.Run(false); + return; + } + int request_id = next_permission_request_id_++; + geolocation_request_callback_map_[request_id] = callback; + + base::DictionaryValue request_info; + request_info.Set(browser_plugin::kURL, + base::Value::CreateStringValue(requesting_frame.spec())); + + SendMessageToEmbedder( + new BrowserPluginMsg_RequestPermission(instance_id(), + BrowserPluginPermissionTypeGeolocation, request_id, request_info)); +} + +void BrowserPluginGuest::CancelGeolocationRequest(int bridge_id) { + GeolocationRequestsMap::iterator callback_iter = + geolocation_request_callback_map_.find(bridge_id); + if (callback_iter != geolocation_request_callback_map_.end()) + geolocation_request_callback_map_.erase(callback_iter); +} + +void BrowserPluginGuest::SetGeolocationPermission(int request_id, + bool allowed) { + GeolocationRequestsMap::iterator callback_iter = + geolocation_request_callback_map_.find(request_id); + if (callback_iter != geolocation_request_callback_map_.end()) { + callback_iter->second.Run(allowed); + geolocation_request_callback_map_.erase(callback_iter); + } +} + void BrowserPluginGuest::DidCommitProvisionalLoadForFrame( int64 frame_id, bool is_main_frame, @@ -697,11 +738,14 @@ void BrowserPluginGuest::OnStop(int instance_id) { } void BrowserPluginGuest::OnRespondPermission( - int /*instance_id*/, + int instance_id, BrowserPluginPermissionType permission_type, int request_id, bool should_allow) { switch (permission_type) { + case BrowserPluginPermissionTypeGeolocation: + OnRespondPermissionGeolocation(request_id, should_allow); + break; case BrowserPluginPermissionTypeMedia: OnRespondPermissionMedia(request_id, should_allow); break; @@ -842,7 +886,7 @@ void BrowserPluginGuest::RequestMediaAccessPermission( WebContents* web_contents, const content::MediaStreamRequest& request, const content::MediaResponseCallback& callback) { - if (media_requests_map_.size() >= kNumMaxOutstandingMediaRequests) { + if (media_requests_map_.size() >= kNumMaxOutstandingPermissionRequests) { // Deny the media request. callback.Run(content::MediaStreamDevices()); return; @@ -917,6 +961,38 @@ void BrowserPluginGuest::OnUpdateRect( new BrowserPluginMsg_UpdateRect(instance_id(), relay_params)); } +void BrowserPluginGuest::OnRespondPermissionGeolocation( + int request_id, bool should_allow) { + if (should_allow && embedder_web_contents_) { + // If renderer side embedder decides to allow gelocation, we need to check + // if the app/embedder itself has geolocation access. + BrowserContext* browser_context = + embedder_web_contents_->GetBrowserContext(); + if (browser_context) { + GeolocationPermissionContext* geolocation_context = + browser_context->GetGeolocationPermissionContext(); + if (geolocation_context) { + base::Callback<void(bool)> geolocation_callback = base::Bind( + &BrowserPluginGuest::SetGeolocationPermission, + weak_ptr_factory_.GetWeakPtr(), + request_id); + geolocation_context->RequestGeolocationPermission( + embedder_web_contents_->GetRenderProcessHost()->GetID(), + embedder_routing_id(), + // The geolocation permission request here is not initiated through + // WebGeolocationPermissionRequest. We are only interested in the + // fact whether the embedder/app has geolocation permission. + // Therefore we use an invalid |bridge_id|. + -1 /* bridge_id */, + embedder_web_contents_->GetURL(), + geolocation_callback); + return; + } + } + } + SetGeolocationPermission(request_id, false); +} + void BrowserPluginGuest::OnRespondPermissionMedia( int request_id, bool should_allow) { MediaStreamRequestsMap::iterator media_request_iter = diff --git a/content/browser/browser_plugin/browser_plugin_guest.h b/content/browser/browser_plugin/browser_plugin_guest.h index e91dc6a..7d03a68 100644 --- a/content/browser/browser_plugin/browser_plugin_guest.h +++ b/content/browser/browser_plugin/browser_plugin_guest.h @@ -25,6 +25,7 @@ #include "base/compiler_specific.h" #include "base/id_map.h" +#include "base/memory/weak_ptr.h" #include "base/shared_memory.h" #include "base/time.h" #include "content/common/browser_plugin/browser_plugin_message_enums.h" @@ -72,6 +73,7 @@ class CONTENT_EXPORT BrowserPluginGuest : public NotificationObserver, public WebContentsDelegate, public WebContentsObserver { public: + typedef base::Callback<void(bool)> GeolocationCallback; virtual ~BrowserPluginGuest(); static BrowserPluginGuest* Create( @@ -173,6 +175,15 @@ class CONTENT_EXPORT BrowserPluginGuest : public NotificationObserver, // messages for testing. virtual void SendMessageToEmbedder(IPC::Message* msg); + // Requests geolocation permission through embedder js api. + void AskEmbedderForGeolocationPermission(int bridge_id, + const GURL& requesting_frame, + GeolocationCallback callback); + // Cancels pending geolocation request. + void CancelGeolocationRequest(int bridge_id); + // Embedder sets permission to allow or deny geolocation request. + void SetGeolocationPermission(int request_id, bool allowed); + // Returns the identifier that uniquely identifies a browser plugin guest // within an embedder. int instance_id() const { return instance_id_; } @@ -330,13 +341,20 @@ class CONTENT_EXPORT BrowserPluginGuest : public NotificationObserver, void OnUpdateRect(const ViewHostMsg_UpdateRect_Params& params); // Helpers for |OnRespondPermission|. + void OnRespondPermissionGeolocation(int request_id, bool should_allow); void OnRespondPermissionMedia(int request_id, bool should_allow); + // Weak pointer used to ask GeolocationPermissionContext about geolocation + // permission. + base::WeakPtrFactory<BrowserPluginGuest> weak_ptr_factory_; + // Static factory instance (always NULL for non-test). static content::BrowserPluginHostFactory* factory_; NotificationRegistrar notification_registrar_; WebContentsImpl* embedder_web_contents_; + typedef std::map<int, GeolocationCallback> GeolocationRequestsMap; + GeolocationRequestsMap geolocation_request_callback_map_; // An identifier that uniquely identifies a browser plugin guest within an // embedder. int instance_id_; diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index b598f4e..fa7fb67 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -41,6 +41,7 @@ #include "content/browser/appcache/chrome_appcache_service.h" #include "content/browser/browser_main.h" #include "content/browser/browser_main_loop.h" +#include "content/browser/browser_plugin/browser_plugin_geolocation_permission_context.h" #include "content/browser/browser_plugin/browser_plugin_message_filter.h" #include "content/browser/child_process_security_policy_impl.h" #include "content/browser/device_orientation/orientation_message_filter.h" @@ -537,8 +538,13 @@ void RenderProcessHostImpl::CreateMessageFilters() { new IndexedDBDispatcherHost( GetID(), storage_partition_impl_->GetIndexedDBContext())); - channel_->AddFilter(GeolocationDispatcherHost::New( - GetID(), browser_context->GetGeolocationPermissionContext())); + if (IsGuest()) { + channel_->AddFilter(GeolocationDispatcherHost::New( + GetID(), new BrowserPluginGeolocationPermissionContext())); + } else { + channel_->AddFilter(GeolocationDispatcherHost::New( + GetID(), browser_context->GetGeolocationPermissionContext())); + } gpu_message_filter_ = new GpuMessageFilter(GetID(), widget_helper_.get()); channel_->AddFilter(gpu_message_filter_); #if defined(ENABLE_WEBRTC) diff --git a/content/common/browser_plugin/browser_plugin_constants.cc b/content/common/browser_plugin/browser_plugin_constants.cc index 6d8405b..d0b3e10 100644 --- a/content/common/browser_plugin/browser_plugin_constants.cc +++ b/content/common/browser_plugin/browser_plugin_constants.cc @@ -56,6 +56,7 @@ const char kOldURL[] = "oldUrl"; const char kOldHeight[] = "oldHeight"; const char kOldWidth[] = "oldWidth"; const char kPermission[] = "permission"; +const char kPermissionTypeGeolocation[] = "geolocation"; const char kPermissionTypeMedia[] = "media"; const char kPersistPrefix[] = "persist:"; const char kProcessId[] = "processId"; diff --git a/content/common/browser_plugin/browser_plugin_constants.h b/content/common/browser_plugin/browser_plugin_constants.h index d49837d..831467d1 100644 --- a/content/common/browser_plugin/browser_plugin_constants.h +++ b/content/common/browser_plugin/browser_plugin_constants.h @@ -57,6 +57,7 @@ extern const char kOldURL[]; extern const char kOldHeight[]; extern const char kOldWidth[]; extern const char kPermission[]; +extern const char kPermissionTypeGeolocation[]; extern const char kPermissionTypeMedia[]; extern const char kPersistPrefix[]; extern const char kProcessId[]; diff --git a/content/common/browser_plugin/browser_plugin_message_enums.h b/content/common/browser_plugin/browser_plugin_message_enums.h index 56f8ea6..9d344ca 100644 --- a/content/common/browser_plugin/browser_plugin_message_enums.h +++ b/content/common/browser_plugin/browser_plugin_message_enums.h @@ -11,6 +11,9 @@ enum BrowserPluginPermissionType { // Media access (audio/video) permission request type. BrowserPluginPermissionTypeMedia, + + // Geolocation. + BrowserPluginPermissionTypeGeolocation, }; #endif // CONTENT_COMMON_BROWSER_PLUGIN_BROWSER_PLUGIN_MESSAGE_ENUMS_H_ diff --git a/content/content_browser.gypi b/content/content_browser.gypi index 846f972..fe33697 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -264,6 +264,8 @@ 'browser/browser_main_runner.cc', 'browser/browser_plugin/browser_plugin_embedder.cc', 'browser/browser_plugin/browser_plugin_embedder.h', + 'browser/browser_plugin/browser_plugin_geolocation_permission_context.cc', + 'browser/browser_plugin/browser_plugin_geolocation_permission_context.h', 'browser/browser_plugin/browser_plugin_guest.cc', 'browser/browser_plugin/browser_plugin_guest.h', 'browser/browser_plugin/browser_plugin_guest_helper.cc', diff --git a/content/renderer/browser_plugin/browser_plugin.cc b/content/renderer/browser_plugin/browser_plugin.cc index 37fe6eb..ee6a713 100644 --- a/content/renderer/browser_plugin/browser_plugin.cc +++ b/content/renderer/browser_plugin/browser_plugin.cc @@ -75,8 +75,11 @@ static std::string GetInternalEventName(const char* event_name) { static std::string PermissionTypeToString(BrowserPluginPermissionType type) { switch (type) { + case BrowserPluginPermissionTypeGeolocation: + return browser_plugin::kPermissionTypeGeolocation; case BrowserPluginPermissionTypeMedia: return browser_plugin::kPermissionTypeMedia; + case BrowserPluginPermissionTypeUnknown: default: NOTREACHED(); break; @@ -419,7 +422,7 @@ void BrowserPlugin::OnBuffersSwapped(int instance_id, } void BrowserPlugin::OnGuestContentWindowReady(int instance_id, - int content_window_routing_id) { + int content_window_routing_id) { DCHECK(content_window_routing_id != MSG_ROUTING_NONE); content_window_routing_id_ = content_window_routing_id; } @@ -538,15 +541,14 @@ void BrowserPlugin::OnLockMouse(int instance_id, } void BrowserPlugin::OnRequestPermission( - int /*instance_id*/, + int instance_id, BrowserPluginPermissionType permission_type, int request_id, const base::DictionaryValue& request_info) { if (!HasEventListeners(browser_plugin::kEventRequestPermission)) { // Automatically deny the request if there are no event listeners for // permissionrequest. - RespondPermission( - permission_type, request_id, false /* allow */); + RespondPermission(permission_type, request_id, false /* allow */); return; } DCHECK(!pending_permission_requests_.count(request_id)); |