OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/permissions/permission_service_impl.h" | 5 #include "content/browser/permissions/permission_service_impl.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
9 #include <memory> | 9 #include <memory> |
10 #include <utility> | 10 #include <utility> |
11 | 11 |
12 #include "base/bind.h" | 12 #include "base/bind.h" |
| 13 #include "base/feature_list.h" |
13 #include "base/memory/ptr_util.h" | 14 #include "base/memory/ptr_util.h" |
14 #include "content/public/browser/browser_context.h" | 15 #include "content/public/browser/browser_context.h" |
15 #include "content/public/browser/permission_manager.h" | 16 #include "content/public/browser/permission_manager.h" |
16 #include "content/public/browser/permission_type.h" | 17 #include "content/public/browser/permission_type.h" |
| 18 #include "content/public/browser/render_frame_host.h" |
| 19 #include "content/public/common/content_features.h" |
| 20 #include "third_party/WebKit/public/platform/WebFeaturePolicy.h" |
17 | 21 |
18 using blink::mojom::PermissionDescriptorPtr; | 22 using blink::mojom::PermissionDescriptorPtr; |
19 using blink::mojom::PermissionName; | 23 using blink::mojom::PermissionName; |
20 using blink::mojom::PermissionObserverPtr; | 24 using blink::mojom::PermissionObserverPtr; |
21 using blink::mojom::PermissionStatus; | 25 using blink::mojom::PermissionStatus; |
22 | 26 |
23 namespace content { | 27 namespace content { |
24 | 28 |
25 namespace { | 29 namespace { |
26 | 30 |
(...skipping 22 matching lines...) Expand all Loading... |
49 case PermissionName::VIDEO_CAPTURE: | 53 case PermissionName::VIDEO_CAPTURE: |
50 return PermissionType::VIDEO_CAPTURE; | 54 return PermissionType::VIDEO_CAPTURE; |
51 case PermissionName::BACKGROUND_SYNC: | 55 case PermissionName::BACKGROUND_SYNC: |
52 return PermissionType::BACKGROUND_SYNC; | 56 return PermissionType::BACKGROUND_SYNC; |
53 } | 57 } |
54 | 58 |
55 NOTREACHED(); | 59 NOTREACHED(); |
56 return PermissionType::NUM; | 60 return PermissionType::NUM; |
57 } | 61 } |
58 | 62 |
59 // This function allows the usage of the the multiple request map | 63 blink::WebFeaturePolicyFeature PermissionTypeToFeaturePolicyFeature( |
60 // with single requests. | 64 PermissionType type) { |
| 65 switch (type) { |
| 66 case PermissionType::MIDI: |
| 67 case PermissionType::MIDI_SYSEX: |
| 68 return blink::WebFeaturePolicyFeature::kMidiFeature; |
| 69 case PermissionType::GEOLOCATION: |
| 70 return blink::WebFeaturePolicyFeature::kGeolocation; |
| 71 case PermissionType::PROTECTED_MEDIA_IDENTIFIER: |
| 72 return blink::WebFeaturePolicyFeature::kEme; |
| 73 case PermissionType::AUDIO_CAPTURE: |
| 74 return blink::WebFeaturePolicyFeature::kMicrophone; |
| 75 case PermissionType::VIDEO_CAPTURE: |
| 76 return blink::WebFeaturePolicyFeature::kCamera; |
| 77 case PermissionType::PUSH_MESSAGING: |
| 78 case PermissionType::NOTIFICATIONS: |
| 79 case PermissionType::DURABLE_STORAGE: |
| 80 case PermissionType::BACKGROUND_SYNC: |
| 81 case PermissionType::FLASH: |
| 82 case PermissionType::NUM: |
| 83 // These aren't exposed by feature policy. |
| 84 return blink::WebFeaturePolicyFeature::kNotFound; |
| 85 } |
| 86 |
| 87 NOTREACHED(); |
| 88 return blink::WebFeaturePolicyFeature::kNotFound; |
| 89 } |
| 90 |
| 91 bool AllowedByFeaturePolicy(RenderFrameHost* rfh, PermissionType type) { |
| 92 if (!base::FeatureList::IsEnabled( |
| 93 features::kUseFeaturePolicyForPermissions)) { |
| 94 // Default to ignoring the feature policy. |
| 95 return true; |
| 96 } |
| 97 |
| 98 blink::WebFeaturePolicyFeature feature_policy_feature = |
| 99 PermissionTypeToFeaturePolicyFeature(type); |
| 100 if (feature_policy_feature == blink::WebFeaturePolicyFeature::kNotFound) |
| 101 return true; |
| 102 |
| 103 // If there is no frame, there is no policy, so disable the feature for |
| 104 // safety. |
| 105 if (!rfh) |
| 106 return false; |
| 107 |
| 108 return rfh->IsFeatureEnabled(feature_policy_feature); |
| 109 } |
| 110 |
| 111 // This function allows the usage of the the multiple request map with single |
| 112 // requests. |
61 void PermissionRequestResponseCallbackWrapper( | 113 void PermissionRequestResponseCallbackWrapper( |
62 const base::Callback<void(PermissionStatus)>& callback, | 114 const base::Callback<void(PermissionStatus)>& callback, |
63 const std::vector<PermissionStatus>& vector) { | 115 const std::vector<PermissionStatus>& vector) { |
64 DCHECK_EQ(vector.size(), 1ul); | 116 DCHECK_EQ(vector.size(), 1ul); |
65 callback.Run(vector[0]); | 117 callback.Run(vector[0]); |
66 } | 118 } |
67 | 119 |
68 } // anonymous namespace | 120 } // anonymous namespace |
69 | 121 |
70 PermissionServiceImpl::PendingRequest::PendingRequest( | 122 class PermissionServiceImpl::PendingRequest { |
71 const RequestPermissionsCallback& callback, | 123 public: |
72 int request_count) | 124 PendingRequest(std::vector<PermissionType> types, |
73 : callback(callback), | 125 const RequestPermissionsCallback& callback) |
74 request_count(request_count) { | 126 : types_(types), |
75 } | 127 callback_(callback), |
| 128 has_result_been_set_(types.size(), false), |
| 129 results_(types.size(), PermissionStatus::DENIED) {} |
76 | 130 |
77 PermissionServiceImpl::PendingRequest::~PendingRequest() { | 131 ~PendingRequest() { |
78 if (callback.is_null()) | 132 if (callback_.is_null()) |
79 return; | 133 return; |
80 | 134 |
81 std::vector<PermissionStatus> result(request_count, PermissionStatus::DENIED); | 135 std::vector<PermissionStatus> result(types_.size(), |
82 callback.Run(result); | 136 PermissionStatus::DENIED); |
83 } | 137 callback_.Run(result); |
| 138 } |
| 139 |
| 140 int id() { return id_; } |
| 141 void set_id(int id) { id_ = id; } |
| 142 |
| 143 size_t PermissionCount() { return types_.size(); } |
| 144 |
| 145 void SetResult(int index, PermissionStatus result) { |
| 146 DCHECK_EQ(false, has_result_been_set_[index]); |
| 147 has_result_been_set_[index] = true; |
| 148 results_[index] = result; |
| 149 } |
| 150 |
| 151 bool HasResultBeenSet(size_t index) const { |
| 152 return has_result_been_set_[index]; |
| 153 } |
| 154 |
| 155 void RunCallback() { |
| 156 // Check that all results have been set. |
| 157 DCHECK(std::find(has_result_been_set_.begin(), has_result_been_set_.end(), |
| 158 false) == has_result_been_set_.end()); |
| 159 callback_.Run(results_); |
| 160 callback_.Reset(); |
| 161 } |
| 162 |
| 163 private: |
| 164 // Request ID received from the PermissionManager. |
| 165 int id_; |
| 166 std::vector<PermissionType> types_; |
| 167 RequestPermissionsCallback callback_; |
| 168 |
| 169 std::vector<bool> has_result_been_set_; |
| 170 std::vector<PermissionStatus> results_; |
| 171 }; |
84 | 172 |
85 PermissionServiceImpl::PermissionServiceImpl(PermissionServiceContext* context) | 173 PermissionServiceImpl::PermissionServiceImpl(PermissionServiceContext* context) |
86 : context_(context), weak_factory_(this) {} | 174 : context_(context), weak_factory_(this) {} |
87 | 175 |
88 PermissionServiceImpl::~PermissionServiceImpl() { | 176 PermissionServiceImpl::~PermissionServiceImpl() { |
89 DCHECK(context_->GetBrowserContext()); | 177 DCHECK(context_->GetBrowserContext()); |
90 | 178 |
91 PermissionManager* permission_manager = | 179 PermissionManager* permission_manager = |
92 context_->GetBrowserContext()->GetPermissionManager(); | 180 context_->GetBrowserContext()->GetPermissionManager(); |
93 if (!permission_manager) | 181 if (!permission_manager) |
94 return; | 182 return; |
95 | 183 |
96 // Cancel pending requests. | 184 // Cancel pending requests. |
97 for (RequestsMap::Iterator<PendingRequest> it(&pending_requests_); | 185 for (RequestsMap::Iterator<PendingRequest> it(&pending_requests_); |
98 !it.IsAtEnd(); it.Advance()) { | 186 !it.IsAtEnd(); it.Advance()) { |
99 permission_manager->CancelPermissionRequest(it.GetCurrentValue()->id); | 187 permission_manager->CancelPermissionRequest(it.GetCurrentValue()->id); |
100 } | 188 } |
101 pending_requests_.Clear(); | 189 pending_requests_.Clear(); |
102 } | 190 } |
103 | 191 |
104 void PermissionServiceImpl::RequestPermission( | 192 void PermissionServiceImpl::RequestPermission( |
105 PermissionDescriptorPtr permission, | 193 PermissionDescriptorPtr permission, |
106 const url::Origin& origin, | 194 const url::Origin& origin, |
107 bool user_gesture, | 195 bool user_gesture, |
108 const PermissionStatusCallback& callback) { | 196 const PermissionStatusCallback& callback) { |
109 // This condition is valid if the call is coming from a ChildThread instead of | 197 std::vector<PermissionDescriptorPtr> permissions; |
110 // a RenderFrame. Some consumers of the service run in Workers and some in | 198 permissions.push_back(std::move(permission)); |
111 // Frames. In the context of a Worker, it is not possible to show a | 199 RequestPermissions( |
112 // permission prompt because there is no tab. In the context of a Frame, we | 200 std::move(permissions), origin, user_gesture, |
113 // can. Even if the call comes from a context where it is not possible to show | 201 base::Bind(&PermissionRequestResponseCallbackWrapper, callback)); |
114 // any UI, we want to still return something relevant so the current | |
115 // permission status is returned. | |
116 BrowserContext* browser_context = context_->GetBrowserContext(); | |
117 DCHECK(browser_context); | |
118 if (!context_->render_frame_host() || | |
119 !browser_context->GetPermissionManager()) { | |
120 callback.Run(GetPermissionStatus(permission, origin)); | |
121 return; | |
122 } | |
123 | |
124 int pending_request_id = | |
125 pending_requests_.Add(base::MakeUnique<PendingRequest>( | |
126 base::Bind(&PermissionRequestResponseCallbackWrapper, callback), 1)); | |
127 int id = browser_context->GetPermissionManager()->RequestPermission( | |
128 PermissionDescriptorToPermissionType(permission), | |
129 context_->render_frame_host(), origin.GetURL(), user_gesture, | |
130 base::Bind(&PermissionServiceImpl::OnRequestPermissionResponse, | |
131 weak_factory_.GetWeakPtr(), pending_request_id)); | |
132 | |
133 // Check if the request still exists. It might have been removed by the | |
134 // callback if it was run synchronously. | |
135 PendingRequest* pending_request = pending_requests_.Lookup( | |
136 pending_request_id); | |
137 if (!pending_request) | |
138 return; | |
139 pending_request->id = id; | |
140 } | |
141 | |
142 void PermissionServiceImpl::OnRequestPermissionResponse( | |
143 int pending_request_id, | |
144 PermissionStatus status) { | |
145 OnRequestPermissionsResponse(pending_request_id, | |
146 std::vector<PermissionStatus>(1, status)); | |
147 } | 202 } |
148 | 203 |
149 void PermissionServiceImpl::RequestPermissions( | 204 void PermissionServiceImpl::RequestPermissions( |
150 std::vector<PermissionDescriptorPtr> permissions, | 205 std::vector<PermissionDescriptorPtr> permissions, |
151 const url::Origin& origin, | 206 const url::Origin& origin, |
152 bool user_gesture, | 207 bool user_gesture, |
153 const RequestPermissionsCallback& callback) { | 208 const RequestPermissionsCallback& callback) { |
154 // This condition is valid if the call is coming from a ChildThread instead of | 209 // This condition is valid if the call is coming from a ChildThread instead of |
155 // a RenderFrame. Some consumers of the service run in Workers and some in | 210 // a RenderFrame. Some consumers of the service run in Workers and some in |
156 // Frames. In the context of a Worker, it is not possible to show a | 211 // Frames. In the context of a Worker, it is not possible to show a |
157 // permission prompt because there is no tab. In the context of a Frame, we | 212 // permission prompt because there is no tab. In the context of a Frame, we |
158 // can. Even if the call comes from a context where it is not possible to show | 213 // can. Even if the call comes from a context where it is not possible to show |
159 // any UI, we want to still return something relevant so the current | 214 // any UI, we want to still return something relevant so the current |
160 // permission status is returned for each permission. | 215 // permission status is returned for each permission. |
161 BrowserContext* browser_context = context_->GetBrowserContext(); | 216 BrowserContext* browser_context = context_->GetBrowserContext(); |
162 DCHECK(browser_context); | 217 DCHECK(browser_context); |
163 if (!context_->render_frame_host() || | 218 if (!context_->render_frame_host() || |
164 !browser_context->GetPermissionManager()) { | 219 !browser_context->GetPermissionManager()) { |
165 std::vector<PermissionStatus> result(permissions.size()); | 220 std::vector<PermissionStatus> result(permissions.size()); |
166 for (size_t i = 0; i < permissions.size(); ++i) | 221 for (size_t i = 0; i < permissions.size(); ++i) |
167 result[i] = GetPermissionStatus(permissions[i], origin); | 222 result[i] = GetPermissionStatus(permissions[i], origin); |
168 callback.Run(result); | 223 callback.Run(result); |
169 return; | 224 return; |
170 } | 225 } |
171 | 226 |
172 std::vector<PermissionType> types(permissions.size()); | 227 std::vector<PermissionType> types(permissions.size()); |
173 for (size_t i = 0; i < types.size(); ++i) | 228 for (size_t i = 0; i < types.size(); ++i) |
174 types[i] = PermissionDescriptorToPermissionType(permissions[i]); | 229 types[i] = PermissionDescriptorToPermissionType(permissions[i]); |
175 | 230 |
176 int pending_request_id = pending_requests_.Add( | 231 std::unique_ptr<PendingRequest> pending_request = |
177 base::MakeUnique<PendingRequest>(callback, permissions.size())); | 232 base::MakeUnique<PendingRequest>(types, callback); |
| 233 std::vector<PermissionType> request_types; |
| 234 for (size_t i = 0; i < types.size(); ++i) { |
| 235 // Check feature policy. |
| 236 if (!AllowedByFeaturePolicy(context_->render_frame_host(), types[i])) |
| 237 pending_request->SetResult(i, PermissionStatus::DENIED); |
| 238 else |
| 239 request_types.push_back(types[i]); |
| 240 } |
| 241 |
| 242 int pending_request_id = pending_requests_.Add(std::move(pending_request)); |
178 int id = browser_context->GetPermissionManager()->RequestPermissions( | 243 int id = browser_context->GetPermissionManager()->RequestPermissions( |
179 types, context_->render_frame_host(), origin.GetURL(), user_gesture, | 244 request_types, context_->render_frame_host(), origin.GetURL(), |
| 245 user_gesture, |
180 base::Bind(&PermissionServiceImpl::OnRequestPermissionsResponse, | 246 base::Bind(&PermissionServiceImpl::OnRequestPermissionsResponse, |
181 weak_factory_.GetWeakPtr(), pending_request_id)); | 247 weak_factory_.GetWeakPtr(), pending_request_id)); |
182 | 248 |
183 // Check if the request still exists. It may have been removed by the | 249 // Check if the request still exists. It may have been removed by the |
184 // the response callback. | 250 // the response callback. |
185 PendingRequest* pending_request = pending_requests_.Lookup( | 251 PendingRequest* in_progress_request = |
186 pending_request_id); | 252 pending_requests_.Lookup(pending_request_id); |
187 if (!pending_request) | 253 if (!in_progress_request) |
188 return; | 254 return; |
189 pending_request->id = id; | 255 in_progress_request->set_id(id); |
190 } | 256 } |
191 | 257 |
192 void PermissionServiceImpl::OnRequestPermissionsResponse( | 258 void PermissionServiceImpl::OnRequestPermissionsResponse( |
193 int pending_request_id, | 259 int pending_request_id, |
194 const std::vector<PermissionStatus>& result) { | 260 const std::vector<PermissionStatus>& partial_result) { |
195 PendingRequest* request = pending_requests_.Lookup(pending_request_id); | 261 PendingRequest* request = pending_requests_.Lookup(pending_request_id); |
196 RequestPermissionsCallback callback(request->callback); | 262 auto partial_result_it = partial_result.begin(); |
197 request->callback.Reset(); | 263 for (size_t i = 0; i < request->PermissionCount(); ++i) { |
| 264 // We fill in the unset results with those from the call to |
| 265 // RequestPermissions. |
| 266 if (!request->HasResultBeenSet(i)) { |
| 267 request->SetResult(i, *partial_result_it); |
| 268 ++partial_result_it; |
| 269 } |
| 270 } |
| 271 DCHECK(partial_result.end() == partial_result_it); |
| 272 |
| 273 request->RunCallback(); |
198 pending_requests_.Remove(pending_request_id); | 274 pending_requests_.Remove(pending_request_id); |
199 callback.Run(result); | |
200 } | 275 } |
201 | 276 |
202 void PermissionServiceImpl::HasPermission( | 277 void PermissionServiceImpl::HasPermission( |
203 PermissionDescriptorPtr permission, | 278 PermissionDescriptorPtr permission, |
204 const url::Origin& origin, | 279 const url::Origin& origin, |
205 const PermissionStatusCallback& callback) { | 280 const PermissionStatusCallback& callback) { |
206 callback.Run(GetPermissionStatus(permission, origin)); | 281 callback.Run(GetPermissionStatus(permission, origin)); |
207 } | 282 } |
208 | 283 |
209 void PermissionServiceImpl::RevokePermission( | 284 void PermissionServiceImpl::RevokePermission( |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
247 const url::Origin& origin) { | 322 const url::Origin& origin) { |
248 return GetPermissionStatusFromType( | 323 return GetPermissionStatusFromType( |
249 PermissionDescriptorToPermissionType(permission), origin); | 324 PermissionDescriptorToPermissionType(permission), origin); |
250 } | 325 } |
251 | 326 |
252 PermissionStatus PermissionServiceImpl::GetPermissionStatusFromType( | 327 PermissionStatus PermissionServiceImpl::GetPermissionStatusFromType( |
253 PermissionType type, | 328 PermissionType type, |
254 const url::Origin& origin) { | 329 const url::Origin& origin) { |
255 BrowserContext* browser_context = context_->GetBrowserContext(); | 330 BrowserContext* browser_context = context_->GetBrowserContext(); |
256 DCHECK(browser_context); | 331 DCHECK(browser_context); |
257 if (!browser_context->GetPermissionManager()) | 332 if (!browser_context->GetPermissionManager() || |
| 333 !AllowedByFeaturePolicy(context_->render_frame_host(), type)) { |
258 return PermissionStatus::DENIED; | 334 return PermissionStatus::DENIED; |
| 335 } |
259 | 336 |
260 GURL requesting_origin(origin.Serialize()); | 337 GURL requesting_origin(origin.Serialize()); |
261 // If the embedding_origin is empty we'll use |origin| instead. | 338 // If the embedding_origin is empty we'll use |origin| instead. |
262 GURL embedding_origin = context_->GetEmbeddingOrigin(); | 339 GURL embedding_origin = context_->GetEmbeddingOrigin(); |
263 return browser_context->GetPermissionManager()->GetPermissionStatus( | 340 return browser_context->GetPermissionManager()->GetPermissionStatus( |
264 type, requesting_origin, | 341 type, requesting_origin, |
265 embedding_origin.is_empty() ? requesting_origin : embedding_origin); | 342 embedding_origin.is_empty() ? requesting_origin : embedding_origin); |
266 } | 343 } |
267 | 344 |
268 void PermissionServiceImpl::ResetPermissionStatus(PermissionType type, | 345 void PermissionServiceImpl::ResetPermissionStatus(PermissionType type, |
269 const url::Origin& origin) { | 346 const url::Origin& origin) { |
270 BrowserContext* browser_context = context_->GetBrowserContext(); | 347 BrowserContext* browser_context = context_->GetBrowserContext(); |
271 DCHECK(browser_context); | 348 DCHECK(browser_context); |
272 if (!browser_context->GetPermissionManager()) | 349 if (!browser_context->GetPermissionManager()) |
273 return; | 350 return; |
274 | 351 |
275 GURL requesting_origin(origin.Serialize()); | 352 GURL requesting_origin(origin.Serialize()); |
276 // If the embedding_origin is empty we'll use |origin| instead. | 353 // If the embedding_origin is empty we'll use |origin| instead. |
277 GURL embedding_origin = context_->GetEmbeddingOrigin(); | 354 GURL embedding_origin = context_->GetEmbeddingOrigin(); |
278 browser_context->GetPermissionManager()->ResetPermission( | 355 browser_context->GetPermissionManager()->ResetPermission( |
279 type, requesting_origin, | 356 type, requesting_origin, |
280 embedding_origin.is_empty() ? requesting_origin : embedding_origin); | 357 embedding_origin.is_empty() ? requesting_origin : embedding_origin); |
281 } | 358 } |
282 | 359 |
283 } // namespace content | 360 } // namespace content |
OLD | NEW |