blob: cbfe1ce319991f42f4a012cf0974584499b8adcd [file] [log] [blame]
// Copyright 2017 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 SERVICES_RESOURCE_COORDINATOR_COORDINATION_UNIT_PAGE_SIGNAL_GENERATOR_IMPL_H_
#define SERVICES_RESOURCE_COORDINATOR_COORDINATION_UNIT_PAGE_SIGNAL_GENERATOR_IMPL_H_
#include <map>
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/timer/timer.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "mojo/public/cpp/bindings/interface_ptr_set.h"
#include "services/resource_coordinator/observers/coordination_unit_graph_observer.h"
#include "services/resource_coordinator/public/mojom/page_signal.mojom.h"
namespace service_manager {
struct BindSourceInfo;
} // namespace service_manager
namespace resource_coordinator {
// The PageSignalGenerator is a dedicated |CoordinationUnitGraphObserver| for
// calculating and emitting page-scoped signals. This observer observes
// PageCoordinationUnits, ProcessCoordinationUnits and FrameCoordinationUnits,
// combining information from the graph to generate page level signals.
class PageSignalGeneratorImpl : public CoordinationUnitGraphObserver,
public mojom::PageSignalGenerator {
public:
PageSignalGeneratorImpl();
~PageSignalGeneratorImpl() override;
// mojom::PageSignalGenerator implementation.
void AddReceiver(mojom::PageSignalReceiverPtr receiver) override;
// CoordinationUnitGraphObserver implementation.
bool ShouldObserve(const CoordinationUnitBase* coordination_unit) override;
void OnCoordinationUnitCreated(const CoordinationUnitBase* cu) override;
void OnBeforeCoordinationUnitDestroyed(
const CoordinationUnitBase* cu) override;
void OnFramePropertyChanged(const FrameCoordinationUnitImpl* frame_cu,
const mojom::PropertyType property_type,
int64_t value) override;
void OnPagePropertyChanged(const PageCoordinationUnitImpl* page_cu,
const mojom::PropertyType property_type,
int64_t value) override;
void OnProcessPropertyChanged(const ProcessCoordinationUnitImpl* process_cu,
const mojom::PropertyType property_type,
int64_t value) override;
void OnFrameEventReceived(const FrameCoordinationUnitImpl* frame_cu,
const mojom::Event event) override;
void OnPageEventReceived(const PageCoordinationUnitImpl* page_cu,
const mojom::Event event) override;
void OnProcessEventReceived(const ProcessCoordinationUnitImpl* page_cu,
const mojom::Event event) override;
void OnSystemEventReceived(const SystemCoordinationUnitImpl* system_cu,
const mojom::Event event) override;
void BindToInterface(
resource_coordinator::mojom::PageSignalGeneratorRequest request,
const service_manager::BindSourceInfo& source_info);
private:
// The amount of time a page has to be idle post-loading in order for it to be
// considered loaded and idle. This is used in UpdateLoadIdleState
// transitions.
static const base::TimeDelta kLoadedAndIdlingTimeout;
// The maximum amount of time post-DidStopLoading a page can be waiting for
// an idle state to occur before the page is simply considered loaded anyways.
// Since PageAlmostIdle is intended as an "initial loading complete" signal,
// it needs to eventually terminate. This is strictly greater than the
// kLoadedAndIdlingTimeout.
static const base::TimeDelta kWaitingForIdleTimeout;
friend class PageSignalGeneratorImplTest;
FRIEND_TEST_ALL_PREFIXES(PageSignalGeneratorImplTest, IsLoading);
FRIEND_TEST_ALL_PREFIXES(PageSignalGeneratorImplTest, IsIdling);
FRIEND_TEST_ALL_PREFIXES(PageSignalGeneratorImplTest,
NonPersistentNotificationCreatedEvent);
FRIEND_TEST_ALL_PREFIXES(PageSignalGeneratorImplTest,
PageDataCorrectlyManaged);
FRIEND_TEST_ALL_PREFIXES(PageSignalGeneratorImplTest,
PageAlmostIdleTransitionsNoTimeout);
FRIEND_TEST_ALL_PREFIXES(PageSignalGeneratorImplTest,
PageAlmostIdleTransitionsWithTimeout);
FRIEND_TEST_ALL_PREFIXES(PageSignalGeneratorImplTest,
OnLoadTimePerformanceEstimate);
// The state transitions for the PageAlmostIdle signal. In general a page
// transitions through these states from top to bottom.
enum LoadIdleState {
// The initial state. Can only transition to kLoading from here.
kLoadingNotStarted,
// Loading has started. Almost idle signals are ignored in this state.
// Can transition to kLoadedNotIdling and kLoadedAndIdling from here.
kLoading,
// Loading has completed, but the page has not started idling. Can only
// transition to kLoadedAndIdling from here.
kLoadedNotIdling,
// Loading has completed, and the page is idling. Can transition to
// kLoadedNotIdling or kLoadedAndIdle from here.
kLoadedAndIdling,
// Loading has completed and the page has been idling for sufficiently long.
// This is the final state. Once this state has been reached a signal will
// be emitted and no further state transitions will be tracked. Committing a
// new non-same document navigation can start the cycle over again.
kLoadedAndIdle
};
// Holds state per page CU. These are created via OnCoordinationUnitCreated
// and destroyed via OnBeforeCoordinationUnitDestroyed.
struct PageData {
// Set the load idle state and the time of change. Also clears the
// |performance_estimate_issued| flag.
void SetLoadIdleState(LoadIdleState new_state, base::TimeTicks now);
LoadIdleState GetLoadIdleState() const { return load_idle_state; }
// Marks the point in time when the DidStopLoading signal was received,
// transitioning to kLoadedAndNotIdling or kLoadedAndIdling. This is used as
// the basis for the kWaitingForIdleTimeout.
base::TimeTicks loading_stopped;
// Marks the point in time when the last transition to kLoadedAndIdling
// occurred. Used for gating the transition to kLoadedAndIdle.
base::TimeTicks idling_started;
// Notes the time of the last state change.
base::TimeTicks last_state_change;
// True iff a performance estimate has been issued for this page.
bool performance_estimate_issued = false;
// A one-shot timer used for transitioning between kLoadedAndIdling and
// kLoadedAndIdle.
base::OneShotTimer idling_timer;
private:
// Initially at kLoadingNotStarted. Transitions through the states via calls
// to UpdateLoadIdleState. Is reset to kLoadingNotStarted when a non-same
// document navigation is committed.
LoadIdleState load_idle_state;
};
// These are called when properties/events affecting the load-idle state are
// observed. Frame and Process variants will eventually all redirect to the
// appropriate Page variant, where the real work is done.
void UpdateLoadIdleStateFrame(const FrameCoordinationUnitImpl* frame_cu);
void UpdateLoadIdleStatePage(const PageCoordinationUnitImpl* page_cu);
void UpdateLoadIdleStateProcess(
const ProcessCoordinationUnitImpl* process_cu);
// This method is called when a property affecting the lifecycle state is
// observed.
void UpdateLifecycleState(const PageCoordinationUnitImpl* page_cu,
mojom::LifecycleState state);
// Helper function for transitioning to the final state.
void TransitionToLoadedAndIdle(const PageCoordinationUnitImpl* page_cu,
base::TimeTicks now);
// Convenience accessors for state associated with a |page_cu|.
PageData* GetPageData(const PageCoordinationUnitImpl* page_cu);
bool IsLoading(const PageCoordinationUnitImpl* page_cu);
bool IsIdling(const PageCoordinationUnitImpl* page_cu);
template <typename Method, typename... Params>
void DispatchPageSignal(const PageCoordinationUnitImpl* page_cu,
Method m,
Params... params);
mojo::BindingSet<mojom::PageSignalGenerator> bindings_;
mojo::InterfacePtrSet<mojom::PageSignalReceiver> receivers_;
// Stores per Page CU data. This set is maintained by
// OnCoordinationUnitCreated and OnBeforeCoordinationUnitDestroyed.
std::map<const PageCoordinationUnitImpl*, PageData> page_data_;
DISALLOW_COPY_AND_ASSIGN(PageSignalGeneratorImpl);
};
} // namespace resource_coordinator
#endif // SERVICES_RESOURCE_COORDINATOR_COORDINATION_UNIT_PAGE_SIGNAL_GENERATOR_IMPL_H_