blob: 47c01660026896f3fe42cd8d6463a6d006176cfa [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_PERMISSIONS_PERMISSION_CONTEXT_BASE_H_
#define COMPONENTS_PERMISSIONS_PERMISSION_CONTEXT_BASE_H_
#include <memory>
#include <unordered_map>
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/content_settings/core/browser/content_settings_observer.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/permissions/permission_request.h"
#include "components/permissions/permission_request_data.h"
#include "components/permissions/resolvers/permission_resolver.h"
#include "content/public/browser/permission_result.h"
#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-forward.h"
class GURL;
namespace permissions {
class PermissionRequestID;
}
namespace content {
class BrowserContext;
class RenderFrameHost;
class WebContents;
} // namespace content
namespace permissions {
class Observer : public base::CheckedObserver {
public:
// Called whenever there is a potential permission status change for the
// specified patterns. This function being called does not necessarily mean
// that the result of |GetPermissionStatus| has changed for any particular
// origin.
virtual void OnPermissionChanged(
const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern,
ContentSettingsTypeSet content_type_set) = 0;
};
// Default one time permission expiration time.
static constexpr base::TimeDelta kOneTimePermissionTimeout = base::Minutes(5);
// A one time grant will never last longer than this value.
static constexpr base::TimeDelta kOneTimePermissionMaximumLifetime =
base::Hours(16);
using BrowserPermissionCallback = base::OnceCallback<void(ContentSetting)>;
// This base class contains common operations for granting permissions.
// It offers the following functionality:
// - Creates a permission request when needed.
// - If accepted/denied the permission is saved in content settings for
// future uses (for the domain that requested it).
// - If dismissed the permission is not saved but it's considered denied for
// this one request
// - In any case the BrowserPermissionCallback is executed once a decision
// about the permission is made by the user.
// The bare minimum you need to create a new permission request is
// - Define your new permission in the ContentSettingsType enum.
// - Create a class that inherits from PermissionContextBase and passes the
// new permission.
// - Edit the PermissionRequest methods to add the new text.
// - Make sure to update
// third_party/blink/public/devtools_protocol/browser_protocol.pdl
// even if you don't intend to do anything DevTools-specific; you will
// run into problems with generated code otherwise.
// - Hit several asserts for the missing plumbing and fix them :)
// After this you can override several other methods to customize behavior,
// in particular it is advised to override UpdateTabContext in order to manage
// the permission from the omnibox.
// See midi_permission_context.h/cc or push_permission_context.cc/h for some
// examples.
class PermissionContextBase : public content_settings::Observer {
public:
PermissionContextBase(
content::BrowserContext* browser_context,
ContentSettingsType content_settings_type,
network::mojom::PermissionsPolicyFeature permissions_policy_feature);
~PermissionContextBase() override;
// A field trial used to enable the global permissions kill switch.
// This is public so permissions that don't yet inherit from
// PermissionContextBase can use it.
static const char kPermissionsKillSwitchFieldStudy[];
// The field trial param to enable the global permissions kill switch.
// This is public so permissions that don't yet inherit from
// PermissionContextBase can use it.
static const char kPermissionsKillSwitchBlockedValue[];
// |callback| is called upon resolution of the request, but not if a prompt
// is shown and ignored.
virtual void RequestPermission(
std::unique_ptr<PermissionRequestData> request_data,
BrowserPermissionCallback callback);
// Returns whether the permission has been granted, denied etc. given a
// PermissionResolver. |render_frame_host| may be nullptr if the call is
// coming from a context other than a specific frame.
content::PermissionResult GetPermissionStatus(
const PermissionResolver& resolver,
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
const GURL& embedding_origin) const;
// Returns whether the permission has been granted, denied etc. given a
// PermissionDescriptorPtr. |render_frame_host| may be nullptr if the call is
// coming from a context other than a specific frame.
content::PermissionResult GetPermissionStatus(
const blink::mojom::PermissionDescriptorPtr& permission_descriptor,
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
const GURL& embedding_origin) const;
// Returns whether the permission is usable by requesting/embedding origins.
bool IsPermissionAvailableToOrigins(const GURL& requesting_origin,
const GURL& embedding_origin) const;
// Update |result| with any modifications based on the device state. For
// example, if |result| is ALLOW but Chrome does not have the relevant
// permission at the device level, but will prompt the user, return ASK.
// This function updates the cached device permission status which can result
// in the permission status changing and observers being notified.
virtual content::PermissionResult UpdatePermissionStatusWithDeviceStatus(
content::WebContents* web_contents,
content::PermissionResult result,
const GURL& requesting_origin,
const GURL& embedding_origin);
// Resets the permission to its default value.
virtual void ResetPermission(const GURL& requesting_origin,
const GURL& embedding_origin);
// Whether the permission status should take into account the device status
// which can be provided by |UpdatePermissionStatusWithDeviceStatus|.
virtual bool AlwaysIncludeDeviceStatus() const;
// Whether the kill switch has been enabled for this permission.
// public for permissions that do not use RequestPermission, like
// camera and microphone, and for testing.
bool IsPermissionKillSwitchOn() const;
void AddObserver(permissions::Observer* permission_observer);
void RemoveObserver(permissions::Observer* permission_observer);
virtual std::unique_ptr<PermissionResolver> CreatePermissionResolver(
const blink::mojom::PermissionDescriptorPtr& permission_descriptor) const;
virtual std::unique_ptr<PermissionResolver>
CreateRequestIndependentPermissionResolver() const;
// Update the value of `last_has_device_permission_result_` and notify
// observers if it changes.
void MaybeUpdateCachedHasDevicePermission(content::WebContents* web_contents);
ContentSettingsType content_settings_type() const {
return content_settings_type_;
}
void set_has_device_permission_for_test(std::optional<bool> has_permission) {
has_device_permission_for_test_ = has_permission;
}
protected:
virtual ContentSetting GetPermissionStatusInternal(
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
const GURL& embedding_origin) const;
// Called if generic checks (existing content setting, embargo, etc.) fail to
// resolve a permission request. The default implementation prompts the user.
virtual void DecidePermission(
std::unique_ptr<PermissionRequestData> request_data,
BrowserPermissionCallback callback);
// Updates stored content setting if persist is set, updates tab indicators
// and runs the callback to finish the request.
virtual void NotifyPermissionSet(const PermissionRequestData& request_data,
BrowserPermissionCallback callback,
bool persist,
ContentSetting content_setting,
bool is_one_time,
bool is_final_decision);
// Implementors can override this method to update the icons on the
// url bar with the result of the new permission.
virtual void UpdateTabContext(const PermissionRequestID& id,
const GURL& requesting_origin,
bool allowed) {}
// Returns the browser context associated with this permission context.
content::BrowserContext* browser_context() const;
// Store the decided permission as a content setting.
// virtual since the permission might be stored with different restrictions
// (for example for desktop notifications).
virtual void UpdateContentSetting(const PermissionRequestData& request_data,
ContentSetting content_setting,
bool is_one_time);
// Whether the permission should be restricted to secure origins.
virtual bool IsRestrictedToSecureOrigins() const;
// Called by PermissionDecided when the user has made a permission decision.
// Subclasses may override this method to perform context-specific logic
// before the content setting is changed and the permission callback is run.
virtual void UserMadePermissionDecision(const PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedding_origin,
ContentSetting content_setting);
// content_settings::Observer:
void OnContentSettingChanged(
const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern,
ContentSettingsTypeSet content_type_set) override;
// Implementors can override this method to use a different PermissionRequest
// implementation.
virtual std::unique_ptr<PermissionRequest> CreatePermissionRequest(
content::WebContents* web_contents,
std::unique_ptr<PermissionRequestData> request_data,
PermissionRequest::PermissionDecidedCallback permission_decided_callback,
base::OnceClosure delete_callback) const;
// Implementors can override this method to avoid using automatic embargo.
virtual bool UsesAutomaticEmbargo() const;
// Derived classes can use this function to find some particular permission
// request.
const PermissionRequest* FindPermissionRequest(
const PermissionRequestID& id) const;
// Implementors can override this method to use a different embedding origin.
// TODO(crbug.com/40220500): This should return a url::Origin instead.
virtual GURL GetEffectiveEmbedderOrigin(content::RenderFrameHost* rfh) const;
base::ObserverList<permissions::Observer> permission_observers_;
// Set by subclasses to inform the base class that they will handle adding
// and removing themselves as observers to the HostContentSettingsMap.
bool content_setting_observer_registered_by_subclass_ = false;
private:
friend class PermissionContextBaseTests;
bool PermissionAllowedByPermissionsPolicy(
content::RenderFrameHost* rfh) const;
// Called when a request is no longer used so it can be cleaned up.
void CleanUpRequest(content::WebContents* web_contents,
const PermissionRequestID& id,
bool embedded_permission_element_initiated);
// This is the callback for PermissionRequest and is called once the user
// allows/blocks/dismisses a permission prompt.
void PermissionDecided(ContentSetting content_setting,
bool is_one_time,
bool is_final_decision,
const PermissionRequestData& request_data);
void NotifyObservers(const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern,
ContentSettingsTypeSet content_type_set) const;
raw_ptr<content::BrowserContext> browser_context_;
const ContentSettingsType content_settings_type_;
const network::mojom::PermissionsPolicyFeature permissions_policy_feature_;
std::unordered_map<
std::string,
std::pair<std::unique_ptr<PermissionRequest>, BrowserPermissionCallback>>
pending_requests_;
mutable std::optional<bool> last_has_device_permission_result_ = std::nullopt;
std::optional<bool> has_device_permission_for_test_;
// Must be the last member, to ensure that it will be
// destroyed first, which will invalidate weak pointers
base::WeakPtrFactory<PermissionContextBase> weak_factory_{this};
};
} // namespace permissions
#endif // COMPONENTS_PERMISSIONS_PERMISSION_CONTEXT_BASE_H_