Upload upstream chromium 108.0.5359.1
[platform/framework/web/chromium-efl.git] / components / permissions / permission_context_base.cc
1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/permissions/permission_context_base.h"
6
7 #include <stddef.h>
8
9 #include <string>
10 #include <utility>
11
12 #include "base/bind.h"
13 #include "base/callback.h"
14 #include "base/logging.h"
15 #include "base/metrics/field_trial_params.h"
16 #include "base/observer_list.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/time/time.h"
19 #include "build/build_config.h"
20 #include "components/content_settings/core/browser/content_settings_registry.h"
21 #include "components/content_settings/core/browser/content_settings_utils.h"
22 #include "components/content_settings/core/browser/host_content_settings_map.h"
23 #include "components/permissions/features.h"
24 #include "components/permissions/permission_decision_auto_blocker.h"
25 #include "components/permissions/permission_request.h"
26 #include "components/permissions/permission_request_id.h"
27 #include "components/permissions/permission_request_manager.h"
28 #include "components/permissions/permission_uma_util.h"
29 #include "components/permissions/permission_util.h"
30 #include "components/permissions/permissions_client.h"
31 #include "components/permissions/request_type.h"
32 #include "components/permissions/unused_site_permissions_service.h"
33 #include "content/public/browser/browser_thread.h"
34 #include "content/public/browser/global_routing_id.h"
35 #include "content/public/browser/navigation_entry.h"
36 #include "content/public/browser/render_frame_host.h"
37 #include "content/public/browser/web_contents.h"
38 #include "content/public/common/content_features.h"
39 #include "services/network/public/cpp/is_potentially_trustworthy.h"
40 #include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom.h"
41 #include "url/gurl.h"
42
43 namespace permissions {
44 namespace {
45
46 const char kPermissionBlockedKillSwitchMessage[] =
47     "%s permission has been blocked.";
48
49 #if BUILDFLAG(IS_ANDROID)
50 const char kPermissionBlockedRepeatedDismissalsMessage[] =
51     "%s permission has been blocked as the user has dismissed the permission "
52     "prompt several times. This can be reset in Site Settings. See "
53     "https://www.chromestatus.com/feature/6443143280984064 for more "
54     "information.";
55
56 const char kPermissionBlockedRepeatedIgnoresMessage[] =
57     "%s permission has been blocked as the user has ignored the permission "
58     "prompt several times. This can be reset in Site Settings. See "
59     "https://www.chromestatus.com/feature/6443143280984064 for more "
60     "information.";
61 #else
62 const char kPermissionBlockedRepeatedDismissalsMessage[] =
63     "%s permission has been blocked as the user has dismissed the permission "
64     "prompt several times. This can be reset in Page Info which can be "
65     "accessed by clicking the lock icon next to the URL. See "
66     "https://www.chromestatus.com/feature/6443143280984064 for more "
67     "information.";
68
69 const char kPermissionBlockedRepeatedIgnoresMessage[] =
70     "%s permission has been blocked as the user has ignored the permission "
71     "prompt several times. This can be reset in Page Info which can be "
72     "accessed by clicking the lock icon next to the URL. See "
73     "https://www.chromestatus.com/feature/6443143280984064 for more "
74     "information.";
75 #endif
76
77 const char kPermissionBlockedPermissionsPolicyMessage[] =
78     "%s permission has been blocked because of a permissions policy applied to"
79     " the current document. See https://goo.gl/EuHzyv for more details.";
80
81 void LogPermissionBlockedMessage(content::RenderFrameHost* rfh,
82                                  const char* message,
83                                  ContentSettingsType type) {
84   rfh->GetOutermostMainFrame()->AddMessageToConsole(
85       blink::mojom::ConsoleMessageLevel::kWarning,
86       base::StringPrintf(message,
87                          PermissionUtil::GetPermissionString(type).c_str()));
88 }
89
90 }  // namespace
91
92 // static
93 const char PermissionContextBase::kPermissionsKillSwitchFieldStudy[] =
94     "PermissionsKillSwitch";
95 // static
96 const char PermissionContextBase::kPermissionsKillSwitchBlockedValue[] =
97     "blocked";
98
99 PermissionContextBase::PermissionContextBase(
100     content::BrowserContext* browser_context,
101     ContentSettingsType content_settings_type,
102     blink::mojom::PermissionsPolicyFeature permissions_policy_feature)
103     : browser_context_(browser_context),
104       content_settings_type_(content_settings_type),
105       permissions_policy_feature_(permissions_policy_feature) {
106   PermissionDecisionAutoBlocker::UpdateFromVariations();
107 }
108
109 PermissionContextBase::~PermissionContextBase() {
110   DCHECK(permission_observers_.empty());
111   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
112 }
113
114 void PermissionContextBase::RequestPermission(
115     const PermissionRequestID& id,
116     const GURL& requesting_frame,
117     bool user_gesture,
118     BrowserPermissionCallback callback) {
119   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
120
121   content::RenderFrameHost* const rfh = content::RenderFrameHost::FromID(
122       id.render_process_id(), id.render_frame_id());
123
124   if (!rfh) {
125     // Permission request is not allowed without a valid RenderFrameHost.
126     std::move(callback).Run(CONTENT_SETTING_ASK);
127     return;
128   }
129
130   const GURL requesting_origin = requesting_frame.DeprecatedGetOriginAsURL();
131   const GURL embedding_origin =
132       PermissionUtil::GetLastCommittedOriginAsURL(rfh->GetMainFrame());
133
134   if (!requesting_origin.is_valid() || !embedding_origin.is_valid()) {
135     std::string type_name =
136         PermissionUtil::GetPermissionString(content_settings_type_);
137
138     DVLOG(1) << "Attempt to use " << type_name
139              << " from an invalid URL: " << requesting_origin << ","
140              << embedding_origin << " (" << type_name
141              << " is not supported in popups)";
142     NotifyPermissionSet(id, requesting_origin, embedding_origin,
143                         std::move(callback), /*persist=*/false,
144                         CONTENT_SETTING_BLOCK, /*is_one_time=*/false);
145     return;
146   }
147
148   // Check the content setting to see if the user has already made a decision,
149   // or if the origin is under embargo. If so, respect that decision.
150   DCHECK(rfh);
151   PermissionResult result =
152       GetPermissionStatus(rfh, requesting_origin, embedding_origin);
153
154   if (result.content_setting == CONTENT_SETTING_ALLOW ||
155       result.content_setting == CONTENT_SETTING_BLOCK) {
156     switch (result.source) {
157       case PermissionStatusSource::KILL_SWITCH:
158         // Block the request and log to the developer console.
159         LogPermissionBlockedMessage(rfh, kPermissionBlockedKillSwitchMessage,
160                                     content_settings_type_);
161         std::move(callback).Run(CONTENT_SETTING_BLOCK);
162         return;
163       case PermissionStatusSource::MULTIPLE_DISMISSALS:
164         LogPermissionBlockedMessage(rfh,
165                                     kPermissionBlockedRepeatedDismissalsMessage,
166                                     content_settings_type_);
167         break;
168       case PermissionStatusSource::MULTIPLE_IGNORES:
169         LogPermissionBlockedMessage(rfh,
170                                     kPermissionBlockedRepeatedIgnoresMessage,
171                                     content_settings_type_);
172         break;
173       case PermissionStatusSource::FEATURE_POLICY:
174         LogPermissionBlockedMessage(rfh,
175                                     kPermissionBlockedPermissionsPolicyMessage,
176                                     content_settings_type_);
177         break;
178       case PermissionStatusSource::PORTAL:
179       case PermissionStatusSource::FENCED_FRAME:
180       case PermissionStatusSource::INSECURE_ORIGIN:
181       case PermissionStatusSource::UNSPECIFIED:
182       case PermissionStatusSource::VIRTUAL_URL_DIFFERENT_ORIGIN:
183         break;
184     }
185
186     // If we are under embargo, record the embargo reason for which we have
187     // suppressed the prompt.
188     PermissionUmaUtil::RecordEmbargoPromptSuppressionFromSource(result.source);
189     NotifyPermissionSet(id, requesting_origin, embedding_origin,
190                         std::move(callback), /*persist=*/false,
191                         result.content_setting, /*is_one_time=*/false);
192     return;
193   }
194
195   // We are going to show a prompt now.
196   PermissionUmaUtil::PermissionRequested(content_settings_type_,
197                                          requesting_origin);
198   PermissionUmaUtil::RecordEmbargoPromptSuppression(
199       PermissionEmbargoStatus::NOT_EMBARGOED);
200
201   DecidePermission(id, requesting_origin, embedding_origin, user_gesture,
202                    std::move(callback));
203 }
204
205 bool PermissionContextBase::IsRestrictedToSecureOrigins() const {
206   return true;
207 }
208
209 void PermissionContextBase::UserMadePermissionDecision(
210     const PermissionRequestID& id,
211     const GURL& requesting_origin,
212     const GURL& embedding_origin,
213     ContentSetting content_setting) {}
214
215 std::unique_ptr<PermissionRequest>
216 PermissionContextBase::CreatePermissionRequest(
217     const GURL& request_origin,
218     ContentSettingsType content_settings_type,
219     bool has_gesture,
220     content::WebContents* web_contents,
221     PermissionRequest::PermissionDecidedCallback permission_decided_callback,
222     base::OnceClosure delete_callback) const {
223   return std::make_unique<PermissionRequest>(
224       request_origin, ContentSettingsTypeToRequestType(content_settings_type),
225       has_gesture, std::move(permission_decided_callback),
226       std::move(delete_callback));
227 }
228
229 PermissionResult PermissionContextBase::GetPermissionStatus(
230     content::RenderFrameHost* render_frame_host,
231     const GURL& requesting_origin,
232     const GURL& embedding_origin) const {
233   // If the permission has been disabled through Finch, block all requests.
234   if (IsPermissionKillSwitchOn()) {
235     return PermissionResult(CONTENT_SETTING_BLOCK,
236                             PermissionStatusSource::KILL_SWITCH);
237   }
238
239   if (!IsPermissionAvailableToOrigins(requesting_origin, embedding_origin)) {
240     return PermissionResult(CONTENT_SETTING_BLOCK,
241                             PermissionStatusSource::INSECURE_ORIGIN);
242   }
243
244   // Check whether the feature is enabled for the frame by permissions policy.
245   // We can only do this when a RenderFrameHost has been provided.
246   if (render_frame_host &&
247       !PermissionAllowedByPermissionsPolicy(render_frame_host)) {
248     return PermissionResult(CONTENT_SETTING_BLOCK,
249                             PermissionStatusSource::FEATURE_POLICY);
250   }
251
252   if (render_frame_host) {
253     content::WebContents* web_contents =
254         content::WebContents::FromRenderFrameHost(render_frame_host);
255
256     // Automatically deny all HTTP or HTTPS requests where the virtual URL and
257     // the loaded URL are for different origins. The loaded URL is the one
258     // actually in the renderer, but the virtual URL is the one
259     // seen by the user. This may be very confusing for a user to see in a
260     // permissions request.
261     content::NavigationEntry* entry =
262         web_contents->GetController().GetLastCommittedEntry();
263     if (entry) {
264       const GURL virtual_url = entry->GetVirtualURL();
265       const GURL loaded_url = entry->GetURL();
266       if (virtual_url.SchemeIsHTTPOrHTTPS() &&
267           loaded_url.SchemeIsHTTPOrHTTPS() &&
268           !url::IsSameOriginWith(virtual_url, loaded_url)) {
269         return PermissionResult(
270             CONTENT_SETTING_BLOCK,
271             PermissionStatusSource::VIRTUAL_URL_DIFFERENT_ORIGIN);
272       }
273     }
274   }
275
276   ContentSetting content_setting = GetPermissionStatusInternal(
277       render_frame_host, requesting_origin, embedding_origin);
278
279   if (content_setting != CONTENT_SETTING_ASK) {
280     return PermissionResult(content_setting,
281                             PermissionStatusSource::UNSPECIFIED);
282   }
283
284   absl::optional<PermissionResult> result =
285       PermissionsClient::Get()
286           ->GetPermissionDecisionAutoBlocker(browser_context_)
287           ->GetEmbargoResult(requesting_origin, content_settings_type_);
288   if (result) {
289     DCHECK(result->content_setting == CONTENT_SETTING_BLOCK);
290     return *result;
291   }
292   return PermissionResult(CONTENT_SETTING_ASK,
293                           PermissionStatusSource::UNSPECIFIED);
294 }
295
296 bool PermissionContextBase::IsPermissionAvailableToOrigins(
297     const GURL& requesting_origin,
298     const GURL& embedding_origin) const {
299   if (IsRestrictedToSecureOrigins()) {
300     if (!network::IsUrlPotentiallyTrustworthy(requesting_origin))
301       return false;
302
303     // TODO(raymes): We should check the entire chain of embedders here whenever
304     // possible as this corresponds to the requirements of the secure contexts
305     // spec and matches what is implemented in blink. Right now we just check
306     // the top level and requesting origins.
307     if (!PermissionsClient::Get()->CanBypassEmbeddingOriginCheck(
308             requesting_origin, embedding_origin) &&
309         !network::IsUrlPotentiallyTrustworthy(embedding_origin)) {
310       return false;
311     }
312   }
313   return true;
314 }
315
316 PermissionResult PermissionContextBase::UpdatePermissionStatusWithDeviceStatus(
317     PermissionResult result,
318     const GURL& requesting_origin,
319     const GURL& embedding_origin) const {
320   return result;
321 }
322
323 void PermissionContextBase::ResetPermission(const GURL& requesting_origin,
324                                             const GURL& embedding_origin) {
325   if (!content_settings::ContentSettingsRegistry::GetInstance()->Get(
326           content_settings_type_)) {
327     return;
328   }
329   PermissionsClient::Get()
330       ->GetSettingsMap(browser_context_)
331       ->SetContentSettingDefaultScope(requesting_origin, embedding_origin,
332                                       content_settings_type_,
333                                       CONTENT_SETTING_DEFAULT);
334 }
335
336 bool PermissionContextBase::IsPermissionKillSwitchOn() const {
337   const std::string param = base::GetFieldTrialParamValue(
338       kPermissionsKillSwitchFieldStudy,
339       PermissionUtil::GetPermissionString(content_settings_type_));
340
341   return param == kPermissionsKillSwitchBlockedValue;
342 }
343
344 ContentSetting PermissionContextBase::GetPermissionStatusInternal(
345     content::RenderFrameHost* render_frame_host,
346     const GURL& requesting_origin,
347     const GURL& embedding_origin) const {
348   return PermissionsClient::Get()
349       ->GetSettingsMap(browser_context_)
350       ->GetContentSetting(requesting_origin, embedding_origin,
351                           content_settings_type_);
352 }
353
354 void PermissionContextBase::DecidePermission(
355     const PermissionRequestID& id,
356     const GURL& requesting_origin,
357     const GURL& embedding_origin,
358     bool user_gesture,
359     BrowserPermissionCallback callback) {
360   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
361
362   // Under permission delegation, when we display a permission prompt, the
363   // origin displayed in the prompt should never differ from the top-level
364   // origin. Storage access API requests are excluded as they are expected to
365   // request permissions from the frame origin needing access.
366   DCHECK(PermissionsClient::Get()->CanBypassEmbeddingOriginCheck(
367              requesting_origin, embedding_origin) ||
368          requesting_origin == embedding_origin ||
369          content_settings_type_ == ContentSettingsType::STORAGE_ACCESS);
370
371   content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
372       id.render_process_id(), id.render_frame_id());
373   DCHECK(rfh);
374
375   content::WebContents* web_contents =
376       content::WebContents::FromRenderFrameHost(rfh);
377   PermissionRequestManager* permission_request_manager =
378       PermissionRequestManager::FromWebContents(web_contents);
379   // TODO(felt): sometimes |permission_request_manager| is null. This check is
380   // meant to prevent crashes. See crbug.com/457091.
381   if (!permission_request_manager)
382     return;
383
384   std::unique_ptr<PermissionRequest> request_ptr = CreatePermissionRequest(
385       requesting_origin, content_settings_type_, user_gesture, web_contents,
386       base::BindOnce(&PermissionContextBase::PermissionDecided,
387                      weak_factory_.GetWeakPtr(), id, requesting_origin,
388                      embedding_origin, std::move(callback)),
389       base::BindOnce(&PermissionContextBase::CleanUpRequest,
390                      weak_factory_.GetWeakPtr(), id));
391   PermissionRequest* request = request_ptr.get();
392
393   bool inserted =
394       pending_requests_
395           .insert(std::make_pair(id.ToString(), std::move(request_ptr)))
396           .second;
397   DCHECK(inserted) << "Duplicate id " << id.ToString();
398
399   permission_request_manager->AddRequest(rfh, request);
400 }
401
402 void PermissionContextBase::PermissionDecided(
403     const PermissionRequestID& id,
404     const GURL& requesting_origin,
405     const GURL& embedding_origin,
406     BrowserPermissionCallback callback,
407     ContentSetting content_setting,
408     bool is_one_time) {
409   DCHECK(content_setting == CONTENT_SETTING_ALLOW ||
410          content_setting == CONTENT_SETTING_BLOCK ||
411          content_setting == CONTENT_SETTING_DEFAULT);
412   UserMadePermissionDecision(id, requesting_origin, embedding_origin,
413                              content_setting);
414
415   bool persist = content_setting != CONTENT_SETTING_DEFAULT;
416   NotifyPermissionSet(id, requesting_origin, embedding_origin,
417                       std::move(callback), persist, content_setting,
418                       is_one_time);
419 }
420
421 content::BrowserContext* PermissionContextBase::browser_context() const {
422   return browser_context_;
423 }
424
425 void PermissionContextBase::OnContentSettingChanged(
426     const ContentSettingsPattern& primary_pattern,
427     const ContentSettingsPattern& secondary_pattern,
428     ContentSettingsTypeSet content_type_set) {
429   if (!content_type_set.Contains(content_settings_type_))
430     return;
431
432   for (permissions::Observer& obs : permission_observers_) {
433     obs.OnPermissionChanged(primary_pattern, secondary_pattern,
434                             content_type_set);
435   }
436 }
437
438 void PermissionContextBase::AddObserver(
439     permissions::Observer* permission_observer) {
440   if (permission_observers_.empty() &&
441       !content_setting_observer_registered_by_subclass_) {
442     PermissionsClient::Get()
443         ->GetSettingsMap(browser_context_)
444         ->AddObserver(this);
445   }
446   permission_observers_.AddObserver(permission_observer);
447 }
448
449 void PermissionContextBase::RemoveObserver(
450     permissions::Observer* permission_observer) {
451   permission_observers_.RemoveObserver(permission_observer);
452   if (permission_observers_.empty() &&
453       !content_setting_observer_registered_by_subclass_) {
454     PermissionsClient::Get()
455         ->GetSettingsMap(browser_context_)
456         ->RemoveObserver(this);
457   }
458 }
459
460 void PermissionContextBase::NotifyPermissionSet(
461     const PermissionRequestID& id,
462     const GURL& requesting_origin,
463     const GURL& embedding_origin,
464     BrowserPermissionCallback callback,
465     bool persist,
466     ContentSetting content_setting,
467     bool is_one_time) {
468   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
469
470   if (persist) {
471     UpdateContentSetting(requesting_origin, embedding_origin, content_setting,
472                          is_one_time);
473   }
474
475   UpdateTabContext(id, requesting_origin,
476                    content_setting == CONTENT_SETTING_ALLOW);
477
478   if (content_setting == CONTENT_SETTING_DEFAULT)
479     content_setting = CONTENT_SETTING_ASK;
480
481   std::move(callback).Run(content_setting);
482 }
483
484 void PermissionContextBase::CleanUpRequest(const PermissionRequestID& id) {
485   size_t success = pending_requests_.erase(id.ToString());
486   DCHECK(success == 1) << "Missing request " << id.ToString();
487 }
488
489 void PermissionContextBase::UpdateContentSetting(const GURL& requesting_origin,
490                                                  const GURL& embedding_origin,
491                                                  ContentSetting content_setting,
492                                                  bool is_one_time) {
493   DCHECK_EQ(requesting_origin, requesting_origin.DeprecatedGetOriginAsURL());
494   DCHECK_EQ(embedding_origin, embedding_origin.DeprecatedGetOriginAsURL());
495   DCHECK(content_setting == CONTENT_SETTING_ALLOW ||
496          content_setting == CONTENT_SETTING_BLOCK);
497
498   content_settings::ContentSettingConstraints constraints = {
499       base::Time(), is_one_time ? content_settings::SessionModel::OneTime
500                                 : content_settings::SessionModel::Durable};
501
502 #if !BUILDFLAG(IS_ANDROID)
503   if (base::FeatureList::IsEnabled(
504           features::kRecordPermissionExpirationTimestamps)) {
505     // The Permissions module in Safety check will revoke permissions after
506     // a finite amount of time.
507     // We're only interested in expiring permissions that:
508     // 1. Are ALLOWed.
509     // 2. Fall back to ASK.
510     // 3. Are not already a one-time grant.
511     if (content_setting == CONTENT_SETTING_ALLOW && !is_one_time &&
512         content_settings::CanTrackLastVisit(content_settings_type_)) {
513       // For #2, by definition, that should be all of them. If that changes in
514       // the future, consider whether revocation for such permission makes
515       // sense, and/or change this to an early return so that we don't
516       // unnecessarily record timestamps where we don't need them.
517       constraints.track_last_visit_for_autoexpiration = true;
518     }
519   }
520 #endif  // !BUILDFLAG(IS_ANDROID)
521
522   PermissionsClient::Get()
523       ->GetSettingsMap(browser_context_)
524       ->SetContentSettingDefaultScope(requesting_origin, embedding_origin,
525                                       content_settings_type_, content_setting,
526                                       constraints);
527 }
528
529 bool PermissionContextBase::PermissionAllowedByPermissionsPolicy(
530     content::RenderFrameHost* rfh) const {
531   // Some features don't have an associated permissions policy yet. Allow those.
532   if (permissions_policy_feature_ ==
533       blink::mojom::PermissionsPolicyFeature::kNotFound)
534     return true;
535
536   return rfh->IsFeatureEnabled(permissions_policy_feature_);
537 }
538
539 }  // namespace permissions