blob: 7dc1cd825cc559f4e3222d220d166b35602c83d5 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_RESOURCE_COORDINATOR_TAB_LOAD_TRACKER_H_
#define CHROME_BROWSER_RESOURCE_COORDINATOR_TAB_LOAD_TRACKER_H_
#include "base/callback.h"
#include "base/containers/flat_map.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/observer_list.h"
#include "base/process/kill.h"
#include "base/sequence_checker.h"
#include "base/strings/string16.h"
#include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom-shared.h"
namespace content {
class WebContents;
} // namespace content
namespace resource_coordinator {
class ResourceCoordinatorParts;
class ResourceCoordinatorTabHelper;
class TabManagerResourceCoordinatorSignalObserverHelper;
// DEPRECATED. New users must observe PageNode::IsLoading() with a
// PageNodeObserver. For guidance: //components/performance_manager/OWNERS
//
// This class has the sole purpose of tracking the state of all tab-related
// WebContents, and whether or not they are in an unloaded, currently loading,
// or loaded state.
//
// This class must be bound to a given Sequence and all access to it must occur
// on that Sequence. In practice, this is intended to be on the UI thread as the
// notifications of interest occur natively on that thread. All calculations are
// very short and quick, so it is suitable for use on that thread.
//
// This class is intended to be created in early startup and persists as a
// singleton in the browser process. It is deliberately leaked at shutdown.
//
// This class isn't directly an observer of anything. An external source must
// invoke the callbacks in the protected section of the class. In the case of
// the TabManager this is done by a combination of the
// ResourceCoordinatorTabHelper and the
// TabManagerResourceCoordinatorSignalObserver.
class TabLoadTracker {
public:
// An observer class. This allows external classes to be notified of loading
// state changes.
class Observer;
using LoadingState = ::mojom::LifecycleUnitLoadingState;
// A brief note around loading states specifically as they are defined in the
// context of a WebContents:
//
// An initially constructed WebContents with no loaded content is UNLOADED.
//
// A WebContents transitions to LOADING when network data starts being
// received for a top-level load to a different document. This considers
// throttled navigations as not yet loading, and will only transition to
// loading once the throttle has been removed.
//
// A LOADING WebContents transitions to LOADED when it reaches an "almost
// idle" state, based on CPU and network quiescence or after an absolute
// timeout (see PageLoadTrackerDecorator).
//
// A WebContents transitions to UNLOADED when its render process is gone.
~TabLoadTracker();
// Returns the singleton TabLoadTracker instance.
static TabLoadTracker* Get();
// Allows querying the state of a tab. The provided |web_contents| must be
// actively tracked.
LoadingState GetLoadingState(content::WebContents* web_contents) const;
// Returns the total number of tabs that are being tracked by this class.
size_t GetTabCount() const;
// Returns the number of tabs in each state.
size_t GetTabCount(LoadingState loading_state) const;
size_t GetUnloadedTabCount() const;
size_t GetLoadingTabCount() const;
size_t GetLoadedTabCount() const;
// Returns the total number of UI tabs that are being tracked by this class.
// Some WebContents being tracked by this class may not yet be associated with
// a UI tab, e.g. prerender contents. To exclude these tabs from counts, use
// the Get*UiTabCount() methods.
size_t GetUiTabCount() const;
// Returns the number of UI tabs in each state.
size_t GetUiTabCount(LoadingState loading_state) const;
size_t GetUnloadedUiTabCount() const;
size_t GetLoadingUiTabCount() const;
size_t GetLoadedUiTabCount() const;
// Adds/removes an observer. It is up to the observer to ensure their lifetime
// exceeds that of the TabLoadTracker, as is removed prior to its destruction.
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
// Exposed so that state transitions can be simulated in tests.
void TransitionStateForTesting(content::WebContents* web_contents,
LoadingState loading_state);
// Called from WebContentsDelegates when |new_contents| is replacing
// |old_contents| in a tab.
void SwapTabContents(content::WebContents* old_contents,
content::WebContents* new_contents);
protected:
friend class ResourceCoordinatorParts;
// For unittesting.
friend class LocalSiteCharacteristicsWebContentsObserverTest;
// These declarations allows the various bits of TabManager plumbing to
// forward notifications to the TabLoadTracker.
friend class resource_coordinator::ResourceCoordinatorTabHelper;
friend class ::resource_coordinator::
TabManagerResourceCoordinatorSignalObserverHelper;
FRIEND_TEST_ALL_PREFIXES(TabLifecycleUnitTest, CannotFreezeAFrozenTab);
// This class is a singleton so the constructor is protected.
TabLoadTracker();
// Initiates tracking of a WebContents. This is fully able to determine the
// initial state of the WebContents, even if it was created long ago
// (is LOADING or LOADED) and only just attached to the tracker. See the
// implementation of DetermineLoadingState for details.
void StartTracking(content::WebContents* web_contents);
// Stops tracking a |web_contents|.
void StopTracking(content::WebContents* web_contents);
// These are analogs of WebContentsObserver functions. This class is not
// actually an observer, but the relevant events are forwarded to it from the
// TabManager.
//
// In all cases, a call to DidReceiveResponse() is expected to be followed by
// a call to StopTracking(), RenderProcessGone() or OnPageStoppedLoading().
void DidReceiveResponse(content::WebContents* web_contents);
void DidStopLoading(content::WebContents* web_contents);
void RenderProcessGone(content::WebContents* web_contents,
base::TerminationStatus status);
// Notifications to this are driven by the
// TabManager::ResourceCoordinatorSignalObserver.
void OnPageStoppedLoading(content::WebContents* web_contents);
// Returns true if |web_contents| is a UI tab and false otherwise. This is
// used to filter out cases where tab helpers are attached to a non-UI tab
// WebContents, e.g prerender contents.
//
// This is virtual and protected for unittesting to control when web
// contentses are considered ui tabs.
virtual bool IsUiTab(content::WebContents* web_contents);
private:
// For unittesting.
friend class TestTabLoadTracker;
// Some metadata used to track the current state of the WebContents.
struct WebContentsData {
LoadingState loading_state = LoadingState::UNLOADED;
bool is_ui_tab = false;
};
using TabMap = base::flat_map<content::WebContents*, WebContentsData>;
// Helper function for determining the current state of a |web_contents|.
LoadingState DetermineLoadingState(content::WebContents* web_contents);
// Transitions a web contents to the given state. This updates the various
// |state_counts_| and |tabs_| data. Setting |validate_transition| to false
// means that valid state machine transitions aren't enforced via checks; this
// is only used by state transitions forced via TransitionStateForTesting.
void TransitionState(TabMap::iterator it, LoadingState loading_state);
// The list of known WebContents and their states. This includes both UI and
// non-UI tabs.
TabMap tabs_;
// The counts of tabs in each state.
size_t state_counts_[static_cast<size_t>(LoadingState::kMaxValue) + 1] = {0};
// The counts of UI tabs in each state.
size_t ui_tab_state_counts_[static_cast<size_t>(LoadingState::kMaxValue) +
1] = {0};
base::ObserverList<Observer>::Unchecked observers_;
SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(TabLoadTracker);
};
// A class for observing loading state changes of WebContents under observation
// by a given TabLoadTracker. All of the callbacks will be invoked on the
// sequence to which the TabLoadTracker is bound.
class TabLoadTracker::Observer {
public:
using LoadingState = TabLoadTracker::LoadingState;
Observer();
virtual ~Observer();
// Called when a |web_contents| is starting to be tracked.
virtual void OnStartTracking(content::WebContents* web_contents,
LoadingState loading_state) {}
// Called for every loading state change observed on a |web_contents|.
virtual void OnLoadingStateChange(content::WebContents* web_contents,
LoadingState old_loading_state,
LoadingState new_loading_state) {}
// Called when a |web_contents| is no longer being tracked.
virtual void OnStopTracking(content::WebContents* web_contents,
LoadingState loading_state) {}
private:
DISALLOW_COPY_AND_ASSIGN(Observer);
};
} // namespace resource_coordinator
#endif // CHROME_BROWSER_RESOURCE_COORDINATOR_TAB_LOAD_TRACKER_H_