// 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_REQUEST_MANAGER_H_
#define COMPONENTS_PERMISSIONS_PERMISSION_REQUEST_MANAGER_H_

#include <algorithm>
#include <list>
#include <map>
#include <memory>
#include <optional>
#include <utility>
#include <vector>

#include "base/callback_list.h"
#include "base/check_is_test.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ref.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/permissions/embedded_permission_prompt_flow_model.h"
#include "components/permissions/features.h"
#include "components/permissions/permission_prompt.h"
#include "components/permissions/permission_request_queue.h"
#include "components/permissions/permission_uma_constants.h"
#include "components/permissions/prediction_service/permission_ui_selector.h"
#include "components/permissions/request_type.h"
#include "components/permissions/resolvers/permission_prompt_options.h"
#include "components/tabs/public/tab_interface.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
#include "ui/gfx/geometry/rect.h"

class GURL;

namespace content {
class RenderFrameHost;
}

namespace test {
class PermissionRequestManagerTestApi;
class MockPermissionRequestManager;
}  // namespace test

namespace permissions {
class EmbeddedPermissionPromptFlowModel;
class PermissionRequest;
enum class PermissionAction;
enum class PermissionPromptDisposition;
enum class PermissionPromptDispositionReason;

// The message to be printed in the Developer Tools console when the quiet
// notification permission prompt UI is shown on sites with abusive permission
// request flows.
extern const char kAbusiveNotificationRequestsEnforcementMessage[];

// The message to be printed in the Developer Tools console when the site is on
// the warning list for abusive permission request flows.
extern const char kAbusiveNotificationRequestsWarningMessage[];

// The message to be printed in the Developer Tools console when the site is on
// the blocking list for showing abusive notification content.
extern const char kAbusiveNotificationContentEnforcementMessage[];

// The message to be printed in the Developer Tools console when the site is on
// the warning list for showing abusive notification content.
extern const char kAbusiveNotificationContentWarningMessage[];

// The message to be printed in the Developer Tools console when the
// Notification permission request was suppressed and shown as a quiet prompt
// because it was not initiated with a user gesture.
extern const char kGestureGatedNotificationMessage[];

// The message to be printed in the Developer Tools console when the
// Geolocation permission request was suppressed and shown as a quiet prompt
// because it was not initiated with a user gesture.
extern const char kGestureGatedGeolocationMessage[];

// Provides access to permissions bubbles. Allows clients to add a request
// callback interface to the existing permission bubble configuration.
// Depending on the situation and policy, that may add new UI to an existing
// permission bubble, create and show a new permission bubble, or provide no
// visible UI action at all. (In that case, the request will be immediately
// informed that the permission request failed.)
//
// A PermissionRequestManager is associated with a particular WebContents.
// Requests attached to a particular WebContents' PBM must outlive it.
//
// The PermissionRequestManager should be addressed on the UI thread.
class PermissionRequestManager
    : public content::WebContentsObserver,
      public content::WebContentsUserData<PermissionRequestManager>,
      public PermissionPrompt::Delegate {
 public:
  class Observer : public base::CheckedObserver {
   public:
    virtual void OnTabActiveChanged(bool is_active) {}
    virtual void OnPromptAdded() {}
    virtual void OnPromptRemoved() {}
    // Called when recreation of the permission prompt is not possible. It means
    // that `PermissionRequestManager` is ready to display a prompt but the UI
    // layer was not able to display it.
    virtual void OnPromptRecreateViewFailed() {}
    // Called when permission prompt creation was aborted because the current
    // tab is no longer visible, hance it is not possible to display a prompt.
    virtual void OnPromptCreationFailedHiddenTab() {}
    // Called when the current batch of requests have been handled and the
    // prompt is no longer visible. Note that there might be some queued
    // permission requests that will get shown after this. This differs from
    // OnPromptRemoved() in that the prompt may disappear while there are
    // still in-flight requests (e.g. when switching tabs while the prompt is
    // still visible).
    virtual void OnRequestsFinalized() {}

    virtual void OnPermissionRequestManagerDestructed() {}
    virtual void OnNavigation(content::NavigationHandle* navigation_handle) {}

    virtual void OnRequestDecided(permissions::PermissionAction action) {}
  };

  enum AutoResponseType { NONE, ACCEPT_ONCE, ACCEPT_ALL, DENY_ALL, DISMISS };

  using UiDecision = PermissionUiSelector::Decision;
  using QuietUiReason = PermissionUiSelector::QuietUiReason;
  using WarningReason = PermissionUiSelector::WarningReason;

  ~PermissionRequestManager() override;

  // Takes ownership of a new request and adds it to the permission bubble. The
  // PermissionRequestManager will destroy the request after handling it.
  // If a bubble is visible when this call is made, the request will be queued
  // up and shown after the current bubble closes. A request with message text
  // identical to an outstanding request will be merged with the outstanding
  // request, and will have the same callbacks called as the outstanding
  // request.
  void AddRequest(content::RenderFrameHost* source_frame,
                  std::unique_ptr<PermissionRequest> request);

  // Will reposition the bubble (may change parent if necessary).
  void UpdateAnchor();

  // For observing the status of the permission bubble manager.
  void AddObserver(Observer* observer);
  void RemoveObserver(Observer* observer);

  bool IsRequestInProgress() const;

  // Returns `true` if a permission request is in progress but a prompt view is
  // nullptr.
  bool CanRestorePrompt();

  // Recreates a permission prompt.
  void RestorePrompt();

  // Do NOT use these methods in production code. Use these methods in browser
  // tests that need to accept or deny permissions when requested in
  // JavaScript. Your test needs to set this appropriately, and then the bubble
  // will proceed as desired as soon as Show() is called.
  void set_auto_response_for_test(AutoResponseType response) {
    auto_response_for_test_ = response;
  }
  void set_auto_response_prompt_options_for_test(PromptOptions prompt_options) {
    CHECK_NE(auto_response_for_test_, AutoResponseType::NONE)
        << "Call set_auto_response_for_test() before calling "
           "set_auto_response_prompt_options_for_test, since this does not "
           "have any effect otherwise.";
    auto_response_prompt_options_for_test_ = std::move(prompt_options);
  }

  void SwitchToLoudPrompt() override;

  // WebContentsObserver:
  void DidStartNavigation(
      content::NavigationHandle* navigation_handle) override;
  void DidFinishNavigation(
      content::NavigationHandle* navigation_handle) override;
  void DocumentOnLoadCompletedInPrimaryMainFrame() override;
  void DOMContentLoaded(content::RenderFrameHost* render_frame_host) override;
  void WebContentsDestroyed() override;
  void OnVisibilityChanged(content::Visibility visibility) override;

  // PermissionPrompt::Delegate:
  const std::vector<std::unique_ptr<PermissionRequest>>& Requests()
      const override;
  GURL GetRequestingOrigin() const override;
  GURL GetEmbeddingOrigin() const override;
  void Accept(const PromptOptions& prompt_options) override;
  void AcceptThisTime(const PromptOptions& prompt_options) override;
  void Deny(const PromptOptions& prompt_options) override;
  void Dismiss(const PromptOptions& prompt_options) override;
  void Ignore(const PromptOptions& prompt_options) override;
  void FinalizeCurrentRequests() override;
  void OpenHelpCenterLink(const ui::Event& event) override;
  void PreIgnoreQuietPrompt() override;
  bool WasCurrentRequestAlreadyDisplayed() override;
  bool ShouldDropCurrentRequestIfCannotShowQuietly() const override;
  bool ShouldCurrentRequestUseQuietUI() const override;
  std::optional<PermissionUiSelector::QuietUiReason> ReasonForUsingQuietUi()
      const override;
  void SetDismissOnTabClose() override;
  void SetPromptShown() override;
  GeolocationAccuracy GetInitialGeolocationAccuracySelection() const override;
  std::optional<GeolocationPromptType> GetGeolocationPromptType()
      const override;
  void SetDecisionTime() override;
  void SetManageClicked() override;
  void SetLearnMoreClicked() override;
  base::WeakPtr<PermissionPrompt::Delegate> GetWeakPtr() override;
  content::WebContents* GetAssociatedWebContents() override;
  bool RecreateView() override;
  const PermissionPrompt* GetCurrentPrompt() const override;
  EmbeddedPermissionPromptFlowModel* GetEmbeddedPromptFlowModel()
      const override;
  void CalculateCurrentVariantForEmbeddedPrompt() override;
  void AdvanceOrFinalizeEmbeddedPromptFlow() override;

  // Returns the bounds of the active permission prompt view if we're
  // displaying one.
  std::optional<gfx::Rect> GetPromptBubbleViewBoundsInScreen() const;

  void set_manage_clicked() { did_click_manage_ = true; }
  void set_learn_more_clicked() { did_click_learn_more_ = true; }

  void set_web_contents_supports_permission_requests(
      bool web_contents_supports_permission_requests) {
    web_contents_supports_permission_requests_ =
        web_contents_supports_permission_requests;
  }

  // For testing only, used to override the default UI selectors and instead use
  // a new one.
  void set_permission_ui_selector_for_testing(
      std::unique_ptr<PermissionUiSelector> selector) {
    clear_permission_ui_selector_for_testing();
    add_permission_ui_selector_for_testing(std::move(selector));
  }

  // For testing only, used to add a new selector without overriding the
  // existing ones.
  void add_permission_ui_selector_for_testing(
      std::unique_ptr<PermissionUiSelector> selector) {
    permission_ui_selectors_.emplace_back(std::move(selector));
  }

  // For testing only, clear the existing ui selectors.
  void clear_permission_ui_selector_for_testing() {
    permission_ui_selectors_.clear();
  }

  // Getter for testing.
  const std::vector<std::unique_ptr<PermissionUiSelector>>&
  get_permission_ui_selectors_for_testing() {
    return permission_ui_selectors_;
  }

  void set_view_factory_for_testing(PermissionPrompt::Factory view_factory) {
    view_factory_ = std::move(view_factory);
  }

  void set_current_request_first_display_time_for_testing(base::Time time) {
    current_request_first_display_time_ = time;
  }

  void set_notification_request_first_display_time_for_testing(
      base::TimeTicks time) {
    notification_request_first_display_time_ = time;
  }

  void set_geolocation_request_first_display_time_for_testing(
      base::TimeTicks time) {
    geolocation_request_first_display_time_ = time;
  }

  void set_on_page_loaded_time_for_testing(base::TimeTicks time) {
    on_page_loaded_time_ = time;
  }

  std::optional<PermissionUiSelector::PredictionGrantLikelihood>
  prediction_grant_likelihood_for_testing() const {
    return prediction_grant_likelihood_;
  }

  std::optional<bool> was_decision_held_back_for_testing() const {
    return was_decision_held_back_;
  }

  std::optional<PermissionRequestRelevance>
  permission_request_relevance_for_testing() const {
    return permission_request_relevance_;
  }

  std::optional<permissions::PermissionPromptDisposition>
  current_request_prompt_disposition_for_testing() const {
    return current_request_prompt_disposition_;
  }

  void set_time_to_decision_for_test(base::TimeDelta time_to_decision) {
    time_to_decision_for_test_ = time_to_decision;
  }

  void set_enabled_app_level_notification_permission_for_testing(bool enabled) {
    enabled_app_level_notification_permission_for_testing_ = enabled;
  }

  void set_embedding_origin_for_testing(const GURL& embedding_origin) {
    embedding_origin_for_testing_ = embedding_origin;
  }

  void set_requesting_origin_for_testing(const GURL& requesting_origin) {
    requesting_origin_for_testing_ = requesting_origin;
  }

  base::ObserverList<
      Observer,
      /*check_empty=*/false,
      /*allow_reentrancy=*/
      base::ObserverListReentrancyPolicy::kAllowReentrancyUntriaged>*
  get_observer_list_for_testing() {
    CHECK_IS_TEST();
    return &observer_list_;
  }

  bool has_pending_requests() {
    return !pending_permission_requests_.IsEmpty();
  }

  void SetHatsShownCallback(base::OnceCallback<void()> callback) override;

  // For permissions that have visible views, we should only record
  // PromptResolved metrics, for ask prompts.
  bool ShouldRecordUmaForCurrentPrompt() const;

#if BUILDFLAG(IS_ANDROID)
  bool has_requested_notifications() const {
    return has_requested_notifications_;
  }
#endif  // BUILDFLAG(IS_ANDROID)

 private:
  friend class test::PermissionRequestManagerTestApi;
  friend class test::MockPermissionRequestManager;
  friend class content::WebContentsUserData<PermissionRequestManager>;
  FRIEND_TEST_ALL_PREFIXES(PermissionRequestManagerTest, WeakDuplicateRequests);
  FRIEND_TEST_ALL_PREFIXES(PermissionRequestManagerTest,
                           WeakDuplicateRequestsAccept);

  explicit PermissionRequestManager(content::WebContents* web_contents);

  // Defines how to handle the current request, when new requests arrive
  enum class CurrentRequestFate {
    // Keep showing the current request. The incoming requests should not take
    // priority over the current request and will be pushed to pending requests
    // queue.
    kKeepCurrent,

    // Put the current request back to the pending requests queue for displaying
    // later when it returns to the front of the queue.
    kPreempt,

    // Finalize/ignore the current request and show the new request.
    kFinalize
  };

  // Reprioritize the current requests (preempting, finalizing) based on what
  // type of UI has been shown for `requests_` and current pending requests
  // queue.
  // Determine the next request candidate would be prompted later and push the
  // candidate to front of the pending queue.
  // Return true if we keep showing the current request, otherwise return false
  bool ReprioritizeCurrentRequestIfNeeded();

  // Returns true if the request's source frame can be found and is active.
  // This means, its render frame host is committed and lives inside a page
  // presented to the user, i.e. its not a prerendered or back-forward cached
  // page. Side effect: In the case of the RenderFrameHost is inactive, this
  // ensures it will be never activated through the following:
  //
  // - For BackForwardCache: it evicts the document from the cache and
  //   triggers deletion.
  // - For Prerendering: it cancels prerendering and triggers deletion.
  //
  //  For more information see
  //  RenderFrameHost::IsInactiveAndDisallowActivation()

  bool HasActiveSourceFrameOrDisallowActivationOtherwise(
      const PermissionRequest& request) const;

  // Cancels a request and removes it from |request_sources_map_| and
  // |validated_requests_|.
  void FinalizeAndCancelRequest(PermissionRequest& request);

  // Adds `request` into `pending_permission_requests_`, and request's
  // `source_frame` into `request_sources_map_`.
  void QueueRequest(content::RenderFrameHost* source_frame,
                    std::unique_ptr<PermissionRequest> request);

  // Because the requests are shown in a different order for Normal and Quiet
  // Chip, pending requests are returned back to pending_permission_requests_ to
  // process them after the new requests.
  void PreemptAndRequeueCurrentRequest();

  // If a request isn't already in progress, dequeue and show the request
  // prompt.
  void DequeueRequestIfNeeded();

  // Schedule a call to |DequeueRequestIfNeeded|. Is needed to ensure requests
  // that can be grouped together have time to all be added to the queue.
  void ScheduleDequeueRequestIfNeeded();

  // Shows the prompt for a request that has just been dequeued, or re-show a
  // prompt after switching tabs away and back.
  void ShowPrompt();

  // Delete the view object
  void DeletePrompt();

  // Finalize request.
  void ResetViewStateForCurrentRequest();

  // Records metrics and informs embargo and autoblocker about the requests
  // being decided and calls |FinalizeCurrentRequests()| or advances embedded
  // flow.
  void CurrentRequestsDecided(PermissionAction permission_action,
                              const PromptOptions& prompt_options);

  // Cancel all pending or active requests and destroy the PermissionPrompt if
  // one exists. This is called if the WebContents is destroyed or navigates its
  // main frame.
  void CleanUpRequests();

  // Searches |requests_| and |pending_permission_requests_| - but *not*
  // |duplicate_requests_| - for a request matching |request|, and returns the
  // matching request, or |nullptr| if no match. Note that the matching request
  // may or may not be the same object as |request|.
  PermissionRequest* GetExistingRequest(PermissionRequest* request) const;

  // Returns an iterator into |duplicate_requests_|, points the matching list,
  // or duplicate_requests_.end() if no match. The matching list contains all
  // the weak requests which are duplicate of the given |request| (see
  // |IsDuplicateOf|)
  using PermissionRequestList =
      std::list<std::list<std::unique_ptr<PermissionRequest>>>;
  PermissionRequestList::iterator FindDuplicateRequestList(
      PermissionRequest* request);

  // Trigger |visitor| for each live weak request which matches the given
  // request (see |IsDuplicateOf|) in the |duplicate_requests_|. Returns an
  // iterator into |duplicate_requests_|, points the matching list, or
  // duplicate_requests_.end() if no match.
  using DuplicateRequestVisitor =
      base::RepeatingCallback<void(const std::unique_ptr<PermissionRequest>&)>;
  PermissionRequestList::iterator VisitDuplicateRequests(
      DuplicateRequestVisitor visitor,
      PermissionRequest* request);

  // Calls PermissionGranted on a request and all its duplicates.
  void PermissionGrantedIncludingDuplicates(PermissionRequest* request,
                                            const PromptOptions& prompt_options,
                                            bool is_one_time);
  // Calls PermissionDenied on a request and all its duplicates.
  void PermissionDeniedIncludingDuplicates(PermissionRequest* request);
  // Calls Cancelled on a request and all its duplicates.
  void CancelRequestIncludingDuplicates(PermissionRequest* request,
                                        bool is_final_decision = true);
  // Erases a request and all its duplicates.
  void FinishRequestIncludingDuplicates(PermissionRequest* request);

  void NotifyTabActiveChanged(bool is_active);
  void NotifyPromptAdded();
  void NotifyPromptRemoved();
  void NotifyPromptRecreateFailed();
  void NotifyPromptCreationFailedHiddenTab();
  void NotifyRequestDecided(permissions::PermissionAction permission_action);

  void StorePermissionActionForUMA(const GURL& origin,
                                   RequestType request_type,
                                   PermissionAction permission_action);

  void OnPermissionUiSelectorDone(size_t selector_index,
                                  const UiDecision& decision);
  std::optional<UiDecision> TakePermissionUiDecisionIfReady();

  PermissionPromptDisposition DetermineCurrentRequestUIDisposition();
  PermissionPromptDispositionReason
  DetermineCurrentRequestUIDispositionReasonForUMA();

  void LogWarningToConsole(const char* message);

  void DoAutoResponseForTesting();

  void PreIgnoreQuietPromptInternal();

  // Returns true if there is a request in progress that is for exclusive
  // access.
  bool IsCurrentRequestExclusiveAccess() const;

  // Calculate and record the PermissionEmbargoStatus.
  PermissionEmbargoStatus RecordActionAndGetEmbargoStatus(
      content::BrowserContext* browser_context,
      PermissionRequest* request,
      PermissionAction permission_action);

  // Take a snapshot of the content setting status for the current requests,
  // which can change based on the user's decision. Used in HaTS as a filter.
  // This defaults to "DEFAULT" if there's no ContentSettingsType associated
  // with the PermissionType.
  void SetCurrentRequestsInitialStatuses();

  ContentSetting GetRequestInitialStatus(PermissionRequest* request);

  // Adopts a TabInterface if not already adopted (e.g. for discarded or
  // prerendered tabs where TabInterface lookup is created after helper
  // attachment).
  void AdoptTabInterfaceIfNeeded(tabs::TabInterface* tab_interface);

  void RegisterTabSubscriptions(tabs::TabInterface* tab_interface);
  void OnTabActiveStatusChanged(bool is_active,
                                tabs::TabInterface* tab_interface);
  void OnTabDetached(tabs::TabInterface* tab_interface,
                     tabs::TabInterface::DetachReason reason);
  void OnTabWillDiscardContents(tabs::TabInterface* tab_interface,
                                content::WebContents* old_contents,
                                content::WebContents* new_contents);
  void OnTabAttached(tabs::TabInterface* tab_interface);
  void OnTabActiveChanged();

  void RecordPostPromptSessionDuration();

  // Factory to be used to create views when needed.
  PermissionPrompt::Factory view_factory_;

  // The UI surface for an active permission prompt if we're displaying one.
  // On Desktop, we destroy this upon tab switching, while on Android we keep
  // the object alive. The infobar system hides the actual infobar UI and modals
  // prevent tab switching.
  std::unique_ptr<PermissionPrompt> view_;
  std::unique_ptr<EmbeddedPermissionPromptFlowModel>
      embedded_prompt_flow_model_;

  // The disposition for the currently active permission prompt, if any.
  // Recorded separately because the `view_` might not be available at prompt
  // resolution in order to determine the disposition.
  std::optional<permissions::PermissionPromptDisposition>
      current_request_prompt_disposition_;

  // We only show new prompts when |tab_is_active_| is true.
  bool tab_is_active_;

  // Holds subscriptions for TabInterface active and visible callbacks.
  std::vector<base::CallbackListSubscription> tab_subscriptions_;
  // Holds subscriptions for TabInterface attach and detach callbacks.
  base::CallbackListSubscription tab_insert_subscription_;

  // The request (or requests) that the user is currently being prompted for.
  // When this is non-empty, the |view_| is generally non-null as long as the
  // tab is visible.
  std::vector<std::unique_ptr<PermissionRequest>> requests_;

  struct PermissionRequestSource {
    content::GlobalRenderFrameHostId requesting_frame_id;

    bool IsSourceFrameInactiveAndDisallowActivation() const;
  };

  PermissionRequestQueue pending_permission_requests_;

  // Stores the unique pointers of duplicated requests in a 2D list.
  PermissionRequestList duplicate_requests_;

  // Maps each PermissionRequest currently in |requests_| or
  // |pending_permission_requests_| to which RenderFrameHost it originated from.
  // Note that no date is stored for |duplicate_requests_|.
  std::map<base::raw_ref<PermissionRequest>, PermissionRequestSource>
      request_sources_map_;

  // Sequence of requests from pending queue will be marked as validated, when
  // we are extracting a group of requests from the queue to show to user. This
  // is an immature solution to avoid an infinitive loop of preempting, we would
  // not prempt a request if the incoming request is already validated.
  std::vector<base::raw_ref<PermissionRequest>> validated_requests_;

  // TODO(crbug.com/484371187): Investigate if reentrancy can be removed.
  base::ObserverList<
      Observer,
      /*check_empty=*/false,
      /*allow_reentrancy=*/
      base::ObserverListReentrancyPolicy::kAllowReentrancyUntriaged>
      observer_list_;
  AutoResponseType auto_response_for_test_ = NONE;
  PromptOptions auto_response_prompt_options_for_test_ = std::monostate();

  // Suppress notification permission prompts in this tab, regardless of the
  // origin requesting the permission.
  bool is_notification_prompt_cooldown_active_ = false;

  // Whether the current page is the result of a same-origin navigation.
  bool had_same_origin_navigation_ = false;

  // A vector of selectors which decide if the quiet prompt UI should be used
  // to display permission requests. Sorted from the highest priority to the
  // lowest priority selector.
  std::vector<std::unique_ptr<PermissionUiSelector>> permission_ui_selectors_;

  // Holds the decisions returned by selectors. Needed in case a lower priority
  // selector returns a decision first and we need to wait for the decisions of
  // higher priority selectors before making use of it.
  std::vector<std::optional<PermissionUiSelector::Decision>>
      selector_decisions_;

  // Whether the view for the current |requests_| has been shown to the user at
  // least once.
  bool current_request_already_displayed_ = false;

  // When the view for the current |requests_| has been first shown to the user,
  // or zero if not at all.
  base::Time current_request_first_display_time_;

  // When the view for the current |requests_| has been first shown to the user,
  // or zero if not at all, specifically for a NOTIFICATIONS request.
  base::TimeTicks notification_request_first_display_time_;

  // When the view for the current |requests_| has been first shown to the user,
  // or zero if not at all, specifically for a GEOLOCATION request.
  base::TimeTicks geolocation_request_first_display_time_;

  // When the page was loaded, or zero if not at all.
  // This is used to record the duration of the browsing session before a
  // permission prompt was displayed.
  base::TimeTicks on_page_loaded_time_;

  // Whether to use the normal or quiet UI to display the current permission
  // |requests_|, and whether to show warnings. This will be nullopt if we are
  // still waiting on the result from |permission_ui_selectors_|.
  std::optional<UiDecision> current_request_ui_to_use_;

  // The likelihood value returned by the Web Permission Predictions Service,
  // to be recorded in UKM.
  std::optional<PermissionUiSelector::PredictionGrantLikelihood>
      prediction_grant_likelihood_;

  // The permission request relevance returned by an on-device ML model,
  // to be recorded in UKM.
  std::optional<PermissionRequestRelevance> permission_request_relevance_;

  // The AI model version used for the permission decision, to be recorded in
  // UKM.
  std::optional<permissions::PermissionAiRelevanceModel>
      permission_ai_relevance_model_;

  // Status of the decision made by the Web Permission Prediction Service, if
  // it was held back or not.
  std::optional<bool> was_decision_held_back_;

  // True when the prompt is being temporary destroyed to be recreated for the
  // correct browser or when the tab is hidden. In those cases, callbacks from
  // the bubble itself should be ignored.
  bool ignore_callbacks_from_prompt_ = false;

  // Whether the web contents associated with this request manager supports
  // permission prompts.
  bool web_contents_supports_permission_requests_ = true;

  // Whether the current request should be dismissed if the current tab is
  // closed.
  bool should_dismiss_current_request_ = false;

  // Whether the permission prompt was shown for the current request.
  bool did_show_prompt_ = false;

  // When the user made any decision for the current |requests_|, or zero if not
  // at all.
  base::Time current_request_decision_time_;

  bool did_click_manage_ = false;

  bool did_click_learn_more_ = false;

  // Whether the current request can be preempted or not. This is set when
  // callbacks are being issued to prevent potential re-entrant behavior of
  // those callbacks requesting a permission that would preempt the current one
  // and thus invalidate the iterator being used to issue the callback.
  bool can_preempt_current_request_ = true;

  std::optional<base::TimeDelta> time_to_decision_for_test_;

  std::optional<bool> enabled_app_level_notification_permission_for_testing_;

  std::optional<GURL> embedding_origin_for_testing_;

  std::optional<GURL> requesting_origin_for_testing_;

  // A timer is used to pre-ignore the permission request if it's been displayed
  // as a quiet chip.
  base::OneShotTimer preignore_timer_;

  std::optional<base::OnceCallback<void()>> hats_shown_callback_;

  // Holds the position of the current prompt, only relevant for permission
  // element prompts.
  std::optional<feature_params::PermissionElementPromptPosition>
      current_request_pepc_prompt_position_;

  // Holds the initial statuses of the current requests, one for each request in
  // |requests_|.
  std::map<PermissionRequest*, ContentSetting>
      current_requests_initial_statuses_;

#if BUILDFLAG(IS_ANDROID)
  // Whether the current page already requested notification permission.
  bool has_requested_notifications_ = false;
#endif  // BUILDFLAG(IS_ANDROID)

  base::WeakPtrFactory<PermissionRequestManager> weak_factory_{this};
  WEB_CONTENTS_USER_DATA_KEY_DECL();
};

}  // namespace permissions

#endif  // COMPONENTS_PERMISSIONS_PERMISSION_REQUEST_MANAGER_H_
