// Copyright 2024 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_NAVIGATION_TRANSITIONS_NAVIGATION_TRANSITION_DATA_H_
#define CONTENT_BROWSER_RENDERER_HOST_NAVIGATION_TRANSITIONS_NAVIGATION_TRANSITION_DATA_H_

#include <optional>

#include "base/types/strong_alias.h"
#include "third_party/blink/public/common/tokens/tokens.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h"

namespace content {

// Holds the relevant information about a navigation transition. Just like the
// `NavigationEntryScreenshot`, this struct is not persistent on the
// `NavigationEntry` (i.e. can't be restored).
class NavigationTransitionData {
 public:
  // Used for recording UMA for cache hit/miss.
  // These values are persisted to logs. Entries should not be renumbered and
  // numeric values should never be reused.
  //
  // LINT.IfChange(CacheHitOrMissReason)
  enum class CacheHitOrMissReason {
    // The screenshot is captured and placed in the cache.
    kCacheHit = 0,

    // Sent a request to capture a screenshot in
    // `CaptureNavigationEntryScreenshotForCrossDocumentNavigations`.
    kSentScreenshotRequest = 1,

    // Received an empty bitmap when capturing the screenshot from web pages.
    kCapturedEmptyBitmapFromWebPage = 2,

    // [DEPRECATED] Screenshot is not captured for subframes.
    // kCacheMissSubframe = 3,

    // Screenshot was evicted because of memory constraints.
    kCacheMissEvicted = 4,

    // Screenshot was purged along with all other screenshots because of memory
    // pressure.
    kCacheMissPurgedMemoryPressure = 5,

    // Screenshot is not available because the app was restarted/killed in the
    // background and we don't persist them to disk.
    // TODO(baranerf): Implement tracking of this case.
    kCacheMissColdStart = 6,

    // Screenshot is not captured for cloned navigations.
    kCacheMissClonedNavigationEntry = 7,

    // Screenshot is not captured for embedded pages.
    kCacheMissEmbeddedPages = 8,

    // [DEPRECATED] Screenshot is not captured since the page has
    // Cache-Control: no-store
    // kCacheMissCCNS = 9,

    // Screenshot was evicted because the tab was invisible for a long duration.
    kCacheMissInvisible = 10,

    // Screenshot was not captured because user had prefers-reduced-motion
    // turned on when the navigation committed.
    kCacheMissPrefersReducedMotion = 11,

    // Screenshot is not displayed since it is captured in a different
    // orientation (horizontal or vertical) compared to the current screen
    // orientation.
    kCacheMissScreenshotOrientation = 12,

    // Screenshot is not captured when the page is crashed.
    kNavigateAwayFromCrashedPage = 13,
    kNavigateAwayFromCrashedPageNoEarlySwap = 14,

    // Screenshot is not captured when the root window or compositor is
    // detached.
    kNoRootWindowOrCompositor = 15,

    // The browser isn't embedding a valid `viz::LocalSurfaceID` when we try
    // to capture the screenshot from the browser.
    kBrowserNotEmbeddingValidSurfaceId = 16,

    // We only cache screenshots for navigations targeting the primary main
    // frame.
    kCacheMissNonPrimaryMainFrame = 17,

    // Received an empty bitmap from embedder when capturing the screenshot.
    kCapturedEmptyBitmapFromEmbedder = 18,

    // Forward navigation transitions were not supported when doing a back
    // history navigation out of this entry.
    kForwardTransitionAnimationNotSupported = 19,

    // A screenshot was captured into a texture, but the read back failed.
    kCacheMissFailedReadBack = 20,

    // The screenshot size exceeds the maximum cache size.
    kCacheMissSizeExceedsLimit = 21,

    kMaxValue = kCacheMissSizeExceedsLimit
  };
  // LINT.ThenChange(//tools/metrics/histograms/metadata/navigation/enums.xml:NavigationTransitionCacheHitOrMissReason)

  NavigationTransitionData();
  ~NavigationTransitionData() = default;
  NavigationTransitionData(NavigationTransitionData&&) = delete;
  NavigationTransitionData& operator=(NavigationTransitionData&&) = delete;
  NavigationTransitionData(const NavigationTransitionData&) = delete;
  NavigationTransitionData& operator=(const NavigationTransitionData&) = delete;

  void SetSameDocumentNavigationEntryScreenshotToken(
      const std::optional<blink::SameDocNavigationScreenshotDestinationToken>&
          token);

  const std::optional<blink::SameDocNavigationScreenshotDestinationToken>&
  same_document_navigation_entry_screenshot_token() const {
    return same_document_navigation_entry_screenshot_token_;
  }

  using UniqueId = base::StrongAlias<class NavigationTransitionDataIdTag, int>;
  constexpr static UniqueId kInvalidId = UniqueId(-1);
  UniqueId unique_id() const { return unique_id_; }

  void set_is_copied_from_embedder(bool is_copied_from_embedder) {
    is_copied_from_embedder_ = is_copied_from_embedder;
  }
  bool is_copied_from_embedder() const { return is_copied_from_embedder_; }

  void set_cache_hit_or_miss_reason(
      std::optional<CacheHitOrMissReason> cache_hit_or_miss_reason) {
    cache_hit_or_miss_reason_ = cache_hit_or_miss_reason;
  }
  std::optional<CacheHitOrMissReason> cache_hit_or_miss_reason() const {
    return cache_hit_or_miss_reason_;
  }

  int copy_output_request_sequence() const {
    return copy_output_request_sequence_number_;
  }
  void increment_copy_output_request_sequence() {
    ++copy_output_request_sequence_number_;
  }

  const SkBitmap& favicon() const { return favicon_; }
  void set_favicon(const SkBitmap& favicon) { favicon_ = favicon; }

 private:
  // A unique ID for this struct. This is synonymous to the owning
  // `NavigationEntry::GetUniqueID()`, and is used to uniquely identify a
  // screenshot and its owning navigation entry.
  //
  // See crbug.com/376944343: the navigation entry's unique ID is actually not
  // unique (`NavigationEntryImpl::set_unique_id()`), which leads to unexpected
  // behavior.
  const UniqueId unique_id_;

  // Whether this screenshot is supplied by the embedder.
  bool is_copied_from_embedder_ = false;

  // Used to map a screenshot for the last frame of this navigation entry
  // captured in Viz and sent back to the browser process. The token is set when
  // `DidCommitSameDocumentNavigation` is received in the browser process from
  // the renderer; and reset when its corresponding screenshot is received by
  // the browser process from Viz.
  std::optional<blink::SameDocNavigationScreenshotDestinationToken>
      same_document_navigation_entry_screenshot_token_;

  // Tracks copy output requests sent for this navigation entry. The embedder
  // increments this number when the entry is committed so that the results of
  // stale copy output requests will be ignored, preventing them from
  // interfering with future requests.
  int copy_output_request_sequence_number_ = 0;

  // TODO(https://crbug.com/40262175): We might want to move the
  // `NavigationEntryScreenshot` here as well when we make the screenshot
  // disk-persistent.

  // Used to record UMA in `BackForwardTransitionAnimator`
  std::optional<CacheHitOrMissReason> cache_hit_or_miss_reason_;

  // The favicon used to compose the fallback UX.
  SkBitmap favicon_;
};

}  // namespace content

#endif  // CONTENT_BROWSER_RENDERER_HOST_NAVIGATION_TRANSITIONS_NAVIGATION_TRANSITION_DATA_H_
