Index: content/browser/permissions/permission_service_impl.cc |
diff --git a/content/browser/permissions/permission_service_impl.cc b/content/browser/permissions/permission_service_impl.cc |
index a62913c822a6c520d0632a9fa58411cf1f4b2f06..686bb31b76543a9297021711b8524a51d9cc71f4 100644 |
--- a/content/browser/permissions/permission_service_impl.cc |
+++ b/content/browser/permissions/permission_service_impl.cc |
@@ -10,10 +10,14 @@ |
#include <utility> |
#include "base/bind.h" |
+#include "base/feature_list.h" |
#include "base/memory/ptr_util.h" |
#include "content/public/browser/browser_context.h" |
#include "content/public/browser/permission_manager.h" |
#include "content/public/browser/permission_type.h" |
+#include "content/public/browser/render_frame_host.h" |
+#include "content/public/common/content_features.h" |
+#include "third_party/WebKit/public/platform/WebFeaturePolicy.h" |
using blink::mojom::PermissionDescriptorPtr; |
using blink::mojom::PermissionName; |
@@ -56,8 +60,56 @@ PermissionType PermissionDescriptorToPermissionType( |
return PermissionType::NUM; |
} |
-// This function allows the usage of the the multiple request map |
-// with single requests. |
+blink::WebFeaturePolicyFeature PermissionTypeToFeaturePolicyFeature( |
+ PermissionType type) { |
+ switch (type) { |
+ case PermissionType::MIDI: |
+ case PermissionType::MIDI_SYSEX: |
+ return blink::WebFeaturePolicyFeature::kMidiFeature; |
+ case PermissionType::GEOLOCATION: |
+ return blink::WebFeaturePolicyFeature::kGeolocation; |
+ case PermissionType::PROTECTED_MEDIA_IDENTIFIER: |
+ return blink::WebFeaturePolicyFeature::kEme; |
+ case PermissionType::AUDIO_CAPTURE: |
+ return blink::WebFeaturePolicyFeature::kMicrophone; |
+ case PermissionType::VIDEO_CAPTURE: |
+ return blink::WebFeaturePolicyFeature::kCamera; |
+ case PermissionType::PUSH_MESSAGING: |
+ case PermissionType::NOTIFICATIONS: |
+ case PermissionType::DURABLE_STORAGE: |
+ case PermissionType::BACKGROUND_SYNC: |
+ case PermissionType::FLASH: |
+ case PermissionType::NUM: |
+ // These aren't exposed by feature policy. |
+ return blink::WebFeaturePolicyFeature::kNotFound; |
+ } |
+ |
+ NOTREACHED(); |
+ return blink::WebFeaturePolicyFeature::kNotFound; |
+} |
+ |
+bool AllowedByFeaturePolicy(RenderFrameHost* rfh, PermissionType type) { |
+ if (!base::FeatureList::IsEnabled( |
+ features::kUseFeaturePolicyForPermissions)) { |
+ // Default to ignoring the feature policy. |
+ return true; |
+ } |
+ |
+ blink::WebFeaturePolicyFeature feature_policy_feature = |
+ PermissionTypeToFeaturePolicyFeature(type); |
+ if (feature_policy_feature == blink::WebFeaturePolicyFeature::kNotFound) |
+ return true; |
+ |
+ // If there is no frame, there is no policy, so disable the feature for |
+ // safety. |
+ if (!rfh) |
+ return false; |
+ |
+ return rfh->IsFeatureEnabled(feature_policy_feature); |
+} |
+ |
+// This function allows the usage of the the multiple request map with single |
+// requests. |
void PermissionRequestResponseCallbackWrapper( |
const base::Callback<void(PermissionStatus)>& callback, |
const std::vector<PermissionStatus>& vector) { |
@@ -67,20 +119,56 @@ void PermissionRequestResponseCallbackWrapper( |
} // anonymous namespace |
-PermissionServiceImpl::PendingRequest::PendingRequest( |
- const RequestPermissionsCallback& callback, |
- int request_count) |
- : callback(callback), |
- request_count(request_count) { |
-} |
+class PermissionServiceImpl::PendingRequest { |
+ public: |
+ PendingRequest(std::vector<PermissionType> types, |
+ const RequestPermissionsCallback& callback) |
+ : types_(types), |
+ callback_(callback), |
+ has_result_been_set_(types.size(), false), |
+ results_(types.size(), PermissionStatus::DENIED) {} |
+ |
+ ~PendingRequest() { |
+ if (callback_.is_null()) |
+ return; |
-PermissionServiceImpl::PendingRequest::~PendingRequest() { |
- if (callback.is_null()) |
- return; |
+ std::vector<PermissionStatus> result(types_.size(), |
+ PermissionStatus::DENIED); |
+ callback_.Run(result); |
+ } |
- std::vector<PermissionStatus> result(request_count, PermissionStatus::DENIED); |
- callback.Run(result); |
-} |
+ int id() const { return id_; } |
+ void set_id(int id) { id_ = id; } |
+ |
+ size_t RequestSize() const { return types_.size(); } |
+ |
+ void SetResult(int index, PermissionStatus result) { |
+ DCHECK_EQ(false, has_result_been_set_[index]); |
+ has_result_been_set_[index] = true; |
+ results_[index] = result; |
+ } |
+ |
+ bool HasResultBeenSet(size_t index) const { |
+ return has_result_been_set_[index]; |
+ } |
+ |
+ void RunCallback() { |
+ // Check that all results have been set. |
+ DCHECK(std::find(has_result_been_set_.begin(), has_result_been_set_.end(), |
+ false) == has_result_been_set_.end()); |
+ callback_.Run(results_); |
+ callback_.Reset(); |
+ } |
+ |
+ private: |
+ // Request ID received from the PermissionManager. |
+ int id_; |
+ std::vector<PermissionType> types_; |
+ RequestPermissionsCallback callback_; |
+ |
+ std::vector<bool> has_result_been_set_; |
+ std::vector<PermissionStatus> results_; |
+}; |
PermissionServiceImpl::PermissionServiceImpl(PermissionServiceContext* context) |
: context_(context), weak_factory_(this) {} |
@@ -96,7 +184,7 @@ PermissionServiceImpl::~PermissionServiceImpl() { |
// Cancel pending requests. |
for (RequestsMap::Iterator<PendingRequest> it(&pending_requests_); |
!it.IsAtEnd(); it.Advance()) { |
- permission_manager->CancelPermissionRequest(it.GetCurrentValue()->id); |
+ permission_manager->CancelPermissionRequest(it.GetCurrentValue()->id()); |
} |
pending_requests_.Clear(); |
} |
@@ -140,30 +228,53 @@ void PermissionServiceImpl::RequestPermissions( |
for (size_t i = 0; i < types.size(); ++i) |
types[i] = PermissionDescriptorToPermissionType(permissions[i]); |
- int pending_request_id = pending_requests_.Add( |
- base::MakeUnique<PendingRequest>(callback, permissions.size())); |
+ std::unique_ptr<PendingRequest> pending_request = |
+ base::MakeUnique<PendingRequest>(types, callback); |
+ std::vector<PermissionType> request_types; |
+ for (size_t i = 0; i < types.size(); ++i) { |
+ // Check feature policy. |
+ if (!AllowedByFeaturePolicy(context_->render_frame_host(), types[i])) |
+ pending_request->SetResult(i, PermissionStatus::DENIED); |
+ else |
+ request_types.push_back(types[i]); |
+ } |
+ |
+ int pending_request_id = pending_requests_.Add(std::move(pending_request)); |
int id = browser_context->GetPermissionManager()->RequestPermissions( |
- types, context_->render_frame_host(), origin.GetURL(), user_gesture, |
+ request_types, context_->render_frame_host(), origin.GetURL(), |
+ user_gesture, |
base::Bind(&PermissionServiceImpl::OnRequestPermissionsResponse, |
weak_factory_.GetWeakPtr(), pending_request_id)); |
// Check if the request still exists. It may have been removed by the |
// the response callback. |
- PendingRequest* pending_request = pending_requests_.Lookup( |
- pending_request_id); |
- if (!pending_request) |
- return; |
- pending_request->id = id; |
+ PendingRequest* in_progress_request = |
+ pending_requests_.Lookup(pending_request_id); |
+ if (!in_progress_request) |
+ return; |
+ in_progress_request->set_id(id); |
} |
void PermissionServiceImpl::OnRequestPermissionsResponse( |
int pending_request_id, |
- const std::vector<PermissionStatus>& result) { |
+ const std::vector<PermissionStatus>& partial_result) { |
PendingRequest* request = pending_requests_.Lookup(pending_request_id); |
- RequestPermissionsCallback callback(request->callback); |
- request->callback.Reset(); |
+ auto partial_result_it = partial_result.begin(); |
+ // Fill in the unset results in the request. Some results in the request are |
+ // set synchronously because they are blocked by feature policy. Others are |
+ // determined by a call to RequestPermission. All unset results will be |
+ // contained in |partial_result| in the same order that they were requested. |
+ // We fill in the unset results in the request with |partial_result|. |
+ for (size_t i = 0; i < request->RequestSize(); ++i) { |
+ if (!request->HasResultBeenSet(i)) { |
+ request->SetResult(i, *partial_result_it); |
+ ++partial_result_it; |
+ } |
+ } |
+ DCHECK(partial_result.end() == partial_result_it); |
+ |
+ request->RunCallback(); |
pending_requests_.Remove(pending_request_id); |
- callback.Run(result); |
} |
void PermissionServiceImpl::HasPermission( |
@@ -221,8 +332,10 @@ PermissionStatus PermissionServiceImpl::GetPermissionStatusFromType( |
const url::Origin& origin) { |
BrowserContext* browser_context = context_->GetBrowserContext(); |
DCHECK(browser_context); |
- if (!browser_context->GetPermissionManager()) |
+ if (!browser_context->GetPermissionManager() || |
+ !AllowedByFeaturePolicy(context_->render_frame_host(), type)) { |
return PermissionStatus::DENIED; |
+ } |
GURL requesting_origin(origin.Serialize()); |
// If the embedding_origin is empty we'll use |origin| instead. |