// 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 COMPONENTS_PAGE_LOAD_METRICS_GOOGLE_BROWSER_GWS_PAGE_LOAD_METRICS_OBSERVER_H_
#define COMPONENTS_PAGE_LOAD_METRICS_GOOGLE_BROWSER_GWS_PAGE_LOAD_METRICS_OBSERVER_H_

#include "components/google/core/common/google_util.h"
#include "components/page_load_metrics/browser/page_load_metrics_observer.h"
#include "components/page_load_metrics/google/browser/prerender_prewarm_navigation_data.h"
#include "content/public/browser/navigation_handle_timing.h"
#include "net/http/http_connection_info.h"

namespace content {
class BrowserContext;
}  // namespace content

namespace internal {
// Exposed for tests.

extern const char kHistogramGWSNavigationStartToFinalRequestStart[];
extern const char kHistogramGWSNavigationStartToFinalResponseStart[];
extern const char kHistogramGWSFinalRequestStartToFinalResponseStart[];
extern const char kHistogramGWSNavigationStartToFinalLoaderCallback[];
extern const char kHistogramGWSNavigationStartToFirstRequestStart[];
extern const char kHistogramGWSNavigationStartToFirstResponseStart[];
extern const char kHistogramGWSFirstRequestStartToFirstResponseStart[];
extern const char kHistogramGWSFirstRequestStartToFinalResponseStart[];
extern const char kHistogramGWSNavigationStartToFirstLoaderCallback[];
extern const char kHistogramGWSNavigationStartToOnComplete[];
extern const char kHistogramGWSAcceptCHFrameReceived[];
extern const char kHistogramGWSOnConnectedCalled[];
extern const char kHistogramGWSMaxStreamLimitPendingDelay[];
extern const char kHistogramGWSFastFetchOpportunityTimeLoaderStart[];
extern const char kHistogramGWSFastFetchOpportunityTimeFetchStart[];

extern const char kHistogramGWSConnectTimingFirstRequestDomainLookupDelay[];
extern const char
    kHistogramGWSConnectTimingFirstRequestDomainLookupDelaySecureDns[];
extern const char
    kHistogramGWSConnectTimingFirstRequestDomainLookupDelayInsecureDns[];
extern const char
    kHistogramGWSConnectTimingFirstRequestResolutionDetailsTaskCompletionDelay
        [];
extern const char
    kHistogramGWSConnectTimingFirstRequestDohDetailsSessionSource[];
extern const char
    kHistogramGWSConnectTimingFirstRequestDohDetailsConnectionInfo[];
extern const char kHistogramGWSConnectTimingFirstRequestConnectDelay[];
extern const char kHistogramGWSConnectTimingFirstRequestSslDelay[];
extern const char kHistogramGWSConnectTimingFinalRequestDomainLookupDelay[];
extern const char kHistogramGWSConnectTimingFinalRequestConnectDelay[];
extern const char kHistogramGWSConnectTimingFinalRequestSslDelay[];

extern const char kHistogramGWSInteractionToActualNavigationStart[];
extern const char kHistogramGWSInteractionToNavigationStart[];
extern const char kHistogramGWSInteractionToAFTEnd[];
extern const char kHistogramGWSNavigationStartToNavigationCommitSent[];
extern const char kHistogramGWSNavigationCommitSentToParseStart[];
extern const char kHistogramGWSParseStartToFirstContentfulPaint[];
extern const char kHistogramGWSParseStartToDOMContentLoaded[];
extern const char kHistogramGWSParseStartToLargestContentfulPaint[];

extern const char kHistogramGWSActualNavigationStartToNavigationStart[];
extern const char kHistogramGWSActualNavigationStartToNavigationCommitSent[];
extern const char kHistogramGWSActualNavigationStartToParseStart[];
extern const char kHistogramGWSActualNavigationStartToFirstContentfulPaint[];
extern const char kHistogramGWSActualNavigationStartToDOMContentLoaded[];
extern const char kHistogramGWSActualNavigationStartToLargestContentfulPaint[];
extern const char kHistogramGWSActualNavigationStartToAFTEnd[];
extern const char
    kHistogramGWSActualNavigationStartToAFTEndWithPreNavigationLatency[];

extern const char kHistogramGWSAFTEnd[];
extern const char kHistogramGWSAFTEndWithPreNavigationLatency[];
extern const char kHistogramGWSAFTStart[];

extern const char kHistogramGWSFirstContentfulPaint[];
extern const char kHistogramGWSLargestContentfulPaint[];
extern const char kHistogramGWSParseStart[];
extern const char kHistogramGWSConnectStart[];
extern const char kHistogramGWSDomainLookupStart[];
extern const char kHistogramGWSDomainLookupEnd[];
extern const char kHistogramGWSBeforeUnloadExecutionMode[];

extern const char kHistogramServiceWorkerParseStartSearch[];
extern const char kHistogramServiceWorkerFirstContentfulPaintSearch[];
extern const char
    kHistogramServiceWorkerParseStartToFirstContentfulPaintSearch[];
extern const char kHistogramServiceWorkerDomContentLoadedSearch[];
extern const char kHistogramServiceWorkerLoadSearch[];
extern const char kHistogramNoServiceWorkerFirstContentfulPaintSearch[];
extern const char
    kHistogramNoServiceWorkerParseStartToFirstContentfulPaintSearch[];
extern const char kHistogramNoServiceWorkerDomContentLoadedSearch[];
extern const char kHistogramNoServiceWorkerLoadSearch[];

extern const char kTraverseNavigation[];
extern const char kRestoreNavigation[];
extern const char kNonRestoreNavigation[];
extern const char kStartedFromContextMenu[];

extern const char kHistogramPrerenderHostReused[];
extern const char kHistogramPrerenderPrewarmNavigationStatus2[];
extern const char kHistogramGWSPrerenderNavigationToActivation[];
extern const char kHistogramGWSActivationToFirstContentfulPaint[];
extern const char kHistogramGWSActivationToLargestContentfulPaint[];
extern const char kHistogramGWSHadPriorPrewarmCommitStatus2[];
extern const char kHistogramSiteInstanceProcessAssignment2[];

extern const char kHistogramPrerenderSuffix[];
extern const char kHistogramNonPrerenderSuffix[];
extern const char kHistogramDuplicateIgnoredSuffix[];

}  // namespace internal

class GWSPageLoadMetricsObserver
    : public page_load_metrics::PageLoadMetricsObserver {
 public:
  // The source of the navigation. Specifically keeps track of special cases
  // such as the navigtation started from NewTabPage, or if the navigation
  // started in the background.
  //
  // These values are persisted to logs. Entries should not be renumbered
  // and numeric values should never be reused.
  //
  // LINT.IfChange(NavigationSourceType)
  enum NavigationSourceType {
    kUnknown,
    kFromNewTabPage,
    kStartedInBackground,
    kStartedInBackgroundFromNewTabPage,
    kFromGWSPage,
    kStartedInBackgroundFromGWSPage,
    kMaxValue = kStartedInBackgroundFromGWSPage,
  };
  // LINT.ThenChange(//tools/metrics/histograms/metadata/page/enums.xml:NavigationSourceTypeEnum)

  // The state of the preconnect. Keeps track of the possible state of
  // preconnect before commit.
  //
  // These values are persisted to logs. Entries should not be renumbered
  // and numeric values should never be reused.
  //
  // LINT.IfChange(ConnectionReuseStatus)
  enum class ConnectionReuseStatus {
    kNonReuse,
    kDNSReused,
    kReused,
    kMaxValue = kReused,
  };
  // LINT.ThenChange(//tools/metrics/histograms/metadata/page/enums.xml:ConnectionReuseStatus)

  GWSPageLoadMetricsObserver();

  GWSPageLoadMetricsObserver(const GWSPageLoadMetricsObserver&) = delete;
  GWSPageLoadMetricsObserver& operator=(const GWSPageLoadMetricsObserver&) =
      delete;
  ~GWSPageLoadMetricsObserver() override;

  // page_load_metrics::PageLoadMetricsObserver implementation:
  ObservePolicy OnStart(content::NavigationHandle* navigation_handle,
                        const GURL& currently_committed_url,
                        bool started_in_foreground) override;
  ObservePolicy OnRedirect(
      content::NavigationHandle* navigation_handle) override;
  ObservePolicy OnCommit(content::NavigationHandle* navigation_handle) override;

  ObservePolicy OnPrerenderStart(content::NavigationHandle* navigation_handle,
                                 const GURL& currently_committed_url) override;
  void DidActivatePrerenderedPage(
      content::NavigationHandle* navigation_handle) override;

  ObservePolicy OnFencedFramesStart(
      content::NavigationHandle* navigation_handle,
      const GURL& currently_committed_url) override;

  ObservePolicy FlushMetricsOnAppEnterBackground(
      const page_load_metrics::mojom::PageLoadTiming& timing) override;

  void OnFirstContentfulPaintInPage(
      const page_load_metrics::mojom::PageLoadTiming& timing) override;
  void OnParseStart(
      const page_load_metrics::mojom::PageLoadTiming& timing) override;
  void OnConnectStart(
      const page_load_metrics::mojom::PageLoadTiming& timing) override;
  void OnDomainLookupStart(
      const page_load_metrics::mojom::PageLoadTiming& timing) override;
  void OnDomainLookupEnd(
      const page_load_metrics::mojom::PageLoadTiming& timing) override;
  void OnComplete(
      const page_load_metrics::mojom::PageLoadTiming& timing) override;
  void OnCustomUserTimingMarkObserved(
      const std::vector<page_load_metrics::mojom::CustomUserTimingMarkPtr>&
          timings) override;
  void OnDomContentLoadedEventStart(
      const page_load_metrics::mojom::PageLoadTiming& timing) override;
  void OnLoadEventStart(
      const page_load_metrics::mojom::PageLoadTiming& timing) override;

  // The methods below are only intended for use in testing.
  void SetIsFirstNavigationForTesting(bool is_first_navigation) {
    is_first_navigation_ = is_first_navigation;
  }

  void SetNewTabPageForTesting(bool is_new_tab_page) {
    source_type_ = kFromNewTabPage;
  }

 private:
  struct PerformanceMarkTimingHistogramInfo {
    const char* histogram_name;
    // We may not have a corresponding field in the class for each
    // PerformanceMark (i.e. will not need to preserve the mark timing, since we
    // do not need them for succeeding histograms), hence the `timing_member`
    // may be std::nullopt.
    std::optional<std::optional<base::TimeDelta> GWSPageLoadMetricsObserver::*>
        timing_member;
  };

  void LogMetricsOnComplete(
      const page_load_metrics::mojom::PageLoadTiming& main_frame_timing);
  void LogFontMetrics();
  void LogFontMetricsAtAFTEnd();
  void RecordNavigationTimingHistograms();
  void RecordLatencyHistograms(base::TimeTicks response_start_time);
  void RecordSessionDetails(
      const content::NavigationHandleTiming::SessionDetails& details,
      std::string_view protocol);

  // Records the histograms required before commit. This is to ensure that we
  // are getting the metrics only for GWS navigations.
  void RecordPreCommitHistograms();

  // Records the histograms for possible connection reuse.
  void RecordConnectionReuseHistograms();

  void RecordGWSSessionStateHistograms();

  void RecordAIOHistograms();

  std::optional<PerformanceMarkTimingHistogramInfo> GetMarkNameToTimingInfo(
      std::string_view mark_name) const;
  base::TimeDelta AdjustPerformanceMarkTiming(
      const page_load_metrics::mojom::CustomUserTimingMarkPtr& mark);

  virtual bool IsFromNewTabPage(
      content::NavigationHandle* navigation_handle) = 0;
  virtual bool IsBrowserStartupComplete() = 0;
  virtual bool IsIncognitoProfile() const = 0;
  virtual bool IsSignedIn(content::BrowserContext* browser_context) const = 0;
  virtual content::BrowserContext* GetOriginalBrowserContext() = 0;
  std::string AddHistogramSuffix(const std::string& histogram_name);

  content::NavigationHandleTiming navigation_handle_timing_;

  bool is_first_navigation_ = false;
  bool was_cached_ = false;
  bool network_accessed_ = false;
  bool is_prerendered_ = false;
  bool is_header_from_synthetic_response_ = false;

  // NavigationActivation.navigationType is traverse.
  bool is_traverse_navigation_ = false;
  // Indicates if it is session restore navigation, a subset of traverse
  // navigation. Restore navigation might be slower than a regular navigation.
  bool is_restore_navigation_ = false;

  // Indicates if the navigation was started from the context menu, for checking
  // the percentage of context menu triggered navigations.
  bool was_started_from_context_menu_ = false;

  // Indicates if this navigation caused a subsequent duplicate request to be
  // ignored.
  bool did_ignore_duplicate_navigation_ = false;

  NavigationSourceType source_type_ = kUnknown;
  net::HttpConnectionInfoCoarse http_connection_info_ =
      net::HttpConnectionInfoCoarse::kOTHER;

  std::optional<base::TimeDelta> aft_start_time_;
  std::optional<base::TimeDelta> aft_end_time_;
  std::optional<base::TimeDelta> body_chunk_start_time_;
  std::optional<base::TimeDelta> head_chunk_start_time_;
  std::optional<base::TimeDelta> head_chunk_end_time_;
  std::optional<base::TimeDelta> sgl_time_;

  std::optional<base::TimeDelta> aio_async_start_time_;
  std::optional<base::TimeDelta> aio_initial_content_time_;
  std::optional<base::TimeDelta> aio_viewport_end_time_;

  int64_t navigation_id_;
};

#endif  // COMPONENTS_PAGE_LOAD_METRICS_GOOGLE_BROWSER_GWS_PAGE_LOAD_METRICS_OBSERVER_H_
