// Copyright 2021 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_RENDERER_HOST_PAGE_IMPL_H_
#define CONTENT_BROWSER_RENDERER_HOST_PAGE_IMPL_H_

#include <memory>
#include <optional>
#include <set>
#include <vector>

#include "base/memory/raw_ref.h"
#include "base/memory/safe_ref.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/unguessable_token.h"
#include "cc/input/browser_controls_state.h"
#include "content/browser/fenced_frame/fenced_frame_url_mapping.h"
#include "content/browser/renderer_host/stored_page.h"
#include "content/common/content_export.h"
#include "content/common/navigation_client.mojom.h"
#include "content/public/browser/page.h"
#include "net/base/schemeful_site.h"
#include "services/metrics/public/cpp/ukm_source.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/fingerprinting_protection/noise_token.h"
#include "third_party/blink/public/mojom/css/preferred_color_scheme.mojom.h"
#include "third_party/blink/public/mojom/favicon/favicon_url.mojom.h"
#include "third_party/blink/public/mojom/media/capture_handle_config.mojom.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/ime/mojom/virtual_keyboard_types.mojom.h"
#include "url/gurl.h"

#if BUILDFLAG(IS_ANDROID)
#include "content/browser/android/page_proxy.h"
#endif

namespace cc {
struct BrowserControlsOffsetTagModifications;
}  // namespace cc

namespace viz {
class PeakGpuMemoryTracker;
}  // namespace viz

namespace content {

class NavigationRequest;
class PageDelegate;
class RenderFrameHostImpl;

// This implements the Page interface that is exposed to embedders of content,
// and adds things only visible to content.

// Please refer to content/public/browser/page.h for more details.
class CONTENT_EXPORT PageImpl : public Page {
 public:
  PageImpl(RenderFrameHostImpl& rfh, PageDelegate& delegate);

  ~PageImpl() override;

  using base::SupportsUserData::ClearAllUserData;

  // Page implementation.
  const std::optional<GURL>& GetManifestUrl() const override;
  void GetManifest(GetManifestCallback callback) override;
  bool IsPrimary() const override;
  void WriteIntoTrace(perfetto::TracedValue context) override;
  base::WeakPtr<Page> GetWeakPtr() override;
  base::SafeRef<Page> GetSafeRef() override;
  bool IsPageScaleFactorOne() override;
  const std::string& GetContentsMimeType() const override;
#if BUILDFLAG(IS_ANDROID)
  base::android::ScopedJavaLocalRef<jobject> GetJavaPage() override;
#endif

  base::WeakPtr<PageImpl> GetWeakPtrImpl();

  virtual void UpdateManifestUrl(const GURL& manifest_url);

  RenderFrameHostImpl& GetMainDocument() const;

  bool is_on_load_completed_in_main_document() const {
    return is_on_load_completed_in_main_document_;
  }
  void set_is_on_load_completed_in_main_document(bool completed) {
    is_on_load_completed_in_main_document_ = completed;
  }

  bool has_recorded_partitioned_cookie_use() const {
    return has_recorded_partitioned_cookie_use_;
  }
  void set_has_recorded_partitioned_cookie_use(bool recorded) {
    has_recorded_partitioned_cookie_use_ = recorded;
  }

  std::optional<base::TimeTicks> GetFirstContentfulPaintInMainDocumentTime()
      const {
    return first_contentful_paint_in_main_document_time_;
  }

  void SetFirstContentfulPaintInMainDocumentTime(
      base::TimeTicks presentation_time) {
    first_contentful_paint_in_main_document_time_ = presentation_time;
  }

  bool is_main_document_element_available() const {
    return is_main_document_element_available_;
  }
  void set_is_main_document_element_available(bool completed) {
    is_main_document_element_available_ = completed;
  }

  bool uses_temporary_zoom_level() const { return uses_temporary_zoom_level_; }
  void set_uses_temporary_zoom_level(bool level) {
    uses_temporary_zoom_level_ = level;
  }

  void OnFirstVisuallyNonEmptyPaint();
  bool did_first_visually_non_empty_paint() const {
    return did_first_visually_non_empty_paint_;
  }

  const std::vector<blink::mojom::FaviconURLPtr>& favicon_urls() const {
    return favicon_urls_;
  }
  void set_favicon_urls(std::vector<blink::mojom::FaviconURLPtr> favicon_urls) {
    favicon_urls_ = std::move(favicon_urls);
  }

  const blink::mojom::CaptureHandleConfig& GetCaptureHandleConfig() override;
  void SetCaptureHandleConfig(
      blink::mojom::CaptureHandleConfigPtr config) override;

  void OnThemeColorChanged(const std::optional<SkColor>& theme_color);

  void DidChangeBackgroundColor(SkColor4f background_color, bool color_adjust);

  // Notifies the page's color scheme was inferred.
  void DidInferColorScheme(blink::mojom::PreferredColorScheme color_scheme);

  void NotifyPageBecameCurrent();

  std::optional<SkColor> theme_color() const {
    return main_document_theme_color_;
  }

  std::optional<SkColor> background_color() const {
    return main_document_background_color_;
  }

  std::optional<blink::mojom::PreferredColorScheme> inferred_color_scheme()
      const {
    return main_document_inferred_color_scheme_;
  }

  void SetContentsMimeType(std::string mime_type);

  FencedFrameURLMapping& fenced_frame_urls_map() {
    return fenced_frame_urls_map_;
  }

  void set_last_main_document_source_id(ukm::SourceId id) {
    last_main_document_source_id_ = id;
  }
  ukm::SourceId last_main_document_source_id() const {
    return last_main_document_source_id_;
  }

  // Sets the start time of the prerender activation navigation for this page.
  // TODO(falken): Plumb NavigationRequest to
  // RenderFrameHostManager::CommitPending and remove this.
  void SetActivationStartTime(base::TimeTicks activation_start);

  void NotifyCrossOriginSubframePrerenderIsAllowed();

  // Called during the activation navigation. Sends an IPC to the RenderViews in
  // the renderers, instructing them to transition their documents from
  // prerendered to activated. Tells the corresponding RenderFrameHostImpls that
  // the renderer will be activating their documents.
  void Activate(
      StoredPage::RenderViewHostImplSafeRefSet& render_view_hosts_to_activate,
      std::optional<blink::ViewTransitionState> view_transition_state,
      base::OnceCallback<void(base::TimeTicks)> completion_callback);

  // Prerender2:
  // Dispatches load events that were deferred to be dispatched after
  // activation. Please note that this should only be called on prerender
  // activation.
  void MaybeDispatchLoadEventsOnPrerenderActivation();

  // Hide or show the browser controls for the given Page, based on allowed
  // states, desired state and whether the transition should be animated or
  // not.
  void UpdateBrowserControlsState(
      cc::BrowserControlsState constraints,
      cc::BrowserControlsState current,
      bool animate,
      const std::optional<cc::BrowserControlsOffsetTagModifications>&
          offset_tag_modifications);

  float GetPageScaleFactor() const;

  void set_load_progress(double load_progress) {
    load_progress_ = load_progress;
  }
  double load_progress() const { return load_progress_; }

  // The env() variables for virtual keyboard overlay and context menu insets
  // are page-level, and don't get propagated into iframes, because a) that
  // would be a cross-site info leak, and b) it's hard to know exactly how they
  // would be used in that context.
  // See https://github.com/w3c/csswg-drafts/issues/4670.
  void NotifyVirtualKeyboardOverlayRect(const gfx::Rect& keyboard_rect);

  // This call will "show interest" in the Element with the provided DOMNodeID,
  // which is presumed to have an `interestfor` attribute.
  void ShowInterestInElement(int);

  void SetVirtualKeyboardMode(ui::mojom::VirtualKeyboardMode mode);
  ui::mojom::VirtualKeyboardMode virtual_keyboard_mode() const {
    return virtual_keyboard_mode_;
  }

  const std::string& GetEncoding() { return canonical_encoding_; }
  void UpdateEncoding(const std::string& encoding_name);

  // Returns the keyboard layout mapping.
  base::flat_map<std::string, std::string> GetKeyboardLayoutMap();


  // See documentation for |credentialless_iframes_nonce_|.
  const base::UnguessableToken& credentialless_iframes_nonce() const {
    return credentialless_iframes_nonce_;
  }

  // Take ownership of the loading memory tracker from the NavigationRequest
  // that navigated to this page.
  void TakeLoadingMemoryTracker(NavigationRequest* request);
  // If we have a loading memory tracker, close it as loading has stopped. It
  // will asynchronously receive the statistics from the GPU process, and update
  // UMA stats.
  void ResetLoadingMemoryTracker();
  // If we have a loading memory tracker, cancel it as loading hasn't stopped
  // and the page is being navigated away from. UMA stats will not be recorded.
  void CancelLoadingMemoryTracker();

  bool is_overriding_user_agent() { return is_overriding_user_agent_; }
  void set_is_overriding_user_agent(bool is_overriding_user_agent) {
    is_overriding_user_agent_ = is_overriding_user_agent;
  }

  // Use to set and release |last_commit_params_|, see documentation of the
  // member for more details. This is only called for outermost pages.
  void SetLastCommitParams(
      mojom::DidCommitProvisionalLoadParamsPtr commit_params);
  mojom::DidCommitProvisionalLoadParamsPtr TakeLastCommitParams();

  // Sets the sandbox origin token for a sandboxed popup (main frame).
  // Generated by `SetOriginDependentStateOfNewFrame()` when the main frame is
  // sandboxed. Consumed by `TakeSandboxOriginTokenForPopup()` when building
  // `CreateNewWindowReply` or `CreateViewParams`.
  void SetSandboxOriginTokenForPopup(
      std::unique_ptr<base::UnguessableToken> token) {
    sandbox_origin_token_for_popup_ = std::move(token);
  }

  // Takes ownership of the sandbox origin token for a popup. Returns nullptr
  // if no token was set (e.g., non-sandboxed popup or already consumed).
  std::unique_ptr<base::UnguessableToken> TakeSandboxOriginTokenForPopup() {
    return std::move(sandbox_origin_token_for_popup_);
  }

 private:
  void DidActivateAllRenderViewsForPrerendering(
      base::OnceCallback<void(base::TimeTicks)> completion_callback);

  // This method is needed to ensure that PageImpl can both implement a Page's
  // method and define a new GetMainDocument(). Please refer to page.h for more
  // details.
  RenderFrameHost& GetMainDocumentHelper() override;

  // True if we've received a notification that the onload() handler has
  // run for the main document.
  bool is_on_load_completed_in_main_document_ = false;

  // True if we have already recorded the PartitionedCookiePresent UKM event
  // for this page.
  bool has_recorded_partitioned_cookie_use_ = false;

  // Renderer-side presentation timestamp of the first contentful paint in the
  // main document.
  std::optional<base::TimeTicks> first_contentful_paint_in_main_document_time_;

  // True if we've received a notification that the window.document element
  // became available for the main document.
  bool is_main_document_element_available_ = false;

  // True if plugin zoom level is set for the main document.
  bool uses_temporary_zoom_level_ = false;

  // Overall load progress of this Page. Initial load progress value is 0.0
  // before the load has begun.
  double load_progress_ = 0.0;

  // Web application manifest URL for this page.
  // See https://w3c.github.io/manifest/#web-application-manifest.
  //
  // This is non-nullopt when the page gets an update of the manifest URL. It
  // can be the empty URL when the manifest url is removed and a non-empty
  // URL when it has a valid URL for the manifest. If this is non-nullopt,
  // WebContentsObserver::DidUpdateWebManifestURL() will be called
  // (either immediately on document load, or on activation in the case
  // of a prerendered page).
  //
  // nullopt indicates that the page did not get an update of the
  // manifest URL, and DidUpdateWebManifestURL() will not be called.
  std::optional<GURL> manifest_url_;

  // Candidate favicon URLs. Each page may have a collection and will be
  // displayed when active (i.e., upon activation for prerendering).
  std::vector<blink::mojom::FaviconURLPtr> favicon_urls_;

  // The capture handle configuration for this page. This allows the app in this
  // page to opt-in to exposing information to apps that capture it.
  blink::mojom::CaptureHandleConfig capture_handle_config_;

  // Whether the first visually non-empty paint has occurred.
  bool did_first_visually_non_empty_paint_ = false;

  // A 64 bit token used as the initial hash value for canvas noising per page,
  // where nullopt indicates canvas noising should not be enabled for the page.
  // The initial hash value will be a combination of the main frame's origin and
  // the browser context (see
  // content/browser/fingerprinting_protection/canvas_noise_token_data.h for
  // more details). Modifying the token value must happen prior to the commit of
  // the page's main frame navigation and will be communicated to the renderer
  // process during commit via CommitNavigationParams.
  std::optional<blink::NoiseToken> canvas_noise_token_ = std::nullopt;

  // The theme color for the underlying document as specified
  // by theme-color meta tag.
  std::optional<SkColor> main_document_theme_color_;

  // The background color for the underlying document as computed by CSS.
  std::optional<SkColor> main_document_background_color_;

  // The inferred color scheme of the document.
  std::optional<blink::mojom::PreferredColorScheme>
      main_document_inferred_color_scheme_;

  // Contents MIME type for the main document. It can be used to check whether
  // we can do something for special contents.
  std::string contents_mime_type_;

  // Fenced frames:
  // Any fenced frames created within this page will access this map.
  FencedFrameURLMapping fenced_frame_urls_map_;


  // This class is owned by the main RenderFrameHostImpl and it's safe to keep a
  // reference to it.
  const raw_ref<RenderFrameHostImpl> main_document_;

  // SourceId of the navigation in this page's main frame. Note that a same
  // document navigation is the only case where this source id can change, since
  // all other navigations create a new PageImpl instance.
  ukm::SourceId last_main_document_source_id_ = ukm::kInvalidSourceId;

  // This page is owned by the RenderFrameHostImpl, which in turn does not
  // outlive the delegate (the contents).
  const raw_ref<PageDelegate> delegate_;

  // Prerender2: The start time of the activation navigation for prerendering,
  // which is passed to the renderer process, and will be accessible in the
  // prerendered page as PerformanceNavigationTiming.activationStart. Set after
  // navigation commit.
  // TODO(b:291867362): Plumb NavigationRequest to
  // RenderFrameHostManager::CommitPending and remove this.
  std::optional<base::TimeTicks> activation_start_time_;

  // True if cross origin iframe prerender is allowed.
  bool is_cross_origin_subframe_prerender_allowed_ = false;

  // The resizing mode requested by Blink for the virtual keyboard.
  ui::mojom::VirtualKeyboardMode virtual_keyboard_mode_ =
      ui::mojom::VirtualKeyboardMode::kUnset;

  // The last reported character encoding, not canonicalized.
  std::string last_reported_encoding_;
  // The canonicalized character encoding.
  std::string canonical_encoding_;

  // Nonce to be used for initializing the storage key and the network isolation
  // key of credentialless iframes which are children of this page's main
  // document.
  const base::UnguessableToken credentialless_iframes_nonce_ =
      base::UnguessableToken::Create();

  // This is only set for primary pages.
  // Created by NavigationRequest; ownership is maintained until the frame has
  // stopped loading, or we navigate away from the page before it finishes
  // loading.
  std::unique_ptr<viz::PeakGpuMemoryTracker> loading_memory_tracker_;

  // Whether the page is overriding the user agent or not.
  bool is_overriding_user_agent_ = false;

  // This is used to re-commit when restoring a page from the BackForwardCache
  // or when activating a prerendered page, with the same params as the original
  // navigation.
  mojom::DidCommitProvisionalLoadParamsPtr last_commit_params_;

  // Token used to deterministically generate the opaque origin for the
  // initial empty document of a sandboxed popup (e.g.,
  // `window.open('', '', 'sandbox=allow-scripts')`). Sent to the renderer via
  // `mojom::CreateNewWindowReply` and `mojom::CreateViewParams` so both
  // processes derive the same origin.
  //
  // Generated on-demand in `SetOriginDependentStateOfNewFrame()` when the main
  // frame is sandboxed. Consumed (reset to nullptr) by
  // `TakeSandboxOriginTokenForPopup()` when building the reply.
  std::unique_ptr<base::UnguessableToken> sandbox_origin_token_for_popup_;

#if BUILDFLAG(IS_ANDROID)
  // For each C++ Page, there is a Java counterpart. It is the JNI bridge in
  // between the two.
  std::unique_ptr<PageProxy> page_proxy_;
#endif

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

}  // namespace content

#endif  // CONTENT_BROWSER_RENDERER_HOST_PAGE_IMPL_H_
