| 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.
|
|
|