// Copyright 2022 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_BROWSER_PRELOADING_H_
#define CONTENT_PUBLIC_BROWSER_PRELOADING_H_

#include <cstdint>
#include <string_view>

#include "content/common/content_export.h"

namespace content {

// If you change any of the following enums or static variables, please follow
// the process in go/preloading-dashboard-updates to update the mapping
// reflected in dashboard, or if you are not a Googler, please file an FYI bug
// on https://crbug.new with component Internals>Preload.

// Defines the different types of preloading speedup techniques. Preloading is a
// holistic term to define all the speculative operations the browser does for
// loading content before a page navigates to make navigation faster.
//
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
//
// LINT.IfChange
enum class PreloadingType {
  // No PreloadingType is present. This may include other preloading operations
  // which will be added later to PreloadingType as we expand.
  kUnspecified = 0,

  // Establishes a connection (including potential TLS handshake) with an
  // origin.
  kPreconnect = 2,

  // Prefetch operation comes after Preresolve and Preconnect. Chrome spends a
  // significant amount time idle, waiting for the main frame HTML fetch. The
  // prefetch preloading operation fetches the HTML before the user starts
  // navigating in order to load the page much faster.
  kPrefetch = 3,

  // This speedup technique comes with the most impact and overhead. We preload
  // and render a page before the user navigates to it. This will make the next
  // page navigation nearly instant as we would activate a fully prepared
  // RenderFrameHost. Both resources are fetched and JS is executed.
  kPrerender = 4,

  // Like prerendering, it fetches resources in advance; but unlike prerendering
  // it does not execute JavaScript or render any part of the page in advance.
  // NoState prefetch only supports the GET HTTP method and doesn't cache
  // resources with the no-store cache-control header.
  kNoStatePrefetch = 5,

  // Link-Preview loads a page with prerendering infrastructures in a dedicated
  // mini tab so that users can take a look at the content before visiting it.
  // TODO(b:291867362): This is not used by the current implementation,
  // but might be reused in the future.
  kLinkPreview = 6,

  // Like prerendering, it fetches resources in advance; but unlike prerendering
  // it does not execute JavaScript. It is designed to replace kNoStatePrefetch.
  kPrerenderUntilScript = 7,
};
// LINT.ThenChange()

// Defines various triggering mechanisms which triggers different preloading
// operations mentioned in preloading.h. The integer portion is used for UKM
// logging, and the string portion is used to dynamically compose UMA histogram
// names. Embedders are allowed to define more predictors.
class CONTENT_EXPORT PreloadingPredictor {
 public:
  constexpr PreloadingPredictor(int64_t ukm_value, std::string_view name)
      : ukm_value_(ukm_value), name_(name) {}
  int64_t ukm_value() const { return ukm_value_; }
  std::string_view name() const { return name_; }

  constexpr bool operator==(const PreloadingPredictor& other) const {
    // There's no need to compare name_ since every PreloadingPredictor has a
    // distinct ukm_value_.
    return other.ukm_value_ == ukm_value_;
  }

  constexpr std::strong_ordering operator<=>(
      const PreloadingPredictor& other) const {
    // There's no need to compare name_ since every PreloadingPredictor has a
    // distinct ukm_value_.
    return other.ukm_value_ <=> ukm_value_;
  }

 private:
  int64_t ukm_value_;
  std::string_view name_;
};

// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
//
// Advance numbering by +1 when adding a new element.
//
// To add additional predictors in content-internals and embedders, wrap the
// new `PreloadingPredictor` in the corresponding namespaces:
// - `content_preloading_predictor` for content-internals;
// - `chrome_preloading_predictor` for Chrome.
//
// See `content/browser/preloading/preloading.h` and
// `chrome/browser/preloading/chrome_preloading.h` for examples.
//
// Please limit content-public `PreloadingPredictor` between 0 to 49
// (inclusive) as 50 and beyond are reserved for content-internal and embedders.
// Both the value and the name should be unique across all the namespaces.
//
// The embedder `PreloadingPredictor` definitions should start at 100 (see
// `chrome/browser/preloading/chrome_preloading.h` for example).
//
// LINT.IfChange
namespace preloading_predictor {
// No PreloadingTrigger is present. This may include the small percentage of
// usages of browser triggers, link-rel, OptimizationGuideService e.t.c which
// will be added later as a separate elements.
inline constexpr PreloadingPredictor kUnspecified(0, "Unspecified");

// Preloading is triggered by OnPointerDown event heuristics.
inline constexpr PreloadingPredictor kUrlPointerDownOnAnchor(
    1,
    "UrlPointerDownOnAnchor");

// Preloading is triggered by OnPointerHover event heuristics.
inline constexpr PreloadingPredictor kUrlPointerHoverOnAnchor(
    2,
    "UrlPointerHoverOnAnchor");

// Preloading was triggered by embedding a keyword for the rel attribute of
// the <link> HTML element to hint to browsers that the user might need it for
// next navigation.
inline constexpr PreloadingPredictor kLinkRel(3, "LinkRel");

// When overscroll that could trigger a back navigation starts.
inline constexpr PreloadingPredictor kBackGestureNavigation(
    4,
    "BackGestureNavigation");

// Preloading heuristics ML model.
inline constexpr PreloadingPredictor kPreloadingHeuristicsMLModel(
    5,
    "PreloadingHeuristicsMLModel");

// Preloading is triggered by a deterministic viewport-based heuristic for
// the "moderate" eagerness. Note that this predictor does nothing for the other
// eagerness like "eager" while the name of this predictor is general for
// historical reasons.
inline constexpr PreloadingPredictor kModerateViewportHeuristic(
    6,
    "ViewportHeuristic");

// Preloading is triggered by a deterministic viewport-based heuristic for
// the "eager" eagerness.
inline constexpr PreloadingPredictor kEagerViewportHeuristic(
    7,
    "EagerViewportHeuristic");
}  // namespace preloading_predictor
// LINT.ThenChange()

// Defines if a preloading operation is eligible for a given preloading
// trigger.
//
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
//
// LINT.IfChange
enum class PreloadingEligibility {
  // Preloading operation is not defined for a particular preloading trigger
  // prediction.
  kUnspecified = 0,

  // Preloading operation is eligible and is triggered for a preloading
  // predictor.
  kEligible = 1,

  // Preloading operation could be ineligible if it is not triggered
  // because some precondition was not satisfied. Preloading here could
  // be ineligible due to various reasons subjective to the preloading
  // operation like the following.
  // These values are used in both //chrome and //content after integration with
  // various preloading features.

  // Preloading operation was ineligible because preloading was disabled.
  kPreloadingDisabled = 2,

  // Preloading operation was ineligible because it was triggered from the
  // background or a hidden page.
  kHidden = 3,

  // Preloading operation was ineligible because it was invoked for cross origin
  // navigation while preloading was restricted to same-origin navigations.
  // (It's plausible that some preloading mechanisms in the future could work
  // for cross-origin navigations as well.)
  kCrossOrigin = 4,

  // Preloading was ineligible due to low memory restrictions.
  kLowMemory = 5,

  // Preloading was ineligible as running JavaScript was disabled for the URL.
  kJavascriptDisabled = 6,

  // Preloading was ineligible because the Data Saver setting was enabled.
  kDataSaverEnabled = 7,

  // Preloading was ineligible because it was triggered from a page that has an
  // effective url.
  kHasEffectiveUrl = 8,

  // Preloading was ineligible because only single renderer process is only
  // allowed.
  kSingleProcess = 9,

  // Preloading was ineligible for link-rel:next URLs.
  kLinkRelNext = 10,

  // Preloading was ineligible due to the page having third party cookies.
  kThirdPartyCookies = 11,

  // Preloading was ineligible due to being called before we reached the time
  // limit to invoke one more preloading operation.
  kPreloadingInvokedWithinTimelimit = 12,

  // Preloading was ineligible because we can't create a new renderer process
  // for exceeding the renderer processes limit.
  kRendererProcessLimitExceeded = 13,

  // Preloading was ineligible because the Battery Saver setting was enabled.
  kBatterySaverEnabled = 14,

  // Preloading was ineligible because it is not supported for WebContents.
  kPreloadingUnsupportedByWebContents = 15,

  // Preloading was ineligible because it was triggered under memory pressure.
  kMemoryPressure = 16,

  // Preloading was ineligible because some DevTools client temporarily
  // disabled.
  kPreloadingDisabledByDevTools = 17,

  // Preloading was ineligible because some triggers only allows https.
  kHttpsOnly = 18,

  // Preloading was ineligible for non-http(s).
  kHttpOrHttpsOnly = 19,

  // Preloading was ineligible because the network is too slow.
  kSlowNetwork = 20,

  // Previously reserved for cases where prerendering was disabled because the
  // v8 optimizer was disabled by site settings. This was previously causing
  // crashes, but the crash has since been fixed. See https://crbug.com/40076091
  // for details.
  // kV8OptimizerDisabled = 21,

  // The initial URL is controlled by a ServiceWorker and then redirected
  // (https://crbug.com/399819894).
  kRedirectFromServiceWorker = 22,

  // The initial URL is redirected to a URL controlled by a ServiceWorker
  // (https://crbug.com/399819894).
  // This case was previously counted as `kUserHasServiceWorker`.
  kRedirectToServiceWorker = 23,

  // The url was not eligible to be prefetched because there was a registered
  // service worker with no fetch handler (when
  // `kPrefetchServiceWorkerNoFetchHandlerFix` is enabled,
  // https://crbug.com/379076354).
  // This case was previously counted as `kUserHasServiceWorker`.
  // Even after the initial ServiceWorker support (https://crbug.com/40947546),
  // this will be still used for ServiceWorker-ineligible prefetches.
  kUserHasServiceWorkerNoFetchHandler = 24,

  // ##########################################################################
  // The range 50-99 is reserved for corresponding values in `PrefetchStatus`.
  // See corresponding values in PrefetchStatus for documentation.
  // Note: The values outside this range also can have corresponding values in
  // `PrefetchStatus`, e.g. `kDataSaverEnabled`.
  // When adding a new prefetch-related `PreloadingEligibility` value, add it
  // outside this range, because this range 50-99 is already full.
  kUserHasCookies = 55,
  kUserHasServiceWorker = 56,
  // This is similar to `kHttpsOnly`, but separately defined here to keep
  // existing metrics values, for cases corresponding to
  // `PrefetchStatus::kPrefetchIneligibleSchemeIsNotHttps`.
  kSchemeIsNotHttps = 57,
  kNonDefaultStoragePartition = 59,
  kRetryAfter = 77,
  kPrefetchProxyNotAvailable = 78,
  kHostIsNonUnique = 86,
  kExistingProxy = 88,
  //  OBSOLETE: kBrowserContextOffTheRecord = 89,
  kSameSiteCrossOriginPrefetchRequiredProxy = 96,

  // ##########################################################################
  // The range 100-199 is reserved for embedders.
  // This constant is used to define the value beyond which embedders can add
  // more enums.
  kPreloadingEligibilityContentEnd = 100,

  // ##########################################################################
  // The range 200-249 is another range reserved for content internal values,
  // namely `PrerenderBackNavigationEligibility`.
  kPreloadingEligibilityContentStart2 = 200,
  kPreloadingEligibilityContentEnd2 = 250,
};
// LINT.ThenChange()

// The outcome of the holdback check. This is not part of eligibility status to
// clarify that this check needs to happen after we are done verifying the
// eligibility of a preloading attempt. In general, eligibility checks can be
// reordered, but the holdback check always needs to come after verifying that
// the preloading attempt was eligible.
//
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
//
// LINT.IfChange
enum class PreloadingHoldbackStatus {
  // The preloading holdback status has not been set yet. This should only
  // happen when the preloading attempt was not eligible.
  kUnspecified = 0,

  // The preload was eligible to be triggered and was not disabled via a field
  // trial holdback. Given enough time, the preload will trigger.
  kAllowed = 1,

  // The preload was eligible to be triggered but was disabled via a field
  // trial holdback. This is useful for measuring the impact of preloading.
  kHoldback = 2,
};
// LINT.ThenChange()

// Defines the post-triggering outcome once the preloading operation is
// triggered.
//
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused. Please update
// "PreloadingTriggeringOutcome" in `tools/metrics/histograms/enums.xml` when
// new enums are added.
//
// LINT.IfChange
enum class PreloadingTriggeringOutcome {
  // The outcome is kUnspecified for attempts that were not triggered due to
  // various ineligibility reasons or due to a field trial holdback.
  kUnspecified = 0,

  // For attempts that we wanted to trigger, but for which we already had an
  // equivalent attempt (same preloading operation and same URL/target) in
  // progress.
  kDuplicate = 2,

  // Preloading was triggered and did not fail, but did not complete in time
  // before the user navigated away (or the browser was shut down).
  kRunning = 3,

  // Preloading triggered and is ready to be used for the next navigation. This
  // doesn't mean preloading attempt was actually used.
  kReady = 4,

  // Preloading was triggered, completed successfully and was used for the next
  // navigation.
  kSuccess = 5,

  // Preloading was triggered but encountered an error and failed.
  kFailure = 6,

  // Some preloading features don't provide a way to keep track of the
  // progression of the preloading attempt. In those cases, we just log
  // kTriggeredButOutcomeUnknown, meaning that preloading was triggered but we
  // don't know if it was successful.
  kTriggeredButOutcomeUnknown = 7,

  // A preloading feature (e.g., prefetch) completed successfully and was
  // upgraded to Prerender. For PreloadingType::kPrefetch this is different from
  // kSuccess reason which only tracks when the navigation directly uses the
  // Prefetch. Please note that this doesn't ensure Prerender was successful.
  kTriggeredButUpgradedToPrerender = 8,

  // Preloading was triggered but was pending for starting its initial
  // navigation.
  kTriggeredButPending = 9,

  // Used for triggers that do not perform a preloading operation. This may be
  // used for a trigger which we're evaluating the accuracy of before actually
  // having it preload.
  kNoOp = 10,

  // Required by UMA histogram macro.
  kMaxValue = kNoOp,
};
// LINT.ThenChange()

// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
//
// LINT.IfChange
enum class PreloadingFailureReason {
  // The failure reason is unspecified if the triggering outcome is not
  // kFailure.
  kUnspecified = 0,

  // This constant is used to define the value from which specifying preloading
  // types can add more enums beyond this value. We mask it by 100 to avoid
  // usage of the same numbers for logging. The semantics of values beyond 100
  // can vary by preloading type (for example 101 might mean "the page was
  // destroyed" for prerender, but "the user already had cookies for a
  // cross-origin prefetch"
  // for prefetch).
  //
  // Values between kPreloadingFailureReasonCommonEnd (included) and
  // kPreloadingFailureReasonContentEnd (excluded) are reserved for enums
  // defined in //content.
  kPreloadingFailureReasonCommonEnd = 100,

  // Values beyond this value are for failure reasons defined by the embedder.
  // The semantics of those values can vary by preloading type (1000 can mean
  // "limit exceeded" for preconnect but "cancelled" for prerender).
  kPreloadingFailureReasonContentEnd = 1000,
};
// LINT.ThenChange()

// Types of URL match:
// Exact match: the URLs are matching exactly.
// NoVarySearch match: No-Vary-Search header allows for inexact match by
// ignoring some query parameters, or the order of query parameters present
// in URLs.
// Custom match: custom URL matching provided by a url matching predicate.
enum class UrlMatchType { kExact, kNoVarySearch, kURLPredicateMatch };

}  // namespace content

#endif  // CONTENT_PUBLIC_BROWSER_PRELOADING_H_