// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_BROWSER_WEBID_REQUEST_H_
#define CONTENT_BROWSER_WEBID_REQUEST_H_

#include <memory>
#include <string>
#include <vector>

#include "base/containers/flat_map.h"
#include "base/containers/queue.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "content/browser/webid/accounts_fetcher.h"
#include "content/browser/webid/delegation/federated_sd_jwt_handler.h"
#include "content/browser/webid/identity_provider_info.h"
#include "content/browser/webid/identity_registry.h"
#include "content/browser/webid/identity_registry_delegate.h"
#include "content/browser/webid/idp_network_request_manager.h"
#include "content/browser/webid/idp_registration_handler.h"
#include "content/browser/webid/metrics.h"
#include "content/browser/webid/url_computations.h"
#include "content/common/content_export.h"
#include "content/public/browser/document_user_data.h"
#include "content/public/browser/navigation_ui_data.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/webid/autofill_source.h"
#include "content/public/browser/webid/federated_identity_api_permission_context_delegate.h"
#include "content/public/browser/webid/federated_identity_permission_context_delegate.h"
#include "content/public/browser/webid/identity_request_dialog_controller.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "third_party/blink/public/mojom/credentialmanagement/credential_manager.mojom.h"
#include "third_party/blink/public/mojom/webid/federated_request.mojom-shared.h"
#include "third_party/blink/public/mojom/webid/federated_request.mojom.h"
#include "url/gurl.h"

namespace content {

class FederatedIdentityAutoReauthnPermissionContextDelegate;
class FederatedIdentityPermissionContextDelegate;
class NavigationHandle;
class RenderFrameHost;

namespace webid {

class RequestService;

// Request represents a single WebID-related request from the renderer. It is
// owned and managed by RequestService.
class CONTENT_EXPORT Request
    : public blink::mojom::FederatedRequest,
      public FederatedIdentityPermissionContextDelegate::
          IdpSigninStatusObserver,
      public IdentityRegistryDelegate,
      public AutofillSource {
 public:
  using RequestTokenCallback =
      base::OnceCallback<void(blink::mojom::RequestTokenStatus,
                              const std::optional<GURL>&,
                              std::optional<base::Value>,
                              blink::mojom::TokenErrorPtr,
                              bool)>;

  Request(RenderFrameHost* rfh, RequestService& request_service);

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

  ~Request() override;

  RenderFrameHost& render_frame_host() const { return *render_frame_host_; }

  const url::Origin& origin() const {
    return render_frame_host().GetLastCommittedOrigin();
  }

  void BindReceiver(
      mojo::PendingReceiver<blink::mojom::FederatedRequest> pending_receiver);

  // Starts the token request. Returns true if the request started successfully
  // and is now pending. Returns false if the request was terminated immediately
  // (e.g. due to permissions policy, invalid parameters, or being rejected in
  // favor of an existing request).
  // This is an overload of the mojo version of RequestToken.
  // |navigation_handle| is passed when this request was triggered by navigation
  // interception, so that we can use this handle for user activation checking
  // and setting up parameters for a later redirect. This is virtual so that it
  // can be mocked.
  virtual bool RequestToken(
      std::vector<blink::mojom::IdentityProviderGetParametersPtr>
          idp_get_params_ptrs,
      ::password_manager::CredentialMediationRequirement requirement,
      NavigationHandle* navigation_handle,
      const GURL& intercepted_url,
      RequestTokenCallback callback);

  // blink::mojom::FederatedRequest:
  void Abort() override;

  // FederatedIdentityPermissionContextDelegate::IdpSigninStatusObserver:
  void OnIdpSigninStatusReceived(const url::Origin& idp_config_origin,
                                 bool idp_signin_status) override;

  base::WeakPtr<Request> GetWeakPtr();

  // FederatedIdentityModalDialogViewDelegate:
  void OnClose() override;
  bool OnResolve(GURL idp_config_url,
                 const std::optional<std::string>& account_id,
                 blink::mojom::ResolveTokenParamsPtr params) override;
  void OnOriginMismatch(Method method,
                        const url::Origin& expected,
                        const url::Origin& actual) override;

  void OnIntentResolved(const std::string& token);

  // content::webid::AutofillSource
  const std::optional<std::vector<IdentityRequestAccountPtr>>
  GetAutofillSuggestions() const override;
  void NotifyAutofillSuggestionAccepted(
      const GURL& idp,
      const std::string& account_id,
      bool show_modal,
      OnFederatedTokenReceivedCallback callback) override;

  // Returns whether the API is enabled or not.
  FederatedIdentityApiPermissionContextDelegate::PermissionStatus
  GetApiPermissionStatus();

  // For use by the devtools protocol for browser automation.
  IdentityRequestDialogController* GetDialogController();

  const std::vector<scoped_refptr<IdentityProviderData>>& GetSortedIdpData()
      const {
    return idp_data_for_display_;
  }

  const std::vector<scoped_refptr<IdentityRequestAccount>>& GetAccounts()
      const {
    return accounts_;
  }

  ::password_manager::CredentialMediationRequirement GetMediationRequirement()
      const {
    return mediation_requirement_;
  }

  // These values are persisted to logs. Entries should not be renumbered and
  // numeric values should never be reused.
  // LINT.IfChange(DialogType)

  enum class DialogType {
    kNone = 0,
    kSelectAccount = 1,
    kAutoReauth = 2,
    kConfirmIdpLogin = 3,
    kError = 4,
    // Popups are not technically dialogs in the strict sense, but because they
    // are mutually exclusive with browser UI dialogs we use this enum for them
    // as well.
    kLoginToIdpPopup = 5,
    kContinueOnPopup = 6,
    kErrorUrlPopup = 7,
    kMaxValue = kErrorUrlPopup
  };

  // LINT.ThenChange(//tools/metrics/histograms/metadata/blink/enums.xml:FedCmDialogType)

  void OnNativeAppResult(
      DialogType dialog_type,
      const GURL& idp_config_url,
      IdentityRequestDialogController::NativeAppResult result);
  void OnNativeAppLoginFinished(const GURL& idp_config_url);

  DialogType GetDialogType() const { return dialog_type_; }

  enum IdentitySelectionType { kExplicit, kAutoPassive, kAutoActive };

  bool ShouldNotifyDevtoolsForDialogType(DialogType type);

  void AcceptAccountsDialogForDevtools(const GURL& config_url,
                                       const IdentityRequestAccount& account);
  void DismissAccountsDialogForDevtools(bool should_embargo);
  void AcceptConfirmIdpLoginDialogForDevtools();
  void DismissConfirmIdpLoginDialogForDevtools();
  bool UseAnotherAccountForDevtools(const IdentityProviderData& provider);
  bool HasMoreDetailsButtonForDevtools();
  void ClickErrorDialogGotItForDevtools();
  void ClickErrorDialogMoreDetailsForDevtools();
  void DismissErrorDialogForDevtools();

  // Whether we can show the continue_on popup (not using mediation: silent,
  // etc.)
  bool CanShowContinueOnPopup() const;

  bool HasUserTriedToSignInToIdp(const GURL& idp_config_url) {
    return idps_user_tried_to_signin_to_.contains(idp_config_url);
  }

  UseOtherAccountResult ComputeUseOtherAccountResult(
      blink::mojom::FederatedRequestResult result,
      const std::optional<GURL>& selected_idp_config_url);

  void FilterAccounts(
      const GURL& idp_config_url,
      const GURL& idp_login_url,
      std::vector<scoped_refptr<IdentityRequestAccount>>& accounts);

  void SetIdpLoginInfo(const GURL& idp_login_url,
                       const std::string& login_hint,
                       const std::string& domain_hint);
  void SetWellKnownAndConfigFetchedTime(base::TimeTicks time);

  // Called when there is an error in fetching information to show the prompt
  // for a given IDP - `idp_info`, but we do not need to show failure UI for the
  // IDP.
  void OnAccountsResultsReceived(
      base::TimeTicks well_known_and_config_fetched_time,
      std::vector<AccountsFetcher::Result> results);

  void OnFetchDataForIdpFailed(std::unique_ptr<IdentityProviderInfo> idp_info,
                               blink::mojom::FederatedRequestResult result,
                               std::optional<RequestIdTokenStatus> token_status,
                               bool should_delay_callback);

  // Called when all of the data needed to display the FedCM prompt has been
  // fetched for `idp_info`. Accounts should be moved instead of copied to this
  // function.
  void OnFetchDataForIdpSucceeded(
      IdpNetworkRequestManager::AccountsResponse accounts,
      std::unique_ptr<IdentityProviderInfo> idp_info);

  void MaybeShowActiveModeModalDialog(const GURL& idp_config_url,
                                      const GURL& idp_login_url);

  void SetAccountsFetchedTime(base::TimeTicks time) {
    accounts_fetched_time_ = time;
  }
  void SetClientMetadataFetchedTime(base::TimeTicks time) {
    client_metadata_fetched_time_ = time;
  }

  url::Origin GetEmbeddingOrigin() const;

  GURL login_url() { return login_url_; }
  const std::vector<GURL>& idp_order() const { return idp_order_; }
  bool HadAccountIdBeforeLogin(const std::string& account_id) {
    return account_ids_before_login_.contains(account_id);
  }

  // Called when there is an error fetching information to show the prompt for a
  // given IDP, and because of the mismatch this IDP must be present in the
  // dialog we show to the user.
  void OnIdpMismatch(std::unique_ptr<IdentityProviderInfo> idp_info);

  void CompleteRequestWithError(
      blink::mojom::FederatedRequestResult result,
      std::optional<RequestIdTokenStatus> token_status,
      bool should_delay_callback);

  // Completes request. Displays a dialog if there is an error and the error is
  // during a fetch triggered by an IdP sign-in status change.
  void CompleteRequest(blink::mojom::FederatedRequestResult result,
                       std::optional<RequestIdTokenStatus> token_status,
                       std::optional<IdentityCredentialTokenError> token_error,
                       const std::optional<GURL>& selected_idp_config_url,
                       std::optional<base::Value> token_data,
                       bool should_delay_callback);

 private:
  friend class RequestTest;
  friend class IdentityCredentialSourceImpl;  // for OnAccountSelected
  friend class TestIdentityCredentialSourceImpl;
  friend class RequestService;

  struct FetchData {
    FetchData();
    ~FetchData();

    // Set of config URLs of IDPs that have yet to be processed.
    std::set<GURL> pending_idps;

    // Whether accounts endpoint fetch succeeded for at least one IdP.
    bool did_succeed_for_at_least_one_idp{false};
  };

  struct AutoReauthnInfo {
    AutoReauthnInfo();
    ~AutoReauthnInfo();
    AutoReauthnInfo(const AutoReauthnInfo&);
    AutoReauthnInfo& operator=(const AutoReauthnInfo&);

    bool is_eligible = false;
    scoped_refptr<IdentityProviderData> idp = nullptr;
    scoped_refptr<IdentityRequestAccount> account = nullptr;
  };

  bool HasPendingRequest() const;

  // Fetch well-known, config, accounts and client metadata endpoints for
  // passed-in IdPs. Uses parameters from `token_request_get_infos_`.
  // When retrying a single IdP whose .well-known and config endpoints are
  // already cached in `idp_infos_`, bypasses ConfigFetcher and directly
  // fetches the accounts endpoint.
  void FetchEndpointsForIdps(const std::set<GURL>& idp_config_urls);

  std::vector<blink::mojom::IdentityProviderRequestOptionsPtr>
  MaybeAddRegisteredProviders(
      std::vector<blink::mojom::IdentityProviderRequestOptionsPtr>& providers);

  void MaybeShowAccountsDialog();
  void OnGetPassiveDialogVolume(
      const std::set<GURL>& unique_idps,
      IdentityRequestDialogController::PassiveDialogVolume dialog_volume);
  // To be called immediately after ShowAccountsDialog for correct devtools
  // integration and metrics reporting.
  // `did_succeed_for_at_least_one_idp` needs to be passed as a parameter
  // because `fetch_data_` has been cleared at this point.
  void AfterAccountsDialogShown(bool did_succeed_for_at_least_one_idp);
  void ShowModalDialog(DialogType dialog_type,
                       const GURL& idp_config_url,
                       const GURL& url_to_show);
  void ShowErrorDialog(const GURL& idp_config_url,
                       FetchStatus status,
                       std::optional<IdentityCredentialTokenError> error);
  // Called when we should show a failure dialog in the case where a single IDP
  // account fetch resulted in a mismatch with its login status.
  void ShowSingleIdpFailureDialog();
  void OnAccountsDisplayed();

  void OnAccountSelected(const GURL& idp_config_url,
                         const std::string& account_id,
                         bool is_sign_in);
  void OnDismissFailureDialog(
      IdentityRequestDialogController::DismissReason dismiss_reason);
  void OnDismissErrorDialog(
      const GURL& idp_config_url,
      FetchStatus status,
      IdentityRequestDialogController::DismissReason dismiss_reason);
  void OnDialogDismissed(
      IdentityRequestDialogController::DismissReason dismiss_reason);
  void CompleteTokenRequest(
      const GURL& idp_config_url,
      FetchStatus status,
      std::optional<base::Value> token,
      std::optional<IdentityCredentialTokenError> token_error,
      bool should_delay_callback);
  void OnTokenResponseReceived(
      blink::mojom::IdentityProviderRequestOptionsPtr idp,
      FetchStatus status,
      IdpNetworkRequestManager::TokenResult&& result);
  void OnContinueOnResponseReceived(
      blink::mojom::IdentityProviderRequestOptionsPtr idp,
      FetchStatus status,
      const GURL& url);

  void OnRedirectToResponseReceived(
      blink::mojom::IdentityProviderRequestOptionsPtr idp,
      FetchStatus status,
      blink::mojom::RedirectParams::Tag method,
      const GURL& redirect_to,
      const std::string& request_body);
  void RedirectTo(const GURL& idp_config_url,
                  blink::mojom::RedirectParams::Tag method,
                  const GURL& redirect_to,
                  const std::string& request_body);

  // Called after we get at token (either from the ID assertion endpoint or
  // from IdentityProvider.resolve) to update our various permissions.
  void MarkUserAsSignedIn(const GURL& idp_config_url,
                          const std::string& account_id);

  void OnConnectionError();

  void CleanUp();

  // Records metrics and console errors.
  void RecordMetricsAndConsoleError(
      blink::mojom::FederatedRequestResult result,
      std::optional<RequestIdTokenStatus> token_status,
      const std::optional<GURL>& selected_idp_config_url);

  void CompleteRequestInternal(
      blink::mojom::FederatedRequestResult result,
      std::optional<IdentityCredentialTokenError> token_error,
      const std::optional<GURL>& selected_idp_config_url,
      std::optional<base::Value> token_data,
      bool is_auto_selected);

  // Creates an inspector issue related to a federated authentication request to
  // the Issues panel in DevTools.
  void AddDevToolsIssue(blink::mojom::FederatedRequestResult result);

  // Adds a console error message related to a federated authentication request
  // issue. The Issues panel is preferred, but for now we also surface console
  // error messages since it is much simpler to add.
  void AddConsoleErrorMessage(blink::mojom::FederatedRequestResult result);

  // Returns true and the `IdentityProviderData` + `IdentityRequestAccount` for
  // the only returning account. Returns false if there are multiple returning
  // accounts or no returning account.
  bool GetAccountForAutoReauthn(
      scoped_refptr<IdentityProviderData>* out_idp_data,
      scoped_refptr<IdentityRequestAccount>* out_account);

  // Check if auto re-authn is available so we can skip fetching accounts if the
  // auto re-authn flow is guaranteed to fail.
  bool ShouldFailBeforeFetchingAccounts(const GURL& config_url);

  void AssembleAndSortAccounts();
  AutoReauthnInfo CheckAutoReauthnEligibility();

  // Check if the site requires user mediation due to a previous
  // `preventSilentAccess` call.
  bool RequiresUserMediation();
  void SetRequiresUserMediation(bool requires_user_mediation,
                                base::OnceClosure callback);

  // Trigger a dialog to prompt the user to login to the IdP. `can_append_hints`
  // is true if the caller allows the login url to be augmented with login and
  // domain hints.
  void LoginToIdP(bool can_append_hints,
                  const GURL& idp_config_url,
                  GURL login_url);

  void RecordErrorMetrics(
      blink::mojom::IdentityProviderRequestOptionsPtr idp,
      IdpNetworkRequestManager::FedCmTokenResponseType token_response_type,
      std::optional<IdpNetworkRequestManager::FedCmErrorDialogType>
          error_dialog_type,
      std::optional<IdpNetworkRequestManager::FedCmErrorUrlType>
          error_url_type);

  std::unique_ptr<Metrics> CreateFedCmMetrics();

  bool IsNewlyLoggedIn(const IdentityRequestAccount& account);

  // Returns whether we'll be using an ambient UI for a passive call.
  bool IsUsingAmbient() const;

  blink::mojom::RpMode GetRpMode() const { return rp_mode_; }

  // If the client metadata has not been received yet the UI may not be able to
  // show a correct title, so we need to indicate that in the RelyingPartyData.
  RelyingPartyData CreateRpData(bool client_metadata_received) const;

  FederatedIdentityApiPermissionContextDelegate* api_permission_delegate()
      const;
  FederatedIdentityAutoReauthnPermissionContextDelegate*
  auto_reauthn_permission_delegate() const;
  FederatedIdentityPermissionContextDelegate* permission_delegate() const;

  std::unique_ptr<IdpNetworkRequestManager> network_manager_;

  // Helper that records FedCM UMA and UKM metrics. Initialized in the
  // RequestToken() method, so all metrics must be recorded after that.
  std::unique_ptr<Metrics> fedcm_metrics_;

  // Populated by OnFetchDataForIdpSucceeded() and OnIdpMismatch().
  base::flat_map<GURL, std::unique_ptr<IdentityProviderInfo>> idp_infos_;
  // Populated by MaybeShowAccountsDialog().
  std::vector<scoped_refptr<IdentityProviderData>> idp_data_for_display_;

  // Populated by OnFetchDataForIdpSucceeded(). Contains the accounts of each
  // IDP. Used to later set accounts_ in the order in which the IDPs are
  // requested.
  base::flat_map<GURL, std::vector<scoped_refptr<IdentityRequestAccount>>>
      idp_accounts_;
  base::flat_map<GURL, std::vector<scoped_refptr<IdentityRequestAccount>>>
      idp_filtered_accounts_;
  // The accounts to be displayed by the UI.
  std::vector<scoped_refptr<IdentityRequestAccount>> accounts_;
  // The accounts that were filtered out during fetching.
  std::vector<scoped_refptr<IdentityRequestAccount>> filtered_accounts_;

  // Contains the set of account IDs of an IDP before a login URL is displayed
  // to the user. Used to compute the account ID of the account that the user
  // logs in to. Populated by LoginToIdP().
  base::flat_set<std::string> account_ids_before_login_;

  // Maps the login URL to the info that may be added as query parameters to
  // that URL. Populated by OnAllConfigAndWellKnownFetched().
  base::flat_map<GURL, IdentityProviderLoginUrlInfo> idp_login_infos_;

  raw_ptr<RenderFrameHost> render_frame_host_;
  // RequestService owns `this`, so it is expected to outlive it.
  const raw_ref<RequestService> request_service_;

  // The account that was selected by the user. This is only applicable to the
  // mediation flow.
  std::string account_id_;
  base::TimeTicks start_time_;
  base::TimeTicks well_known_and_config_fetched_time_;
  base::TimeTicks accounts_fetched_time_;
  base::TimeTicks client_metadata_fetched_time_;
  base::TimeTicks ready_to_display_accounts_dialog_time_;
  base::TimeTicks accounts_dialog_display_time_;
  base::TimeTicks select_account_time_;
  base::TimeTicks id_assertion_response_time_;
  // This gets set at the beginning of a request. It indicates whether we
  // should bypass the delay to notify the renderer, for use in automated
  // tests when the delay is irrelevant to the test but slows it down
  // unncessarily.
  bool should_complete_request_immediately_{false};
  // While there could be both token request and user info request when a user
  // visits a site, it's worth noting that they must be from different render
  // frames. e.g. one top frame rp.example and one iframe idp.example.
  // Therefore,
  // 1. if one of the requests exists, the other one shouldn't. e.g. if the
  // iframe requests user info, it cannot request token at the same time.
  // 2. the user info request should not set "HasPendingRequest" on the page
  // (multiple per page). It's OK to have multiple concurrent user info requests
  // since there's no browser UI involved. e.g. rp.example embeds
  // iframe1.example and iframe2.example. Both iframes can request user info
  // simultaneously.
  RequestTokenCallback request_token_callback_;

  OnFederatedTokenReceivedCallback token_received_callback_for_autofill_;

  std::unique_ptr<AccountsFetcher> fedcm_accounts_fetcher_;

  std::unique_ptr<FederatedSdJwtHandler> federated_sdjwt_handler_;

  // TODO(crbug.com/40238075): Refactor these member variables introduced
  // through the multi IDP prototype implementation to make them less confusing.

  // Parameters passed to RequestToken().
  base::flat_map<GURL, AccountsFetcher::IdentityProviderGetInfo>
      token_request_get_infos_;

  // Data related to in-progress FetchEndpointsForIdps() fetch.
  FetchData fetch_data_;

  // The set of IDPs that the user has tried to sign in to since the start of
  // the current request.
  base::flat_set<GURL> idps_user_tried_to_signin_to_;

  // List of config URLs of IDPs in the same order as the providers specified in
  // the navigator.credentials.get call.
  std::vector<GURL> idp_order_;

  // If dialog_type_ is kConfirmIdpLogin, this is the login URL for the IDP. If
  // LoginToIdp() is called, this is the login URL for the IDP. Does not include
  // the filters as query parameters, if any.
  GURL login_url_;

  // If dialog_type_ is kError or a popup is open, this is the config URL for
  // the IDP.
  GURL config_url_;

  // If dialog_type_ is kError, this is the fetch status of the token request.
  FetchStatus token_request_status_;

  // If dialog_type_ is kError, this is the token error.
  std::optional<IdentityCredentialTokenError> token_error_;

  DialogType dialog_type_ = DialogType::kNone;
  ::password_manager::CredentialMediationRequirement mediation_requirement_;
  IdentitySelectionType identity_selection_type_ = kExplicit;
  blink::mojom::RpMode rp_mode_{blink::mojom::RpMode::kPassive};
  IdentityRequestDialogController::PassiveDialogVolume passive_dialog_volume_ =
      IdentityRequestDialogController::PassiveDialogVolume::kDefault;

  // Time when the accounts dialog is last shown for metrics purposes.
  std::optional<base::TimeTicks> accounts_dialog_shown_time_;

  // Time when the mismatch dialog is last shown for metrics purposes.
  std::optional<base::TimeTicks> mismatch_dialog_shown_time_;
  // Whether a mismatch dialog has been shown for the current request.
  bool has_shown_mismatch_{false};

  // Type of error URL for metrics and devtools issue purposes.
  std::optional<IdpNetworkRequestManager::FedCmErrorUrlType> error_url_type_;

  // The active flow requires user activation to be kicked off. We'd also need
  // this information along the way. e.g. showing pop-up window when accounts
  // fetch is failed. However, the function `HasTransientUserActivation` may
  // return false at that time because the network requests may be very slow
  // such that the previous user gesture is expired. Therefore we store the
  // information to use it during the entire the active flow.
  bool had_transient_user_activation_{false};

  // Keeps track of the state of the use other account flow. Is std::nullopt
  // when the flow is not active.
  std::optional<UseOtherAccountResult> use_other_account_account_result_;

  // Whether a token request has been sent.
  bool has_sent_token_request_{false};

  // Keeps track of the state of the verifying dialog. Is std::nullopt when the
  // verifying dialog has not been shown.
  std::optional<VerifyingDialogResult> verifying_dialog_result_;

  perfetto::NamedTrack perfetto_track_;

  // Whether this Request can make top level redirections, available
  // currently only for interception-initiated requests.
  bool can_accept_redirect_to_{false};

  // Stores the URL that we intercepted. This will be used as the referrer when
  // loading the redirect target so that to the RP this looks like the load was
  // initiated by the IDP.
  GURL intercepted_url_;

  // Whether the callback for the current request has been delayed.
  bool complete_request_delayed_{false};

  // Whether we are currently in the `RedirectTo` flow. This is used to ignore
  // dismissals triggered by tab closure on Android during the navigation.
  bool in_redirect_to_{false};

  bool did_show_ui_{false};

  mojo::ReceiverSet<blink::mojom::FederatedRequest> receivers_;

  base::WeakPtrFactory<Request> weak_ptr_factory_{this};
};

}  // namespace webid
}  // namespace content

#endif  // CONTENT_BROWSER_WEBID_REQUEST_H_
