| 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 50f13c585def91fab0832f7983efcc2d75e9ccff..6ba36310b120dcdc4f7baeed778ae91f0b4026df 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() { return id_; }
|
| + void set_id(int id) { id_ = id; }
|
| +
|
| + size_t PermissionCount() { 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) {}
|
| @@ -106,44 +194,11 @@ void PermissionServiceImpl::RequestPermission(
|
| const url::Origin& origin,
|
| bool user_gesture,
|
| const PermissionStatusCallback& callback) {
|
| - // This condition is valid if the call is coming from a ChildThread instead of
|
| - // a RenderFrame. Some consumers of the service run in Workers and some in
|
| - // Frames. In the context of a Worker, it is not possible to show a
|
| - // permission prompt because there is no tab. In the context of a Frame, we
|
| - // can. Even if the call comes from a context where it is not possible to show
|
| - // any UI, we want to still return something relevant so the current
|
| - // permission status is returned.
|
| - BrowserContext* browser_context = context_->GetBrowserContext();
|
| - DCHECK(browser_context);
|
| - if (!context_->render_frame_host() ||
|
| - !browser_context->GetPermissionManager()) {
|
| - callback.Run(GetPermissionStatus(permission, origin));
|
| - return;
|
| - }
|
| -
|
| - int pending_request_id =
|
| - pending_requests_.Add(base::MakeUnique<PendingRequest>(
|
| - base::Bind(&PermissionRequestResponseCallbackWrapper, callback), 1));
|
| - int id = browser_context->GetPermissionManager()->RequestPermission(
|
| - PermissionDescriptorToPermissionType(permission),
|
| - context_->render_frame_host(), origin.GetURL(), user_gesture,
|
| - base::Bind(&PermissionServiceImpl::OnRequestPermissionResponse,
|
| - weak_factory_.GetWeakPtr(), pending_request_id));
|
| -
|
| - // Check if the request still exists. It might have been removed by the
|
| - // callback if it was run synchronously.
|
| - PendingRequest* pending_request = pending_requests_.Lookup(
|
| - pending_request_id);
|
| - if (!pending_request)
|
| - return;
|
| - pending_request->id = id;
|
| -}
|
| -
|
| -void PermissionServiceImpl::OnRequestPermissionResponse(
|
| - int pending_request_id,
|
| - PermissionStatus status) {
|
| - OnRequestPermissionsResponse(pending_request_id,
|
| - std::vector<PermissionStatus>(1, status));
|
| + std::vector<PermissionDescriptorPtr> permissions;
|
| + permissions.push_back(std::move(permission));
|
| + RequestPermissions(
|
| + std::move(permissions), origin, user_gesture,
|
| + base::Bind(&PermissionRequestResponseCallbackWrapper, callback));
|
| }
|
|
|
| void PermissionServiceImpl::RequestPermissions(
|
| @@ -173,30 +228,50 @@ 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();
|
| + for (size_t i = 0; i < request->PermissionCount(); ++i) {
|
| + // We fill in the unset results with those from the call to
|
| + // RequestPermissions.
|
| + 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(
|
| @@ -254,8 +329,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.
|
|
|