From 85e13333c3e0bd1b15738fb98c23ebaedbf1f6e8 Mon Sep 17 00:00:00 2001 From: Robo Date: Sat, 23 Jan 2016 18:59:47 +0530 Subject: [PATCH 1/9] webview: add permission-request event --- atom/browser/api/atom_api_web_contents.cc | 13 ++ atom/browser/api/atom_api_web_contents.h | 4 + atom/browser/atom_browser_client.cc | 18 +++ atom/browser/atom_browser_client.h | 2 + atom/browser/lib/guest-view-manager.js | 22 +++- .../browser/web_contents_permission_helper.cc | 114 ++++++++++++++++++ atom/browser/web_contents_permission_helper.h | 48 ++++++++ atom/common/api/event_emitter_caller.h | 1 + .../lib/web-view/guest-view-internal.js | 12 +- .../lib/web-view/web-view-attributes.js | 1 + docs/api/web-view-tag.md | 26 ++++ filenames.gypi | 2 + 12 files changed, 260 insertions(+), 3 deletions(-) create mode 100644 atom/browser/web_contents_permission_helper.cc create mode 100644 atom/browser/web_contents_permission_helper.h diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index 2b14bdc60d83..990d78aa9caf 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -14,6 +14,7 @@ #include "atom/browser/atom_browser_context.h" #include "atom/browser/atom_browser_main_parts.h" #include "atom/browser/native_window.h" +#include "atom/browser/web_contents_permission_helper.h" #include "atom/browser/web_contents_preferences.h" #include "atom/browser/web_view_guest_delegate.h" #include "atom/common/api/api_messages.h" @@ -262,6 +263,9 @@ WebContents::WebContents(v8::Isolate* isolate, // Save the preferences in C++. new WebContentsPreferences(web_contents, options); + // Initialze permission helper. + new WebContentsPermissionHelper(web_contents, this); + web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent()); if (is_guest) { @@ -444,6 +448,15 @@ void WebContents::FindReply(content::WebContents* web_contents, } } +void WebContents::RequestMediaAccessPermission( + content::WebContents* web_contents, + const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback) { + auto permission_helper = + WebContentsPermissionHelper::FromWebContents(web_contents); + permission_helper->RequestMediaAccessPermission(request, callback); +} + void WebContents::BeforeUnloadFired(const base::TimeTicks& proceed_time) { // Do nothing, we override this method just to avoid compilation error since // there are two virtual functions named BeforeUnloadFired. diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index 10ac7a4f7695..6587759efb9f 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -195,6 +195,10 @@ class WebContents : public mate::TrackableObject, const gfx::Rect& selection_rect, int active_match_ordinal, bool final_update) override; + void RequestMediaAccessPermission( + content::WebContents* web_contents, + const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback) override; // content::WebContentsObserver: void BeforeUnloadFired(const base::TimeTicks& proceed_time) override; diff --git a/atom/browser/atom_browser_client.cc b/atom/browser/atom_browser_client.cc index 5ad8e69ffc74..49637f5342f0 100644 --- a/atom/browser/atom_browser_client.cc +++ b/atom/browser/atom_browser_client.cc @@ -15,6 +15,7 @@ #include "atom/browser/atom_resource_dispatcher_host_delegate.h" #include "atom/browser/atom_speech_recognition_manager_delegate.h" #include "atom/browser/native_window.h" +#include "atom/browser/web_contents_permission_helper.h" #include "atom/browser/web_contents_preferences.h" #include "atom/browser/window_list.h" #include "atom/common/options_switches.h" @@ -281,6 +282,23 @@ brightray::BrowserMainParts* AtomBrowserClient::OverrideCreateBrowserMainParts( return new AtomBrowserMainParts; } +void AtomBrowserClient::WebNotificationAllowed(int render_process_id, + const base::Closure& callback) { + content::WebContents* web_contents = content::WebContents::FromRenderViewHost( + content::RenderViewHost::FromID(render_process_id, kDefaultRoutingID)); + if (!web_contents) { + callback.Run(); + return; + } + auto permission_helper = + WebContentsPermissionHelper::FromWebContents(web_contents); + if (!permission_helper) { + callback.Run(); + return; + } + permission_helper->RequestWebNotificationPermission(callback); +} + void AtomBrowserClient::RenderProcessHostDestroyed( content::RenderProcessHost* host) { int process_id = host->GetID(); diff --git a/atom/browser/atom_browser_client.h b/atom/browser/atom_browser_client.h index 3c54fab40bc1..9e0e979db881 100644 --- a/atom/browser/atom_browser_client.h +++ b/atom/browser/atom_browser_client.h @@ -81,6 +81,8 @@ class AtomBrowserClient : public brightray::BrowserClient, // brightray::BrowserClient: brightray::BrowserMainParts* OverrideCreateBrowserMainParts( const content::MainFunctionParams&) override; + void WebNotificationAllowed(int render_process_id, + const base::Closure& callback) override; // content::RenderProcessHostObserver: void RenderProcessHostDestroyed(content::RenderProcessHost* host) override; diff --git a/atom/browser/lib/guest-view-manager.js b/atom/browser/lib/guest-view-manager.js index 40f59691e7a1..233b1f1e4151 100644 --- a/atom/browser/lib/guest-view-manager.js +++ b/atom/browser/lib/guest-view-manager.js @@ -6,10 +6,12 @@ var slice = [].slice; // Doesn't exist in early initialization. var webViewManager = null; -var supportedWebViewEvents = ['load-commit', 'did-finish-load', 'did-fail-load', 'did-frame-finish-load', 'did-start-loading', 'did-stop-loading', 'did-get-response-details', 'did-get-redirect-request', 'dom-ready', 'console-message', 'devtools-opened', 'devtools-closed', 'devtools-focused', 'new-window', 'will-navigate', 'did-navigate', 'did-navigate-in-page', 'close', 'crashed', 'gpu-crashed', 'plugin-crashed', 'destroyed', 'page-title-updated', 'page-favicon-updated', 'enter-html-full-screen', 'leave-html-full-screen', 'media-started-playing', 'media-paused', 'found-in-page', 'did-change-theme-color']; +var supportedWebViewEvents = ['load-commit', 'did-finish-load', 'did-fail-load', 'did-frame-finish-load', 'did-start-loading', 'did-stop-loading', 'did-get-response-details', 'did-get-redirect-request', 'dom-ready', 'console-message', 'devtools-opened', 'devtools-closed', 'devtools-focused', 'new-window', 'will-navigate', 'did-navigate', 'did-navigate-in-page', 'close', 'crashed', 'gpu-crashed', 'plugin-crashed', 'destroyed', 'page-title-updated', 'page-favicon-updated', 'enter-html-full-screen', 'leave-html-full-screen', 'media-started-playing', 'media-paused', 'found-in-page', 'did-change-theme-color', 'permission-request']; var nextInstanceId = 0; +var permissionRequests; var guestInstances = {}; +var guestPermissionRequestsMap = {}; var embedderElementsMap = {}; var reverseEmbedderElementsMap = {}; @@ -110,6 +112,13 @@ var createGuest = function(embedder, params) { fn = function(event) { return guest.on(event, function() { var args = 2 <= arguments.length ? slice.call(arguments, 1) : []; + if (event === 'permission-request') { + if (!guestPermissionRequestsMap[guest.viewInstanceId]) + guestPermissionRequestsMap[guest.viewInstanceId] = {}; + var permission = args[0]; + guestPermissionRequestsMap[guest.viewInstanceId][permission] = args[1]; + args.pop(); + } return embedder.send.apply(embedder, ["ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-" + guest.viewInstanceId, event].concat(slice.call(args))); }); }; @@ -157,7 +166,8 @@ var attachGuest = function(embedder, elementInstanceId, guestInstanceId, params) nodeIntegration: (ref1 = params.nodeintegration) != null ? ref1 : false, plugins: params.plugins, webSecurity: !params.disablewebsecurity, - blinkFeatures: params.blinkfeatures + blinkFeatures: params.blinkfeatures, + webNotification: !params.disablewebnotification, }; if (params.preload) { webPreferences.preloadURL = params.preload; @@ -174,6 +184,7 @@ var destroyGuest = function(embedder, id) { webViewManager.removeGuest(embedder, id); guestInstances[id].guest.destroy(); delete guestInstances[id]; + delete permissionRequests[id]; key = reverseEmbedderElementsMap[id]; if (key != null) { delete reverseEmbedderElementsMap[id]; @@ -193,6 +204,13 @@ ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_DESTROY_GUEST', function(event, id) { return destroyGuest(event.sender, id); }); +ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_SET_PERMISSION_RESPONSE', function(event, viewInstanceId, permission, allowed) { + permissionRequests = guestPermissionRequestsMap[viewInstanceId]; + if (permissionRequests && permissionRequests[permission] !== null) { + permissionRequests[permission].apply(null, [allowed]); + } +}); + ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_SET_SIZE', function(event, id, params) { var ref1; return (ref1 = guestInstances[id]) != null ? ref1.guest.setSize(params) : void 0; diff --git a/atom/browser/web_contents_permission_helper.cc b/atom/browser/web_contents_permission_helper.cc new file mode 100644 index 000000000000..91ae60461770 --- /dev/null +++ b/atom/browser/web_contents_permission_helper.cc @@ -0,0 +1,114 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/web_contents_permission_helper.h" + +#include + +#include "atom/browser/api/atom_api_web_contents.h" +#include "content/public/browser/media_capture_devices.h" + +DEFINE_WEB_CONTENTS_USER_DATA_KEY(atom::WebContentsPermissionHelper); + +namespace atom { + +namespace { + +const content::MediaStreamDevice* FindDeviceWithId( + const content::MediaStreamDevices& devices, + const std::string& device_id) { + if (device_id.empty()) + return &(*devices.begin()); + for (const auto& iter : devices) + if (iter.id == device_id) + return &(iter); + return nullptr; +} + +void MediaAccessAllowed( + const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback) { + content::MediaStreamDevices devices; + content::MediaStreamRequestResult result = content::MEDIA_DEVICE_NO_HARDWARE; + + if (request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE) { + const content::MediaStreamDevices& audio_devices = + content::MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices(); + const content::MediaStreamDevice* audio_device = + FindDeviceWithId(audio_devices, request.requested_audio_device_id); + if (audio_device) + devices.push_back(*audio_device); + } + + if (request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) { + const content::MediaStreamDevices& video_devices = + content::MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices(); + const content::MediaStreamDevice* video_device = + FindDeviceWithId(video_devices, request.requested_video_device_id); + if (video_device) + devices.push_back(*video_device); + } + + if (!devices.empty()) + result = content::MEDIA_DEVICE_OK; + + callback.Run(devices, result, scoped_ptr()); +} + +} // namespace + +WebContentsPermissionHelper::WebContentsPermissionHelper( + content::WebContents* web_contents, + api::WebContents* api_web_contents) + : api_web_contents_(api_web_contents) { + web_contents->SetUserData(UserDataKey(), this); +} + +WebContentsPermissionHelper::~WebContentsPermissionHelper() { +} + +void WebContentsPermissionHelper::RequestMediaAccessPermission( + const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback) { + if (api_web_contents_->IsGuest()) { + const std::string& permission = "media"; + permission_map_[permission] = base::Bind(&MediaAccessAllowed, + request, + callback); + api_web_contents_->Emit( + "permission-request", + permission, + base::Bind(&WebContentsPermissionHelper::OnPermissionResponse, + base::Unretained(this), permission)); + return; + } + MediaAccessAllowed(request, callback); +} + +void WebContentsPermissionHelper::RequestWebNotificationPermission( + const base::Closure& callback) { + if (api_web_contents_->IsGuest()) { + const std::string& permission = "webNotification"; + permission_map_[permission] = callback; + api_web_contents_->Emit( + "permission-request", + permission, + base::Bind(&WebContentsPermissionHelper::OnPermissionResponse, + base::Unretained(this), permission)); + return; + } + callback.Run(); +} + +void WebContentsPermissionHelper::OnPermissionResponse( + const std::string& permission, bool allowed) { + auto it = permission_map_.find(permission); + if (it != permission_map_.end()) { + if (allowed) + it->second.Run(); + permission_map_.erase(permission); + } +} + +} // namespace atom diff --git a/atom/browser/web_contents_permission_helper.h b/atom/browser/web_contents_permission_helper.h new file mode 100644 index 000000000000..80e76fd685a3 --- /dev/null +++ b/atom/browser/web_contents_permission_helper.h @@ -0,0 +1,48 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_WEB_CONTENTS_PERMISSION_HELPER_H_ +#define ATOM_BROWSER_WEB_CONTENTS_PERMISSION_HELPER_H_ + +#include +#include + +#include "base/callback.h" +#include "content/public/browser/web_contents_user_data.h" +#include "content/public/common/media_stream_request.h" + +namespace atom { + +namespace api { +class WebContents; +} + +// Applies the permission requested for WebContents. +class WebContentsPermissionHelper + : public content::WebContentsUserData { + public: + WebContentsPermissionHelper(content::WebContents* web_contents, + api::WebContents* api_web_contents); + ~WebContentsPermissionHelper() override; + + void RequestMediaAccessPermission( + const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback); + void RequestWebNotificationPermission(const base::Closure& callback); + + void OnPermissionResponse(const std::string& permission, bool allowed); + + private: + friend class content::WebContentsUserData; + + std::map permission_map_; + + api::WebContents* api_web_contents_; // Weak reference + + DISALLOW_COPY_AND_ASSIGN(WebContentsPermissionHelper); +}; + +} // namespace atom + +#endif // ATOM_BROWSER_WEB_CONTENTS_PERMISSION_HELPER_H_ diff --git a/atom/common/api/event_emitter_caller.h b/atom/common/api/event_emitter_caller.h index a2567da9d109..26a4cbc45b90 100644 --- a/atom/common/api/event_emitter_caller.h +++ b/atom/common/api/event_emitter_caller.h @@ -7,6 +7,7 @@ #include +#include "atom/common/native_mate_converters/callback.h" #include "native_mate/converter.h" namespace mate { diff --git a/atom/renderer/lib/web-view/guest-view-internal.js b/atom/renderer/lib/web-view/guest-view-internal.js index a7427abd631c..e6a1519b0f4c 100644 --- a/atom/renderer/lib/web-view/guest-view-internal.js +++ b/atom/renderer/lib/web-view/guest-view-internal.js @@ -34,7 +34,8 @@ var WEB_VIEW_EVENTS = { 'page-favicon-updated': ['favicons'], 'enter-html-full-screen': [], 'leave-html-full-screen': [], - 'found-in-page': ['result'] + 'found-in-page': ['result'], + 'permission-request': ['permission', 'allow', 'deny'] }; var DEPRECATED_EVENTS = { @@ -64,6 +65,15 @@ module.exports = { ipcRenderer.on("ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-" + viewInstanceId, function() { var eventName = arguments[1]; var args = 3 <= arguments.length ? slice.call(arguments, 2) : []; + if (eventName === 'permission-request') { + var allow = function allow() { + ipcRenderer.send("ATOM_SHELL_GUEST_VIEW_MANAGER_SET_PERMISSION_RESPONSE", viewInstanceId, args[0], true); + }; + var deny = function deny() { + ipcRenderer.send("ATOM_SHELL_GUEST_VIEW_MANAGER_SET_PERMISSION_RESPONSE", viewInstanceId, args[0], false); + }; + args = args.concat([allow, deny]); + } return dispatchEvent.apply(null, [webView, eventName, eventName].concat(slice.call(args))); }); ipcRenderer.on("ATOM_SHELL_GUEST_VIEW_INTERNAL_IPC_MESSAGE-" + viewInstanceId, function() { diff --git a/atom/renderer/lib/web-view/web-view-attributes.js b/atom/renderer/lib/web-view/web-view-attributes.js index 4ad4bd725012..180924f5b569 100644 --- a/atom/renderer/lib/web-view/web-view-attributes.js +++ b/atom/renderer/lib/web-view/web-view-attributes.js @@ -311,6 +311,7 @@ WebViewImpl.prototype.setupWebViewAttributes = function() { this.attributes[webViewConstants.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWPOPUPS, this); this.attributes[webViewConstants.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this); this.attributes[webViewConstants.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this); + this.attributes[webViewConstants.ATTRIBUTE_DISABLEWEBNOTIFICATION] = new BooleanAttribute(webViewConstants.ATTRIBUTE_DISABLEWEBNOTIFICATION, this); autosizeAttributes = [webViewConstants.ATTRIBUTE_MAXHEIGHT, webViewConstants.ATTRIBUTE_MAXWIDTH, webViewConstants.ATTRIBUTE_MINHEIGHT, webViewConstants.ATTRIBUTE_MINWIDTH]; results = []; for (i = 0, len = autosizeAttributes.length; i < len; i++) { diff --git a/docs/api/web-view-tag.md b/docs/api/web-view-tag.md index 9cb8f49af352..630dbdc95857 100644 --- a/docs/api/web-view-tag.md +++ b/docs/api/web-view-tag.md @@ -167,6 +167,14 @@ A list of strings which specifies the blink features to be enabled separated by The full list of supported feature strings can be found in the [setFeatureEnabledFromString][blink-feature-string] function. +### `disablewebnotification` + +```html + +``` + +If "on", the guest page will have web notifications disabled. + ## Methods The `webview` tag has the following methods: @@ -736,4 +744,22 @@ Emitted when DevTools is closed. Emitted when DevTools is focused / opened. +### Event: 'permission-request' + +Returns: + +* `permission` String - The type of permission being requested. Enum of 'media', 'webNotification'. +* `allow` Function - Allows the permission. +* `deny` Function - Deny the permission. This is the default behaviour if `allow` is not called. + +Emitted when guest page requires special permission. + +```javascript +// This will deny guest page access to the webkitGetUserMedia API. +webview.addEventListener('permission-request', function(e) { + if (e.permission === 'media') + e.deny(); +}); +``` + [blink-feature-string]: https://code.google.com/p/chromium/codesearch#chromium/src/out/Debug/gen/blink/platform/RuntimeEnabledFeatures.cpp&sq=package:chromium&type=cs&l=527 diff --git a/filenames.gypi b/filenames.gypi index 4aa47fc3580f..3c9cd4b69198 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -256,6 +256,8 @@ 'atom/browser/ui/x/window_state_watcher.h', 'atom/browser/ui/x/x_window_utils.cc', 'atom/browser/ui/x/x_window_utils.h', + 'atom/browser/web_contents_permission_helper.cc', + 'atom/browser/web_contents_permission_helper.h', 'atom/browser/web_contents_preferences.cc', 'atom/browser/web_contents_preferences.h', 'atom/browser/web_dialog_helper.cc', From f32bf08eb3fa3ad63d663e81eb8af558b39dea87 Mon Sep 17 00:00:00 2001 From: Robo Date: Mon, 25 Jan 2016 22:07:15 +0530 Subject: [PATCH 2/9] add spec --- atom/browser/atom_browser_client.cc | 9 ++--- atom/browser/atom_browser_client.h | 5 +-- atom/browser/lib/guest-view-manager.js | 36 +++++++++++++++++-- .../browser/web_contents_permission_helper.cc | 19 ++++++---- atom/browser/web_contents_permission_helper.h | 5 +-- .../lib/web-view/web-view-attributes.js | 1 - docs/api/web-view-tag.md | 8 ----- spec/fixtures/pages/permission-request.html | 7 ++++ spec/webview-spec.js | 19 +++++++++- 9 files changed, 83 insertions(+), 26 deletions(-) create mode 100644 spec/fixtures/pages/permission-request.html diff --git a/atom/browser/atom_browser_client.cc b/atom/browser/atom_browser_client.cc index 49637f5342f0..b395f3dd0cb5 100644 --- a/atom/browser/atom_browser_client.cc +++ b/atom/browser/atom_browser_client.cc @@ -282,18 +282,19 @@ brightray::BrowserMainParts* AtomBrowserClient::OverrideCreateBrowserMainParts( return new AtomBrowserMainParts; } -void AtomBrowserClient::WebNotificationAllowed(int render_process_id, - const base::Closure& callback) { +void AtomBrowserClient::WebNotificationAllowed( + int render_process_id, + const base::Callback& callback) { content::WebContents* web_contents = content::WebContents::FromRenderViewHost( content::RenderViewHost::FromID(render_process_id, kDefaultRoutingID)); if (!web_contents) { - callback.Run(); + callback.Run(true); return; } auto permission_helper = WebContentsPermissionHelper::FromWebContents(web_contents); if (!permission_helper) { - callback.Run(); + callback.Run(true); return; } permission_helper->RequestWebNotificationPermission(callback); diff --git a/atom/browser/atom_browser_client.h b/atom/browser/atom_browser_client.h index 9e0e979db881..4af66cc041ae 100644 --- a/atom/browser/atom_browser_client.h +++ b/atom/browser/atom_browser_client.h @@ -81,8 +81,9 @@ class AtomBrowserClient : public brightray::BrowserClient, // brightray::BrowserClient: brightray::BrowserMainParts* OverrideCreateBrowserMainParts( const content::MainFunctionParams&) override; - void WebNotificationAllowed(int render_process_id, - const base::Closure& callback) override; + void WebNotificationAllowed( + int render_process_id, + const base::Callback& callback) override; // content::RenderProcessHostObserver: void RenderProcessHostDestroyed(content::RenderProcessHost* host) override; diff --git a/atom/browser/lib/guest-view-manager.js b/atom/browser/lib/guest-view-manager.js index 233b1f1e4151..b50d0c9503fe 100644 --- a/atom/browser/lib/guest-view-manager.js +++ b/atom/browser/lib/guest-view-manager.js @@ -6,7 +6,39 @@ var slice = [].slice; // Doesn't exist in early initialization. var webViewManager = null; -var supportedWebViewEvents = ['load-commit', 'did-finish-load', 'did-fail-load', 'did-frame-finish-load', 'did-start-loading', 'did-stop-loading', 'did-get-response-details', 'did-get-redirect-request', 'dom-ready', 'console-message', 'devtools-opened', 'devtools-closed', 'devtools-focused', 'new-window', 'will-navigate', 'did-navigate', 'did-navigate-in-page', 'close', 'crashed', 'gpu-crashed', 'plugin-crashed', 'destroyed', 'page-title-updated', 'page-favicon-updated', 'enter-html-full-screen', 'leave-html-full-screen', 'media-started-playing', 'media-paused', 'found-in-page', 'did-change-theme-color', 'permission-request']; +var supportedWebViewEvents = [ + 'load-commit', + 'did-finish-load', + 'did-fail-load', + 'did-frame-finish-load', + 'did-start-loading', + 'did-stop-loading', + 'did-get-response-details', + 'did-get-redirect-request', + 'dom-ready', + 'console-message', + 'devtools-opened', + 'devtools-closed', + 'devtools-focused', + 'new-window', + 'will-navigate', + 'did-navigate', + 'did-navigate-in-page', + 'close', + 'crashed', + 'gpu-crashed', + 'plugin-crashed', + 'destroyed', + 'page-title-updated', + 'page-favicon-updated', + 'enter-html-full-screen', + 'leave-html-full-screen', + 'media-started-playing', + 'media-paused', + 'found-in-page', + 'did-change-theme-color', + 'permission-request' +]; var nextInstanceId = 0; var permissionRequests; @@ -184,7 +216,7 @@ var destroyGuest = function(embedder, id) { webViewManager.removeGuest(embedder, id); guestInstances[id].guest.destroy(); delete guestInstances[id]; - delete permissionRequests[id]; + delete guestPermissionRequestsMap[id]; key = reverseEmbedderElementsMap[id]; if (key != null) { delete reverseEmbedderElementsMap[id]; diff --git a/atom/browser/web_contents_permission_helper.cc b/atom/browser/web_contents_permission_helper.cc index 91ae60461770..5c82aa41df60 100644 --- a/atom/browser/web_contents_permission_helper.cc +++ b/atom/browser/web_contents_permission_helper.cc @@ -28,7 +28,15 @@ const content::MediaStreamDevice* FindDeviceWithId( void MediaAccessAllowed( const content::MediaStreamRequest& request, - const content::MediaResponseCallback& callback) { + const content::MediaResponseCallback& callback, + bool allowed) { + if (!allowed) { + callback.Run(content::MediaStreamDevices(), + content::MEDIA_DEVICE_PERMISSION_DENIED, + scoped_ptr()); + return; + } + content::MediaStreamDevices devices; content::MediaStreamRequestResult result = content::MEDIA_DEVICE_NO_HARDWARE; @@ -83,11 +91,11 @@ void WebContentsPermissionHelper::RequestMediaAccessPermission( base::Unretained(this), permission)); return; } - MediaAccessAllowed(request, callback); + MediaAccessAllowed(request, callback, true); } void WebContentsPermissionHelper::RequestWebNotificationPermission( - const base::Closure& callback) { + const base::Callback& callback) { if (api_web_contents_->IsGuest()) { const std::string& permission = "webNotification"; permission_map_[permission] = callback; @@ -98,15 +106,14 @@ void WebContentsPermissionHelper::RequestWebNotificationPermission( base::Unretained(this), permission)); return; } - callback.Run(); + callback.Run(true); } void WebContentsPermissionHelper::OnPermissionResponse( const std::string& permission, bool allowed) { auto it = permission_map_.find(permission); if (it != permission_map_.end()) { - if (allowed) - it->second.Run(); + it->second.Run(allowed); permission_map_.erase(permission); } } diff --git a/atom/browser/web_contents_permission_helper.h b/atom/browser/web_contents_permission_helper.h index 80e76fd685a3..4beac035c024 100644 --- a/atom/browser/web_contents_permission_helper.h +++ b/atom/browser/web_contents_permission_helper.h @@ -29,14 +29,15 @@ class WebContentsPermissionHelper void RequestMediaAccessPermission( const content::MediaStreamRequest& request, const content::MediaResponseCallback& callback); - void RequestWebNotificationPermission(const base::Closure& callback); + void RequestWebNotificationPermission( + const base::Callback& callback); void OnPermissionResponse(const std::string& permission, bool allowed); private: friend class content::WebContentsUserData; - std::map permission_map_; + std::map> permission_map_; api::WebContents* api_web_contents_; // Weak reference diff --git a/atom/renderer/lib/web-view/web-view-attributes.js b/atom/renderer/lib/web-view/web-view-attributes.js index 180924f5b569..4ad4bd725012 100644 --- a/atom/renderer/lib/web-view/web-view-attributes.js +++ b/atom/renderer/lib/web-view/web-view-attributes.js @@ -311,7 +311,6 @@ WebViewImpl.prototype.setupWebViewAttributes = function() { this.attributes[webViewConstants.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWPOPUPS, this); this.attributes[webViewConstants.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this); this.attributes[webViewConstants.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this); - this.attributes[webViewConstants.ATTRIBUTE_DISABLEWEBNOTIFICATION] = new BooleanAttribute(webViewConstants.ATTRIBUTE_DISABLEWEBNOTIFICATION, this); autosizeAttributes = [webViewConstants.ATTRIBUTE_MAXHEIGHT, webViewConstants.ATTRIBUTE_MAXWIDTH, webViewConstants.ATTRIBUTE_MINHEIGHT, webViewConstants.ATTRIBUTE_MINWIDTH]; results = []; for (i = 0, len = autosizeAttributes.length; i < len; i++) { diff --git a/docs/api/web-view-tag.md b/docs/api/web-view-tag.md index 630dbdc95857..67a3d12171ce 100644 --- a/docs/api/web-view-tag.md +++ b/docs/api/web-view-tag.md @@ -167,14 +167,6 @@ A list of strings which specifies the blink features to be enabled separated by The full list of supported feature strings can be found in the [setFeatureEnabledFromString][blink-feature-string] function. -### `disablewebnotification` - -```html - -``` - -If "on", the guest page will have web notifications disabled. - ## Methods The `webview` tag has the following methods: diff --git a/spec/fixtures/pages/permission-request.html b/spec/fixtures/pages/permission-request.html new file mode 100644 index 000000000000..0d968a9a66b9 --- /dev/null +++ b/spec/fixtures/pages/permission-request.html @@ -0,0 +1,7 @@ + diff --git a/spec/webview-spec.js b/spec/webview-spec.js index 0eaa59c5fd3c..e40afcaefeb1 100644 --- a/spec/webview-spec.js +++ b/spec/webview-spec.js @@ -624,7 +624,7 @@ describe(' tag', function() { return document.body.appendChild(webview); }); }); - return xdescribe('did-change-theme-color event', function() { + xdescribe('did-change-theme-color event', function() { return it('emits when theme color changes', function(done) { webview.addEventListener('did-change-theme-color', function() { return done(); @@ -633,4 +633,21 @@ describe(' tag', function() { return document.body.appendChild(webview); }); }); + describe('permission-request event', function() { + it ('emits when using navigator.getUserMedia api', function(done) { + webview.addEventListener('ipc-message', function(e) { + assert(e.channel, 'message'); + assert(e.args, ['PermissionDeniedError']); + done(); + }); + webview.addEventListener('permission-request', function(e) { + if (e.permission === 'media') { + e.deny(); + } + }); + webview.src = "file://" + fixtures + "/pages/permission-request.html"; + webview.setAttribute('nodeintegration', 'on'); + document.body.appendChild(webview); + }); + }); }); From 2a278ceb8fe1c0c40a70de40c39f400db901fd09 Mon Sep 17 00:00:00 2001 From: Robo Date: Sat, 30 Jan 2016 16:49:18 +0530 Subject: [PATCH 3/9] delegate to permission manager for other permissions --- atom/browser/api/atom_api_web_contents.cc | 18 ++- atom/browser/api/atom_api_web_contents.h | 4 + atom/browser/atom_browser_client.cc | 4 +- atom/browser/atom_browser_context.cc | 6 + atom/browser/atom_browser_context.h | 7 ++ atom/browser/atom_permission_manager.cc | 107 ++++++++++++++++++ atom/browser/atom_permission_manager.h | 75 ++++++++++++ atom/browser/lib/guest-view-manager.js | 38 +++---- .../browser/web_contents_permission_helper.cc | 56 +++------ atom/browser/web_contents_permission_helper.h | 22 ++-- .../content_converter.cc | 44 +++++++ .../content_converter.h | 14 +++ .../lib/web-view/guest-view-internal.js | 4 +- filenames.gypi | 2 + 14 files changed, 324 insertions(+), 77 deletions(-) create mode 100644 atom/browser/atom_permission_manager.cc create mode 100644 atom/browser/atom_permission_manager.h diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index 990d78aa9caf..01c069cf87d9 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -13,6 +13,7 @@ #include "atom/browser/atom_browser_client.h" #include "atom/browser/atom_browser_context.h" #include "atom/browser/atom_browser_main_parts.h" +#include "atom/browser/atom_permission_manager.h" #include "atom/browser/native_window.h" #include "atom/browser/web_contents_permission_helper.h" #include "atom/browser/web_contents_preferences.h" @@ -263,8 +264,8 @@ WebContents::WebContents(v8::Isolate* isolate, // Save the preferences in C++. new WebContentsPreferences(web_contents, options); - // Initialze permission helper. - new WebContentsPermissionHelper(web_contents, this); + // Intialize permission helper. + WebContentsPermissionHelper::CreateForWebContents(web_contents); web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent()); @@ -1066,6 +1067,17 @@ bool WebContents::IsGuest() const { return type_ == WEB_VIEW; } +void WebContents::SetPermissionRequestHandler(v8::Local val, + mate::Arguments* args) { + AtomPermissionManager::RequestHandler handler; + if (!(val->IsNull() || mate::ConvertFromV8(args->isolate(), val, &handler))) { + args->ThrowError("Must pass null or function"); + return; + } + GetBrowserContext()->permission_manager() + ->SetPermissionRequestHandler(GetID(), handler); +} + v8::Local WebContents::GetWebPreferences(v8::Isolate* isolate) { WebContentsPreferences* web_preferences = WebContentsPreferences::FromWebContents(web_contents()); @@ -1165,6 +1177,8 @@ void WebContents::BuildPrototype(v8::Isolate* isolate, .SetMethod("_printToPDF", &WebContents::PrintToPDF) .SetMethod("addWorkSpace", &WebContents::AddWorkSpace) .SetMethod("removeWorkSpace", &WebContents::RemoveWorkSpace) + .SetMethod("setPermissionRequestHandler", + &WebContents::SetPermissionRequestHandler) .SetProperty("session", &WebContents::Session) .SetProperty("devToolsWebContents", &WebContents::DevToolsWebContents) .SetProperty("debugger", &WebContents::Debugger); diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index 6587759efb9f..6232aa3bf0cf 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -133,6 +133,10 @@ class WebContents : public mate::TrackableObject, void SetAllowTransparency(bool allow); bool IsGuest() const; + // Handler for permission requests. + void SetPermissionRequestHandler(v8::Local val, + mate::Arguments* args); + // Returns the web preferences of current WebContents. v8::Local GetWebPreferences(v8::Isolate* isolate); diff --git a/atom/browser/atom_browser_client.cc b/atom/browser/atom_browser_client.cc index b395f3dd0cb5..1fe435673db6 100644 --- a/atom/browser/atom_browser_client.cc +++ b/atom/browser/atom_browser_client.cc @@ -288,13 +288,13 @@ void AtomBrowserClient::WebNotificationAllowed( content::WebContents* web_contents = content::WebContents::FromRenderViewHost( content::RenderViewHost::FromID(render_process_id, kDefaultRoutingID)); if (!web_contents) { - callback.Run(true); + callback.Run(false); return; } auto permission_helper = WebContentsPermissionHelper::FromWebContents(web_contents); if (!permission_helper) { - callback.Run(true); + callback.Run(false); return; } permission_helper->RequestWebNotificationPermission(callback); diff --git a/atom/browser/atom_browser_context.cc b/atom/browser/atom_browser_context.cc index ac59f8c31330..39eee1b8aff3 100644 --- a/atom/browser/atom_browser_context.cc +++ b/atom/browser/atom_browser_context.cc @@ -13,6 +13,7 @@ #include "atom/browser/net/atom_url_request_job_factory.h" #include "atom/browser/net/asar/asar_protocol_handler.h" #include "atom/browser/net/http_protocol_handler.h" +#include "atom/browser/atom_permission_manager.h" #include "atom/browser/web_view_manager.h" #include "atom/common/atom_version.h" #include "atom/common/chrome_version.h" @@ -67,6 +68,7 @@ AtomBrowserContext::AtomBrowserContext(const std::string& partition, cert_verifier_(nullptr), job_factory_(new AtomURLRequestJobFactory), network_delegate_(new AtomNetworkDelegate), + permission_manager_(new AtomPermissionManager), allow_ntlm_everywhere_(false) { } @@ -169,6 +171,10 @@ content::BrowserPluginGuestManager* AtomBrowserContext::GetGuestManager() { return guest_manager_.get(); } +content::PermissionManager* AtomBrowserContext::GetPermissionManager() { + return permission_manager_.get(); +} + scoped_ptr AtomBrowserContext::CreateCertVerifier() { DCHECK(!cert_verifier_); cert_verifier_ = new AtomCertVerifier; diff --git a/atom/browser/atom_browser_context.h b/atom/browser/atom_browser_context.h index 9c94a60c305b..44cd1f5b79ee 100644 --- a/atom/browser/atom_browser_context.h +++ b/atom/browser/atom_browser_context.h @@ -14,6 +14,7 @@ namespace atom { class AtomDownloadManagerDelegate; class AtomCertVerifier; class AtomNetworkDelegate; +class AtomPermissionManager; class AtomURLRequestJobFactory; class WebViewManager; @@ -37,6 +38,7 @@ class AtomBrowserContext : public brightray::BrowserContext { // content::BrowserContext: content::DownloadManagerDelegate* GetDownloadManagerDelegate() override; content::BrowserPluginGuestManager* GetGuestManager() override; + content::PermissionManager* GetPermissionManager() override; // brightray::BrowserContext: void RegisterPrefs(PrefRegistrySimple* pref_registry) override; @@ -49,9 +51,14 @@ class AtomBrowserContext : public brightray::BrowserContext { AtomNetworkDelegate* network_delegate() const { return network_delegate_; } + AtomPermissionManager* permission_manager() const { + return permission_manager_.get(); + } + private: scoped_ptr download_manager_delegate_; scoped_ptr guest_manager_; + scoped_ptr permission_manager_; // Managed by brightray::BrowserContext. AtomCertVerifier* cert_verifier_; diff --git a/atom/browser/atom_permission_manager.cc b/atom/browser/atom_permission_manager.cc new file mode 100644 index 000000000000..badef0a1d343 --- /dev/null +++ b/atom/browser/atom_permission_manager.cc @@ -0,0 +1,107 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/atom_permission_manager.h" + +#include "content/public/browser/permission_type.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" + +namespace atom { + +AtomPermissionManager::AtomPermissionManager() + : request_id_(0) { +} + +AtomPermissionManager::~AtomPermissionManager() { +} + +void AtomPermissionManager::SetPermissionRequestHandler( + int id, + const RequestHandler& handler) { + if (handler.is_null()) { + request_handler_map_.erase(id); + return; + } + request_handler_map_[id] = handler; +} + +void AtomPermissionManager::RequestPermission( + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& origin, + const base::Callback& callback) { + bool user_gesture = false; + RequestPermission(permission, render_frame_host, origin, user_gesture, + base::Bind(&AtomPermissionManager::OnPermissionResponse, + base::Unretained(this), callback)); +} + +void AtomPermissionManager::OnPermissionResponse( + const base::Callback& callback, + content::PermissionStatus status) { + if (status == content::PERMISSION_STATUS_GRANTED) + callback.Run(true); + else + callback.Run(false); +} + +int AtomPermissionManager::RequestPermission( + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + bool user_gesture, + const ResponseCallback& callback) { + auto request_handler = + request_handler_map_.find(render_frame_host->GetProcess()->GetID()); + if (request_handler != request_handler_map_.end()) { + pending_requests_[++request_id_] = callback; + request_handler->second.Run(permission, callback); + return request_id_; + } + + callback.Run(content::PERMISSION_STATUS_GRANTED); + return kNoPendingOperation; +} + +void AtomPermissionManager::CancelPermissionRequest(int request_id) { + auto request = pending_requests_.find(request_id); + if (request != pending_requests_.end()) { + request->second.Run(content::PERMISSION_STATUS_DENIED); + pending_requests_.erase(request); + } +} + +void AtomPermissionManager::ResetPermission( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) { +} + +content::PermissionStatus AtomPermissionManager::GetPermissionStatus( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) { + return content::PERMISSION_STATUS_DENIED; +} + +void AtomPermissionManager::RegisterPermissionUsage( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) { +} + +int AtomPermissionManager::SubscribePermissionStatusChange( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin, + const ResponseCallback& callback) { + return -1; +} + +void AtomPermissionManager::UnsubscribePermissionStatusChange( + int subscription_id) { +} + +} // namespace atom diff --git a/atom/browser/atom_permission_manager.h b/atom/browser/atom_permission_manager.h new file mode 100644 index 000000000000..697bcda6f37e --- /dev/null +++ b/atom/browser/atom_permission_manager.h @@ -0,0 +1,75 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_ATOM_PERMISSION_MANAGER_H_ +#define ATOM_BROWSER_ATOM_PERMISSION_MANAGER_H_ + +#include + +#include "base/callback_forward.h" +#include "base/macros.h" +#include "content/public/browser/permission_manager.h" + +namespace atom { + +class AtomPermissionManager : public content::PermissionManager { + public: + AtomPermissionManager(); + ~AtomPermissionManager() override; + + using ResponseCallback = + base::Callback; + using RequestHandler = + base::Callback; + + void SetPermissionRequestHandler(int id, const RequestHandler& handler); + void RequestPermission( + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& origin, + const base::Callback& callback); + void OnPermissionResponse( + const base::Callback& callback, + content::PermissionStatus status); + + protected: + // content::PermissionManager: + int RequestPermission( + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + bool user_gesture, + const ResponseCallback& callback) override; + void CancelPermissionRequest(int request_id) override; + void ResetPermission(content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) override; + content::PermissionStatus GetPermissionStatus( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) override; + void RegisterPermissionUsage(content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) override; + int SubscribePermissionStatusChange( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin, + const base::Callback& callback) override; + void UnsubscribePermissionStatusChange(int subscription_id) override; + + private: + std::map request_handler_map_; + + std::map pending_requests_; + + int request_id_; + + DISALLOW_COPY_AND_ASSIGN(AtomPermissionManager); +}; + +} // namespace atom + +#endif // ATOM_BROWSER_ATOM_PERMISSION_MANAGER_H_ diff --git a/atom/browser/lib/guest-view-manager.js b/atom/browser/lib/guest-view-manager.js index b50d0c9503fe..f529260e7971 100644 --- a/atom/browser/lib/guest-view-manager.js +++ b/atom/browser/lib/guest-view-manager.js @@ -36,15 +36,13 @@ var supportedWebViewEvents = [ 'media-started-playing', 'media-paused', 'found-in-page', - 'did-change-theme-color', - 'permission-request' + 'did-change-theme-color' ]; var nextInstanceId = 0; -var permissionRequests; var guestInstances = {}; -var guestPermissionRequestsMap = {}; var embedderElementsMap = {}; +var pendingRequestsMap = {}; var reverseEmbedderElementsMap = {}; // Moves the last element of array to the first one. @@ -137,20 +135,19 @@ var createGuest = function(embedder, params) { if (params.allowtransparency != null) { this.setAllowTransparency(params.allowtransparency); } - return guest.allowPopups = params.allowpopups; + guest.allowPopups = params.allowpopups; + this.setPermissionRequestHandler((permission, callback) => { + if (!pendingRequestsMap[this.viewInstanceId]) + pendingRequestsMap[this.viewInstanceId] = {}; + pendingRequestsMap[this.viewInstanceId][permission] = callback; + embedder.send.apply(embedder, ["ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-" + this.viewInstanceId, "permission-request", permission]); + }); }); // Dispatch events to embedder. fn = function(event) { return guest.on(event, function() { var args = 2 <= arguments.length ? slice.call(arguments, 1) : []; - if (event === 'permission-request') { - if (!guestPermissionRequestsMap[guest.viewInstanceId]) - guestPermissionRequestsMap[guest.viewInstanceId] = {}; - var permission = args[0]; - guestPermissionRequestsMap[guest.viewInstanceId][permission] = args[1]; - args.pop(); - } return embedder.send.apply(embedder, ["ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-" + guest.viewInstanceId, event].concat(slice.call(args))); }); }; @@ -171,6 +168,7 @@ var createGuest = function(embedder, params) { var args = 2 <= arguments.length ? slice.call(arguments, 1) : []; return embedder.send.apply(embedder, ["ATOM_SHELL_GUEST_VIEW_INTERNAL_SIZE_CHANGED-" + guest.viewInstanceId].concat(slice.call(args))); }); + return id; }; @@ -216,7 +214,7 @@ var destroyGuest = function(embedder, id) { webViewManager.removeGuest(embedder, id); guestInstances[id].guest.destroy(); delete guestInstances[id]; - delete guestPermissionRequestsMap[id]; + delete pendingRequestsMap[id]; key = reverseEmbedderElementsMap[id]; if (key != null) { delete reverseEmbedderElementsMap[id]; @@ -236,13 +234,6 @@ ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_DESTROY_GUEST', function(event, id) { return destroyGuest(event.sender, id); }); -ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_SET_PERMISSION_RESPONSE', function(event, viewInstanceId, permission, allowed) { - permissionRequests = guestPermissionRequestsMap[viewInstanceId]; - if (permissionRequests && permissionRequests[permission] !== null) { - permissionRequests[permission].apply(null, [allowed]); - } -}); - ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_SET_SIZE', function(event, id, params) { var ref1; return (ref1 = guestInstances[id]) != null ? ref1.guest.setSize(params) : void 0; @@ -253,6 +244,13 @@ ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_SET_ALLOW_TRANSPARENCY', function(even return (ref1 = guestInstances[id]) != null ? ref1.guest.setAllowTransparency(allowtransparency) : void 0; }); +ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_SET_PERMISSION_RESPONSE', function(event, id, permission, allowed) { + if (pendingRequestsMap[id] != null) { + const callback = pendingRequestsMap[id][permission]; + callback.apply(null, [allowed]); + } +}); + // Returns WebContents from its guest id. exports.getGuest = function(id) { var ref1; diff --git a/atom/browser/web_contents_permission_helper.cc b/atom/browser/web_contents_permission_helper.cc index 5c82aa41df60..04a6a5d9883f 100644 --- a/atom/browser/web_contents_permission_helper.cc +++ b/atom/browser/web_contents_permission_helper.cc @@ -6,8 +6,9 @@ #include -#include "atom/browser/api/atom_api_web_contents.h" +#include "atom/browser/atom_permission_manager.h" #include "content/public/browser/media_capture_devices.h" +#include "content/public/browser/render_process_host.h" DEFINE_WEB_CONTENTS_USER_DATA_KEY(atom::WebContentsPermissionHelper); @@ -67,55 +68,32 @@ void MediaAccessAllowed( } // namespace WebContentsPermissionHelper::WebContentsPermissionHelper( - content::WebContents* web_contents, - api::WebContents* api_web_contents) - : api_web_contents_(api_web_contents) { - web_contents->SetUserData(UserDataKey(), this); + content::WebContents* web_contents) + : web_contents_(web_contents) { } WebContentsPermissionHelper::~WebContentsPermissionHelper() { } +void WebContentsPermissionHelper::RequestPermission( + content::PermissionType permission, + const base::Callback& callback) { + auto rfh = web_contents_->GetMainFrame(); + auto permission_manager = browser_context()->permission_manager(); + auto origin = web_contents_->GetLastCommittedURL(); + permission_manager->RequestPermission(permission, rfh, origin, callback); +} + void WebContentsPermissionHelper::RequestMediaAccessPermission( const content::MediaStreamRequest& request, - const content::MediaResponseCallback& callback) { - if (api_web_contents_->IsGuest()) { - const std::string& permission = "media"; - permission_map_[permission] = base::Bind(&MediaAccessAllowed, - request, - callback); - api_web_contents_->Emit( - "permission-request", - permission, - base::Bind(&WebContentsPermissionHelper::OnPermissionResponse, - base::Unretained(this), permission)); - return; - } - MediaAccessAllowed(request, callback, true); + const content::MediaResponseCallback& response_callback) { + auto callback = base::Bind(&MediaAccessAllowed, request, response_callback); + RequestPermission(content::PermissionType::AUDIO_CAPTURE, callback); } void WebContentsPermissionHelper::RequestWebNotificationPermission( const base::Callback& callback) { - if (api_web_contents_->IsGuest()) { - const std::string& permission = "webNotification"; - permission_map_[permission] = callback; - api_web_contents_->Emit( - "permission-request", - permission, - base::Bind(&WebContentsPermissionHelper::OnPermissionResponse, - base::Unretained(this), permission)); - return; - } - callback.Run(true); -} - -void WebContentsPermissionHelper::OnPermissionResponse( - const std::string& permission, bool allowed) { - auto it = permission_map_.find(permission); - if (it != permission_map_.end()) { - it->second.Run(allowed); - permission_map_.erase(permission); - } + RequestPermission(content::PermissionType::NOTIFICATIONS, callback); } } // namespace atom diff --git a/atom/browser/web_contents_permission_helper.h b/atom/browser/web_contents_permission_helper.h index 4beac035c024..2c845a60f76a 100644 --- a/atom/browser/web_contents_permission_helper.h +++ b/atom/browser/web_contents_permission_helper.h @@ -5,25 +5,18 @@ #ifndef ATOM_BROWSER_WEB_CONTENTS_PERMISSION_HELPER_H_ #define ATOM_BROWSER_WEB_CONTENTS_PERMISSION_HELPER_H_ -#include -#include - +#include "atom/browser/atom_browser_context.h" #include "base/callback.h" +#include "content/public/browser/permission_type.h" #include "content/public/browser/web_contents_user_data.h" #include "content/public/common/media_stream_request.h" namespace atom { -namespace api { -class WebContents; -} - // Applies the permission requested for WebContents. class WebContentsPermissionHelper : public content::WebContentsUserData { public: - WebContentsPermissionHelper(content::WebContents* web_contents, - api::WebContents* api_web_contents); ~WebContentsPermissionHelper() override; void RequestMediaAccessPermission( @@ -32,14 +25,19 @@ class WebContentsPermissionHelper void RequestWebNotificationPermission( const base::Callback& callback); - void OnPermissionResponse(const std::string& permission, bool allowed); + AtomBrowserContext* browser_context() const { + return static_cast(web_contents_->GetBrowserContext()); + } private: + explicit WebContentsPermissionHelper(content::WebContents* web_contents); friend class content::WebContentsUserData; - std::map> permission_map_; + void RequestPermission( + content::PermissionType permission, + const base::Callback& callback); - api::WebContents* api_web_contents_; // Weak reference + content::WebContents* web_contents_; DISALLOW_COPY_AND_ASSIGN(WebContentsPermissionHelper); }; diff --git a/atom/common/native_mate_converters/content_converter.cc b/atom/common/native_mate_converters/content_converter.cc index d79094f79d43..d85188dad1fa 100644 --- a/atom/common/native_mate_converters/content_converter.cc +++ b/atom/common/native_mate_converters/content_converter.cc @@ -98,6 +98,50 @@ v8::Local Converter::ToV8( return mate::ConvertToV8(isolate, dict); } +// static +bool Converter::FromV8( + v8::Isolate* isolate, + v8::Local val, + content::PermissionStatus* out) { + std::string status; + if (!ConvertFromV8(isolate, val, &status)) + return false; + + if (status == "granted") + *out = content::PERMISSION_STATUS_GRANTED; + else if (status == "denied" || status.empty()) + *out = content::PERMISSION_STATUS_DENIED; + else + return false; + + return true; +} + +// static +v8::Local Converter::ToV8( + v8::Isolate* isolate, const content::PermissionType& val) { + switch (val) { + case content::PermissionType::MIDI_SYSEX: + return StringToV8(isolate, "midiSysex"); + case content::PermissionType::PUSH_MESSAGING: + return StringToV8(isolate, "pushMessaging"); + case content::PermissionType::NOTIFICATIONS: + return StringToV8(isolate, "notifications"); + case content::PermissionType::GEOLOCATION: + return StringToV8(isolate, "geolocation"); + case content::PermissionType::AUDIO_CAPTURE: + case content::PermissionType::VIDEO_CAPTURE: + return StringToV8(isolate, "media"); + case content::PermissionType::PROTECTED_MEDIA_IDENTIFIER: + return StringToV8(isolate, "mediaKeySystem"); + case content::PermissionType::MIDI: + return StringToV8(isolate, "midi"); + case content::PermissionType::DURABLE_STORAGE: + default: + return StringToV8(isolate, "unknown"); + } +} + // static bool Converter::FromV8( v8::Isolate* isolate, diff --git a/atom/common/native_mate_converters/content_converter.h b/atom/common/native_mate_converters/content_converter.h index a5708e022b57..6c9130b18875 100644 --- a/atom/common/native_mate_converters/content_converter.h +++ b/atom/common/native_mate_converters/content_converter.h @@ -7,7 +7,9 @@ #include +#include "content/public/browser/permission_type.h" #include "content/public/common/menu_item.h" +#include "content/public/common/permission_status.mojom.h" #include "content/public/common/stop_find_action.h" #include "native_mate/converter.h" @@ -33,6 +35,18 @@ struct Converter { const ContextMenuParamsWithWebContents& val); }; +template<> +struct Converter { + static bool FromV8(v8::Isolate* isolate, v8::Local val, + content::PermissionStatus* out); +}; + +template<> +struct Converter { + static v8::Local ToV8(v8::Isolate* isolate, + const content::PermissionType& val); +}; + template<> struct Converter { static bool FromV8(v8::Isolate* isolate, v8::Local val, diff --git a/atom/renderer/lib/web-view/guest-view-internal.js b/atom/renderer/lib/web-view/guest-view-internal.js index e6a1519b0f4c..acade1f41c0f 100644 --- a/atom/renderer/lib/web-view/guest-view-internal.js +++ b/atom/renderer/lib/web-view/guest-view-internal.js @@ -67,10 +67,10 @@ module.exports = { var args = 3 <= arguments.length ? slice.call(arguments, 2) : []; if (eventName === 'permission-request') { var allow = function allow() { - ipcRenderer.send("ATOM_SHELL_GUEST_VIEW_MANAGER_SET_PERMISSION_RESPONSE", viewInstanceId, args[0], true); + ipcRenderer.send("ATOM_SHELL_GUEST_VIEW_MANAGER_SET_PERMISSION_RESPONSE", viewInstanceId, args[0], "granted"); }; var deny = function deny() { - ipcRenderer.send("ATOM_SHELL_GUEST_VIEW_MANAGER_SET_PERMISSION_RESPONSE", viewInstanceId, args[0], false); + ipcRenderer.send("ATOM_SHELL_GUEST_VIEW_MANAGER_SET_PERMISSION_RESPONSE", viewInstanceId, args[0], "denied"); }; args = args.concat([allow, deny]); } diff --git a/filenames.gypi b/filenames.gypi index 3c9cd4b69198..beb4fcac5481 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -144,6 +144,8 @@ 'atom/browser/atom_browser_main_parts_posix.cc', 'atom/browser/atom_javascript_dialog_manager.cc', 'atom/browser/atom_javascript_dialog_manager.h', + 'atom/browser/atom_permission_manager.cc', + 'atom/browser/atom_permission_manager.h', 'atom/browser/atom_quota_permission_context.cc', 'atom/browser/atom_quota_permission_context.h', 'atom/browser/atom_resource_dispatcher_host_delegate.cc', From f7556de9fb4e6a6ccc96e07e120749ca32902afd Mon Sep 17 00:00:00 2001 From: Robo Date: Sat, 30 Jan 2016 19:01:10 +0530 Subject: [PATCH 4/9] add spec for new permissions --- atom/browser/api/atom_api_web_contents.cc | 2 +- atom/browser/atom_browser_context.cc | 2 +- atom/browser/atom_permission_manager.cc | 13 +++++-- atom/browser/atom_permission_manager.h | 2 ++ atom/browser/lib/guest-view-manager.js | 4 ++- atom/browser/web_contents_permission_helper.h | 1 - atom/common/api/event_emitter_caller.h | 1 - docs/api/web-view-tag.md | 2 +- .../pages/permissions/geolocation.html | 5 +++ .../media.html} | 0 spec/fixtures/pages/permissions/midi.html | 5 +++ spec/webview-spec.js | 34 ++++++++++++++++++- 12 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 spec/fixtures/pages/permissions/geolocation.html rename spec/fixtures/pages/{permission-request.html => permissions/media.html} (100%) create mode 100644 spec/fixtures/pages/permissions/midi.html diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index 01c069cf87d9..c6ab110b8327 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -1177,7 +1177,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate, .SetMethod("_printToPDF", &WebContents::PrintToPDF) .SetMethod("addWorkSpace", &WebContents::AddWorkSpace) .SetMethod("removeWorkSpace", &WebContents::RemoveWorkSpace) - .SetMethod("setPermissionRequestHandler", + .SetMethod("_setPermissionRequestHandler", &WebContents::SetPermissionRequestHandler) .SetProperty("session", &WebContents::Session) .SetProperty("devToolsWebContents", &WebContents::DevToolsWebContents) diff --git a/atom/browser/atom_browser_context.cc b/atom/browser/atom_browser_context.cc index 39eee1b8aff3..35358ac08a46 100644 --- a/atom/browser/atom_browser_context.cc +++ b/atom/browser/atom_browser_context.cc @@ -65,10 +65,10 @@ std::string RemoveWhitespace(const std::string& str) { AtomBrowserContext::AtomBrowserContext(const std::string& partition, bool in_memory) : brightray::BrowserContext(partition, in_memory), + permission_manager_(new AtomPermissionManager), cert_verifier_(nullptr), job_factory_(new AtomURLRequestJobFactory), network_delegate_(new AtomNetworkDelegate), - permission_manager_(new AtomPermissionManager), allow_ntlm_everywhere_(false) { } diff --git a/atom/browser/atom_permission_manager.cc b/atom/browser/atom_permission_manager.cc index badef0a1d343..ccd456e7c201 100644 --- a/atom/browser/atom_permission_manager.cc +++ b/atom/browser/atom_permission_manager.cc @@ -4,6 +4,7 @@ #include "atom/browser/atom_permission_manager.h" +#include "content/public/browser/child_process_security_policy.h" #include "content/public/browser/permission_type.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" @@ -53,8 +54,14 @@ int AtomPermissionManager::RequestPermission( const GURL& requesting_origin, bool user_gesture, const ResponseCallback& callback) { - auto request_handler = - request_handler_map_.find(render_frame_host->GetProcess()->GetID()); + int process_id = render_frame_host->GetProcess()->GetID(); + auto request_handler = request_handler_map_.find(process_id); + + if (permission == content::PermissionType::MIDI_SYSEX) { + content::ChildProcessSecurityPolicy::GetInstance()-> + GrantSendMidiSysExMessage(process_id); + } + if (request_handler != request_handler_map_.end()) { pending_requests_[++request_id_] = callback; request_handler->second.Run(permission, callback); @@ -83,7 +90,7 @@ content::PermissionStatus AtomPermissionManager::GetPermissionStatus( content::PermissionType permission, const GURL& requesting_origin, const GURL& embedding_origin) { - return content::PERMISSION_STATUS_DENIED; + return content::PERMISSION_STATUS_GRANTED; } void AtomPermissionManager::RegisterPermissionUsage( diff --git a/atom/browser/atom_permission_manager.h b/atom/browser/atom_permission_manager.h index 697bcda6f37e..fb449037f64d 100644 --- a/atom/browser/atom_permission_manager.h +++ b/atom/browser/atom_permission_manager.h @@ -24,7 +24,9 @@ class AtomPermissionManager : public content::PermissionManager { base::Callback; + // Handler to dispatch permission requests in JS. void SetPermissionRequestHandler(int id, const RequestHandler& handler); + void RequestPermission( content::PermissionType permission, content::RenderFrameHost* render_frame_host, diff --git a/atom/browser/lib/guest-view-manager.js b/atom/browser/lib/guest-view-manager.js index f529260e7971..d4332070fa62 100644 --- a/atom/browser/lib/guest-view-manager.js +++ b/atom/browser/lib/guest-view-manager.js @@ -136,7 +136,9 @@ var createGuest = function(embedder, params) { this.setAllowTransparency(params.allowtransparency); } guest.allowPopups = params.allowpopups; - this.setPermissionRequestHandler((permission, callback) => { + + // Dispatches permission request event. + this._setPermissionRequestHandler((permission, callback) => { if (!pendingRequestsMap[this.viewInstanceId]) pendingRequestsMap[this.viewInstanceId] = {}; pendingRequestsMap[this.viewInstanceId][permission] = callback; diff --git a/atom/browser/web_contents_permission_helper.h b/atom/browser/web_contents_permission_helper.h index 2c845a60f76a..749e90184c22 100644 --- a/atom/browser/web_contents_permission_helper.h +++ b/atom/browser/web_contents_permission_helper.h @@ -6,7 +6,6 @@ #define ATOM_BROWSER_WEB_CONTENTS_PERMISSION_HELPER_H_ #include "atom/browser/atom_browser_context.h" -#include "base/callback.h" #include "content/public/browser/permission_type.h" #include "content/public/browser/web_contents_user_data.h" #include "content/public/common/media_stream_request.h" diff --git a/atom/common/api/event_emitter_caller.h b/atom/common/api/event_emitter_caller.h index 26a4cbc45b90..a2567da9d109 100644 --- a/atom/common/api/event_emitter_caller.h +++ b/atom/common/api/event_emitter_caller.h @@ -7,7 +7,6 @@ #include -#include "atom/common/native_mate_converters/callback.h" #include "native_mate/converter.h" namespace mate { diff --git a/docs/api/web-view-tag.md b/docs/api/web-view-tag.md index 67a3d12171ce..229ee12f1a5b 100644 --- a/docs/api/web-view-tag.md +++ b/docs/api/web-view-tag.md @@ -740,7 +740,7 @@ Emitted when DevTools is focused / opened. Returns: -* `permission` String - The type of permission being requested. Enum of 'media', 'webNotification'. +* `permission` String - The type of permission being requested. Enum of 'media', 'notifications', 'midiSysex', 'geolocation'. * `allow` Function - Allows the permission. * `deny` Function - Deny the permission. This is the default behaviour if `allow` is not called. diff --git a/spec/fixtures/pages/permissions/geolocation.html b/spec/fixtures/pages/permissions/geolocation.html new file mode 100644 index 000000000000..1d1b4fc42451 --- /dev/null +++ b/spec/fixtures/pages/permissions/geolocation.html @@ -0,0 +1,5 @@ + diff --git a/spec/fixtures/pages/permission-request.html b/spec/fixtures/pages/permissions/media.html similarity index 100% rename from spec/fixtures/pages/permission-request.html rename to spec/fixtures/pages/permissions/media.html diff --git a/spec/fixtures/pages/permissions/midi.html b/spec/fixtures/pages/permissions/midi.html new file mode 100644 index 000000000000..c6bd8c137e36 --- /dev/null +++ b/spec/fixtures/pages/permissions/midi.html @@ -0,0 +1,5 @@ + diff --git a/spec/webview-spec.js b/spec/webview-spec.js index e40afcaefeb1..190a32660901 100644 --- a/spec/webview-spec.js +++ b/spec/webview-spec.js @@ -645,7 +645,39 @@ describe(' tag', function() { e.deny(); } }); - webview.src = "file://" + fixtures + "/pages/permission-request.html"; + webview.src = "file://" + fixtures + "/pages/permissions/media.html"; + webview.setAttribute('nodeintegration', 'on'); + document.body.appendChild(webview); + }); + + it ('emits when using navigator.geolocation api', function(done) { + webview.addEventListener('ipc-message', function(e) { + assert(e.channel, 'message'); + assert(e.args, ['ERROR(1): User denied Geolocation']); + done(); + }); + webview.addEventListener('permission-request', function(e) { + if (e.permission === 'geolocation') { + e.deny(); + } + }); + webview.src = "file://" + fixtures + "/pages/permissions/geolocation.html"; + webview.setAttribute('nodeintegration', 'on'); + document.body.appendChild(webview); + }); + + it ('emits when using navigator.requestMIDIAccess api', function(done) { + webview.addEventListener('ipc-message', function(e) { + assert(e.channel, 'message'); + assert(e.args, ['SecurityError']); + done(); + }); + webview.addEventListener('permission-request', function(e) { + if (e.permission === 'midiSysex') { + e.deny(); + } + }); + webview.src = "file://" + fixtures + "/pages/permissions/midi.html"; webview.setAttribute('nodeintegration', 'on'); document.body.appendChild(webview); }); From 04c3e083fb9922f3a2f4a11cf55002a1639eebbf Mon Sep 17 00:00:00 2001 From: Robo Date: Mon, 1 Feb 2016 00:43:29 +0530 Subject: [PATCH 5/9] remove unnecessary getter --- atom/browser/api/atom_api_web_contents.cc | 5 +++-- atom/browser/atom_browser_context.cc | 3 ++- atom/browser/atom_browser_context.h | 4 ---- atom/browser/web_contents_permission_helper.cc | 4 +++- atom/browser/web_contents_permission_helper.h | 5 ----- 5 files changed, 8 insertions(+), 13 deletions(-) diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index c6ab110b8327..fa19130422a8 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -1074,8 +1074,9 @@ void WebContents::SetPermissionRequestHandler(v8::Local val, args->ThrowError("Must pass null or function"); return; } - GetBrowserContext()->permission_manager() - ->SetPermissionRequestHandler(GetID(), handler); + auto permission_manager = static_cast( + web_contents()->GetBrowserContext()->GetPermissionManager()); + permission_manager->SetPermissionRequestHandler(GetID(), handler); } v8::Local WebContents::GetWebPreferences(v8::Isolate* isolate) { diff --git a/atom/browser/atom_browser_context.cc b/atom/browser/atom_browser_context.cc index 35358ac08a46..be004c8f6db8 100644 --- a/atom/browser/atom_browser_context.cc +++ b/atom/browser/atom_browser_context.cc @@ -65,7 +65,6 @@ std::string RemoveWhitespace(const std::string& str) { AtomBrowserContext::AtomBrowserContext(const std::string& partition, bool in_memory) : brightray::BrowserContext(partition, in_memory), - permission_manager_(new AtomPermissionManager), cert_verifier_(nullptr), job_factory_(new AtomURLRequestJobFactory), network_delegate_(new AtomNetworkDelegate), @@ -172,6 +171,8 @@ content::BrowserPluginGuestManager* AtomBrowserContext::GetGuestManager() { } content::PermissionManager* AtomBrowserContext::GetPermissionManager() { + if (!permission_manager_.get()) + permission_manager_.reset(new AtomPermissionManager); return permission_manager_.get(); } diff --git a/atom/browser/atom_browser_context.h b/atom/browser/atom_browser_context.h index 44cd1f5b79ee..d959adbc753a 100644 --- a/atom/browser/atom_browser_context.h +++ b/atom/browser/atom_browser_context.h @@ -51,10 +51,6 @@ class AtomBrowserContext : public brightray::BrowserContext { AtomNetworkDelegate* network_delegate() const { return network_delegate_; } - AtomPermissionManager* permission_manager() const { - return permission_manager_.get(); - } - private: scoped_ptr download_manager_delegate_; scoped_ptr guest_manager_; diff --git a/atom/browser/web_contents_permission_helper.cc b/atom/browser/web_contents_permission_helper.cc index 04a6a5d9883f..cf7ae1f30ecd 100644 --- a/atom/browser/web_contents_permission_helper.cc +++ b/atom/browser/web_contents_permission_helper.cc @@ -7,6 +7,7 @@ #include #include "atom/browser/atom_permission_manager.h" +#include "content/public/browser/browser_context.h" #include "content/public/browser/media_capture_devices.h" #include "content/public/browser/render_process_host.h" @@ -79,7 +80,8 @@ void WebContentsPermissionHelper::RequestPermission( content::PermissionType permission, const base::Callback& callback) { auto rfh = web_contents_->GetMainFrame(); - auto permission_manager = browser_context()->permission_manager(); + auto permission_manager = static_cast( + web_contents_->GetBrowserContext()->GetPermissionManager()); auto origin = web_contents_->GetLastCommittedURL(); permission_manager->RequestPermission(permission, rfh, origin, callback); } diff --git a/atom/browser/web_contents_permission_helper.h b/atom/browser/web_contents_permission_helper.h index 749e90184c22..6bd2bb2559e5 100644 --- a/atom/browser/web_contents_permission_helper.h +++ b/atom/browser/web_contents_permission_helper.h @@ -5,7 +5,6 @@ #ifndef ATOM_BROWSER_WEB_CONTENTS_PERMISSION_HELPER_H_ #define ATOM_BROWSER_WEB_CONTENTS_PERMISSION_HELPER_H_ -#include "atom/browser/atom_browser_context.h" #include "content/public/browser/permission_type.h" #include "content/public/browser/web_contents_user_data.h" #include "content/public/common/media_stream_request.h" @@ -24,10 +23,6 @@ class WebContentsPermissionHelper void RequestWebNotificationPermission( const base::Callback& callback); - AtomBrowserContext* browser_context() const { - return static_cast(web_contents_->GetBrowserContext()); - } - private: explicit WebContentsPermissionHelper(content::WebContents* web_contents); friend class content::WebContentsUserData; From db26dcaf4c6c22f5bc04f074d0e98c056f9fd312 Mon Sep 17 00:00:00 2001 From: Robo Date: Mon, 1 Feb 2016 03:05:34 +0530 Subject: [PATCH 6/9] expose handler from session and remove permission-request event --- atom/browser/api/atom_api_session.cc | 16 +++++ atom/browser/api/atom_api_session.h | 2 + atom/browser/api/atom_api_web_contents.cc | 15 ----- atom/browser/api/atom_api_web_contents.h | 4 -- atom/browser/atom_permission_manager.cc | 63 +++++++++---------- atom/browser/atom_permission_manager.h | 32 +++++----- atom/browser/lib/guest-view-manager.js | 23 +------ .../browser/web_contents_permission_helper.cc | 15 ++++- .../content_converter.cc | 7 +++ .../content_converter.h | 6 ++ .../lib/web-view/guest-view-internal.js | 12 +--- docs/api/session.md | 23 +++++++ docs/api/web-view-tag.md | 18 ------ spec/webview-spec.js | 32 +++++----- 14 files changed, 134 insertions(+), 134 deletions(-) diff --git a/atom/browser/api/atom_api_session.cc b/atom/browser/api/atom_api_session.cc index 1cc76ebfa23d..b0c99141f75b 100644 --- a/atom/browser/api/atom_api_session.cc +++ b/atom/browser/api/atom_api_session.cc @@ -14,8 +14,10 @@ #include "atom/browser/api/save_page_handler.h" #include "atom/browser/atom_browser_context.h" #include "atom/browser/atom_browser_main_parts.h" +#include "atom/browser/atom_permission_manager.h" #include "atom/browser/net/atom_cert_verifier.h" #include "atom/common/native_mate_converters/callback.h" +#include "atom/common/native_mate_converters/content_converter.h" #include "atom/common/native_mate_converters/gurl_converter.h" #include "atom/common/native_mate_converters/file_path_converter.h" #include "atom/common/native_mate_converters/net_converter.h" @@ -397,6 +399,18 @@ void Session::SetCertVerifyProc(v8::Local val, browser_context_->cert_verifier()->SetVerifyProc(proc); } +void Session::SetPermissionRequestHandler(v8::Local val, + mate::Arguments* args) { + AtomPermissionManager::RequestHandler handler; + if (!(val->IsNull() || mate::ConvertFromV8(args->isolate(), val, &handler))) { + args->ThrowError("Must pass null or function"); + return; + } + auto permission_manager = static_cast( + browser_context()->GetPermissionManager()); + permission_manager->SetPermissionRequestHandler(handler); +} + v8::Local Session::Cookies(v8::Isolate* isolate) { if (cookies_.IsEmpty()) { auto handle = atom::api::Cookies::Create(isolate, browser_context()); @@ -448,6 +462,8 @@ void Session::BuildPrototype(v8::Isolate* isolate, .SetMethod("enableNetworkEmulation", &Session::EnableNetworkEmulation) .SetMethod("disableNetworkEmulation", &Session::DisableNetworkEmulation) .SetMethod("setCertificateVerifyProc", &Session::SetCertVerifyProc) + .SetMethod("setPermissionRequestHandler", + &Session::SetPermissionRequestHandler) .SetProperty("cookies", &Session::Cookies) .SetProperty("webRequest", &Session::WebRequest); } diff --git a/atom/browser/api/atom_api_session.h b/atom/browser/api/atom_api_session.h index 37a5a45a6c95..efcafbfe5e33 100644 --- a/atom/browser/api/atom_api_session.h +++ b/atom/browser/api/atom_api_session.h @@ -76,6 +76,8 @@ class Session: public mate::TrackableObject, void EnableNetworkEmulation(const mate::Dictionary& options); void DisableNetworkEmulation(); void SetCertVerifyProc(v8::Local proc, mate::Arguments* args); + void SetPermissionRequestHandler(v8::Local val, + mate::Arguments* args); v8::Local Cookies(v8::Isolate* isolate); v8::Local WebRequest(v8::Isolate* isolate); diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index fa19130422a8..0a9e6c3ad6fe 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -13,7 +13,6 @@ #include "atom/browser/atom_browser_client.h" #include "atom/browser/atom_browser_context.h" #include "atom/browser/atom_browser_main_parts.h" -#include "atom/browser/atom_permission_manager.h" #include "atom/browser/native_window.h" #include "atom/browser/web_contents_permission_helper.h" #include "atom/browser/web_contents_preferences.h" @@ -1067,18 +1066,6 @@ bool WebContents::IsGuest() const { return type_ == WEB_VIEW; } -void WebContents::SetPermissionRequestHandler(v8::Local val, - mate::Arguments* args) { - AtomPermissionManager::RequestHandler handler; - if (!(val->IsNull() || mate::ConvertFromV8(args->isolate(), val, &handler))) { - args->ThrowError("Must pass null or function"); - return; - } - auto permission_manager = static_cast( - web_contents()->GetBrowserContext()->GetPermissionManager()); - permission_manager->SetPermissionRequestHandler(GetID(), handler); -} - v8::Local WebContents::GetWebPreferences(v8::Isolate* isolate) { WebContentsPreferences* web_preferences = WebContentsPreferences::FromWebContents(web_contents()); @@ -1178,8 +1165,6 @@ void WebContents::BuildPrototype(v8::Isolate* isolate, .SetMethod("_printToPDF", &WebContents::PrintToPDF) .SetMethod("addWorkSpace", &WebContents::AddWorkSpace) .SetMethod("removeWorkSpace", &WebContents::RemoveWorkSpace) - .SetMethod("_setPermissionRequestHandler", - &WebContents::SetPermissionRequestHandler) .SetProperty("session", &WebContents::Session) .SetProperty("devToolsWebContents", &WebContents::DevToolsWebContents) .SetProperty("debugger", &WebContents::Debugger); diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index 6232aa3bf0cf..6587759efb9f 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -133,10 +133,6 @@ class WebContents : public mate::TrackableObject, void SetAllowTransparency(bool allow); bool IsGuest() const; - // Handler for permission requests. - void SetPermissionRequestHandler(v8::Local val, - mate::Arguments* args); - // Returns the web preferences of current WebContents. v8::Local GetWebPreferences(v8::Isolate* isolate); diff --git a/atom/browser/atom_permission_manager.cc b/atom/browser/atom_permission_manager.cc index ccd456e7c201..c245a278c3b2 100644 --- a/atom/browser/atom_permission_manager.cc +++ b/atom/browser/atom_permission_manager.cc @@ -8,6 +8,7 @@ #include "content/public/browser/permission_type.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/web_contents.h" namespace atom { @@ -19,33 +20,13 @@ AtomPermissionManager::~AtomPermissionManager() { } void AtomPermissionManager::SetPermissionRequestHandler( - int id, const RequestHandler& handler) { - if (handler.is_null()) { - request_handler_map_.erase(id); - return; + if (handler.is_null() && !pending_requests_.empty()) { + for (const auto& request : pending_requests_) + request.second.Run(content::PERMISSION_STATUS_DENIED); + pending_requests_.clear(); } - request_handler_map_[id] = handler; -} - -void AtomPermissionManager::RequestPermission( - content::PermissionType permission, - content::RenderFrameHost* render_frame_host, - const GURL& origin, - const base::Callback& callback) { - bool user_gesture = false; - RequestPermission(permission, render_frame_host, origin, user_gesture, - base::Bind(&AtomPermissionManager::OnPermissionResponse, - base::Unretained(this), callback)); -} - -void AtomPermissionManager::OnPermissionResponse( - const base::Callback& callback, - content::PermissionStatus status) { - if (status == content::PERMISSION_STATUS_GRANTED) - callback.Run(true); - else - callback.Run(false); + request_handler_ = handler; } int AtomPermissionManager::RequestPermission( @@ -53,25 +34,39 @@ int AtomPermissionManager::RequestPermission( content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, bool user_gesture, - const ResponseCallback& callback) { - int process_id = render_frame_host->GetProcess()->GetID(); - auto request_handler = request_handler_map_.find(process_id); - + const ResponseCallback& response_callback) { if (permission == content::PermissionType::MIDI_SYSEX) { content::ChildProcessSecurityPolicy::GetInstance()-> - GrantSendMidiSysExMessage(process_id); + GrantSendMidiSysExMessage(render_frame_host->GetProcess()->GetID()); } - if (request_handler != request_handler_map_.end()) { - pending_requests_[++request_id_] = callback; - request_handler->second.Run(permission, callback); + if (!request_handler_.is_null()) { + auto web_contents = + content::WebContents::FromRenderFrameHost(render_frame_host); + ++request_id_; + auto callback = base::Bind(&AtomPermissionManager::OnPermissionResponse, + base::Unretained(this), + request_id_, + requesting_origin, + response_callback); + pending_requests_[request_id_] = callback; + request_handler_.Run(web_contents, permission, callback); return request_id_; } - callback.Run(content::PERMISSION_STATUS_GRANTED); + response_callback.Run(content::PERMISSION_STATUS_GRANTED); return kNoPendingOperation; } +void AtomPermissionManager::OnPermissionResponse( + int request_id, + const GURL& origin, + const ResponseCallback& callback, + content::PermissionStatus status) { + callback.Run(status); + pending_requests_.erase(request_id); +} + void AtomPermissionManager::CancelPermissionRequest(int request_id) { auto request = pending_requests_.find(request_id); if (request != pending_requests_.end()) { diff --git a/atom/browser/atom_permission_manager.h b/atom/browser/atom_permission_manager.h index fb449037f64d..8d8729acc66f 100644 --- a/atom/browser/atom_permission_manager.h +++ b/atom/browser/atom_permission_manager.h @@ -7,10 +7,13 @@ #include -#include "base/callback_forward.h" -#include "base/macros.h" +#include "base/callback.h" #include "content/public/browser/permission_manager.h" +namespace content { +class WebContents; +} + namespace atom { class AtomPermissionManager : public content::PermissionManager { @@ -21,22 +24,13 @@ class AtomPermissionManager : public content::PermissionManager { using ResponseCallback = base::Callback; using RequestHandler = - base::Callback; // Handler to dispatch permission requests in JS. - void SetPermissionRequestHandler(int id, const RequestHandler& handler); + void SetPermissionRequestHandler(const RequestHandler& handler); - void RequestPermission( - content::PermissionType permission, - content::RenderFrameHost* render_frame_host, - const GURL& origin, - const base::Callback& callback); - void OnPermissionResponse( - const base::Callback& callback, - content::PermissionStatus status); - - protected: // content::PermissionManager: int RequestPermission( content::PermissionType permission, @@ -44,6 +38,14 @@ class AtomPermissionManager : public content::PermissionManager { const GURL& requesting_origin, bool user_gesture, const ResponseCallback& callback) override; + + protected: + void OnPermissionResponse(int request_id, + const GURL& url, + const ResponseCallback& callback, + content::PermissionStatus status); + + // content::PermissionManager: void CancelPermissionRequest(int request_id) override; void ResetPermission(content::PermissionType permission, const GURL& requesting_origin, @@ -63,7 +65,7 @@ class AtomPermissionManager : public content::PermissionManager { void UnsubscribePermissionStatusChange(int subscription_id) override; private: - std::map request_handler_map_; + RequestHandler request_handler_; std::map pending_requests_; diff --git a/atom/browser/lib/guest-view-manager.js b/atom/browser/lib/guest-view-manager.js index d4332070fa62..b41b9b3a0f5c 100644 --- a/atom/browser/lib/guest-view-manager.js +++ b/atom/browser/lib/guest-view-manager.js @@ -42,7 +42,6 @@ var supportedWebViewEvents = [ var nextInstanceId = 0; var guestInstances = {}; var embedderElementsMap = {}; -var pendingRequestsMap = {}; var reverseEmbedderElementsMap = {}; // Moves the last element of array to the first one. @@ -135,15 +134,7 @@ var createGuest = function(embedder, params) { if (params.allowtransparency != null) { this.setAllowTransparency(params.allowtransparency); } - guest.allowPopups = params.allowpopups; - - // Dispatches permission request event. - this._setPermissionRequestHandler((permission, callback) => { - if (!pendingRequestsMap[this.viewInstanceId]) - pendingRequestsMap[this.viewInstanceId] = {}; - pendingRequestsMap[this.viewInstanceId][permission] = callback; - embedder.send.apply(embedder, ["ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-" + this.viewInstanceId, "permission-request", permission]); - }); + return guest.allowPopups = params.allowpopups; }); // Dispatch events to embedder. @@ -170,7 +161,6 @@ var createGuest = function(embedder, params) { var args = 2 <= arguments.length ? slice.call(arguments, 1) : []; return embedder.send.apply(embedder, ["ATOM_SHELL_GUEST_VIEW_INTERNAL_SIZE_CHANGED-" + guest.viewInstanceId].concat(slice.call(args))); }); - return id; }; @@ -198,8 +188,7 @@ var attachGuest = function(embedder, elementInstanceId, guestInstanceId, params) nodeIntegration: (ref1 = params.nodeintegration) != null ? ref1 : false, plugins: params.plugins, webSecurity: !params.disablewebsecurity, - blinkFeatures: params.blinkfeatures, - webNotification: !params.disablewebnotification, + blinkFeatures: params.blinkfeatures }; if (params.preload) { webPreferences.preloadURL = params.preload; @@ -216,7 +205,6 @@ var destroyGuest = function(embedder, id) { webViewManager.removeGuest(embedder, id); guestInstances[id].guest.destroy(); delete guestInstances[id]; - delete pendingRequestsMap[id]; key = reverseEmbedderElementsMap[id]; if (key != null) { delete reverseEmbedderElementsMap[id]; @@ -246,13 +234,6 @@ ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_SET_ALLOW_TRANSPARENCY', function(even return (ref1 = guestInstances[id]) != null ? ref1.guest.setAllowTransparency(allowtransparency) : void 0; }); -ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_SET_PERMISSION_RESPONSE', function(event, id, permission, allowed) { - if (pendingRequestsMap[id] != null) { - const callback = pendingRequestsMap[id][permission]; - callback.apply(null, [allowed]); - } -}); - // Returns WebContents from its guest id. exports.getGuest = function(id) { var ref1; diff --git a/atom/browser/web_contents_permission_helper.cc b/atom/browser/web_contents_permission_helper.cc index cf7ae1f30ecd..da2ad57ad979 100644 --- a/atom/browser/web_contents_permission_helper.cc +++ b/atom/browser/web_contents_permission_helper.cc @@ -66,6 +66,14 @@ void MediaAccessAllowed( callback.Run(devices, result, scoped_ptr()); } +void OnPermissionResponse(const base::Callback& callback, + content::PermissionStatus status) { + if (status == content::PERMISSION_STATUS_GRANTED) + callback.Run(true); + else + callback.Run(false); +} + } // namespace WebContentsPermissionHelper::WebContentsPermissionHelper( @@ -83,13 +91,18 @@ void WebContentsPermissionHelper::RequestPermission( auto permission_manager = static_cast( web_contents_->GetBrowserContext()->GetPermissionManager()); auto origin = web_contents_->GetLastCommittedURL(); - permission_manager->RequestPermission(permission, rfh, origin, callback); + bool user_gesture = false; + permission_manager->RequestPermission( + permission, rfh, origin, user_gesture, + base::Bind(&OnPermissionResponse, callback)); } void WebContentsPermissionHelper::RequestMediaAccessPermission( const content::MediaStreamRequest& request, const content::MediaResponseCallback& response_callback) { auto callback = base::Bind(&MediaAccessAllowed, request, response_callback); + // The permission type doesn't matter here, AUDIO_CAPTURE/VIDEO_CAPTURE + // are presented as same type in content_converter.h. RequestPermission(content::PermissionType::AUDIO_CAPTURE, callback); } diff --git a/atom/common/native_mate_converters/content_converter.cc b/atom/common/native_mate_converters/content_converter.cc index d85188dad1fa..15066085af09 100644 --- a/atom/common/native_mate_converters/content_converter.cc +++ b/atom/common/native_mate_converters/content_converter.cc @@ -7,6 +7,7 @@ #include #include +#include "atom/browser/api/atom_api_web_contents.h" #include "atom/common/native_mate_converters/callback.h" #include "atom/common/native_mate_converters/string16_converter.h" #include "content/public/browser/web_contents.h" @@ -163,4 +164,10 @@ bool Converter::FromV8( return true; } +// static +v8::Local Converter::ToV8( + v8::Isolate* isolate, content::WebContents* val) { + return atom::api::WebContents::CreateFrom(isolate, val).ToV8(); +} + } // namespace mate diff --git a/atom/common/native_mate_converters/content_converter.h b/atom/common/native_mate_converters/content_converter.h index 6c9130b18875..b1a42b6897ca 100644 --- a/atom/common/native_mate_converters/content_converter.h +++ b/atom/common/native_mate_converters/content_converter.h @@ -53,6 +53,12 @@ struct Converter { content::StopFindAction* out); }; +template<> +struct Converter { + static v8::Local ToV8(v8::Isolate* isolate, + content::WebContents* val); +}; + } // namespace mate #endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_CONTENT_CONVERTER_H_ diff --git a/atom/renderer/lib/web-view/guest-view-internal.js b/atom/renderer/lib/web-view/guest-view-internal.js index acade1f41c0f..a7427abd631c 100644 --- a/atom/renderer/lib/web-view/guest-view-internal.js +++ b/atom/renderer/lib/web-view/guest-view-internal.js @@ -34,8 +34,7 @@ var WEB_VIEW_EVENTS = { 'page-favicon-updated': ['favicons'], 'enter-html-full-screen': [], 'leave-html-full-screen': [], - 'found-in-page': ['result'], - 'permission-request': ['permission', 'allow', 'deny'] + 'found-in-page': ['result'] }; var DEPRECATED_EVENTS = { @@ -65,15 +64,6 @@ module.exports = { ipcRenderer.on("ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-" + viewInstanceId, function() { var eventName = arguments[1]; var args = 3 <= arguments.length ? slice.call(arguments, 2) : []; - if (eventName === 'permission-request') { - var allow = function allow() { - ipcRenderer.send("ATOM_SHELL_GUEST_VIEW_MANAGER_SET_PERMISSION_RESPONSE", viewInstanceId, args[0], "granted"); - }; - var deny = function deny() { - ipcRenderer.send("ATOM_SHELL_GUEST_VIEW_MANAGER_SET_PERMISSION_RESPONSE", viewInstanceId, args[0], "denied"); - }; - args = args.concat([allow, deny]); - } return dispatchEvent.apply(null, [webView, eventName, eventName].concat(slice.call(args))); }); ipcRenderer.on("ATOM_SHELL_GUEST_VIEW_INTERNAL_IPC_MESSAGE-" + viewInstanceId, function() { diff --git a/docs/api/session.md b/docs/api/session.md index da5bac23592d..c70be61de892 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -289,6 +289,29 @@ myWindow.webContents.session.setCertificateVerifyProc(function(hostname, cert, c }); ``` +#### `ses.setPermissionRequestHandler(handler)` + +* `handler` Function + * `webContents` Object - [WebContents](web-contents.md) requesting the permission. + * `permission` String - Enum of 'media', 'geolocation', 'notifications', 'midiSysex'. + * `callback` Function - Allow or deny the permission. + +Sets the handler which can be used to respond to permission requests for the `session`. +Calling `callback('granted')` will allow the permission and `callback('denied')` will reject it. + +```javascript +session.fromPartition(partition).setPermissionRequestHandler(function(webContents, permission, callback) { + if (webContents.getURL() === host) { + if (permission == "notifications") { + callback(); // denied. + return; + } + } + + callback('granted'); +}); +``` + #### `ses.webRequest` The `webRequest` API set allows to intercept and modify contents of a request at diff --git a/docs/api/web-view-tag.md b/docs/api/web-view-tag.md index 229ee12f1a5b..9cb8f49af352 100644 --- a/docs/api/web-view-tag.md +++ b/docs/api/web-view-tag.md @@ -736,22 +736,4 @@ Emitted when DevTools is closed. Emitted when DevTools is focused / opened. -### Event: 'permission-request' - -Returns: - -* `permission` String - The type of permission being requested. Enum of 'media', 'notifications', 'midiSysex', 'geolocation'. -* `allow` Function - Allows the permission. -* `deny` Function - Deny the permission. This is the default behaviour if `allow` is not called. - -Emitted when guest page requires special permission. - -```javascript -// This will deny guest page access to the webkitGetUserMedia API. -webview.addEventListener('permission-request', function(e) { - if (e.permission === 'media') - e.deny(); -}); -``` - [blink-feature-string]: https://code.google.com/p/chromium/codesearch#chromium/src/out/Debug/gen/blink/platform/RuntimeEnabledFeatures.cpp&sq=package:chromium&type=cs&l=527 diff --git a/spec/webview-spec.js b/spec/webview-spec.js index 190a32660901..6a3866046764 100644 --- a/spec/webview-spec.js +++ b/spec/webview-spec.js @@ -634,19 +634,27 @@ describe(' tag', function() { }); }); describe('permission-request event', function() { + function setUpRequestHandler(webview, requested_permission) { + const session = require('electron').remote.session; + var listener = function(webContents, permission, callback) { + if (webContents.getId() === webview.getId() ) { + assert.equal(permission, requested_permission); + callback("denied"); + } + }; + session.fromPartition(webview.partition).setPermissionRequestHandler(listener); + } + it ('emits when using navigator.getUserMedia api', function(done) { webview.addEventListener('ipc-message', function(e) { assert(e.channel, 'message'); assert(e.args, ['PermissionDeniedError']); done(); }); - webview.addEventListener('permission-request', function(e) { - if (e.permission === 'media') { - e.deny(); - } - }); webview.src = "file://" + fixtures + "/pages/permissions/media.html"; + webview.partition = "permissionTest"; webview.setAttribute('nodeintegration', 'on'); + setUpRequestHandler(webview, "media"); document.body.appendChild(webview); }); @@ -656,13 +664,10 @@ describe(' tag', function() { assert(e.args, ['ERROR(1): User denied Geolocation']); done(); }); - webview.addEventListener('permission-request', function(e) { - if (e.permission === 'geolocation') { - e.deny(); - } - }); webview.src = "file://" + fixtures + "/pages/permissions/geolocation.html"; + webview.partition = "permissionTest"; webview.setAttribute('nodeintegration', 'on'); + setUpRequestHandler(webview, "geolocation"); document.body.appendChild(webview); }); @@ -672,13 +677,10 @@ describe(' tag', function() { assert(e.args, ['SecurityError']); done(); }); - webview.addEventListener('permission-request', function(e) { - if (e.permission === 'midiSysex') { - e.deny(); - } - }); webview.src = "file://" + fixtures + "/pages/permissions/midi.html"; + webview.partition = "permissionTest"; webview.setAttribute('nodeintegration', 'on'); + setUpRequestHandler(webview, "midiSysex"); document.body.appendChild(webview); }); }); From 45eada306f84f7974f85b1a8e1ecdf1f88f28f4d Mon Sep 17 00:00:00 2001 From: Robo Date: Mon, 1 Feb 2016 15:13:49 +0530 Subject: [PATCH 7/9] add pointerlock permission type --- atom/browser/api/atom_api_web_contents.cc | 9 ++++ atom/browser/api/atom_api_web_contents.h | 4 ++ atom/browser/atom_permission_manager.cc | 41 +++++++++++++++---- atom/browser/atom_permission_manager.h | 7 +++- .../browser/web_contents_permission_helper.cc | 16 +++++++- atom/browser/web_contents_permission_helper.h | 9 +++- .../content_converter.cc | 22 ++++++---- 7 files changed, 89 insertions(+), 19 deletions(-) diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index 0a9e6c3ad6fe..67af2022dbc8 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -457,6 +457,15 @@ void WebContents::RequestMediaAccessPermission( permission_helper->RequestMediaAccessPermission(request, callback); } +void WebContents::RequestToLockMouse( + content::WebContents* web_contents, + bool user_gesture, + bool last_unlocked_by_target) { + auto permission_helper = + WebContentsPermissionHelper::FromWebContents(web_contents); + permission_helper->RequestPointerLockPermission(user_gesture); +} + void WebContents::BeforeUnloadFired(const base::TimeTicks& proceed_time) { // Do nothing, we override this method just to avoid compilation error since // there are two virtual functions named BeforeUnloadFired. diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index 6587759efb9f..cd6d0e7b6083 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -199,6 +199,10 @@ class WebContents : public mate::TrackableObject, content::WebContents* web_contents, const content::MediaStreamRequest& request, const content::MediaResponseCallback& callback) override; + void RequestToLockMouse( + content::WebContents* web_contents, + bool user_gesture, + bool last_unlocked_by_target) override; // content::WebContentsObserver: void BeforeUnloadFired(const base::TimeTicks& proceed_time) override; diff --git a/atom/browser/atom_permission_manager.cc b/atom/browser/atom_permission_manager.cc index c245a278c3b2..e7fdaaffea71 100644 --- a/atom/browser/atom_permission_manager.cc +++ b/atom/browser/atom_permission_manager.cc @@ -8,10 +8,28 @@ #include "content/public/browser/permission_type.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" namespace atom { +namespace { + +// Must be kept in sync with atom_browser_client.cc +int kDefaultRoutingID = 2; + +bool WebContentsDestroyed(int process_id) { + auto rvh = content::RenderViewHost::FromID(process_id, kDefaultRoutingID); + if (rvh) { + auto contents = content::WebContents::FromRenderViewHost(rvh); + return contents->IsBeingDestroyed(); + } + + return true; +} + +} // namespace + AtomPermissionManager::AtomPermissionManager() : request_id_(0) { } @@ -22,8 +40,10 @@ AtomPermissionManager::~AtomPermissionManager() { void AtomPermissionManager::SetPermissionRequestHandler( const RequestHandler& handler) { if (handler.is_null() && !pending_requests_.empty()) { - for (const auto& request : pending_requests_) - request.second.Run(content::PERMISSION_STATUS_DENIED); + for (const auto& request : pending_requests_) { + if (!WebContentsDestroyed(request.second.render_process_id)) + request.second.callback.Run(content::PERMISSION_STATUS_DENIED); + } pending_requests_.clear(); } request_handler_ = handler; @@ -35,9 +55,11 @@ int AtomPermissionManager::RequestPermission( const GURL& requesting_origin, bool user_gesture, const ResponseCallback& response_callback) { + int process_id = render_frame_host->GetProcess()->GetID(); + if (permission == content::PermissionType::MIDI_SYSEX) { content::ChildProcessSecurityPolicy::GetInstance()-> - GrantSendMidiSysExMessage(render_frame_host->GetProcess()->GetID()); + GrantSendMidiSysExMessage(process_id); } if (!request_handler_.is_null()) { @@ -49,7 +71,7 @@ int AtomPermissionManager::RequestPermission( request_id_, requesting_origin, response_callback); - pending_requests_[request_id_] = callback; + pending_requests_[request_id_] = { process_id, callback }; request_handler_.Run(web_contents, permission, callback); return request_id_; } @@ -63,14 +85,19 @@ void AtomPermissionManager::OnPermissionResponse( const GURL& origin, const ResponseCallback& callback, content::PermissionStatus status) { - callback.Run(status); - pending_requests_.erase(request_id); + auto request = pending_requests_.find(request_id); + if (request != pending_requests_.end()) { + if (!WebContentsDestroyed(request->second.render_process_id)) + callback.Run(status); + pending_requests_.erase(request); + } } void AtomPermissionManager::CancelPermissionRequest(int request_id) { auto request = pending_requests_.find(request_id); if (request != pending_requests_.end()) { - request->second.Run(content::PERMISSION_STATUS_DENIED); + if (!WebContentsDestroyed(request->second.render_process_id)) + request->second.callback.Run(content::PERMISSION_STATUS_DENIED); pending_requests_.erase(request); } } diff --git a/atom/browser/atom_permission_manager.h b/atom/browser/atom_permission_manager.h index 8d8729acc66f..4bebbbc84f29 100644 --- a/atom/browser/atom_permission_manager.h +++ b/atom/browser/atom_permission_manager.h @@ -65,9 +65,14 @@ class AtomPermissionManager : public content::PermissionManager { void UnsubscribePermissionStatusChange(int subscription_id) override; private: + struct RequestInfo { + int render_process_id; + ResponseCallback callback; + }; + RequestHandler request_handler_; - std::map pending_requests_; + std::map pending_requests_; int request_id_; diff --git a/atom/browser/web_contents_permission_helper.cc b/atom/browser/web_contents_permission_helper.cc index da2ad57ad979..6b017aa1342e 100644 --- a/atom/browser/web_contents_permission_helper.cc +++ b/atom/browser/web_contents_permission_helper.cc @@ -66,6 +66,11 @@ void MediaAccessAllowed( callback.Run(devices, result, scoped_ptr()); } +void OnPointerLockResponse(content::WebContents* web_contents, bool allowed) { + if (web_contents) + web_contents->GotResponseToLockMouseRequest(allowed); +} + void OnPermissionResponse(const base::Callback& callback, content::PermissionStatus status) { if (status == content::PERMISSION_STATUS_GRANTED) @@ -86,12 +91,12 @@ WebContentsPermissionHelper::~WebContentsPermissionHelper() { void WebContentsPermissionHelper::RequestPermission( content::PermissionType permission, - const base::Callback& callback) { + const base::Callback& callback, + bool user_gesture) { auto rfh = web_contents_->GetMainFrame(); auto permission_manager = static_cast( web_contents_->GetBrowserContext()->GetPermissionManager()); auto origin = web_contents_->GetLastCommittedURL(); - bool user_gesture = false; permission_manager->RequestPermission( permission, rfh, origin, user_gesture, base::Bind(&OnPermissionResponse, callback)); @@ -111,4 +116,11 @@ void WebContentsPermissionHelper::RequestWebNotificationPermission( RequestPermission(content::PermissionType::NOTIFICATIONS, callback); } +void WebContentsPermissionHelper::RequestPointerLockPermission( + bool user_gesture) { + RequestPermission((content::PermissionType)(PermissionType::POINTER_LOCK), + base::Bind(&OnPointerLockResponse, web_contents_), + user_gesture); +} + } // namespace atom diff --git a/atom/browser/web_contents_permission_helper.h b/atom/browser/web_contents_permission_helper.h index 6bd2bb2559e5..68682340ceb6 100644 --- a/atom/browser/web_contents_permission_helper.h +++ b/atom/browser/web_contents_permission_helper.h @@ -17,11 +17,17 @@ class WebContentsPermissionHelper public: ~WebContentsPermissionHelper() override; + enum class PermissionType { + POINTER_LOCK = static_cast(content::PermissionType::NUM) + 1, + FULLSCREEN + }; + void RequestMediaAccessPermission( const content::MediaStreamRequest& request, const content::MediaResponseCallback& callback); void RequestWebNotificationPermission( const base::Callback& callback); + void RequestPointerLockPermission(bool user_gesture); private: explicit WebContentsPermissionHelper(content::WebContents* web_contents); @@ -29,7 +35,8 @@ class WebContentsPermissionHelper void RequestPermission( content::PermissionType permission, - const base::Callback& callback); + const base::Callback& callback, + bool user_gesture = false); content::WebContents* web_contents_; diff --git a/atom/common/native_mate_converters/content_converter.cc b/atom/common/native_mate_converters/content_converter.cc index 15066085af09..699f101d0e4b 100644 --- a/atom/common/native_mate_converters/content_converter.cc +++ b/atom/common/native_mate_converters/content_converter.cc @@ -8,6 +8,7 @@ #include #include "atom/browser/api/atom_api_web_contents.h" +#include "atom/browser/web_contents_permission_helper.h" #include "atom/common/native_mate_converters/callback.h" #include "atom/common/native_mate_converters/string16_converter.h" #include "content/public/browser/web_contents.h" @@ -104,16 +105,14 @@ bool Converter::FromV8( v8::Isolate* isolate, v8::Local val, content::PermissionStatus* out) { - std::string status; - if (!ConvertFromV8(isolate, val, &status)) + bool result; + if (!ConvertFromV8(isolate, val, &result)) return false; - if (status == "granted") + if (result) *out = content::PERMISSION_STATUS_GRANTED; - else if (status == "denied" || status.empty()) - *out = content::PERMISSION_STATUS_DENIED; else - return false; + *out = content::PERMISSION_STATUS_DENIED; return true; } @@ -121,6 +120,7 @@ bool Converter::FromV8( // static v8::Local Converter::ToV8( v8::Isolate* isolate, const content::PermissionType& val) { + using PermissionType = atom::WebContentsPermissionHelper::PermissionType; switch (val) { case content::PermissionType::MIDI_SYSEX: return StringToV8(isolate, "midiSysex"); @@ -137,10 +137,16 @@ v8::Local Converter::ToV8( return StringToV8(isolate, "mediaKeySystem"); case content::PermissionType::MIDI: return StringToV8(isolate, "midi"); - case content::PermissionType::DURABLE_STORAGE: default: - return StringToV8(isolate, "unknown"); + break; } + + if (val == (content::PermissionType)(PermissionType::POINTER_LOCK)) + return StringToV8(isolate, "pointerLock"); + else if (val == (content::PermissionType)(PermissionType::FULLSCREEN)) + return StringToV8(isolate, "fullscreen"); + + return StringToV8(isolate, "unknown"); } // static From b575cd0ef936531b178408eef628fc3033e57cd4 Mon Sep 17 00:00:00 2001 From: Robo Date: Mon, 1 Feb 2016 15:33:38 +0530 Subject: [PATCH 8/9] add fullscreen permission type --- atom/browser/api/atom_api_web_contents.cc | 12 ++++++++++++ atom/browser/api/atom_api_web_contents.h | 5 +++++ atom/browser/web_contents_permission_helper.cc | 6 ++++++ atom/browser/web_contents_permission_helper.h | 2 ++ docs/api/session.md | 8 ++++---- spec/webview-spec.js | 2 +- 6 files changed, 30 insertions(+), 5 deletions(-) diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index 67af2022dbc8..d26a3593cb09 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -390,6 +390,18 @@ void WebContents::HandleKeyboardEvent( void WebContents::EnterFullscreenModeForTab(content::WebContents* source, const GURL& origin) { + auto permission_helper = + WebContentsPermissionHelper::FromWebContents(source); + auto callback = base::Bind(&WebContents::OnEnterFullscreenModeForTab, + base::Unretained(this), source, origin); + permission_helper->RequestFullscreenPermission(callback); +} + +void WebContents::OnEnterFullscreenModeForTab(content::WebContents* source, + const GURL& origin, + bool allowed) { + if (!allowed) + return; CommonWebContentsDelegate::EnterFullscreenModeForTab(source, origin); Emit("enter-html-full-screen"); } diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index cd6d0e7b6083..3e31d26efdf1 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -133,6 +133,11 @@ class WebContents : public mate::TrackableObject, void SetAllowTransparency(bool allow); bool IsGuest() const; + // Callback triggered on permission response. + void OnEnterFullscreenModeForTab(content::WebContents* source, + const GURL& origin, + bool allowed); + // Returns the web preferences of current WebContents. v8::Local GetWebPreferences(v8::Isolate* isolate); diff --git a/atom/browser/web_contents_permission_helper.cc b/atom/browser/web_contents_permission_helper.cc index 6b017aa1342e..6cade6aa3526 100644 --- a/atom/browser/web_contents_permission_helper.cc +++ b/atom/browser/web_contents_permission_helper.cc @@ -102,6 +102,12 @@ void WebContentsPermissionHelper::RequestPermission( base::Bind(&OnPermissionResponse, callback)); } +void WebContentsPermissionHelper::RequestFullscreenPermission( + const base::Callback& callback) { + RequestPermission((content::PermissionType)(PermissionType::FULLSCREEN), + callback); +} + void WebContentsPermissionHelper::RequestMediaAccessPermission( const content::MediaStreamRequest& request, const content::MediaResponseCallback& response_callback) { diff --git a/atom/browser/web_contents_permission_helper.h b/atom/browser/web_contents_permission_helper.h index 68682340ceb6..90ae6dff56f5 100644 --- a/atom/browser/web_contents_permission_helper.h +++ b/atom/browser/web_contents_permission_helper.h @@ -22,6 +22,8 @@ class WebContentsPermissionHelper FULLSCREEN }; + void RequestFullscreenPermission( + const base::Callback& callback); void RequestMediaAccessPermission( const content::MediaStreamRequest& request, const content::MediaResponseCallback& callback); diff --git a/docs/api/session.md b/docs/api/session.md index c70be61de892..724dab390d23 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -293,22 +293,22 @@ myWindow.webContents.session.setCertificateVerifyProc(function(hostname, cert, c * `handler` Function * `webContents` Object - [WebContents](web-contents.md) requesting the permission. - * `permission` String - Enum of 'media', 'geolocation', 'notifications', 'midiSysex'. + * `permission` String - Enum of 'media', 'geolocation', 'notifications', 'midiSysex', 'pointerLock', 'fullscreen'. * `callback` Function - Allow or deny the permission. Sets the handler which can be used to respond to permission requests for the `session`. -Calling `callback('granted')` will allow the permission and `callback('denied')` will reject it. +Calling `callback(true)` will allow the permission and `callback(false)` will reject it. ```javascript session.fromPartition(partition).setPermissionRequestHandler(function(webContents, permission, callback) { if (webContents.getURL() === host) { if (permission == "notifications") { - callback(); // denied. + callback(false); // denied. return; } } - callback('granted'); + callback(true); }); ``` diff --git a/spec/webview-spec.js b/spec/webview-spec.js index 6a3866046764..cac5fb56b444 100644 --- a/spec/webview-spec.js +++ b/spec/webview-spec.js @@ -639,7 +639,7 @@ describe(' tag', function() { var listener = function(webContents, permission, callback) { if (webContents.getId() === webview.getId() ) { assert.equal(permission, requested_permission); - callback("denied"); + callback(false); } }; session.fromPartition(webview.partition).setPermissionRequestHandler(listener); From ff5fb512b885b4e5ac4dfb16e1a54c82318a8329 Mon Sep 17 00:00:00 2001 From: Robo Date: Mon, 1 Feb 2016 17:45:53 +0530 Subject: [PATCH 9/9] use mediastreamdevicescontroller from brightray to enumerate available devices --- atom/browser/api/atom_api_session.cc | 2 +- .../browser/web_contents_permission_helper.cc | 50 +++---------------- 2 files changed, 7 insertions(+), 45 deletions(-) diff --git a/atom/browser/api/atom_api_session.cc b/atom/browser/api/atom_api_session.cc index b0c99141f75b..b9032409e463 100644 --- a/atom/browser/api/atom_api_session.cc +++ b/atom/browser/api/atom_api_session.cc @@ -400,7 +400,7 @@ void Session::SetCertVerifyProc(v8::Local val, } void Session::SetPermissionRequestHandler(v8::Local val, - mate::Arguments* args) { + mate::Arguments* args) { AtomPermissionManager::RequestHandler handler; if (!(val->IsNull() || mate::ConvertFromV8(args->isolate(), val, &handler))) { args->ThrowError("Must pass null or function"); diff --git a/atom/browser/web_contents_permission_helper.cc b/atom/browser/web_contents_permission_helper.cc index 6cade6aa3526..f5ce414dd2ca 100644 --- a/atom/browser/web_contents_permission_helper.cc +++ b/atom/browser/web_contents_permission_helper.cc @@ -7,8 +7,8 @@ #include #include "atom/browser/atom_permission_manager.h" +#include "brightray/browser/media/media_stream_devices_controller.h" #include "content/public/browser/browser_context.h" -#include "content/public/browser/media_capture_devices.h" #include "content/public/browser/render_process_host.h" DEFINE_WEB_CONTENTS_USER_DATA_KEY(atom::WebContentsPermissionHelper); @@ -17,53 +17,15 @@ namespace atom { namespace { -const content::MediaStreamDevice* FindDeviceWithId( - const content::MediaStreamDevices& devices, - const std::string& device_id) { - if (device_id.empty()) - return &(*devices.begin()); - for (const auto& iter : devices) - if (iter.id == device_id) - return &(iter); - return nullptr; -} - void MediaAccessAllowed( const content::MediaStreamRequest& request, const content::MediaResponseCallback& callback, bool allowed) { - if (!allowed) { - callback.Run(content::MediaStreamDevices(), - content::MEDIA_DEVICE_PERMISSION_DENIED, - scoped_ptr()); - return; - } - - content::MediaStreamDevices devices; - content::MediaStreamRequestResult result = content::MEDIA_DEVICE_NO_HARDWARE; - - if (request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE) { - const content::MediaStreamDevices& audio_devices = - content::MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices(); - const content::MediaStreamDevice* audio_device = - FindDeviceWithId(audio_devices, request.requested_audio_device_id); - if (audio_device) - devices.push_back(*audio_device); - } - - if (request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) { - const content::MediaStreamDevices& video_devices = - content::MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices(); - const content::MediaStreamDevice* video_device = - FindDeviceWithId(video_devices, request.requested_video_device_id); - if (video_device) - devices.push_back(*video_device); - } - - if (!devices.empty()) - result = content::MEDIA_DEVICE_OK; - - callback.Run(devices, result, scoped_ptr()); + brightray::MediaStreamDevicesController controller(request, callback); + if (allowed) + controller.Accept(); + else + controller.Deny(content::MEDIA_DEVICE_PERMISSION_DENIED); } void OnPointerLockResponse(content::WebContents* web_contents, bool allowed) {