diff options
author | lazyboy@chromium.org <lazyboy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-01 09:36:45 +0000 |
---|---|---|
committer | lazyboy@chromium.org <lazyboy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-01 09:36:45 +0000 |
commit | c006fb585bbb94a5f8fc03f0926f4fd51c0def66 (patch) | |
tree | ad30688d9431514ea5c9cae339a35dc6496afaa2 /content | |
parent | 71a640b4629389ce23c5f87fe552a0a01cb2367d (diff) | |
download | chromium_src-c006fb585bbb94a5f8fc03f0926f4fd51c0def66.zip chromium_src-c006fb585bbb94a5f8fc03f0926f4fd51c0def66.tar.gz chromium_src-c006fb585bbb94a5f8fc03f0926f4fd51c0def66.tar.bz2 |
<webview>: First stab at implementing media permission request for guests.
We allow/deny the media permission on embedder's WebContents when a guest's WebContents requests for the permission.
The embedder js api looks like:
1.
<webview id="webview1" src=...></webview>
webview1.addEventListener('permissionrequest', function(e) {
if (e.permission == 'media') e.request.allow(); // Or e.request.deny();
// if no listener call allow/deny, the request is auto denied.
});
2. If embedder wants to defer on deciding, they must call preventDefault();
<webview ...>
webview.addEventListener('permissionrequest', function(e) {
e.preventDefault();
setTimeout(function() { e.request.allow(); }, 10000);
});
3. If there are no listeners attached, request is auto denied.
4. If there are multiple listeners attached and each are trying to call allow/deny, we implement 'first call wins', subsequent calls to allow/deny will throw exception.
<webview ...>
webview.addEventListener('permissionrequest', function(e) {
// assume this listener fires first.
e.request.allow();
});
webview.addEventListener('permissionrequest', function(e) {
// Assume this listener fires later.
// Calling e.request.allow() or e.request.deny() will throw exception.
});
5. Deferring on calling allow/deny can be done indefinitely and once the event.request object goes out of scope, we auto deny if decision has not yet been made:
<webview ...>
webview.addEventListener('permissionrequest', function(e) {
e.preventDefault();
setTimeout(function() {
if (e.type == 'something') do_something_else; // so 'e.request' is still live.
}, 2000); // After 2000ms, e/e.request goes out of scope == auto deny on next gc.
});
When the guest requests a permission (BrowserPluginGuest.RequestMediaAccessPermission()), we send the info to the renderer BrowserPlugin, which runs the embedder js api function as above.
BUG=141197,153540
TEST=Added browser_tests: WebViewTest.MediaAccess*
Review URL: https://chromiumcodereview.appspot.com/11093080
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@185503 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
12 files changed, 404 insertions, 7 deletions
diff --git a/content/browser/browser_plugin/browser_plugin_embedder.cc b/content/browser/browser_plugin/browser_plugin_embedder.cc index cb7283f..6d89b7a 100644 --- a/content/browser/browser_plugin/browser_plugin_embedder.cc +++ b/content/browser/browser_plugin/browser_plugin_embedder.cc @@ -221,6 +221,7 @@ bool BrowserPluginEmbedder::ShouldForwardToBrowserPluginGuest( case BrowserPluginHostMsg_PluginDestroyed::ID: case BrowserPluginHostMsg_Reload::ID: case BrowserPluginHostMsg_ResizeGuest::ID: + case BrowserPluginHostMsg_RespondPermission::ID: case BrowserPluginHostMsg_SetAutoSize::ID: case BrowserPluginHostMsg_SetFocus::ID: case BrowserPluginHostMsg_SetName::ID: diff --git a/content/browser/browser_plugin/browser_plugin_guest.cc b/content/browser/browser_plugin/browser_plugin_guest.cc index cfe44e6..5e64ff8 100644 --- a/content/browser/browser_plugin/browser_plugin_guest.cc +++ b/content/browser/browser_plugin/browser_plugin_guest.cc @@ -18,8 +18,8 @@ #include "content/common/browser_plugin_messages.h" #include "content/common/content_constants_internal.h" #include "content/common/drag_messages.h" -#include "content/common/view_messages.h" #include "content/common/gpu/gpu_messages.h" +#include "content/common/view_messages.h" #include "content/port/browser/render_view_host_delegate_view.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/notification_service.h" @@ -29,6 +29,7 @@ #include "content/public/browser/resource_request_details.h" #include "content/public/browser/user_metrics.h" #include "content/public/browser/web_contents_view.h" +#include "content/public/common/media_stream_request.h" #include "content/public/common/result_codes.h" #include "content/browser/browser_plugin/browser_plugin_host_factory.h" #include "net/base/net_errors.h" @@ -46,6 +47,10 @@ namespace content { // static BrowserPluginHostFactory* BrowserPluginGuest::factory_ = NULL; +namespace { +const size_t kNumMaxOutstandingMediaRequests = 1024; +} + BrowserPluginGuest::BrowserPluginGuest( int instance_id, WebContentsImpl* embedder_web_contents, @@ -66,7 +71,8 @@ BrowserPluginGuest::BrowserPluginGuest( auto_size_enabled_(params.auto_size_params.enable), max_auto_size_(params.auto_size_params.max_size), min_auto_size_(params.auto_size_params.min_size), - destroy_called_(false) { + destroy_called_(false), + current_media_access_request_id_(0) { DCHECK(web_contents); web_contents->SetDelegate(this); @@ -112,6 +118,8 @@ bool BrowserPluginGuest::OnMessageReceivedFromEmbedder( IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_PluginDestroyed, OnPluginDestroyed) IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_Reload, OnReload) IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_ResizeGuest, OnResizeGuest) + IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_RespondPermission, + OnRespondPermission) IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetAutoSize, OnSetSize) IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetFocus, OnSetFocus) IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetName, OnSetName) @@ -648,6 +656,35 @@ void BrowserPluginGuest::AcknowledgeBufferPresent( ack_params); } +void BrowserPluginGuest::OnRespondPermission( + int /*instance_id*/, + BrowserPluginPermissionType permission_type, + int request_id, + bool should_allow) { + if (permission_type != BrowserPluginPermissionTypeMedia) + return; + + MediaStreamRequestsMap::iterator media_request_iter = + media_requests_map_.find(request_id); + if (media_request_iter == media_requests_map_.end()) { + LOG(INFO) << "Not a valid request ID."; + return; + } + const content::MediaStreamRequest& request = media_request_iter->second.first; + const content::MediaResponseCallback& callback = + media_request_iter->second.second; + + if (should_allow && embedder_web_contents_) { + // Re-route the request to the embedder's WebContents; the guest gets the + // permission this way. + embedder_web_contents_->RequestMediaAccessPermission(request, callback); + } else { + // Deny the request. + callback.Run(content::MediaStreamDevices()); + } + media_requests_map_.erase(media_request_iter); +} + void BrowserPluginGuest::OnSwapBuffersACK(int instance_id, int route_id, int gpu_host_id, @@ -764,6 +801,28 @@ void BrowserPluginGuest::OnUpdateFrameName(int frame_id, SendMessageToEmbedder(new BrowserPluginMsg_UpdatedName(instance_id_, name)); } +void BrowserPluginGuest::RequestMediaAccessPermission( + WebContents* web_contents, + const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback) { + if (media_requests_map_.size() >= kNumMaxOutstandingMediaRequests) { + // Deny the media request. + callback.Run(content::MediaStreamDevices()); + return; + } + int request_id = current_media_access_request_id_++; + media_requests_map_.insert( + std::make_pair(request_id, + std::make_pair(request, callback))); + + base::DictionaryValue request_info; + request_info.Set( + "url", base::Value::CreateStringValue(request.security_origin.spec())); + SendMessageToEmbedder(new BrowserPluginMsg_RequestPermission( + instance_id(), BrowserPluginPermissionTypeMedia, + request_id, request_info)); +} + void BrowserPluginGuest::OnUpdateRect( const ViewHostMsg_UpdateRect_Params& params) { diff --git a/content/browser/browser_plugin/browser_plugin_guest.h b/content/browser/browser_plugin/browser_plugin_guest.h index 9fe993d..12bf515 100644 --- a/content/browser/browser_plugin/browser_plugin_guest.h +++ b/content/browser/browser_plugin/browser_plugin_guest.h @@ -27,6 +27,7 @@ #include "base/id_map.h" #include "base/shared_memory.h" #include "base/time.h" +#include "content/common/browser_plugin_message_enums.h" #include "content/port/common/input_event_ack_state.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" @@ -59,6 +60,7 @@ class BrowserPluginHostFactory; class BrowserPluginEmbedder; class RenderProcessHost; class RenderWidgetHostView; +struct MediaStreamRequest; // A browser plugin guest provides functionality for WebContents to operate in // the guest role and implements guest specific overrides for ViewHostMsg_* @@ -150,6 +152,10 @@ class CONTENT_EXPORT BrowserPluginGuest : public NotificationObserver, virtual void RunFileChooser(WebContents* web_contents, const FileChooserParams& params) OVERRIDE; virtual bool ShouldFocusPageAfterCrash() OVERRIDE; + virtual void RequestMediaAccessPermission( + WebContents* web_contents, + const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback) OVERRIDE; // Exposes the protected web_contents() from WebContentsObserver. WebContents* GetWebContents(); @@ -181,6 +187,11 @@ class CONTENT_EXPORT BrowserPluginGuest : public NotificationObserver, uint32 sync_point); private: + typedef std::pair<content::MediaStreamRequest, content::MediaResponseCallback> + MediaStreamRequestAndCallbackPair; + typedef std::map<int, MediaStreamRequestAndCallbackPair> + MediaStreamRequestsMap; + friend class TestBrowserPluginGuest; BrowserPluginGuest(int instance_id, @@ -213,6 +224,12 @@ class CONTENT_EXPORT BrowserPluginGuest : public NotificationObserver, // Message handlers for messsages from embedder. + // Allows or denies a permission request access, after the embedder has had a + // chance to decide. + void OnRespondPermission(int instance_id, + BrowserPluginPermissionType permission_type, + int request_id, + bool should_allow); // Handles drag events from the embedder. // When dragging, the drag events go to the embedder first, and if the drag // happens on the browser plugin, then the plugin sends a corresponding @@ -331,6 +348,15 @@ class CONTENT_EXPORT BrowserPluginGuest : public NotificationObserver, gfx::Size min_auto_size_; bool destroy_called_; + // A counter to generate unique request id for a media access request. + // We only need the ids to be unique for a given BrowserPluginGuest. + int current_media_access_request_id_; + // A map to store WebContents's media request object and callback. + // We need to store these because we need a roundtrip to the embedder to know + // if we allow or disallow the request. The key of the map is unique only for + // a given BrowserPluginGuest. + MediaStreamRequestsMap media_requests_map_; + DISALLOW_COPY_AND_ASSIGN(BrowserPluginGuest); }; diff --git a/content/common/browser_plugin_message_enums.h b/content/common/browser_plugin_message_enums.h new file mode 100644 index 0000000..fc7954c --- /dev/null +++ b/content/common/browser_plugin_message_enums.h @@ -0,0 +1,16 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_COMMON_BROWSER_PLUGIN_MESSAGE_ENUMS_H_ +#define CONTENT_COMMON_BROWSER_PLUGIN_MESSAGE_ENUMS_H_ + +enum BrowserPluginPermissionType { + // Unknown type of permission request. + BrowserPluginPermissionTypeUnknown, + + // Media access (audio/video) permission request type. + BrowserPluginPermissionTypeMedia, +}; + +#endif // CONTENT_COMMON_BROWSER_PLUGIN_MESSAGE_ENUMS_H_ diff --git a/content/common/browser_plugin_messages.h b/content/common/browser_plugin_messages.h index 5adfc57..8c111e1 100644 --- a/content/common/browser_plugin_messages.h +++ b/content/common/browser_plugin_messages.h @@ -9,6 +9,8 @@ #include "base/basictypes.h" #include "base/process.h" #include "base/shared_memory.h" +#include "base/values.h" +#include "content/common/browser_plugin_message_enums.h" #include "content/common/content_export.h" #include "content/common/content_param_traits.h" #include "content/public/common/common_param_traits.h" @@ -28,6 +30,8 @@ #define IPC_MESSAGE_START BrowserPluginMsgStart + +IPC_ENUM_TRAITS(BrowserPluginPermissionType) IPC_ENUM_TRAITS(WebKit::WebDragStatus) IPC_STRUCT_BEGIN(BrowserPluginHostMsg_AutoSize_Params) @@ -236,6 +240,18 @@ IPC_MESSAGE_ROUTED2(BrowserPluginHostMsg_SetName, int /* instance_id */, std::string /* name */) +// Tells the guest that its request for an API permission has been allowed or +// denied. +// Note that |allow| = true does not readily mean that the guest will be granted +// permission, since a security check in the embedder might follow. For example +// for media access permission, the guest will be granted permission only if its +// embedder also has access. +IPC_MESSAGE_ROUTED4(BrowserPluginHostMsg_RespondPermission, + int /* instance_id */, + BrowserPluginPermissionType /* permission_type */, + int /* request_id */, + bool /* allow */) + // ----------------------------------------------------------------------------- // These messages are from the guest renderer to the browser process @@ -363,3 +379,11 @@ IPC_MESSAGE_CONTROL5(BrowserPluginMsg_BuffersSwapped, std::string /* mailbox_name */, int /* route_id */, int /* gpu_host_id */) + +// When the guest requests permission, the browser process forwards this +// request to the embeddder through this message. +IPC_MESSAGE_CONTROL4(BrowserPluginMsg_RequestPermission, + int /* instance_id */, + BrowserPluginPermissionType /* permission_type */, + int /* request_id */, + DictionaryValue /* request_info */) diff --git a/content/content_common.gypi b/content/content_common.gypi index 96c79c4..b168a4b 100644 --- a/content/content_common.gypi +++ b/content/content_common.gypi @@ -128,6 +128,7 @@ 'common/appcache/appcache_dispatcher.cc', 'common/appcache/appcache_dispatcher.h', 'common/appcache_messages.h', + 'common/browser_plugin_message_enums.h', 'common/browser_plugin_messages.h', 'common/cc_messages.cc', 'common/cc_messages.h', diff --git a/content/renderer/browser_plugin/browser_plugin.cc b/content/renderer/browser_plugin/browser_plugin.cc index 73fedee..573ff76 100644 --- a/content/renderer/browser_plugin/browser_plugin.cc +++ b/content/renderer/browser_plugin/browser_plugin.cc @@ -98,7 +98,9 @@ BrowserPlugin::BrowserPlugin( browser_plugin_manager_(render_view->browser_plugin_manager()), current_nav_entry_index_(0), nav_entry_count_(0), - compositing_enabled_(false) { + compositing_enabled_(false), + ALLOW_THIS_IN_INITIALIZER_LIST( + weak_ptr_factory_(this)) { } BrowserPlugin::~BrowserPlugin() { @@ -128,6 +130,7 @@ bool BrowserPlugin::OnMessageReceived(const IPC::Message& message) { IPC_MESSAGE_HANDLER(BrowserPluginMsg_LoadRedirect, OnLoadRedirect) IPC_MESSAGE_HANDLER(BrowserPluginMsg_LoadStart, OnLoadStart) IPC_MESSAGE_HANDLER(BrowserPluginMsg_LoadStop, OnLoadStop) + IPC_MESSAGE_HANDLER(BrowserPluginMsg_RequestPermission, OnRequestPermission) IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetCursor, OnSetCursor) IPC_MESSAGE_HANDLER(BrowserPluginMsg_ShouldAcceptTouchEvents, OnShouldAcceptTouchEvents) @@ -511,6 +514,15 @@ void BrowserPlugin::OnLoadStop(int instance_id) { TriggerEvent(browser_plugin::kEventLoadStop, NULL); } +void BrowserPlugin::OnRequestPermission( + int instance_id, + BrowserPluginPermissionType permission_type, + int request_id, + const base::DictionaryValue& request_info) { + if (permission_type == BrowserPluginPermissionTypeMedia) + RequestMediaPermission(request_id, request_info); +} + void BrowserPlugin::OnSetCursor(int instance_id, const WebCursor& cursor) { cursor_ = cursor; } @@ -527,6 +539,55 @@ void BrowserPlugin::OnUpdatedName(int instance_id, const std::string& name) { UpdateDOMAttribute(browser_plugin::kAttributeName, name); } +void BrowserPlugin::RequestMediaPermission( + int request_id, const base::DictionaryValue& request_info) { + if (!HasEventListeners(browser_plugin::kEventRequestPermission)) { + // Automatically deny the request if there are no event listeners for + // permissionrequest. + RespondPermission( + BrowserPluginPermissionTypeMedia, request_id, false /* allow */); + return; + } + DCHECK(!pending_permission_requests_.count(request_id)); + pending_permission_requests_.insert( + std::make_pair(request_id, + std::make_pair(request_id, + BrowserPluginPermissionTypeMedia))); + + std::map<std::string, base::Value*> props; + props[browser_plugin::kPermission] = + base::Value::CreateStringValue(browser_plugin::kPermissionTypeMedia); + props[browser_plugin::kRequestId] = + base::Value::CreateIntegerValue(request_id); + + // Fill in the info provided by the browser. + for (DictionaryValue::Iterator iter(request_info); !iter.IsAtEnd(); + iter.Advance()) { + props[iter.key()] = iter.value().DeepCopy(); + } + TriggerEvent(browser_plugin::kEventRequestPermission, &props); +} + +bool BrowserPlugin::HasEventListeners(const std::string& event_name) { + if (!container()) + return false; + + WebKit::WebNode node = container()->element(); + // Escape the <webview> shim if this BrowserPlugin has one. + WebKit::WebElement shim = node.shadowHost(); + if (!shim.isNull()) + node = shim; + + const WebKit::WebString& web_event_name = + WebKit::WebString::fromUTF8(event_name); + while (!node.isNull()) { + if (node.hasEventListeners(web_event_name)) + return true; + node = node.parentNode(); + } + return false; +} + void BrowserPlugin::OnUpdateRect( int instance_id, const BrowserPluginMsg_UpdateRect_Params& params) { @@ -774,6 +835,67 @@ void BrowserPlugin::TriggerEvent(const std::string& event_name, container()->element().dispatchEvent(event); } +void BrowserPlugin::OnRequestObjectGarbageCollected(int request_id) { + // Remove from alive objects. + std::map<int, AliveV8PermissionRequestItem*>::iterator iter = + alive_v8_permission_request_objects_.find(request_id); + if (iter != alive_v8_permission_request_objects_.end()) + alive_v8_permission_request_objects_.erase(iter); + + // If a decision has not been made for this request yet, deny it. + RespondPermissionIfRequestIsPending(request_id, false /*allow*/); +} + +void BrowserPlugin::PersistRequestObject( + const NPVariant* request, const std::string& type, int id) { + CHECK(alive_v8_permission_request_objects_.find(id) == + alive_v8_permission_request_objects_.end()); + if (pending_permission_requests_.find(id) == + pending_permission_requests_.end()) { + return; + } + + v8::Persistent<v8::Value> weak_request = + v8::Persistent<v8::Value>::New(WebKit::WebBindings::toV8Value(request)); + + AliveV8PermissionRequestItem* new_item = + new std::pair<int, base::WeakPtr<BrowserPlugin> >( + id, weak_ptr_factory_.GetWeakPtr()); + + std::pair<std::map<int, AliveV8PermissionRequestItem*>::iterator, bool> + result = alive_v8_permission_request_objects_.insert( + std::make_pair(id, new_item)); + CHECK(result.second); // Inserted in the map. + AliveV8PermissionRequestItem* request_item = result.first->second; + weak_request.MakeWeak(request_item, WeakCallbackForPersistObject); +} + +// static +void BrowserPlugin::WeakCallbackForPersistObject( + v8::Persistent<v8::Value> object, void* param) { + v8::Persistent<v8::Object> persistent_object = + v8::Persistent<v8::Object>::Cast(object); + + AliveV8PermissionRequestItem* item_ptr = + static_cast<AliveV8PermissionRequestItem*>(param); + int request_id = item_ptr->first; + base::WeakPtr<BrowserPlugin> plugin = item_ptr->second; + delete item_ptr; + + persistent_object.Dispose(); + persistent_object.Clear(); + + if (plugin) { + // Asynchronously remove item from |alive_v8_permission_request_objects_|. + // Note that we are using weak pointer for the following PostTask, so we + // don't need to worry about BrowserPlugin going away. + MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&BrowserPlugin::OnRequestObjectGarbageCollected, + plugin, request_id)); + } +} + void BrowserPlugin::Back() { if (!navigate_src_sent_) return; @@ -844,6 +966,39 @@ WebKit::WebPluginContainer* BrowserPlugin::container() const { return container_; } +void BrowserPlugin::RespondPermission( + BrowserPluginPermissionType permission_type, int request_id, bool allow) { + switch (permission_type) { + case BrowserPluginPermissionTypeMedia: + browser_plugin_manager()->Send( + new BrowserPluginHostMsg_RespondPermission( + render_view_->GetRoutingID(), instance_id_, + BrowserPluginPermissionTypeMedia, request_id, allow)); + break; + case BrowserPluginPermissionTypeUnknown: + default: + // Not a valid permission type. + NOTREACHED(); + break; + } +} + +void BrowserPlugin::RespondPermissionIfRequestIsPending( + int request_id, bool allow) { + PendingPermissionRequests::iterator iter = + pending_permission_requests_.find(request_id); + if (iter == pending_permission_requests_.end()) + return; + + BrowserPluginPermissionType permission_type = iter->second.second; + pending_permission_requests_.erase(iter); + RespondPermission(permission_type, request_id, allow); +} + +void BrowserPlugin::OnEmbedderDecidedPermission(int request_id, bool allow) { + RespondPermissionIfRequestIsPending(request_id, allow); +} + bool BrowserPlugin::initialize(WebPluginContainer* container) { if (!container) return false; diff --git a/content/renderer/browser_plugin/browser_plugin.h b/content/renderer/browser_plugin/browser_plugin.h index 3e63f11..14ea949 100644 --- a/content/renderer/browser_plugin/browser_plugin.h +++ b/content/renderer/browser_plugin/browser_plugin.h @@ -14,6 +14,7 @@ #if defined(OS_WIN) #include "base/shared_memory.h" #endif +#include "content/common/browser_plugin_message_enums.h" #include "content/renderer/browser_plugin/browser_plugin_backing_store.h" #include "content/renderer/browser_plugin/browser_plugin_bindings.h" #include "content/renderer/render_view_impl.h" @@ -114,12 +115,18 @@ class CONTENT_EXPORT BrowserPlugin : // Tells the BrowserPlugin to terminate the guest process. void TerminateGuest(); - // A request from Javascript has been made to stop the loading of the page. + // A request from JavaScript has been made to stop the loading of the page. void Stop(); - // A request from Javascript has been made to reload the page. + // A request from JavaScript has been made to reload the page. void Reload(); // A request to enable hardware compositing. void EnableCompositing(bool enable); + // A request from content client to track lifetime of a JavaScript object + // related to a permission request object. + // This is used to clean up hanging permission request objects. + void PersistRequestObject(const NPVariant* request, + const std::string& type, + int id); // Returns true if |point| lies within the bounds of the plugin rectangle. // Not OK to use this function for making security-sensitive decision since it @@ -128,6 +135,9 @@ class CONTENT_EXPORT BrowserPlugin : bool InBounds(const gfx::Point& point) const; gfx::Point ToLocalCoordinates(const gfx::Point& point) const; + // Called by browser plugin binding. + void OnEmbedderDecidedPermission(int request_id, bool allow); + // WebKit::WebPlugin implementation. virtual WebKit::WebPluginContainer* container() const OVERRIDE; @@ -245,6 +255,8 @@ class CONTENT_EXPORT BrowserPlugin : // Informs the BrowserPlugin that guest has changed its size in autosize mode. void SizeChangedDueToAutoSize(const gfx::Size& old_view_size); + bool HasEventListeners(const std::string& event_name); + // Indicates whether a damage buffer was used by the guest process for the // provided |params|. static bool UsesDamageBuffer( @@ -259,6 +271,26 @@ class CONTENT_EXPORT BrowserPlugin : // browser process. void SetInstanceID(int instance_id); + // Requests media access permission from the embedder. + void RequestMediaPermission(int request_id, + const base::DictionaryValue& request_info); + // Informs the BrowserPlugin that the guest's permission request has been + // allowed or denied by the embedder. + void RespondPermission(BrowserPluginPermissionType permission_type, + int request_id, + bool allow); + + // If the request with id |request_id| is pending then informs the + // BrowserPlugin that the guest's permission request has been allowed or + // denied by the embedder. + void RespondPermissionIfRequestIsPending(int request_id, bool allow); + // Cleans up pending permission request once the associated event.request + // object goes out of scope in JavaScript. + void OnRequestObjectGarbageCollected(int request_id); + // V8 garbage collection callback for |object|. + static void WeakCallbackForPersistObject(v8::Persistent<v8::Value> object, + void* param); + // IPC message handlers. // Please keep in alphabetical order. void OnAdvanceFocus(int instance_id, bool reverse); @@ -284,6 +316,11 @@ class CONTENT_EXPORT BrowserPlugin : bool is_top_level); void OnLoadStart(int instance_id, const GURL& url, bool is_top_level); void OnLoadStop(int instance_id); + // Requests permission from the embedder. + void OnRequestPermission(int instance_id, + BrowserPluginPermissionType permission_type, + int request_id, + const base::DictionaryValue& request_info); void OnSetCursor(int instance_id, const WebCursor& cursor); void OnShouldAcceptTouchEvents(int instance_id, bool accept); void OnUpdatedName(int instance_id, const std::string& name); @@ -328,6 +365,17 @@ class CONTENT_EXPORT BrowserPlugin : bool size_changed_in_flight_; bool allocate_instance_id_sent_; + // Each permission request item in the map is a pair of request id and + // permission type. + typedef std::map<int, std::pair<int, BrowserPluginPermissionType> > + PendingPermissionRequests; + PendingPermissionRequests pending_permission_requests_; + + typedef std::pair<int, base::WeakPtr<BrowserPlugin> > + AliveV8PermissionRequestItem; + std::map<int, AliveV8PermissionRequestItem*> + alive_v8_permission_request_objects_; + // BrowserPlugin outlives RenderViewImpl in Chrome Apps and so we need to // store the BrowserPlugin's BrowserPluginManager in a member variable to // avoid accessing the RenderViewImpl. @@ -350,6 +398,10 @@ class CONTENT_EXPORT BrowserPlugin : bool compositing_enabled_; scoped_refptr<BrowserPluginCompositingHelper> compositing_helper_; + // Weak factory used in v8 |MakeWeak| callback, since the v8 callback might + // get called after BrowserPlugin has been destroyed. + base::WeakPtrFactory<BrowserPlugin> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(BrowserPlugin); }; diff --git a/content/renderer/browser_plugin/browser_plugin_bindings.cc b/content/renderer/browser_plugin/browser_plugin_bindings.cc index adefa0d..05d57a9 100644 --- a/content/renderer/browser_plugin/browser_plugin_bindings.cc +++ b/content/renderer/browser_plugin/browser_plugin_bindings.cc @@ -15,15 +15,15 @@ #include "base/utf_string_conversions.h" #include "content/renderer/browser_plugin/browser_plugin.h" #include "content/renderer/browser_plugin/browser_plugin_constants.h" -#include "third_party/npapi/bindings/npapi.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebString.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h" -#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDOMMessageEvent.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h" +#include "third_party/npapi/bindings/npapi.h" #include "v8/include/v8.h" using WebKit::WebBindings; @@ -338,6 +338,28 @@ class BrowserPluginBindingGo : public BrowserPluginMethodBinding { DISALLOW_COPY_AND_ASSIGN(BrowserPluginBindingGo); }; +// Note: This is a method that is used internally by the <webview> shim only. +// This should not be exposed to developers. +class BrowserPluginBindingPersistRequestObject + : public BrowserPluginMethodBinding { + public: + BrowserPluginBindingPersistRequestObject() + : BrowserPluginMethodBinding(browser_plugin::kMethodInternalPersistObject, + 3) { + } + + virtual bool Invoke(BrowserPluginBindings* bindings, + const NPVariant* args, + NPVariant* result) OVERRIDE { + bindings->instance()->PersistRequestObject( + args, StringFromNPVariant(args[1]), Int32FromNPVariant(args[2])); + return true; + } + + private: + DISALLOW_COPY_AND_ASSIGN(BrowserPluginBindingPersistRequestObject); +}; + class BrowserPluginBindingReload : public BrowserPluginMethodBinding { public: BrowserPluginBindingReload() @@ -372,6 +394,28 @@ class BrowserPluginBindingStop : public BrowserPluginMethodBinding { DISALLOW_COPY_AND_ASSIGN(BrowserPluginBindingStop); }; +// Note: This is a method that is used internally by the <webview> shim only. +// This should not be exposed to developers. +class BrowserPluginBindingSetPermission : public BrowserPluginMethodBinding { + public: + BrowserPluginBindingSetPermission() + : BrowserPluginMethodBinding( + browser_plugin::kMethodInternalSetPermission, 2) { + } + + virtual bool Invoke(BrowserPluginBindings* bindings, + const NPVariant* args, + NPVariant* result) OVERRIDE { + int request_id = Int32FromNPVariant(args[0]); + bool allow = NPVARIANT_TO_BOOLEAN(args[1]); + bindings->instance()->OnEmbedderDecidedPermission(request_id, allow); + return true; + } + + private: + DISALLOW_COPY_AND_ASSIGN(BrowserPluginBindingSetPermission); +}; + class BrowserPluginBindingTerminate : public BrowserPluginMethodBinding { public: BrowserPluginBindingTerminate() @@ -771,7 +815,9 @@ BrowserPluginBindings::BrowserPluginBindings(BrowserPlugin* instance) method_bindings_.push_back(new BrowserPluginBindingGetProcessID); method_bindings_.push_back(new BrowserPluginBindingGetRouteID); method_bindings_.push_back(new BrowserPluginBindingGo); + method_bindings_.push_back(new BrowserPluginBindingPersistRequestObject); method_bindings_.push_back(new BrowserPluginBindingReload); + method_bindings_.push_back(new BrowserPluginBindingSetPermission); method_bindings_.push_back(new BrowserPluginBindingStop); method_bindings_.push_back(new BrowserPluginBindingTerminate); diff --git a/content/renderer/browser_plugin/browser_plugin_constants.cc b/content/renderer/browser_plugin/browser_plugin_constants.cc index 92ed335..0f4bc0b 100644 --- a/content/renderer/browser_plugin/browser_plugin_constants.cc +++ b/content/renderer/browser_plugin/browser_plugin_constants.cc @@ -20,6 +20,10 @@ const char kMethodReload[] = "reload"; const char kMethodStop[] = "stop"; const char kMethodTerminate[] = "terminate"; +// Internal method bindings. +const char kMethodInternalPersistObject[] = "-internal-persistObject"; +const char kMethodInternalSetPermission[] = "-internal-setPermission"; + // Attributes. const char kAttributeAutoSize[] = "autosize"; const char kAttributeContentWindow[] = "contentWindow"; @@ -38,6 +42,7 @@ const char kEventLoadCommit[] = "loadcommit"; const char kEventLoadRedirect[] = "loadredirect"; const char kEventLoadStart[] = "loadstart"; const char kEventLoadStop[] = "loadstop"; +const char kEventRequestPermission[] = "permissionrequest"; const char kEventResponsive[] = "responsive"; const char kEventSizeChanged[] = "sizechanged"; const char kEventUnresponsive[] = "unresponsive"; @@ -50,9 +55,12 @@ const char kNewWidth[] = "newWidth"; const char kOldURL[] = "oldUrl"; const char kOldHeight[] = "oldHeight"; const char kOldWidth[] = "oldWidth"; +const char kPermission[] = "permission"; +const char kPermissionTypeMedia[] = "media"; const char kPersistPrefix[] = "persist:"; const char kProcessId[] = "processId"; const char kReason[] = "reason"; +const char kRequestId[] = "requestId"; const char kURL[] = "url"; // Error messages. diff --git a/content/renderer/browser_plugin/browser_plugin_constants.h b/content/renderer/browser_plugin/browser_plugin_constants.h index 6219151..b6e2ee2 100644 --- a/content/renderer/browser_plugin/browser_plugin_constants.h +++ b/content/renderer/browser_plugin/browser_plugin_constants.h @@ -21,6 +21,10 @@ extern const char kMethodReload[]; extern const char kMethodStop[]; extern const char kMethodTerminate[]; +// Internal method bindings. +extern const char kMethodInternalPersistObject[]; +extern const char kMethodInternalSetPermission[]; + // Attributes. extern const char kAttributeAutoSize[]; extern const char kAttributeContentWindow[]; @@ -39,6 +43,7 @@ extern const char kEventLoadCommit[]; extern const char kEventLoadRedirect[]; extern const char kEventLoadStart[]; extern const char kEventLoadStop[]; +extern const char kEventRequestPermission[]; extern const char kEventResponsive[]; extern const char kEventSizeChanged[]; extern const char kEventUnresponsive[]; @@ -51,9 +56,12 @@ extern const char kNewWidth[]; extern const char kOldURL[]; extern const char kOldHeight[]; extern const char kOldWidth[]; +extern const char kPermission[]; +extern const char kPermissionTypeMedia[]; extern const char kPersistPrefix[]; extern const char kProcessId[]; extern const char kReason[]; +extern const char kRequestId[]; extern const char kURL[]; // Error messages. diff --git a/content/renderer/browser_plugin/browser_plugin_manager_impl.cc b/content/renderer/browser_plugin/browser_plugin_manager_impl.cc index 76b2aa1..3c77fca 100644 --- a/content/renderer/browser_plugin/browser_plugin_manager_impl.cc +++ b/content/renderer/browser_plugin/browser_plugin_manager_impl.cc @@ -131,6 +131,7 @@ bool BrowserPluginManagerImpl::ShouldForwardToBrowserPlugin( case BrowserPluginMsg_LoadRedirect::ID: case BrowserPluginMsg_LoadStart::ID: case BrowserPluginMsg_LoadStop::ID: + case BrowserPluginMsg_RequestPermission::ID: case BrowserPluginMsg_SetCursor::ID: case BrowserPluginMsg_ShouldAcceptTouchEvents::ID: case BrowserPluginMsg_UpdatedName::ID: |