webview: add permission-request event

This commit is contained in:
Robo 2016-01-23 18:59:47 +05:30
parent 30b35644f6
commit 85e13333c3
12 changed files with 260 additions and 3 deletions

View file

@ -14,6 +14,7 @@
#include "atom/browser/atom_browser_context.h" #include "atom/browser/atom_browser_context.h"
#include "atom/browser/atom_browser_main_parts.h" #include "atom/browser/atom_browser_main_parts.h"
#include "atom/browser/native_window.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_contents_preferences.h"
#include "atom/browser/web_view_guest_delegate.h" #include "atom/browser/web_view_guest_delegate.h"
#include "atom/common/api/api_messages.h" #include "atom/common/api/api_messages.h"
@ -262,6 +263,9 @@ WebContents::WebContents(v8::Isolate* isolate,
// Save the preferences in C++. // Save the preferences in C++.
new WebContentsPreferences(web_contents, options); new WebContentsPreferences(web_contents, options);
// Initialze permission helper.
new WebContentsPermissionHelper(web_contents, this);
web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent()); web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent());
if (is_guest) { 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) { void WebContents::BeforeUnloadFired(const base::TimeTicks& proceed_time) {
// Do nothing, we override this method just to avoid compilation error since // Do nothing, we override this method just to avoid compilation error since
// there are two virtual functions named BeforeUnloadFired. // there are two virtual functions named BeforeUnloadFired.

View file

@ -195,6 +195,10 @@ class WebContents : public mate::TrackableObject<WebContents>,
const gfx::Rect& selection_rect, const gfx::Rect& selection_rect,
int active_match_ordinal, int active_match_ordinal,
bool final_update) override; bool final_update) override;
void RequestMediaAccessPermission(
content::WebContents* web_contents,
const content::MediaStreamRequest& request,
const content::MediaResponseCallback& callback) override;
// content::WebContentsObserver: // content::WebContentsObserver:
void BeforeUnloadFired(const base::TimeTicks& proceed_time) override; void BeforeUnloadFired(const base::TimeTicks& proceed_time) override;

View file

@ -15,6 +15,7 @@
#include "atom/browser/atom_resource_dispatcher_host_delegate.h" #include "atom/browser/atom_resource_dispatcher_host_delegate.h"
#include "atom/browser/atom_speech_recognition_manager_delegate.h" #include "atom/browser/atom_speech_recognition_manager_delegate.h"
#include "atom/browser/native_window.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_contents_preferences.h"
#include "atom/browser/window_list.h" #include "atom/browser/window_list.h"
#include "atom/common/options_switches.h" #include "atom/common/options_switches.h"
@ -281,6 +282,23 @@ brightray::BrowserMainParts* AtomBrowserClient::OverrideCreateBrowserMainParts(
return new AtomBrowserMainParts; 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( void AtomBrowserClient::RenderProcessHostDestroyed(
content::RenderProcessHost* host) { content::RenderProcessHost* host) {
int process_id = host->GetID(); int process_id = host->GetID();

View file

@ -81,6 +81,8 @@ class AtomBrowserClient : public brightray::BrowserClient,
// brightray::BrowserClient: // brightray::BrowserClient:
brightray::BrowserMainParts* OverrideCreateBrowserMainParts( brightray::BrowserMainParts* OverrideCreateBrowserMainParts(
const content::MainFunctionParams&) override; const content::MainFunctionParams&) override;
void WebNotificationAllowed(int render_process_id,
const base::Closure& callback) override;
// content::RenderProcessHostObserver: // content::RenderProcessHostObserver:
void RenderProcessHostDestroyed(content::RenderProcessHost* host) override; void RenderProcessHostDestroyed(content::RenderProcessHost* host) override;

View file

@ -6,10 +6,12 @@ var slice = [].slice;
// Doesn't exist in early initialization. // Doesn't exist in early initialization.
var webViewManager = null; 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 nextInstanceId = 0;
var permissionRequests;
var guestInstances = {}; var guestInstances = {};
var guestPermissionRequestsMap = {};
var embedderElementsMap = {}; var embedderElementsMap = {};
var reverseEmbedderElementsMap = {}; var reverseEmbedderElementsMap = {};
@ -110,6 +112,13 @@ var createGuest = function(embedder, params) {
fn = function(event) { fn = function(event) {
return guest.on(event, function() { return guest.on(event, function() {
var args = 2 <= arguments.length ? slice.call(arguments, 1) : []; 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))); 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, nodeIntegration: (ref1 = params.nodeintegration) != null ? ref1 : false,
plugins: params.plugins, plugins: params.plugins,
webSecurity: !params.disablewebsecurity, webSecurity: !params.disablewebsecurity,
blinkFeatures: params.blinkfeatures blinkFeatures: params.blinkfeatures,
webNotification: !params.disablewebnotification,
}; };
if (params.preload) { if (params.preload) {
webPreferences.preloadURL = params.preload; webPreferences.preloadURL = params.preload;
@ -174,6 +184,7 @@ var destroyGuest = function(embedder, id) {
webViewManager.removeGuest(embedder, id); webViewManager.removeGuest(embedder, id);
guestInstances[id].guest.destroy(); guestInstances[id].guest.destroy();
delete guestInstances[id]; delete guestInstances[id];
delete permissionRequests[id];
key = reverseEmbedderElementsMap[id]; key = reverseEmbedderElementsMap[id];
if (key != null) { if (key != null) {
delete reverseEmbedderElementsMap[id]; delete reverseEmbedderElementsMap[id];
@ -193,6 +204,13 @@ ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_DESTROY_GUEST', function(event, id) {
return destroyGuest(event.sender, 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) { ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_SET_SIZE', function(event, id, params) {
var ref1; var ref1;
return (ref1 = guestInstances[id]) != null ? ref1.guest.setSize(params) : void 0; return (ref1 = guestInstances[id]) != null ? ref1.guest.setSize(params) : void 0;

View file

@ -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 <string>
#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<content::MediaStreamUI>());
}
} // 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

View file

@ -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 <map>
#include <string>
#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<WebContentsPermissionHelper> {
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<WebContentsPermissionHelper>;
std::map<std::string, base::Closure> permission_map_;
api::WebContents* api_web_contents_; // Weak reference
DISALLOW_COPY_AND_ASSIGN(WebContentsPermissionHelper);
};
} // namespace atom
#endif // ATOM_BROWSER_WEB_CONTENTS_PERMISSION_HELPER_H_

View file

@ -7,6 +7,7 @@
#include <vector> #include <vector>
#include "atom/common/native_mate_converters/callback.h"
#include "native_mate/converter.h" #include "native_mate/converter.h"
namespace mate { namespace mate {

View file

@ -34,7 +34,8 @@ var WEB_VIEW_EVENTS = {
'page-favicon-updated': ['favicons'], 'page-favicon-updated': ['favicons'],
'enter-html-full-screen': [], 'enter-html-full-screen': [],
'leave-html-full-screen': [], 'leave-html-full-screen': [],
'found-in-page': ['result'] 'found-in-page': ['result'],
'permission-request': ['permission', 'allow', 'deny']
}; };
var DEPRECATED_EVENTS = { var DEPRECATED_EVENTS = {
@ -64,6 +65,15 @@ module.exports = {
ipcRenderer.on("ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-" + viewInstanceId, function() { ipcRenderer.on("ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-" + viewInstanceId, function() {
var eventName = arguments[1]; var eventName = arguments[1];
var args = 3 <= arguments.length ? slice.call(arguments, 2) : []; 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))); return dispatchEvent.apply(null, [webView, eventName, eventName].concat(slice.call(args)));
}); });
ipcRenderer.on("ATOM_SHELL_GUEST_VIEW_INTERNAL_IPC_MESSAGE-" + viewInstanceId, function() { ipcRenderer.on("ATOM_SHELL_GUEST_VIEW_INTERNAL_IPC_MESSAGE-" + viewInstanceId, function() {

View file

@ -311,6 +311,7 @@ WebViewImpl.prototype.setupWebViewAttributes = function() {
this.attributes[webViewConstants.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWPOPUPS, this); this.attributes[webViewConstants.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWPOPUPS, this);
this.attributes[webViewConstants.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this); this.attributes[webViewConstants.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this);
this.attributes[webViewConstants.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(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]; autosizeAttributes = [webViewConstants.ATTRIBUTE_MAXHEIGHT, webViewConstants.ATTRIBUTE_MAXWIDTH, webViewConstants.ATTRIBUTE_MINHEIGHT, webViewConstants.ATTRIBUTE_MINWIDTH];
results = []; results = [];
for (i = 0, len = autosizeAttributes.length; i < len; i++) { for (i = 0, len = autosizeAttributes.length; i < len; i++) {

View file

@ -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 The full list of supported feature strings can be found in the
[setFeatureEnabledFromString][blink-feature-string] function. [setFeatureEnabledFromString][blink-feature-string] function.
### `disablewebnotification`
```html
<webview src="https://www.github.com/" disablewebnotification></webview>
```
If "on", the guest page will have web notifications disabled.
## Methods ## Methods
The `webview` tag has the following methods: The `webview` tag has the following methods:
@ -736,4 +744,22 @@ Emitted when DevTools is closed.
Emitted when DevTools is focused / opened. 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 [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

View file

@ -256,6 +256,8 @@
'atom/browser/ui/x/window_state_watcher.h', 'atom/browser/ui/x/window_state_watcher.h',
'atom/browser/ui/x/x_window_utils.cc', 'atom/browser/ui/x/x_window_utils.cc',
'atom/browser/ui/x/x_window_utils.h', '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.cc',
'atom/browser/web_contents_preferences.h', 'atom/browser/web_contents_preferences.h',
'atom/browser/web_dialog_helper.cc', 'atom/browser/web_dialog_helper.cc',