chromium/ios/chrome/browser/metrics/model/tab_usage_recorder_browser_agent.h

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef IOS_CHROME_BROWSER_METRICS_MODEL_TAB_USAGE_RECORDER_BROWSER_AGENT_H_
#define IOS_CHROME_BROWSER_METRICS_MODEL_TAB_USAGE_RECORDER_BROWSER_AGENT_H_

#import <map>
#import <memory>
#import <vector>

#import "base/containers/circular_deque.h"
#import "base/memory/raw_ptr.h"
#import "base/scoped_observation.h"
#import "base/time/time.h"
#import "ios/chrome/browser/metrics/model/tab_usage_recorder_metrics.h"
#import "ios/chrome/browser/sessions/model/session_restoration_observer.h"
#import "ios/chrome/browser/shared/model/browser/browser_observer.h"
#import "ios/chrome/browser/shared/model/browser/browser_user_data.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_list_observer.h"
#import "ios/web/common/user_agent.h"
#import "ios/web/public/web_state_observer.h"

class PrerenderService;
class SessionRestorationService;
class WebStateList;

namespace web {
class WebState;
}

// Reports usage about the lifecycle of a single Browser's tabs.
class TabUsageRecorderBrowserAgent
    : public BrowserObserver,
      public BrowserUserData<TabUsageRecorderBrowserAgent>,
      public web::WebStateObserver,
      public WebStateListObserver,
      public SessionRestorationObserver {
 public:
  // Not copyable or moveable
  TabUsageRecorderBrowserAgent(const TabUsageRecorderBrowserAgent&) = delete;
  TabUsageRecorderBrowserAgent& operator=(const TabUsageRecorderBrowserAgent&) =
      delete;

  ~TabUsageRecorderBrowserAgent() override;

  // Called during startup when the tab model is created, or shortly after a
  // post-crash launch if the tabs are restored.  `web_states` is an array
  // containing/ the tabs being restored in the current tab model.
  // `active_web_state` is the tab currently in the foreground.
  void InitialRestoredTabs(web::WebState* active_web_state,
                           const std::vector<web::WebState*>& web_states);

  // Called when a tab switch is made.  Determines what value to record, and
  // when to reset the page load counter.
  void RecordTabSwitched(web::WebState* old_web_state,
                         web::WebState* new_web_state);

  // Called when the Browser which the user is primarily interacting with has
  // changed. If the user began interacting with `active_web_state`,
  // `primary_browser` should be true. If the user stopped interacting with
  // `active_web_state`, `primary_browser` should be false.
  void RecordPrimaryBrowserChange(bool primary_browser);

  // Called when a page load begins, to keep track of how many page loads
  // happen before an evicted tab is seen.
  void RecordPageLoadStart(web::WebState* web_state);

  // Called when a page load finishes, to track the load time for evicted tabs.
  void RecordPageLoadDone(web::WebState* web_state);

  // Called when there is a user-initiated reload.
  void RecordReload(web::WebState* web_state);

  // Called when WKWebView's renderer is terminated. `tab` contains the tab
  // whose renderer was terminated, `tab_visible` indicates whether or not
  // the tab was visible when the renderer terminated and `application_active`
  // indicates whether the application was in the foreground or background.
  void RendererTerminated(web::WebState* web_state,
                          bool web_state_visible,
                          bool application_active);

  // Called when the app has been backgrounded.
  void AppDidEnterBackground();

  // Called when the app has been foregrounded.
  void AppWillEnterForeground();

  // Resets the page load count.
  void ResetPageLoads();

  // Size of `evicted_web_states_`.  Used for testing.
  int EvictedTabsMapSize();

  // Resets all tracked data.  Used for testing.
  void ResetAll();

 private:
  // TODO(crbug.com/41324440): remove this once the code has been refactored not
  // to depends on injecting values in `termination_timestamps_`.
  friend class TabUsageRecorderBrowserAgentTest;

  friend class BrowserUserData<TabUsageRecorderBrowserAgent>;
  BROWSER_USER_DATA_KEY_DECL();

  explicit TabUsageRecorderBrowserAgent(Browser* browser);

  // BrowserObserver methods
  void BrowserDestroyed(Browser* browser) override;

  // Clear out all state regarding a current evicted tab.
  void ResetEvictedTab();

  // Whether or not a tab can be disregarded by the metrics.
  bool ShouldIgnoreWebState(web::WebState* web_state);

  // Whether or not the tab has already been evicted.
  bool WebStateAlreadyEvicted(web::WebState* web_state);

  // Returns the state of the given tab.  Call only once per tab, as it removes
  // the tab from `evicted_web_states_`.
  tab_usage_recorder::TabStateWhenSelected ExtractWebStateState(
      web::WebState* web_state);

  // Records various time metrics when a restore of an evicted tab begins.
  void RecordRestoreStartTime();

  // Returns the number of WebState that are still alive (in-memory).
  int GetLiveWebStatesCount() const;

  // Called before one of the tracked WebState is destroyed. The WebState is
  // still valid but will become invalid afterwards, so any reference to it
  // should be removed.
  void OnWebStateDestroyed(web::WebState* web_state);

  // Returns whether `agent_type` and `other_agent_type` are different user
  // agent types. If either of them is web::UserAgentType::NONE, then return
  // false.
  bool IsTransitionBetweenDesktopAndMobileUserAgent(
      web::UserAgentType agent_type,
      web::UserAgentType other_agent_type);

  // Returns whether RecordPageLoadStart should be called for the given
  // navigation.
  bool ShouldRecordPageLoadStartForNavigation(
      web::NavigationContext* navigation);

  // web::WebStateObserver implementation.
  void DidStartNavigation(web::WebState* web_state,
                          web::NavigationContext* navigation_context) override;
  void PageLoaded(
      web::WebState* web_state,
      web::PageLoadCompletionStatus load_completion_status) override;
  void RenderProcessGone(web::WebState* web_state) override;
  void WebStateDestroyed(web::WebState* web_state) override;

  // WebStateListObserver implementation.
  void WebStateListDidChange(WebStateList* web_state_list,
                             const WebStateListChange& change,
                             const WebStateListStatus& status) override;

  // SessionRestorationObserver implementation.
  void WillStartSessionRestoration(Browser* browser) override;
  void SessionRestorationFinished(
      Browser* browser,
      const std::vector<web::WebState*>& restored_web_states) override;

  // Keep track of when the most recent tab restore begins, to record the time
  // between evicted-tab-reloads.
  base::TimeTicks restore_start_time_;

  // Keep track of the timestamps of renderer terminations in order to find the
  // number of recently alive tabs when a renderer termination occurs.
  base::circular_deque<base::TimeTicks> termination_timestamps_;

  // Number of page loads since the last evicted tab was seen.
  unsigned int page_loads_ = 0;

  // Keep track of the current tab, but only if it has been evicted.
  // This is kept as a pointer value only - it should never be dereferenced.
  raw_ptr<web::WebState> evicted_web_state_ = nullptr;

  // State of `evicted_web_state_` at the time it became the current tab.
  tab_usage_recorder::TabStateWhenSelected evicted_web_state_state_ =
      tab_usage_recorder::IN_MEMORY;

  // Keep track of the tab last selected when this tab model was switched
  // away from to another mode (e.g. to incognito).
  // Kept as a pointer value only - it should never be dereferenced.
  raw_ptr<web::WebState> mode_switch_web_state_ = nullptr;

  // Keep track of a tab that was created to be immediately selected.  It should
  // not contribute to the "StatusWhenSwitchedBackToForeground" metric.
  raw_ptr<web::WebState> web_state_created_selected_ = nullptr;

  // Keep track of when the evicted tab starts to reload, so that the total
  // time it takes to reload can be recorded.
  base::TimeTicks evicted_web_state_reload_start_time_;

  // Keep track of the tabs that have a known eviction cause.
  std::map<web::WebState*, tab_usage_recorder::TabStateWhenSelected>
      evicted_web_states_;

  // The WebStateList containing all the monitored tabs.
  raw_ptr<WebStateList> web_state_list_;  // weak

  // The PrerenderService used to check whether a tab is pre-rendering. May
  // be null during unit testing.
  raw_ptr<PrerenderService> prerender_service_;

  // Observation for SessionRestorationService events.
  base::ScopedObservation<SessionRestorationService, SessionRestorationObserver>
      session_restoration_service_observation_{this};

  // Observers for NSNotificationCenter notifications.
  __strong id<NSObject> application_backgrounding_observer_;
  __strong id<NSObject> application_foregrounding_observer_;
};

#endif  // IOS_CHROME_BROWSER_METRICS_MODEL_TAB_USAGE_RECORDER_BROWSER_AGENT_H_