// Copyright 2012 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_CONTENT_SETTINGS_BROWSER_PAGE_SPECIFIC_CONTENT_SETTINGS_H_
#define COMPONENTS_CONTENT_SETTINGS_BROWSER_PAGE_SPECIFIC_CONTENT_SETTINGS_H_

#include <stdint.h>

#include <map>
#include <memory>
#include <set>
#include <string>
#include <variant>
#include <vector>

#include "base/containers/enum_set.h"
#include "base/containers/flat_set.h"
#include "base/containers/span.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/scoped_observation.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
#include "components/browsing_data/content/browsing_data_model.h"
#include "components/browsing_data/content/cookie_helper.h"
#include "components/content_settings/common/content_settings_manager.mojom.h"
#include "components/content_settings/core/browser/content_settings_observer.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "content/public/browser/allow_service_worker_result.h"
#include "content/public/browser/page_user_data.h"
#include "content/public/browser/render_frame_host.h"
#include "net/base/schemeful_site.h"
#include "url/gurl.h"

namespace blink {
class StorageKey;
}  // namespace blink

namespace content {
class WebContents;
class WebContentsObserver;
}  // namespace content

namespace url {
class Origin;
}  // namespace url

namespace content_settings {

enum class SiteDataType {
  kUnknown,
  kStorage,
  kCookies,
  kServiceWorker,
  kSharedWorker,
  kInterestGroup,
  kTopic,
  kTrustToken,
};

enum class AccessType {
  // This value represents situations where by the PSCS doesn't have enough
  // information to assess the accurate nature of site data access.
  kUnknown,
  kRead,
  kWrite,
};

// Holds extra details about the site data access deemed useful by PSCS
// observers.
struct AccessDetails {
  AccessDetails();
  AccessDetails(SiteDataType site_data_type,
                AccessType access_type,
                GURL url,
                bool blocked_by_policy,
                bool is_from_primary_page);
  ~AccessDetails();
  bool operator<(const AccessDetails& other) const;

  SiteDataType site_data_type = SiteDataType::kUnknown;
  AccessType access_type = AccessType::kUnknown;
  GURL url;
  bool blocked_by_policy = false;
  // Specifies whether the access occurred in the primary page.
  bool is_from_primary_page = false;
};

// TODO(msramek): Media is storing their state in PageSpecificContentSettings:
// |microphone_camera_state_| without being tied to a single content setting.
// This state is not ideal, potential solution is to save this information via
// content::WebContentsUserData

// This class manages state about permissions, content settings, cookies and
// site data for a specific page (main document and all of its child frames). It
// tracks which content was accessed and which content was blocked. Based on
// this it provides information about which types of content were accessed and
// blocked.
//
// Tracking is done per main document so instances of this class will be deleted
// when the main document is deleted. This can happen after the tab navigates
// away to a new document or when the tab itself is deleted, so you should not
// keep references to objects of this class.
//
// When a page enters the back-forward cache its associated
// PageSpecificContentSettings are not cleared and will be restored along with
// the document when navigating back. These stored instances still listen to
// content settings updates and keep their internal state up to date.
//
// Events tied to a main frame navigation will be associated with the newly
// loaded page once the navigation commits or discarded if it does not.
//
// `PageSpecificContentSettings` can't observe cookie events of navigation using
// prefetch. So, we need to disable prefetch ahead of prerender for
// SpeculationRules. For more details, see
// https://docs.google.com/document/d/1gYanzL8zrrulVdJds9IxoCwlNs0Xstc6bTuHVgrVGn4
// See also the test
// ContentSettingsWithPrerenderingBrowserTest.PrerenderingPageSetsCookie.
//
// TODO(crbug.com/493711325): Revisit and decide that we should fix or not.
class PageSpecificContentSettings
    : public content_settings::Observer,
      public content::PageUserData<PageSpecificContentSettings> {
 public:
  // Fields describing the current mic/camera state. If a page has attempted to
  // access a device, the kXxxAccessed bit will be set. If access was blocked,
  // kXxxBlocked will be set.
  enum MicrophoneCameraStateFlags {
    kMicrophoneAccessed,
    kMicrophoneBlocked,
    kCameraAccessed,
    kCameraBlocked,

    kMinValue = kMicrophoneAccessed,
    kMaxValue = kCameraBlocked,
  };
  using MicrophoneCameraState = base::EnumSet<MicrophoneCameraStateFlags>;

  class Delegate {
   public:
    virtual ~Delegate() = default;

    // Called when content settings state changes that might require updating
    // the location bar.
    virtual void UpdateLocationBar() = 0;

    // Gets the pref service for the current web contents.
    virtual PrefService* GetPrefs() = 0;

    // Gets the settings map for the current web contents.
    virtual HostContentSettingsMap* GetSettingsMap() = 0;

    // Gets the `BrowsingDataModel::Delegate` for the current profile.
    virtual std::unique_ptr<BrowsingDataModel::Delegate>
    CreateBrowsingDataModelDelegate() = 0;

    // Allows delegate to override content setting rules that will be sent to
    // the renderer.
    virtual void SetDefaultRendererContentSettingRules(
        content::RenderFrameHost* rfh,
        RendererContentSettingRules* rules) = 0;

    // Allows the delegate to provide additional logic for getting microphone
    // and camera state on top of the microphone and camera state at the last
    // media stream request.
    virtual MicrophoneCameraState GetMicrophoneCameraState() = 0;

    // If there is a document picture-in-picture window open, check if the given
    // web contents is the opener web contents or child web contents, and return
    // the other web contents to be synced.
    virtual content::WebContents* MaybeGetSyncedWebContentsForPictureInPicture(
        content::WebContents* web_contents) = 0;

    // Returns `true` if `web_contents` represents a PiP window. Returns `false`
    // otherwise.
    virtual bool IsPiPWindow(content::WebContents* web_contents) = 0;

    // Notifies the delegate a particular content settings type was allowed for
    // the first time on this page.
    virtual void OnContentAllowed(ContentSettingsType type) = 0;

    // Notifies the delegate a particular content settings type was blocked.
    virtual void OnContentBlocked(ContentSettingsType type) = 0;

    // Returns `true` if ContentSettingsType is blocked on the system level.
    virtual bool IsBlockedOnSystemLevel(ContentSettingsType type) = 0;

    // Returns true if `render_frame_host` should be allowlisted for using
    // JavaScript.
    virtual bool IsFrameAllowlistedForJavaScript(
        content::RenderFrameHost* render_frame_host) = 0;
  };

  // Classes that want to be notified about site data events must implement
  // this abstract class and add themselves as observer to the
  // |PageSpecificContentSettings|.
  class SiteDataObserver {
   public:
    explicit SiteDataObserver(content::WebContents* web_contents);

    SiteDataObserver(const SiteDataObserver&) = delete;
    SiteDataObserver& operator=(const SiteDataObserver&) = delete;

    virtual ~SiteDataObserver();

    // Called whenever site data is accessed.
    virtual void OnSiteDataAccessed(const AccessDetails& access_details) = 0;

    // Called whenever this page is loaded via a redirect with stateful bounces.
    virtual void OnStatefulBounceDetected() = 0;

    content::WebContents* web_contents() { return web_contents_; }

    // Called when the WebContents is destroyed; nulls out
    // the local reference.
    void WebContentsDestroyed();

   private:
    raw_ptr<content::WebContents, DanglingUntriaged> web_contents_;
  };

  class PermissionUsageObserver : public base::CheckedObserver {
   public:
    virtual void OnPermissionUsageChange() = 0;
  };

  PageSpecificContentSettings(const PageSpecificContentSettings&) = delete;
  PageSpecificContentSettings& operator=(const PageSpecificContentSettings&) =
      delete;

  ~PageSpecificContentSettings() override;

  static void CreateForWebContents(content::WebContents* web_contents,
                                   std::unique_ptr<Delegate> delegate);
  static void DeleteForWebContentsForTest(content::WebContents* web_contents);

  // Returns the object given a RenderFrameHost ids. Returns nullptr if the
  // frame no longer exist or there are no PageSpecificContentSettings attached
  // to the document.
  static PageSpecificContentSettings* GetForFrame(
      const content::GlobalRenderFrameHostId& id);

  // Returns the object given a RenderFrameHost. Returns nullptr if the frame
  // is nullptr or there are no PageSpecificContentSettings attached to the
  // document.
  static PageSpecificContentSettings* GetForFrame(
      content::RenderFrameHost* rfh);

  // Returns the Delegate that was associated to |web_contents| in
  // CreateForWebContents. Null if CreateForWebContents was not called for
  // |web_contents|.
  static PageSpecificContentSettings::Delegate* GetDelegateForWebContents(
      content::WebContents* web_contents);

  // Called by GeolocationNavigationThrottle when the X-Geo header is attached
  // to a search navigation. This ensures the geolocation usage indicator is
  // displayed in the Omnibox upon commit.
  static void GeolocationHeaderAttachedToNavigation(
      content::NavigationHandle* navigation);

  // Called by GeolocationNavigationThrottle when the X-Geo header is removed
  // from a navigation (e.g. due to a redirect to a non-search URL).
  static void GeolocationHeaderRemovedFromNavigation(
      content::NavigationHandle* navigation);

  static void StorageAccessed(
      mojom::ContentSettingsManager::StorageType storage_type,
      std::variant<content::GlobalRenderFrameHostToken,
                   content::GlobalRenderFrameHostId> frame_id,
      const blink::StorageKey& storage_key,
      bool blocked_by_policy);

  static void BrowsingDataAccessed(content::RenderFrameHost* rfh,
                                   BrowsingDataModel::DataKey data_key,
                                   BrowsingDataModel::StorageType storage_type,
                                   bool blocked);

  // Called when content access is blocked in the renderer process.
  static void ContentBlocked(
      const content::GlobalRenderFrameHostToken& frame_token,
      ContentSettingsType type);

  // Called when a specific Shared Worker was accessed.
  static void SharedWorkerAccessed(
      int render_process_id,
      int render_frame_id,
      const GURL& worker_url,
      const std::string& name,
      const blink::StorageKey& storage_key,
      const blink::mojom::SharedWorkerSameSiteCookies same_site_cookies,
      bool blocked_by_policy);

  // Called when |api_origin| attempts to join an interest group via the
  // Interest Group API.
  static void InterestGroupJoined(content::RenderFrameHost* rfh,
                                  const url::Origin& api_origin,
                                  bool blocked_by_policy);

  // Called when notifications are accessed on `rfh`.
  static void NotificationsAccessed(content::RenderFrameHost* rfh,
                                    bool blocked);

  static content::WebContentsObserver* GetWebContentsObserverForTest(
      content::WebContents* web_contents);

  // Returns a WeakPtr to this instance. Given that PageSpecificContentSettings
  // instances are tied to a page it is generally unsafe to store these
  // references, instead a WeakPtr should be used instead.
  base::WeakPtr<PageSpecificContentSettings> AsWeakPtr() {
    return weak_factory_.GetWeakPtr();
  }

  // Add/remove observer.
  void AddPermissionUsageObserver(PermissionUsageObserver* observer);
  void RemovePermissionUsageObserver(PermissionUsageObserver* observer);

  // Changes the |content_blocked_| entry for popups.
  void ClearPopupsBlocked();

  // Called when audio has been blocked on the page.
  void OnAudioBlocked();

  // Records one additional stateful bounce during the navigation that led to
  // this page.
  void IncrementStatefulBounceCount();

  // Returns whether a particular kind of content has been blocked for this
  // page.
  bool IsContentBlocked(ContentSettingsType content_type) const;

  // Returns whether a particular kind of content has been allowed. Currently
  // only tracks cookies.
  bool IsContentAllowed(ContentSettingsType content_type) const;

  // Returns a map from sites that requested |content_setting| to whether the
  // permission was granted. This method is only supported for permissions that
  // are scoped to sites and apply to embedded content, e.g. StorageAccess.
  std::map<net::SchemefulSite, /*is_allowed*/ bool> GetTwoSiteRequests(
      ContentSettingsType content_type);

  const GURL& media_stream_access_origin() const {
    return media_stream_access_origin_;
  }

  bool camera_was_just_granted_on_site_level() {
    return camera_was_just_granted_on_site_level_;
  }

  bool mic_was_just_granted_on_site_level() {
    return mic_was_just_granted_on_site_level_;
  }

  bool geolocation_was_just_granted_on_site_level() {
    return geolocation_was_just_granted_on_site_level_;
  }

  // Called when notifications permission was auto-denied because the system
  // level permission for a PWA was previously denied. This is currently only
  // possible/used on macOS.
  void SetNotificationsWasDeniedBecauseOfSystemPermission();

  bool notifications_was_denied_because_of_system_permission() {
    return notifications_was_denied_because_of_system_permission_;
  }

  void SetRequestedSensorIsAvailable(bool is_available);
  bool is_any_requested_sensor_available() const {
    return any_requested_sensor_is_available_;
  }

  // Support for tracking active sensors
  void OnSensorStarted();
  void OnSensorStopped();
  int active_available_sensors() const;

  // Returns the state of the camera and microphone usage.
  // The return value always includes all active media capture devices, on top
  // of the devices from the last request.
  MicrophoneCameraState GetMicrophoneCameraState() const;

  // Returns whether the camera or microphone permission or media device setting
  // has changed since the last permission request.
  bool IsMicrophoneCameraStateChanged() const;

  int stateful_bounce_count() const { return stateful_bounce_count_; }

  BrowsingDataModel* allowed_browsing_data_model() const {
    return allowed_browsing_data_model_.get();
  }

  BrowsingDataModel* blocked_browsing_data_model() const {
    return blocked_browsing_data_model_.get();
  }

  void OnContentBlocked(ContentSettingsType type);
  void OnContentAllowed(ContentSettingsType type);

  // Call when a two-site permission was prompted or modified in order to
  // display a ContentSettingsImageModel icon.
  void OnTwoSitePermissionChanged(ContentSettingsType type,
                                  net::SchemefulSite requesting_site,
                                  ContentSetting content_setting);
  void OnInterestGroupJoined(const url::Origin& api_origin,
                             bool blocked_by_policy);
  void OnTrustTokenAccessed(const url::Origin& api_origin, bool blocked);
  void OnBrowsingDataAccessed(BrowsingDataModel::DataKey data_key,
                              BrowsingDataModel::StorageType storage_type,
                              bool blocked,
                              content::Page* originating_page = nullptr);

#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
  void OnProtectedMediaIdentifierPermissionSet(const GURL& requesting_frame,
                                               bool allowed);
#endif

  // This method is called to update the status about the microphone and
  // camera stream access.
  void OnMediaStreamPermissionSet(
      const GURL& request_origin,
      MicrophoneCameraState new_microphone_camera_state);

  // |originating_page| is non-null when it differs from page(), which happens
  // when an embedding page's PSCS is notified of an access that happens in an
  // embedded page (through |MaybeUpdateParent|).
  void OnCookiesAccessed(const content::CookieAccessDetails& details,
                         content::Page* originating_page = nullptr);
  void OnServiceWorkerAccessed(const GURL& scope,
                               const blink::StorageKey& storage_key,
                               content::AllowServiceWorkerResult allowed,
                               content::Page* originating_page = nullptr);

  // Block all content. Used for testing content setting bubbles.
  void BlockAllContentForTesting();

  static void SetIgnoreBlockedMediaIndicatorTimerForTesting(bool ignore);

  // Stores content settings changed by the user via PageInfo.
  void ContentSettingChangedViaPageInfo(ContentSettingsType type);

  // Returns true if the user changed the given ContentSettingsType via PageInfo
  // since the last navigation.
  bool HasContentSettingChangedViaPageInfo(ContentSettingsType type) const;

  // Returns true if the user was joined to an interest group and if the page
  // is the joining origin.
  bool HasJoinedUserToInterestGroup() const;

  // Runs any queued updates in |updates_queued_during_prerender_|, should be
  // called after the page activates.
  void OnPrerenderingPageActivation();

  // This method is called when audio or video capturing is started or finished.
  void OnCapturingStateChanged(ContentSettingsType type, bool is_capturing);

#if BUILDFLAG(IS_CHROMEOS)
  // This method is called every time when device capabilities (currently, only
  // Smart Cards) are used to communicate with a device. Because device
  // connections can be long-lived, this method may be called multiple times
  // during a connection.
  void OnDeviceUsed(ContentSettingsType type);
  // This is called when the last connection from this page to devices of `type`
  // is closed.
  void OnLastDeviceConnectionLost(ContentSettingsType type);
#endif

  // Returns true if a page is currently using a feature gated behind `type`
  // permission. Returns false otherwise.
  bool IsInUse(ContentSettingsType type) const;

#if BUILDFLAG(IS_CHROMEOS)
  // This is specific to the object based content settings (for now, just smart
  // cards) - they should show for 15 more seconds after `IsInUse` turns false.
  bool ShouldShowDeviceInUseIndicator(ContentSettingsType type) const;
#endif

  // Returns a time of last usage of a feature gated behind `type` permission.
  // Returns base::Time() if `type` was not used in the last 24 hours.
  const base::Time GetLastUsedTime(ContentSettingsType type) const;

  // This method is called when audio or video activity indicator is opened.
  void OnActivityIndicatorBubbleOpened(ContentSettingsType type);

  // This method is called when audio or video activity indicator is closed.
  void OnActivityIndicatorBubbleClosed(ContentSettingsType type);

  bool IsIndicatorVisible(ContentSettingsType type) const;
  // Returns `true` if an activity indicator is displaying for any of the
  // provided `types`. Returns `false` otherwise.
  bool IsAnyIndicatorVisible(base::span<const ContentSettingsType> types) const;
  // Save `ContentSettingsType` to a set of currently displaying activity
  // indicators.
  void OnPermissionIndicatorShown(ContentSettingsType type);
  // Remove `ContentSettingsType` from a set of currently displaying activity
  // indicators.
  void OnPermissionIndicatorHidden(ContentSettingsType type);

  // If permission requests need to be cleaned up due to a page refresh, PSCS
  // should be temporarily frozen to prevent unnecessary update of activity
  // indicators.
  void OnPermissionRequestCleanupStart() { freeze_indicators_ = true; }
  void OnPermissionRequestCleanupEnd() { freeze_indicators_ = false; }

  // This method resets a media blocked state for `type`. If `update_indicators`
  // is true, then it will try to update activity indicators in the location
  // bar.
  void ResetMediaBlockedState(ContentSettingsType type, bool update_indicators);

  // Called to update the location bar when a site first registers for automatic
  // picture-in-picture.
  void OnRegisteredForAutoPictureInPictureChanged();

  void set_media_stream_access_origin_for_testing(const GURL& url) {
    media_stream_access_origin_ = url;
  }

  void set_last_used_time_for_testing(ContentSettingsType type,
                                      base::Time time) {
    last_used_time_[type] = time;
  }

  std::map<ContentSettingsType, base::OneShotTimer>&
  get_media_blocked_indicator_timer_for_testing() {
    return media_blocked_indicator_timer_;
  }

  std::map<ContentSettingsType, base::OneShotTimer>&
  get_indicators_hiding_delay_timer_for_testing() {
    return indicators_hiding_delay_timer_;
  }

 private:
  friend class content::PageUserData<PageSpecificContentSettings>;

  struct PendingUpdates {
    PendingUpdates();
    ~PendingUpdates();

    std::vector<base::OnceClosure> delegate_updates;
    bool site_data_accessed = false;
    AccessDetails access_details;
  };

  explicit PageSpecificContentSettings(content::Page& page, Delegate* delegate);

  // Updates `microphone_camera_state_` after audio/video is started/finished.
  void OnCapturingStateChangedInternal(ContentSettingsType type,
                                       bool is_capturing);

  // This method is called when a camera and/or mic blocked indicator is
  // displayed.
  void StartBlockedIndicatorTimer(ContentSettingsType type);

  // content_settings::Observer implementation.
  void OnContentSettingChanged(const ContentSettingsPattern& primary_pattern,
                               const ContentSettingsPattern& secondary_pattern,
                               ContentSettingsType content_type) override;

  // Clears settings changed by the user via PageInfo since the last navigation.
  void ClearContentSettingsChangedViaPageInfo();

  bool IsPagePrerendering() const;
  bool IsEmbeddedPage() const;

  // Delays the call of the delegate method if the page is currently
  // prerendering until the page is activated; directly calls the method
  // otherwise.
  template <typename DelegateMethod, typename... Args>
  void NotifyDelegate(DelegateMethod method, Args... args) {
    if (IsEmbeddedPage()) {
      return;
    }
    if (IsPagePrerendering()) {
      DCHECK(updates_queued_during_prerender_);
      updates_queued_during_prerender_->delegate_updates.emplace_back(
          base::BindOnce(method, base::Unretained(delegate_), args...));
      return;
    }
    (*delegate_.*method)(args...);
  }
  // Used to notify the parent page's PSCS of a content access.
  template <typename PSCSMethod, typename... Args>
  void MaybeUpdateParent(PSCSMethod method, Args... args) {
    if (IsEmbeddedPage()) {
      PageSpecificContentSettings* pscs =
          PageSpecificContentSettings::GetForFrame(
              page().GetMainDocument().GetParentOrOuterDocument());
      DCHECK(pscs);
      (*pscs.*method)(args...);
    }
  }

  // Notifies observers. Like |NotifyDelegate|, the notification is delayed for
  // prerendering pages until the page is activated. Embedded pages will not
  // notify observers directly and rely on the outermost page to do so.
  void MaybeNotifySiteDataObservers(const AccessDetails& access_details);

  // Tells the delegate to update the location bar. This method is a no-op if
  // the page is currently prerendering or is embedded.
  void MaybeUpdateLocationBar();

  content::WebContents* GetWebContents() const;

  // Document picture-in-picture allows changing content settings in both the
  // browser window and the PiP window. When the settings is changed in one
  // place, return the settings in another place to be synced as well. We should
  // update settings in either place at most once, so we will avoid getting into
  // deadlock by using |is_updating_synced_pscs_|.
  PageSpecificContentSettings* MaybeGetSyncedSettingsForPictureInPicture();

  // An auto reset variable to make sure we do not get into deadlock when
  // updating synced PageSpecificContentSettings for the document
  // picture-in-picture case.
  bool is_updating_synced_pscs_ = false;

  // If `false` PSCS is allowed to save a new blocked state for an activity
  // indicator. If `true` PSCS is frozen and a new blocked state will be
  // ignored. This variable is controlled by PermissionRequestManager to prevent
  // showing indicators after requests were cleaned up due to a new navigation.
  bool freeze_indicators_ = false;

  raw_ptr<Delegate> delegate_;

  struct ContentSettingsStatus {
    bool blocked;
    bool allowed;
  };
  // Stores which content setting types actually have blocked content.
  std::map<ContentSettingsType, ContentSettingsStatus> content_settings_status_;

  // Stores embedded sites that requested a permission. Only applies to
  // permissions that are scoped to two sites, e.g. StorageAccess.
  std::map<ContentSettingsType, std::map<net::SchemefulSite, bool>>
      content_settings_two_site_requests_;

  // Profile-bound, this will outlive this class (which is WebContents bound).
  raw_ptr<HostContentSettingsMap> map_;

  // Stores the count of stateful bounces during the navigation that led to this
  // page.
  int stateful_bounce_count_ = 0u;

  std::unique_ptr<BrowsingDataModel> allowed_browsing_data_model_;
  std::unique_ptr<BrowsingDataModel> blocked_browsing_data_model_;

  // The origin of the media stream request. Note that we only support handling
  // settings for one request per tab. The latest request's origin will be
  // stored here. http://crbug.com/259794
  // TODO(crbug.com/40276922): Remove `media_stream_access_origin_` and
  // calculate a proper origin internaly.
  GURL media_stream_access_origin_;

  // The microphone and camera state at the last media stream request.
  MicrophoneCameraState microphone_camera_state_;

  // Contains URLs which attempted to join interest groups. Note: The UI will
  // only currently show the top frame as having attempted to join.
  std::vector<url::Origin> allowed_interest_group_api_;
  std::vector<url::Origin> blocked_interest_group_api_;

  // The Geolocation, camera, and/or microphone permission was granted to this
  // origin from a permission prompt that was triggered by the currently active
  // document.
  bool camera_was_just_granted_on_site_level_ = false;
  bool mic_was_just_granted_on_site_level_ = false;
  bool geolocation_was_just_granted_on_site_level_ = false;

  bool notifications_was_denied_because_of_system_permission_ = false;

  // The time when the media indicator was displayed.
  base::TimeTicks media_indicator_time_;

  // Stores timers for delaying hiding an activity indicators.
  std::map<ContentSettingsType, base::OneShotTimer>
      indicators_hiding_delay_timer_;
  // Stores last used time when a permission-gate feature is no longer in use.
  std::map<ContentSettingsType, base::Time> last_used_time_;
  // Stores `ContentSettingsType` that is currently used by a page.
  std::set<ContentSettingsType> in_use_;

  // A timer to removed a blocked media indicator.
  std::map<ContentSettingsType, base::OneShotTimer>
      media_blocked_indicator_timer_;

  // Stores `ContentSettingsType` that is currently displaying. It is used only
  // for the Left-Hand Side indicators.
  std::set<ContentSettingsType> visible_indicators_;

  // True if at least one sensor requested by the page is available.
  // We use a single boolean instead of a map because the UI
  // indicator is generic ("Sensors") and doesn't distinguish between specific
  // sensor types.
  bool any_requested_sensor_is_available_ = false;
  int active_available_sensors_ = 0;

  // Observer to watch for content settings changed.
  base::ScopedObservation<HostContentSettingsMap, content_settings::Observer>
      observation_{this};

  // Stores content settings changed by the user via page info since the last
  // navigation. Used to determine whether to display the settings in page info.
  std::set<ContentSettingsType> content_settings_changed_via_page_info_;

  // Calls to |delegate_| and SiteDataObservers that have been queued up while
  // the page is prerendering. These calls are run when the page is activated.
  std::unique_ptr<PendingUpdates> updates_queued_during_prerender_;

  base::ObserverList<PermissionUsageObserver> permission_usage_observers_;

  PAGE_USER_DATA_KEY_DECL();

  base::WeakPtrFactory<PageSpecificContentSettings> weak_factory_{this};
};

}  // namespace content_settings

#endif  // COMPONENTS_CONTENT_SETTINGS_BROWSER_PAGE_SPECIFIC_CONTENT_SETTINGS_H_
