// Copyright 2018 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_PUBLIC_TEST_MOCK_NAVIGATION_HANDLE_H_
#define CONTENT_PUBLIC_TEST_MOCK_NAVIGATION_HANDLE_H_

#include <string>
#include <string_view>
#include <vector>

#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/no_destructor.h"
#include "base/notimplemented.h"
#include "base/types/optional_util.h"
#include "base/unguessable_token.h"
#include "content/public/browser/child_process_host.h"
#include "content/public/browser/error_navigation_trigger.h"
#include "content/public/browser/global_request_id.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/process_selection_user_data.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "net/base/ip_endpoint.h"
#include "net/base/isolation_info.h"
#include "net/http/http_connection_info.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/blink/public/mojom/lcp_critical_path_predictor/lcp_critical_path_predictor.mojom.h"
#include "third_party/blink/public/mojom/loader/referrer.mojom.h"
#include "third_party/blink/public/mojom/loader/transferrable_url_loader.mojom.h"
#include "third_party/blink/public/mojom/navigation/renderer_content_settings.mojom.h"
#include "third_party/perfetto/include/perfetto/tracing/traced_proto.h"
#include "url/gurl.h"

namespace content {

class MockNavigationHandle : public NavigationHandle {
 public:
  MockNavigationHandle();
  explicit MockNavigationHandle(WebContents* web_contents);
  MockNavigationHandle(const GURL& url, RenderFrameHost* render_frame_host);
  ~MockNavigationHandle() override;

  // NavigationHandle implementation:
  int64_t GetNavigationId() const override { return navigation_id_; }
  ukm::SourceId GetNextPageUkmSourceId() override {
    return ukm::ConvertToSourceId(navigation_id_,
                                  ukm::SourceIdObj::Type::NAVIGATION_ID);
  }
  const GURL& GetURL() override { return url_; }
  const GURL& GetPreviousPrimaryMainFrameURL() override {
    return previous_primary_main_frame_url_;
  }
  SiteInstance* GetStartingSiteInstance() override {
    return starting_site_instance_;
  }
  SiteInstance* GetSourceSiteInstance() override {
    return source_site_instance_;
  }
  bool IsInMainFrame() const override {
    return render_frame_host_ ? !render_frame_host_->GetParent() : true;
  }
  MOCK_CONST_METHOD0(IsInPrerenderedMainFrame, bool());
  bool IsPrerenderedPageActivation() const override {
    return is_prerendered_page_activation_;
  }
  PrerenderHostId GetPrerenderHostId() const override {
    return PrerenderHostId();
  }
  bool IsInFencedFrameTree() const override { return is_in_fenced_frame_tree_; }
  bool IsGuestViewMainFrame() const override {
    return GetNavigatingFrameType() == FrameType::kGuestMainFrame;
  }
  FrameType GetNavigatingFrameType() const override {
    NOTIMPLEMENTED();
    return FrameType::kPrimaryMainFrame;
  }
  // By default, MockNavigationHandles are renderer-initiated navigations.
  bool IsRendererInitiated() override { return is_renderer_initiated_; }
  bool IsSameOrigin() override {
    NOTIMPLEMENTED();
    return false;
  }
  bool IsInPrimaryMainFrame() const override {
    return is_in_primary_main_frame_;
  }
  const std::optional<base::UnguessableToken>& GetScriptToolInvocationId()
      const override {
    return script_tool_invocation_id_;
  }
  void set_script_tool_invocation_id(const base::UnguessableToken& id) {
    script_tool_invocation_id_ = id;
  }
  bool IsInOutermostMainFrame() const override {
    return !GetConstParentFrameOrOuterDocument();
  }
  size_t GetIgnoredDuplicateNavigationCount() const override {
    return ignored_duplicate_navigation_count_;
  }
  content::FrameTreeNodeId GetFrameTreeNodeId() override {
    if (IsInPrimaryMainFrame()) {
      CHECK(web_contents_);
      return web_contents_->GetPrimaryMainFrame()->GetFrameTreeNodeId();
    }
    CHECK(render_frame_host_);
    return render_frame_host_->GetFrameTreeNodeId();
  }
  MOCK_METHOD0(GetPreviousRenderFrameHostId, GlobalRenderFrameHostId());
  MOCK_METHOD(ChildProcessId, GetExpectedRenderProcessHostId, ());
  bool IsServedFromBackForwardCache() const override {
    return is_served_from_bfcache_;
  }
  bool IsPageActivation() const override {
    MockNavigationHandle* handle = const_cast<MockNavigationHandle*>(this);
    return handle->IsPrerenderedPageActivation() ||
           handle->IsServedFromBackForwardCache();
  }
  bool IsBlockedByConnectionAllowlist() const override {
    return is_blocked_by_connection_allowlist_;
  }
  MOCK_CONST_METHOD0(IsNavigatingFromInitialEmptyDocument, bool());
  RenderFrameHost* GetParentFrame() override {
    return render_frame_host_ ? render_frame_host_->GetParent() : nullptr;
  }
  RenderFrameHost* GetParentFrameOrOuterDocument() override {
    return const_cast<RenderFrameHost*>(GetConstParentFrameOrOuterDocument());
  }
  WebContents* GetWebContents() override { return web_contents_; }
  MOCK_METHOD0(NavigationStart, base::TimeTicks());
  MOCK_METHOD0(NavigationInputStart, base::TimeTicks());
  MOCK_METHOD0(GetNavigationHandleTiming, const NavigationHandleTiming&());
  bool WasStartedFromContextMenu() override {
    return was_started_from_context_menu_;
  }
  MOCK_METHOD0(GetSearchableFormURL, const GURL&());
  MOCK_METHOD0(GetSearchableFormEncoding, const std::string&());
  ReloadType GetReloadType() const override { return reload_type_; }
  RestoreType GetRestoreType() const override {
    return RestoreType::kNotRestored;
  }
  const GURL& GetBaseURLForDataURL() override { return base_url_for_data_url_; }
  MOCK_METHOD0(IsPost, bool());
  MOCK_METHOD0(GetRequestMethod, std::string());
  const blink::mojom::Referrer& GetReferrer() override { return referrer_; }
  void SetReferrer(blink::mojom::ReferrerPtr referrer) override {
    referrer_ = *referrer;
  }
  MOCK_METHOD0(HasUserGesture, bool());
  bool StartedWithTransientActivation() override { return false; }
  bool StartedByAd() override { return false; }
  ui::PageTransition GetPageTransition() override { return page_transition_; }
  MOCK_METHOD0(GetNavigationUIData, NavigationUIData*());
  MOCK_METHOD0(IsExternalProtocol, bool());
  net::Error GetNetErrorCode() override { return net_error_code_; }
  int GetNetExtendedErrorCode() override { return net_extended_error_code_; }
  std::optional<ErrorNavigationTrigger> GetErrorNavigationTrigger() override {
    return error_navigation_trigger_;
  }
  RenderFrameHost* GetRenderFrameHost() const override {
    return render_frame_host_;
  }
  bool IsSameDocument() const override { return is_same_document_; }
  std::optional<base::UnguessableToken> GetSameDocumentMetricsToken()
      const override {
    return same_document_metrics_token_;
  }
  void set_same_document_metrics_token(base::UnguessableToken token) {
    same_document_metrics_token_ = token;
  }
  bool IsHistory() const override {
    NOTIMPLEMENTED();
    return false;
  }
  MOCK_METHOD0(WasServerRedirect, bool());
  const std::vector<GURL>& GetRedirectChain() override {
    return redirect_chain_;
  }
  bool HasCommitted() const override { return has_committed_; }
  bool IsErrorPage() const override { return is_error_page_; }
  MOCK_METHOD0(HasSubframeNavigationEntryCommitted, bool());
  MOCK_METHOD0(DidReplaceEntry, bool());
  MOCK_METHOD0(ShouldUpdateHistory, bool());
  MOCK_METHOD0(GetSocketAddress, net::IPEndPoint());
  const net::HttpRequestHeaders& GetRequestHeaders() override {
    return request_headers_;
  }
  MOCK_METHOD1(RemoveRequestHeader, void(std::string_view));
  MOCK_METHOD2(SetRequestHeader, void(std::string_view, std::string_view));
  const net::HttpResponseHeaders* GetResponseHeaders() override {
    return response_headers_.get();
  }
  MOCK_METHOD0(GetDeclarativePerformanceObserverPolicy,
               const network::mojom::DeclarativePerformanceObserverPolicy*());
  MOCK_METHOD1(
      SetLCPPNavigationHint,
      void(blink::mojom::LCPCriticalPathPredictorNavigationTimeHintPtr));
  MOCK_METHOD0(
      GetLCPPNavigationHint,
      const blink::mojom::LCPCriticalPathPredictorNavigationTimeHintPtr&());
  MOCK_METHOD0(GetConnectionInfo, net::HttpConnectionInfo());
  const std::optional<net::SSLInfo>& GetSSLInfo() override { return ssl_info_; }
  const std::optional<net::AuthChallengeInfo>& GetAuthChallengeInfo() override {
    return auth_challenge_info_;
  }
  void SetAuthChallengeInfo(const net::AuthChallengeInfo& challenge);
  net::ResolveErrorInfo GetResolveErrorInfo() override {
    return resolve_error_info_;
  }
  MOCK_METHOD0(GetIsolationInfo, net::IsolationInfo());
  const GlobalRequestID& GetGlobalRequestID() override {
    return global_request_id_;
  }
  MOCK_METHOD0(IsDownload, bool());
  bool IsFormSubmission() override { return is_form_submission_; }
  MOCK_METHOD0(WasInitiatedByLinkClick, bool());
  MOCK_METHOD0(IsSignedExchangeInnerResponse, bool());
  MOCK_METHOD0(HasPrefetchedAlternativeSubresourceSignedExchange, bool());
  bool WasResponseCached() override { return was_response_cached_; }
  bool NetworkAccessed() override { return network_accessed_; }
  const std::string& GetHrefTranslate() override { return href_translate_; }
  const std::optional<blink::LocalFrameToken>& GetInitiatorFrameToken()
      override {
    return initiator_frame_token_;
  }
  ChildProcessId GetInitiatorProcessId() override {
    return initiator_process_id_;
  }
  const std::optional<url::Origin>& GetInitiatorOrigin() override {
    return initiator_origin_;
  }
  const std::optional<GURL>& GetInitiatorBaseUrl() override {
    return initiator_base_url_;
  }
  MOCK_METHOD(scoped_refptr<InitiatorNavigationState>,
              GetInitiatorNavigationState,
              ());
  const std::vector<std::string>& GetDnsAliases() override {
    static const base::NoDestructor<std::vector<std::string>>
        emptyvector_result;
    return *emptyvector_result;
  }
  MOCK_METHOD(void,
              RegisterThrottleForTesting,
              (std::unique_ptr<NavigationThrottle>));
  MOCK_METHOD(bool, IsDeferredForTesting, ());
  MOCK_METHOD(bool, IsCommitDeferringConditionDeferredForTesting, ());
  MOCK_METHOD(void,
              RegisterSubresourceOverride,
              (blink::mojom::TransferrableURLLoaderPtr));
  MOCK_METHOD(bool, IsSameProcess, ());
  MOCK_METHOD(NavigationEntry*, GetNavigationEntry, (), (const, override));
  MOCK_METHOD(int, GetNavigationEntryOffset, (), (const, override));
  MOCK_METHOD(void,
              ForceEnableOriginTrials,
              (const std::vector<std::string>& trials));
  MOCK_METHOD(void, SetIsOverridingUserAgent, (bool));
  MOCK_METHOD(void, SetSilentlyIgnoreErrors, ());
  MOCK_METHOD(void, SetVisitedLinkSalt, (uint64_t));
  MOCK_METHOD(network::mojom::WebSandboxFlags, SandboxFlagsInitiator, ());
  MOCK_METHOD(network::mojom::WebSandboxFlags, SandboxFlagsInherited, ());
  MOCK_METHOD(network::mojom::WebSandboxFlags, SandboxFlagsToCommit, ());
  MOCK_METHOD(bool, IsWaitingToCommit, ());
  MOCK_METHOD(bool, WasResourceHintsReceived, ());
  MOCK_METHOD(bool, IsPdf, ());
  void WriteIntoTrace(perfetto::TracedProto<TraceProto>) const override {}
  MOCK_METHOD(bool, SetNavigationTimeout, (base::TimeDelta));
  MOCK_METHOD(void, CancelNavigationTimeout, ());
  MOCK_METHOD(PreloadingTriggerType, GetPrerenderTriggerType, ());
  MOCK_METHOD(std::string, GetPrerenderHistogramSuffix, ());
  MOCK_METHOD(bool, IsPrerenderHostReused, ());
  MOCK_METHOD(void, SetAllowCookiesFromBrowser, (bool));
  MOCK_METHOD(void, GetResponseBody, (ResponseBodyCallback));
  MOCK_METHOD(std::optional<NavigationDiscardReason>,
              GetNavigationDiscardReason,
              ());
  MOCK_METHOD(bool, NeedsUrlLoader, ());
  MOCK_METHOD(bool, IsInitialWebUINavigation, ());
  MOCK_METHOD(void, SetBypassRedirectChecksForNextRedirect, (bool));
  MOCK_METHOD(bool, ConsumeBypassRedirectChecksForNextRedirect, ());

#if BUILDFLAG(IS_ANDROID)
  MOCK_METHOD(const base::android::JavaRef<jobject>&,
              GetJavaNavigationHandle,
              ());
#endif

  base::SafeRef<NavigationHandle> GetSafeRef() override {
    return weak_factory_.GetSafeRef();
  }
  MOCK_METHOD(bool, ExistingDocumentWasDiscarded, (), (const));

  CommitDeferringCondition* GetCommitDeferringConditionForTesting() override {
    return nullptr;
  }

  void SetContentSettings(
      blink::mojom::RendererContentSettingsPtr content_settings) override {}
  blink::mojom::RendererContentSettingsPtr GetContentSettingsForTesting()
      override {
    return nullptr;
  }

  ProcessSelectionUserData& GetProcessSelectionUserData() override {
    return process_selection_user_data_;
  }

  MOCK_METHOD(BeforeUnloadExecutionMode,
              GetBeforeUnloadExecutionMode,
              (),
              (const, override));
  MOCK_METHOD(void, SetIsAdTagged, ());
  MOCK_METHOD(void, SetIsAdTaggedByHostFilter, ());
  MOCK_METHOD(bool, IsAdTaggedByHostFilter, (), (const, override));

  blink::RuntimeFeatureStateContext& GetMutableRuntimeFeatureStateContext()
      override {
    return runtime_feature_state_context_;
  }
  MOCK_METHOD(std::optional<url::Origin>, GetOriginToCommit, ());
  // End of NavigationHandle implementation.

  void set_url(const GURL& url) { url_ = url; }
  void set_previous_primary_main_frame_url(
      const GURL& previous_primary_main_frame_url) {
    previous_primary_main_frame_url_ = previous_primary_main_frame_url;
  }
  void set_starting_site_instance(SiteInstance* site_instance) {
    starting_site_instance_ = site_instance;
  }
  void set_source_site_instance(SiteInstance* site_instance) {
    source_site_instance_ = site_instance;
  }
  void set_page_transition(ui::PageTransition page_transition) {
    page_transition_ = page_transition;
  }
  void set_net_error_code(net::Error error_code) {
    net_error_code_ = error_code;
  }
  void set_net_extended_error_code(int net_extended_error_code) {
    net_extended_error_code_ = net_extended_error_code;
  }
  void set_error_navigation_trigger(
      std::optional<ErrorNavigationTrigger> error_navigation_trigger) {
    error_navigation_trigger_ = error_navigation_trigger;
  }
  void set_render_frame_host(RenderFrameHost* render_frame_host) {
    render_frame_host_ = render_frame_host;
  }
  void set_is_same_document(bool is_same_document) {
    is_same_document_ = is_same_document;
  }
  void set_is_served_from_bfcache(bool is_served_from_bfcache) {
    is_served_from_bfcache_ = is_served_from_bfcache;
  }
  void set_is_prerendered_page_activation(bool is_prerendered_page_activation) {
    is_prerendered_page_activation_ = is_prerendered_page_activation;
  }
  void set_is_in_fenced_frame_tree(bool is_in_fenced_frame_tree) {
    is_in_fenced_frame_tree_ = is_in_fenced_frame_tree;
  }
  void set_is_renderer_initiated(bool is_renderer_initiated) {
    is_renderer_initiated_ = is_renderer_initiated;
  }
  void set_is_in_primary_main_frame(bool is_in_primary_main_frame) {
    is_in_primary_main_frame_ = is_in_primary_main_frame;
  }
  void set_redirect_chain(const std::vector<GURL>& redirect_chain) {
    redirect_chain_ = redirect_chain;
  }
  void set_has_committed(bool has_committed) { has_committed_ = has_committed; }
  void set_is_error_page(bool is_error_page) { is_error_page_ = is_error_page; }
  void set_is_blocked_by_connection_allowlist(bool value) {
    is_blocked_by_connection_allowlist_ = value;
  }
  void set_request_headers(const net::HttpRequestHeaders& request_headers) {
    request_headers_ = request_headers;
  }
  void set_response_headers(
      scoped_refptr<net::HttpResponseHeaders> response_headers) {
    response_headers_ = response_headers;
  }
  void set_ssl_info(const net::SSLInfo& ssl_info) { ssl_info_ = ssl_info; }
  void set_global_request_id(const GlobalRequestID& global_request_id) {
    global_request_id_ = global_request_id;
  }
  void set_is_form_submission(bool is_form_submission) {
    is_form_submission_ = is_form_submission;
  }
  void set_was_response_cached(bool was_response_cached) {
    was_response_cached_ = was_response_cached;
  }
  void set_initiator_frame_token(
      const blink::LocalFrameToken* initiator_frame_token) {
    initiator_frame_token_ = base::OptionalFromPtr(initiator_frame_token);
  }
  void set_initiator_process_id(ChildProcessId process_id) {
    initiator_process_id_ = process_id;
  }
  void set_initiator_origin(const url::Origin& initiator_origin) {
    initiator_origin_ = initiator_origin;
  }
  void set_reload_type(ReloadType reload_type) { reload_type_ = reload_type; }
  void set_was_started_from_context_menu(bool was_started_from_context_menu) {
    was_started_from_context_menu_ = was_started_from_context_menu;
  }

 private:
  const RenderFrameHost* GetConstParentFrameOrOuterDocument() const {
    return render_frame_host_ ? render_frame_host_->GetParentOrOuterDocument()
                              : nullptr;
  }

  int64_t navigation_id_;
  GURL url_;
  GURL previous_primary_main_frame_url_;
  raw_ptr<SiteInstance> starting_site_instance_ = nullptr;
  raw_ptr<SiteInstance, DanglingUntriaged> source_site_instance_ = nullptr;
  raw_ptr<WebContents, DanglingUntriaged> web_contents_ = nullptr;
  GURL base_url_for_data_url_;
  blink::mojom::Referrer referrer_;
  ui::PageTransition page_transition_ = ui::PAGE_TRANSITION_LINK;
  net::Error net_error_code_ = net::OK;
  int net_extended_error_code_ = 0;
  std::optional<ErrorNavigationTrigger> error_navigation_trigger_;
  raw_ptr<RenderFrameHost, DanglingUntriaged> render_frame_host_ = nullptr;
  bool is_same_document_ = false;
  bool is_served_from_bfcache_ = false;
  bool is_prerendered_page_activation_ = false;
  bool is_in_fenced_frame_tree_ = false;
  bool is_renderer_initiated_ = true;
  bool is_in_primary_main_frame_ = true;
  size_t ignored_duplicate_navigation_count_ = 0;
  std::optional<base::UnguessableToken> script_tool_invocation_id_;
  std::vector<GURL> redirect_chain_;
  bool has_committed_ = false;
  bool is_error_page_ = false;
  bool is_blocked_by_connection_allowlist_ = false;
  net::HttpRequestHeaders request_headers_;
  scoped_refptr<net::HttpResponseHeaders> response_headers_;
  std::optional<net::SSLInfo> ssl_info_;
  std::optional<net::AuthChallengeInfo> auth_challenge_info_;
  net::ResolveErrorInfo resolve_error_info_;
  content::GlobalRequestID global_request_id_;
  bool is_form_submission_ = false;
  bool was_response_cached_ = false;
  bool network_accessed_ = false;
  std::optional<url::Origin> initiator_origin_;
  std::optional<GURL> initiator_base_url_;
  ReloadType reload_type_ = content::ReloadType::NONE;
  std::string href_translate_;
  std::optional<blink::LocalFrameToken> initiator_frame_token_;
  ChildProcessId initiator_process_id_;
  bool was_started_from_context_menu_ = false;
  blink::RuntimeFeatureStateContext runtime_feature_state_context_;
  ProcessSelectionUserData process_selection_user_data_;
  std::optional<base::UnguessableToken> same_document_metrics_token_;

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

}  // namespace content

#endif  // CONTENT_PUBLIC_TEST_MOCK_NAVIGATION_HANDLE_H_
