// Copyright 2015 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_BROWSER_PAGE_LOAD_METRICS_OBSERVER_H_
#define COMPONENTS_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_METRICS_OBSERVER_H_

#include <memory>
#include <string>

#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "components/page_load_metrics/browser/page_load_metrics_observer_delegate.h"
#include "components/page_load_metrics/browser/page_load_metrics_observer_interface.h"
#include "components/page_load_metrics/common/page_load_timing.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/web_contents_observer.h"
#include "net/base/host_port_pair.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/cookies/canonical_cookie.h"
#include "services/network/public/mojom/fetch_api.mojom.h"
#include "third_party/blink/public/common/input/web_input_event.h"
#include "third_party/blink/public/common/use_counter/use_counter_feature.h"
#include "url/gurl.h"

namespace blink {
struct JavaScriptFrameworkDetectionResult;
}  // namespace blink

namespace page_load_metrics {

// Information related to whether an associated action, such as a navigation or
// an abort, was initiated by a user. Clicking a link or tapping on a UI
// element are examples of user initiation actions.
struct UserInitiatedInfo {
  static UserInitiatedInfo NotUserInitiated() {
    return UserInitiatedInfo(false, false, false);
  }

  static UserInitiatedInfo BrowserInitiated() {
    return UserInitiatedInfo(true, false, false);
  }

  static UserInitiatedInfo RenderInitiated(bool user_gesture,
                                           bool user_input_event) {
    return UserInitiatedInfo(false, user_gesture, user_input_event);
  }

  // Whether the associated action was initiated from the browser process, as
  // opposed to from the render process. We generally assume that all actions
  // initiated from the browser process are user initiated.
  bool browser_initiated;

  // Whether the associated action was initiated by a user, according to user
  // gesture tracking in content and Blink, as reported by NavigationHandle.
  // This is based on the heuristic the popup blocker uses.
  bool user_gesture;

  // Whether an input even directly led to the navigation, according to
  // input start time tracking in the renderer, as reported by NavigationHandle.
  // Note that this metric is still experimental and may not be fully
  // implemented. All known issues are blocking crbug.com/889220. Currently
  // all known gaps affect browser-side navigations.
  bool user_input_event;

 private:
  UserInitiatedInfo(bool browser_initiated,
                    bool user_gesture,
                    bool user_input_event)
      : browser_initiated(browser_initiated),
        user_gesture(user_gesture),
        user_input_event(user_input_event) {}
};

// Information about how the page rendered during the browsing session.
// Derived from the FrameRenderDataUpdate that is sent via UpdateTiming IPC.
struct PageRenderData {
  PageRenderData() = default;

  // How much visible elements on the page shifted (bit.ly/lsm-explainer).
  float layout_shift_score = 0;

  // How much visible elements on the page shifted (bit.ly/lsm-explainer),
  // before user input or document scroll. This field's meaning is context-
  // dependent (see comments on page_render_data_ and main_frame_render_data_
  // in PageLoadMetricsUpdateDispatcher).
  float layout_shift_score_before_input_or_scroll = 0;
};

// Information related to layout shift normalization for different strategies.
struct NormalizedCLSData {
  // Maximum CLS of session windows. The gap between two consecutive shifts is
  // not bigger than 1000ms and the maximum window size is 5000ms.
  float session_windows_gap1000ms_max5000ms_max_cls = 0.0;

  // If true, will not report the data in UKM.
  bool data_tainted = false;
};

// Base class for PageLoadMetrics observers. All instances of this class are
// owned by the PageLoadTracker tracking a page load. The page would be a
// primary page, Prerendering page, FencedFrames page, or pages for new other
// features based on MPArch.
class PageLoadMetricsObserver : public PageLoadMetricsObserverInterface {
 public:
  // These values are persisted to logs. Entries should not be renumbered and
  // numeric values should never be reused.
  enum class LargestContentState {
    kReported = 0,
    kLargestImageLoading = 1,
    kNotFound = 2,
    kFoundButNotReported = 3,
    kMaxValue = kFoundButNotReported,
  };

  PageLoadMetricsObserver();
  ~PageLoadMetricsObserver() override;

  static bool IsStandardWebPageMimeType(const std::string& mime_type);

  // PageLoadMetricsObserverInterface implementation:
  const PageLoadMetricsObserverDelegate& GetDelegate() const override;
  void SetDelegate(PageLoadMetricsObserverDelegate*) override;
  const char* GetObserverName() const override;
  ObservePolicy OnStart(content::NavigationHandle* navigation_handle,
                        const GURL& currently_committed_url,
                        bool started_in_foreground) override;
  ObservePolicy OnNavigationHandleTimingUpdated(
      content::NavigationHandle* navigation_handle) override;
  ObservePolicy OnRedirect(
      content::NavigationHandle* navigation_handle) override;
  ObservePolicy OnCommit(content::NavigationHandle* navigation_handle) override;
  void OnDidInternalNavigationAbort(
      content::NavigationHandle* navigation_handle) override {}
  void ReadyToCommitNextNavigation(
      content::NavigationHandle* navigation_handle) override {}
  void OnDidFinishSubFrameNavigation(
      content::NavigationHandle* navigation_handle) override {}
  void OnCommitSameDocumentNavigation(
      content::NavigationHandle* navigation_handle) override {}
  ObservePolicy OnHidden(const mojom::PageLoadTiming& timing) override;
  ObservePolicy OnShown() override;
  ObservePolicy OnEnterBackForwardCache(
      const mojom::PageLoadTiming& timing) override;
  void OnRestoreFromBackForwardCache(
      const mojom::PageLoadTiming& timing,
      content::NavigationHandle* navigation_handle) override {}
  ObservePolicy ShouldObserveMimeType(
      const std::string& mime_type) const override;
  ObservePolicy ShouldObserveScheme(const GURL& url) const override;
  void OnTimingUpdate(content::RenderFrameHost* subframe_rfh,
                      const mojom::PageLoadTiming& timing) override {}
  void OnSoftNavigationFirstContentfulPaint(
      const mojom::SoftNavigationMetrics& soft_navigation_metrics) override {}
  void OnSoftNavigationCompleted(
      const SoftNavigationData& soft_navigation_data) override {}
  void OnSoftNavigationLargestContentfulPaint(uint64_t num_soft_lcps) override {
  }
  void OnEventTimingUpdate(
      content::RenderFrameHost* subframe_rfh,
      const std::vector<mojom::EventTimingPtr>& event_timings) override {}
  void OnPageEventTimingUpdate(uint64_t num_interactions) override {}
  void OnPageRenderDataUpdate(const mojom::FrameRenderDataUpdate& render_data,
                              bool is_main_frame) override {}
  void OnSubFrameRenderDataUpdate(
      content::RenderFrameHost* subframe_rfh,
      const mojom::FrameRenderDataUpdate& render_data) override {}
  void OnCpuTimingUpdate(content::RenderFrameHost* subframe_rfh,
                         const mojom::CpuTiming& timing) override {}
  void OnUserInput(const blink::WebInputEvent& event,
                   const mojom::PageLoadTiming& timing) override {}
  void OnDomContentLoadedEventStart(
      const mojom::PageLoadTiming& timing) override {}
  void OnLoadEventStart(const mojom::PageLoadTiming& timing) override {}
  void OnParseStart(const mojom::PageLoadTiming& timing) override {}
  void OnParseStop(const mojom::PageLoadTiming& timing) override {}
  void OnConnectStart(const mojom::PageLoadTiming& timing) override {}
  void OnConnectEnd(const mojom::PageLoadTiming& timing) override {}
  void OnDomainLookupStart(const mojom::PageLoadTiming& timing) override {}
  void OnDomainLookupEnd(const mojom::PageLoadTiming& timing) override {}
  void OnFirstPaintInPage(const mojom::PageLoadTiming& timing) override {}
  void OnFirstImagePaintInPage(const mojom::PageLoadTiming& timing) override {}
  void OnFirstContentfulPaintInPage(
      const mojom::PageLoadTiming& timing) override {}
  void OnMonotonicFirstPaintInPage(
      const mojom::PageLoadTiming& timing) override {}
  void OnMonotonicFirstContentfulPaintInPage(
      const mojom::PageLoadTiming& timing) override {}
  void OnFirstPaintAfterBackForwardCacheRestoreInPage(
      const mojom::BackForwardCacheTiming& timing,
      size_t index) override {}
  void OnFirstInputAfterBackForwardCacheRestoreInPage(
      const mojom::BackForwardCacheTiming& timing,
      size_t index) override {}
  void OnRequestAnimationFramesAfterBackForwardCacheRestoreInPage(
      const mojom::BackForwardCacheTiming& timing,
      size_t index) override {}
  void OnFirstMeaningfulPaintInMainFrameDocument(
      const mojom::PageLoadTiming& timing) override {}
  void OnFirstInputInPage(const mojom::PageLoadTiming& timing) override {}
  void OnUserTimingMarkFullyLoaded(
      const mojom::PageLoadTiming& timing) override {}
  void OnUserTimingMarkFullyVisible(
      const mojom::PageLoadTiming& timing) override {}
  void OnUserTimingMarkInteractive(
      const mojom::PageLoadTiming& timing) override {}
  void OnLoadingBehaviorObserved(content::RenderFrameHost* rfh,
                                 int behavior_flags) override {}
  void OnJavaScriptFrameworksObserved(
      content::RenderFrameHost* rfh,
      const blink::JavaScriptFrameworkDetectionResult&) override {}
  void OnFeaturesUsageObserved(
      content::RenderFrameHost* rfh,
      const std::vector<blink::UseCounterFeature>& features) override {}
  void OnResourceDataUseObserved(
      content::RenderFrameHost* rfh,
      const std::vector<mojom::ResourceDataUpdatePtr>& resources) override {}
  void MediaStartedPlaying(
      const content::WebContentsObserver::MediaPlayerInfo& video_type,
      content::RenderFrameHost* render_frame_host) override {}
  void OnMainFrameRectChanged(const gfx::Rect& main_frame_rect) override {}
  void OnMainFrameViewportRectChanged(
      const gfx::Rect& main_frame_viewport_rect) override {}
  void OnMainFrameAdRectsChanged(
      const base::flat_map<int, gfx::Rect>& main_frame_ad_rects) override {}
  ObservePolicy FlushMetricsOnAppEnterBackground(
      const mojom::PageLoadTiming& timing) override;
  void OnComplete(const mojom::PageLoadTiming& timing) override {}
  void OnFailedProvisionalLoad(
      const FailedProvisionalLoadInfo& failed_provisional_load_info) override {}
  void OnLoadedResource(
      const ExtraRequestCompleteInfo& extra_request_complete_info) override {}
  void DidLoadResourceFromMemoryCache(
      const MemoryResourceLoadInfo& memory_resource_load_info) override {}
  void FrameReceivedUserActivation(
      content::RenderFrameHost* render_frame_host) override {}
  void FrameDisplayStateChanged(content::RenderFrameHost* render_frame_host,
                                bool is_display_none) override {}
  void FrameSizeChanged(content::RenderFrameHost* render_frame_host,
                        const gfx::Size& frame_size) override {}
  void OnRenderFrameDeleted(
      content::RenderFrameHost* render_frame_host) override {}
  void OnSubFrameDeleted(content::FrameTreeNodeId frame_tree_node_id) override {
  }
  void OnCookiesRead(
      const GURL& url,
      const GURL& first_party_url,
      bool blocked_by_policy,
      bool is_ad_tagged,
      const net::CookieSettingOverrides& cookie_setting_overrides,
      bool is_partitioned_access) override {}
  void OnCookieChange(
      const GURL& url,
      const GURL& first_party_url,
      const net::CanonicalCookie& cookie,
      bool blocked_by_policy,
      bool is_ad_tagged,
      const net::CookieSettingOverrides& cookie_setting_overrides,
      bool is_partitioned_access) override {}
  void OnStorageAccessed(const GURL& url,
                         const GURL& first_party_url,
                         bool blocked_by_policy,
                         StorageType access_type) override {}
  void DidActivatePrerenderedPage(
      content::NavigationHandle* navigation_handle) override {}
  void OnSharedStorageWorkletHostCreated() override {}
  void OnSharedStorageSelectURLCalled() override {}
  void OnCustomUserTimingMarkObserved(
      const std::vector<mojom::CustomUserTimingMarkPtr>& timings) override {}
  void OnPrimaryPageRenderProcessGone() override {}

 private:
  raw_ptr<PageLoadMetricsObserverDelegate> delegate_ = nullptr;
};

}  // namespace page_load_metrics

#endif  // COMPONENTS_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_METRICS_OBSERVER_H_
