From a25acea73199aa28da92bb24b1bd0bf4380fe20b Mon Sep 17 00:00:00 2001 From: "mthiesse@chromium.org" Date: Sun, 10 Mar 2013 23:41:28 +0000 Subject: [PATCH] Initial implementation of the permission API for Browser Plugin PointerLock Exposed permission event parameters: userGesture - true if the lock request originated from a user gesture. lastUnlockedBySelf - true if the a previous mouse lock to the page was unlocked by the page itself (i.e. not through a user alt+tabbing out). originUrl - the URL of the page the request originated from. BUG=166228 Review URL: https://chromiumcodereview.appspot.com/12455011 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@187221 0039d316-1c4b-4281-b951-d872f2087c98 --- .../extensions/web_view_interactive_browsertest.cc | 20 +++--- .../resources/extensions/web_view_experimental.js | 16 +++-- .../platform_apps/web_view/pointer_lock/guest.js | 42 +++++++------ .../platform_apps/web_view/pointer_lock/main.js | 73 +++++++++++++++++++--- .../browser/browser_plugin/browser_plugin_guest.cc | 28 +++++++-- .../browser/browser_plugin/browser_plugin_guest.h | 3 +- .../browser_plugin/browser_plugin_constants.cc | 3 + .../browser_plugin/browser_plugin_constants.h | 3 + .../browser_plugin/browser_plugin_message_enums.h | 3 + content/renderer/browser_plugin/browser_plugin.cc | 48 +++++++------- content/renderer/browser_plugin/browser_plugin.h | 11 ++-- 11 files changed, 171 insertions(+), 79 deletions(-) diff --git a/chrome/browser/extensions/web_view_interactive_browsertest.cc b/chrome/browser/extensions/web_view_interactive_browsertest.cc index 2f625d8b842e..d3c3c98e52ac 100644 --- a/chrome/browser/extensions/web_view_interactive_browsertest.cc +++ b/chrome/browser/extensions/web_view_interactive_browsertest.cc @@ -116,12 +116,6 @@ class WebViewInteractiveTest return corner_; } - protected: - virtual void SetUpCommandLine(CommandLine* command_line) { - command_line->AppendSwitch(switches::kEnableBrowserPluginPointerLock); - extensions::PlatformAppBrowserTest::SetUpCommandLine(command_line); - } - private: content::WebContents* guest_web_contents_; content::WebContents* embedder_web_contents_; @@ -139,17 +133,17 @@ IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, PointerLock) { SetupTest("web_view/pointer_lock", "files/extensions/platform_apps/web_view/pointer_lock/guest.html"); - ExtensionTestMessageListener guest_clicked_listener("clicked", false); - // We need to simulate a mouse click in the window or else SendMouseMoveSync - // will cause the browser to crash (not sure why exactly...) - SimulateMouseClickAt(guest_web_contents(), 0, - WebKit::WebMouseEvent::ButtonLeft, gfx::Point(75, 75)); - ASSERT_TRUE(guest_clicked_listener.WaitUntilSatisfied()); - // Move the mouse over the Lock Pointer button. ASSERT_TRUE(ui_test_utils::SendMouseMoveSync( gfx::Point(corner().x() + 75, corner().y() + 25))); + // Click the Lock Pointer button. The first two times the button is clicked + // the permission API will deny the request (intentional). + ExtensionTestMessageListener exception_listener("request exception", false); + SendMouseClickWithListener(ui_controls::LEFT, "lock error"); + ASSERT_TRUE(exception_listener.WaitUntilSatisfied()); + SendMouseClickWithListener(ui_controls::LEFT, "lock error"); + // Click the Lock Pointer button, locking the mouse to lockTarget1. SendMouseClickWithListener(ui_controls::LEFT, "locked"); diff --git a/chrome/renderer/resources/extensions/web_view_experimental.js b/chrome/renderer/resources/extensions/web_view_experimental.js index 73bc2d091578..d7e80bdcb0ba 100644 --- a/chrome/renderer/resources/extensions/web_view_experimental.js +++ b/chrome/renderer/resources/extensions/web_view_experimental.js @@ -14,12 +14,19 @@ var WebView = require('webView').WebView; /** @type {Array.} */ -var PERMISSION_TYPES = ['media', 'geolocation']; +var PERMISSION_TYPES = ['media', 'geolocation', 'pointerLock']; /** @type {string} */ var ERROR_MSG_PERMISSION_ALREADY_DECIDED = ': ' + 'Permission has already been decided for this "permissionrequest" event.'; +var EXPOSED_PERMISSION_EVENT_ATTRIBS = [ + 'lastUnlockedBySelf', + 'permission', + 'url', + 'userGesture' +]; + /** * @param {!Object} detail The event details, originated from . * @private @@ -30,12 +37,13 @@ WebView.prototype.maybeSetupPermissionEvent_ = function() { this.objectNode_.addEventListener('-internal-permissionrequest', function(e) { var evt = new Event('permissionrequest', {bubbles: true, cancelable: true}); var detail = e.detail ? JSON.parse(e.detail) : {}; - ['permission', 'url'].forEach(function(attribName) { - evt[attribName] = detail[attribName]; + EXPOSED_PERMISSION_EVENT_ATTRIBS.forEach(function(attribName) { + if (detail[attribName] !== 'undefined') + evt[attribName] = detail[attribName]; }); var requestId = detail.requestId; - if (detail.requestId !== undefined && + if (detail.requestId !== 'undefined' && PERMISSION_TYPES.indexOf(detail.permission) >= 0) { // TODO(lazyboy): Also fill in evt.details (see webview specs). // http://crbug.com/141197. diff --git a/chrome/test/data/extensions/platform_apps/web_view/pointer_lock/guest.js b/chrome/test/data/extensions/platform_apps/web_view/pointer_lock/guest.js index ada01ce3bc72..987f2607c58c 100644 --- a/chrome/test/data/extensions/platform_apps/web_view/pointer_lock/guest.js +++ b/chrome/test/data/extensions/platform_apps/web_view/pointer_lock/guest.js @@ -10,54 +10,60 @@ function LockMouse(element) { element.requestPointerLock(); } var first_lock = true; -document.onwebkitpointerlockchange = function () { +document.onwebkitpointerlockchange = function() { if (document.webkitPointerLockElement) { if (first_lock) { console.log('locked'); - setTimeout(function () { embedder.postMessage("locked", "*"); }, 500); + setTimeout(function() { embedder.postMessage('locked', '*'); }, 500); } else { console.log('deleting...'); - setTimeout(function () { embedder.postMessage("delete me", "*"); }, 500); + setTimeout(function() { embedder.postMessage('delete me', '*'); }, 500); } first_lock = false; } else { console.log('unlocked'); - embedder.postMessage('unlocked', "*"); + embedder.postMessage('unlocked', '*'); } } +document.onwebkitpointerlockerror = function() { + console.log('lock error', '*'); + setTimeout(function() { embedder.postMessage('lock error', '*'); }, 1000); +} + var embedder = null; -window.addEventListener("message", function(e) { +window.addEventListener('message', function(e) { embedder = e.source; embedder.postMessage('connected', '*'); }); -document.getElementById('locktarget1').addEventListener("mousemove", +document.getElementById('locktarget1').addEventListener('mousemove', function (e) { - setTimeout(function () { embedder.postMessage("mouse-move", "*"); }, 500); - if (info.innerHTML != "fail") - info.innerHTML = "Info: movementX: "+ e.webkitMovementX + - ", movementY: " + e.webkitMovementY; + setTimeout(function() { embedder.postMessage('mouse-move', '*'); }, 500); + if (info.innerHTML != 'fail') { + info.innerHTML = 'Info: movementX: '+ e.webkitMovementX + + ', movementY: ' + e.webkitMovementY; + } }); -document.getElementById('locktarget2').addEventListener("mousemove", +document.getElementById('locktarget2').addEventListener('mousemove', function (e) { - info.innerHTML = "fail"; - embedder.postMessage('Pointer was not locked to locktarget1.', "*"); + info.innerHTML = 'fail'; + embedder.postMessage('Pointer was not locked to locktarget1.', '*'); }); -document.getElementById('button1').addEventListener("click", function (e) { - console.log("click captured, locking mouse"); +document.getElementById('button1').addEventListener('click', function (e) { + console.log('click captured, locking mouse'); LockMouse(locktarget1); }, false); -document.getElementById('button2').addEventListener("click", function (e) { +document.getElementById('button2').addEventListener('click', function (e) { console.log('clicked button 2'); - embedder.postMessage('clicked', "*"); + embedder.postMessage('clicked', '*'); }, false); document.onkeydown = function (e) { - console.log("key handled, unlocking"); + console.log('key handled, unlocking'); document.webkitExitPointerLock(); }; diff --git a/chrome/test/data/extensions/platform_apps/web_view/pointer_lock/main.js b/chrome/test/data/extensions/platform_apps/web_view/pointer_lock/main.js index eb567ade0169..30ca4f55cc6d 100644 --- a/chrome/test/data/extensions/platform_apps/web_view/pointer_lock/main.js +++ b/chrome/test/data/extensions/platform_apps/web_view/pointer_lock/main.js @@ -4,13 +4,69 @@ // See chrome/browser/extensions/web_view_interactive_browsertest.cc // (WebViewInteractiveTest, PointerLock) for documentation on this test. - +var requestCount = 0; +var guestURL; var startTest = function() { - window.addEventListener("message", receiveMessage, false); + window.addEventListener('message', receiveMessage, false); chrome.test.sendMessage('guest-loaded'); var webview = document.getElementById('webview'); webview.addEventListener('loadstop', function(e) { - webview.contentWindow.postMessage("msg", "*"); + webview.contentWindow.postMessage('msg', '*'); + }); + webview.addEventListener('permissionrequest', function(e) { + if (requestCount == 0) { + setTimeout(function() { + try { + e.request.allow(); + } catch (exception) { + chrome.test.sendMessage('request exception'); + } + }, 10); + } else if (requestCount == 1) { + e.request.deny(); + } else if (requestCount == 2) { + if (e.permission == 'pointerLock' && !e.lastUnlockedBySelf && + e.url == guestURL && e.userGesture) { + e.preventDefault(); + setTimeout(function() { e.request.allow(); }, 0); + } else { + if (e.permission != 'pointerLock') { + console.log('Permission was, "' + e.permission + '" when it ' + + 'should have been "pointerLock"'); + } + if (e.lastUnlockedBySelf) { + console.log('e.lastUnlockedBySelf should have been false.'); + } + if (e.url == guestURL) { + console.log('e.url was, "' + e.url + '" when it ' + + 'should have been "' + guestURL + '"'); + } + if (!e.userGesture) { + console.log('e.userGesture should have been true.'); + } + } + } else if (requestCount == 3) { + if (e.permission == 'pointerLock' && e.lastUnlockedBySelf && + e.url == guestURL && e.userGesture) { + e.request.allow(); + } else { + if (e.permission != 'pointerLock') { + console.log('Permission was, "' + e.permission + '" when it ' + + 'should have been "pointerLock"'); + } + if (!e.lastUnlockedBySelf) { + console.log('e.lastUnlockedBySelf should have been true.'); + } + if (e.url == guestURL) { + console.log('e.url was, "' + e.url + '" when it ' + + 'should have been "' + guestURL + '"'); + } + if (!e.userGesture) { + console.log('e.userGesture should have been true.'); + } + } + } + requestCount++; }); }; var fail = false; @@ -21,23 +77,22 @@ var receiveMessage = function(event) { } else if (event.data == 'delete me') { var webview_container = document.getElementById('webview-tag-container'); webview_container.parentNode.removeChild(webview_container); - setTimeout(function () { chrome.test.sendMessage('timeout'); }, 5000); + setTimeout(function() { chrome.test.sendMessage('timeout'); }, 5000); document.getElementById('mousemove-capture-container').addEventListener( - "mousemove", function (e) { + 'mousemove', function (e) { console.log('move-captured'); chrome.test.sendMessage('move-captured'); - - }); console.log('deleted'); chrome.test.sendMessage('deleted'); } - if (!fail) + if (!fail) { chrome.test.sendMessage(event.data); + } } chrome.test.getConfig(function(config) { - var guestURL = 'http://localhost:' + config.testServer.port + + guestURL = 'http://localhost:' + config.testServer.port + '/files/extensions/platform_apps/web_view/pointer_lock/guest.html'; document.querySelector('#webview-tag-container').innerHTML = 'HasSwitch( - switches::kEnableBrowserPluginPointerLock)) - render_view_->mouse_lock_dispatcher()->LockMouse(this); - else - OnLockMouseACK(false); -} - void BrowserPlugin::OnRequestPermission( int instance_id, BrowserPluginPermissionType permission_type, @@ -551,10 +539,7 @@ void BrowserPlugin::OnRequestPermission( RespondPermission(permission_type, request_id, false /* allow */); return; } - DCHECK(!pending_permission_requests_.count(request_id)); - pending_permission_requests_.insert( - std::make_pair(request_id, - std::make_pair(request_id, permission_type))); + AddPermissionRequestToMap(request_id, permission_type); std::map props; props[browser_plugin::kPermission] = @@ -590,6 +575,12 @@ void BrowserPlugin::OnUpdatedName(int instance_id, const std::string& name) { UpdateDOMAttribute(browser_plugin::kAttributeName, name); } +void BrowserPlugin::AddPermissionRequestToMap(int request_id, + BrowserPluginPermissionType type) { + DCHECK(!pending_permission_requests_.count(request_id)); + pending_permission_requests_.insert(std::make_pair(request_id, type)); +} + bool BrowserPlugin::HasEventListeners(const std::string& event_name) { if (!container()) return false; @@ -996,10 +987,20 @@ WebKit::WebPluginContainer* BrowserPlugin::container() const { void BrowserPlugin::RespondPermission( BrowserPluginPermissionType permission_type, int request_id, bool allow) { - browser_plugin_manager()->Send( - new BrowserPluginHostMsg_RespondPermission( - render_view_->GetRoutingID(), instance_id_, permission_type, - request_id, allow)); + if (permission_type == BrowserPluginPermissionTypePointerLock) + RespondPermissionPointerLock(allow); + else + browser_plugin_manager()->Send( + new BrowserPluginHostMsg_RespondPermission( + render_view_->GetRoutingID(), instance_id_, permission_type, + request_id, allow)); +} + +void BrowserPlugin::RespondPermissionPointerLock(bool allow) { + if (allow) + render_view_->mouse_lock_dispatcher()->LockMouse(this); + else + OnLockMouseACK(false); } void BrowserPlugin::RespondPermissionIfRequestIsPending( @@ -1009,7 +1010,7 @@ void BrowserPlugin::RespondPermissionIfRequestIsPending( if (iter == pending_permission_requests_.end()) return; - BrowserPluginPermissionType permission_type = iter->second.second; + BrowserPluginPermissionType permission_type = iter->second; pending_permission_requests_.erase(iter); RespondPermission(permission_type, request_id, allow); } @@ -1167,7 +1168,6 @@ bool BrowserPlugin::ShouldForwardToBrowserPlugin( case BrowserPluginMsg_LoadRedirect::ID: case BrowserPluginMsg_LoadStart::ID: case BrowserPluginMsg_LoadStop::ID: - case BrowserPluginMsg_LockMouse::ID: case BrowserPluginMsg_RequestPermission::ID: case BrowserPluginMsg_SetCursor::ID: case BrowserPluginMsg_ShouldAcceptTouchEvents::ID: diff --git a/content/renderer/browser_plugin/browser_plugin.h b/content/renderer/browser_plugin/browser_plugin.h index 7a166b9d4b76..cd8f01368a36 100644 --- a/content/renderer/browser_plugin/browser_plugin.h +++ b/content/renderer/browser_plugin/browser_plugin.h @@ -284,12 +284,18 @@ class CONTENT_EXPORT BrowserPlugin : // browser process. void SetInstanceID(int instance_id); + void AddPermissionRequestToMap(int request_id, + BrowserPluginPermissionType type); + // Informs the BrowserPlugin that the guest's permission request has been // allowed or denied by the embedder. void RespondPermission(BrowserPluginPermissionType permission_type, int request_id, bool allow); + // Handles the response to the pointerLock permission request. + void RespondPermissionPointerLock(bool allow); + // If the request with id |request_id| is pending then informs the // BrowserPlugin that the guest's permission request has been allowed or // denied by the embedder. @@ -327,8 +333,6 @@ class CONTENT_EXPORT BrowserPlugin : bool is_top_level); void OnLoadStart(int instance_id, const GURL& url, bool is_top_level); void OnLoadStop(int instance_id); - void OnLockMouse(int instance_id, bool user_gesture, - bool last_unlocked_by_target, bool privileged); // Requests permission from the embedder. void OnRequestPermission(int instance_id, BrowserPluginPermissionType permission_type, @@ -379,8 +383,7 @@ class CONTENT_EXPORT BrowserPlugin : // Each permission request item in the map is a pair of request id and // permission type. - typedef std::map > - PendingPermissionRequests; + typedef std::map PendingPermissionRequests; PendingPermissionRequests pending_permission_requests_; typedef std::pair > -- 2.11.4.GIT