blob: c71308d1cbe18a2565b41efdb9b2c54d4098360d [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.
#include "chrome/browser/performance_manager/graph/frame_node_impl.h"
#include "chrome/browser/performance_manager/graph/page_node_impl.h"
#include "chrome/browser/performance_manager/graph/process_node_impl.h"
#include "chrome/browser/performance_manager/observers/coordination_unit_graph_observer.h"
#include "chrome/browser/performance_manager/resource_coordinator_clock.h"
namespace performance_manager {
FrameNodeImpl::FrameNodeImpl(const resource_coordinator::CoordinationUnitID& id,
Graph* graph)
: CoordinationUnitInterface(id, graph),
parent_frame_coordination_unit_(nullptr),
page_coordination_unit_(nullptr),
process_coordination_unit_(nullptr) {
for (size_t i = 0; i < base::size(intervention_policy_); ++i)
intervention_policy_[i] =
resource_coordinator::mojom::InterventionPolicy::kUnknown;
DETACH_FROM_SEQUENCE(sequence_checker_);
}
FrameNodeImpl::~FrameNodeImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (parent_frame_coordination_unit_)
parent_frame_coordination_unit_->RemoveChildFrame(this);
if (page_coordination_unit_)
page_coordination_unit_->RemoveFrameImpl(this);
if (process_coordination_unit_)
process_coordination_unit_->RemoveFrame(this);
for (auto* child_frame : child_frame_coordination_units_)
child_frame->RemoveParentFrame(this);
}
void FrameNodeImpl::SetProcess(
const resource_coordinator::CoordinationUnitID& cu_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ProcessNodeImpl* process_cu = ProcessNodeImpl::GetNodeByID(graph_, cu_id);
if (!process_cu)
return;
DCHECK(!process_coordination_unit_);
process_coordination_unit_ = process_cu;
process_cu->AddFrame(this);
}
void FrameNodeImpl::AddChildFrame(
const resource_coordinator::CoordinationUnitID& cu_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(cu_id != id());
FrameNodeImpl* frame_cu = FrameNodeImpl::GetNodeByID(graph_, cu_id);
if (!frame_cu)
return;
if (HasFrameNodeInAncestors(frame_cu) ||
frame_cu->HasFrameNodeInDescendants(this)) {
DCHECK(false) << "Cyclic reference in frame coordination units detected!";
return;
}
if (AddChildFrameImpl(frame_cu)) {
frame_cu->AddParentFrame(this);
}
}
void FrameNodeImpl::RemoveChildFrame(
const resource_coordinator::CoordinationUnitID& cu_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(cu_id != id());
FrameNodeImpl* frame_cu = FrameNodeImpl::GetNodeByID(graph_, cu_id);
if (!frame_cu)
return;
if (RemoveChildFrame(frame_cu)) {
frame_cu->RemoveParentFrame(this);
}
}
void FrameNodeImpl::SetNetworkAlmostIdle(bool network_almost_idle) {
SetPropertyAndNotifyObservers(&GraphObserver::OnNetworkAlmostIdleChanged,
network_almost_idle, this,
&network_almost_idle_);
}
void FrameNodeImpl::SetLifecycleState(
resource_coordinator::mojom::LifecycleState state) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (state == lifecycle_state_)
return;
resource_coordinator::mojom::LifecycleState old_state = lifecycle_state_;
lifecycle_state_ = state;
// Notify parents of this change.
if (process_coordination_unit_)
process_coordination_unit_->OnFrameLifecycleStateChanged(this, old_state);
if (page_coordination_unit_)
page_coordination_unit_->OnFrameLifecycleStateChanged(this, old_state);
}
void FrameNodeImpl::SetHasNonEmptyBeforeUnload(bool has_nonempty_beforeunload) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
has_nonempty_beforeunload_ = has_nonempty_beforeunload;
}
void FrameNodeImpl::SetInterventionPolicy(
resource_coordinator::mojom::PolicyControlledIntervention intervention,
resource_coordinator::mojom::InterventionPolicy policy) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
size_t i = static_cast<size_t>(intervention);
DCHECK_LT(i, base::size(intervention_policy_));
// This can only be called to set a policy, but not to revert a policy to the
// unset state.
DCHECK_NE(resource_coordinator::mojom::InterventionPolicy::kUnknown, policy);
// We expect intervention policies to be initially set in order, and rely on
// that as a synchronization primitive. Ensure this is the case.
DCHECK(i == 0 ||
intervention_policy_[i - 1] !=
resource_coordinator::mojom::InterventionPolicy::kUnknown);
if (policy == intervention_policy_[i])
return;
// Only notify of actual changes.
resource_coordinator::mojom::InterventionPolicy old_policy =
intervention_policy_[i];
intervention_policy_[i] = policy;
if (auto* page_cu = GetPageNode()) {
page_cu->OnFrameInterventionPolicyChanged(this, intervention, old_policy,
policy);
}
}
void FrameNodeImpl::OnNonPersistentNotificationCreated() {
SendEvent(
resource_coordinator::mojom::Event::kNonPersistentNotificationCreated);
}
FrameNodeImpl* FrameNodeImpl::GetParentFrameNode() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return parent_frame_coordination_unit_;
}
PageNodeImpl* FrameNodeImpl::GetPageNode() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return page_coordination_unit_;
}
ProcessNodeImpl* FrameNodeImpl::GetProcessNode() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return process_coordination_unit_;
}
bool FrameNodeImpl::IsMainFrame() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return !parent_frame_coordination_unit_;
}
bool FrameNodeImpl::AreAllInterventionPoliciesSet() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// The convention is that policies are first set en masse, in order. So if
// the last policy is set then they are all considered to be set. Check this
// in DEBUG builds.
#if DCHECK_IS_ON()
bool seen_unset_policy = false;
for (size_t i = 0; i < base::size(intervention_policy_); ++i) {
if (!seen_unset_policy) {
seen_unset_policy =
intervention_policy_[i] !=
resource_coordinator::mojom::InterventionPolicy::kUnknown;
} else {
// Once a first unset policy is seen, all subsequent policies must be
// unset.
DCHECK_NE(resource_coordinator::mojom::InterventionPolicy::kUnknown,
intervention_policy_[i]);
}
}
#endif
return intervention_policy_[base::size(intervention_policy_) - 1] !=
resource_coordinator::mojom::InterventionPolicy::kUnknown;
} // namespace performance_manager
void FrameNodeImpl::SetAllInterventionPoliciesForTesting(
resource_coordinator::mojom::InterventionPolicy policy) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (size_t i = 0; i < base::size(intervention_policy_); ++i) {
SetInterventionPolicy(
static_cast<resource_coordinator::mojom::PolicyControlledIntervention>(
i),
policy);
}
}
void FrameNodeImpl::OnEventReceived(resource_coordinator::mojom::Event event) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (auto& observer : observers())
observer.OnFrameEventReceived(this, event);
}
void FrameNodeImpl::OnPropertyChanged(
resource_coordinator::mojom::PropertyType property_type,
int64_t value) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (auto& observer : observers())
observer.OnFramePropertyChanged(this, property_type, value);
}
bool FrameNodeImpl::HasFrameNodeInAncestors(FrameNodeImpl* frame_cu) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (parent_frame_coordination_unit_ == frame_cu ||
(parent_frame_coordination_unit_ &&
parent_frame_coordination_unit_->HasFrameNodeInAncestors(frame_cu))) {
return true;
}
return false;
}
bool FrameNodeImpl::HasFrameNodeInDescendants(FrameNodeImpl* frame_cu) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (FrameNodeImpl* child : child_frame_coordination_units_) {
if (child == frame_cu || child->HasFrameNodeInDescendants(frame_cu)) {
return true;
}
}
return false;
}
void FrameNodeImpl::AddParentFrame(FrameNodeImpl* parent_frame_cu) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
parent_frame_coordination_unit_ = parent_frame_cu;
}
bool FrameNodeImpl::AddChildFrameImpl(FrameNodeImpl* child_frame_cu) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return child_frame_coordination_units_.count(child_frame_cu)
? false
: child_frame_coordination_units_.insert(child_frame_cu).second;
}
void FrameNodeImpl::RemoveParentFrame(FrameNodeImpl* parent_frame_cu) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(parent_frame_coordination_unit_ == parent_frame_cu);
parent_frame_coordination_unit_ = nullptr;
}
bool FrameNodeImpl::RemoveChildFrame(FrameNodeImpl* child_frame_cu) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return child_frame_coordination_units_.erase(child_frame_cu) > 0;
}
void FrameNodeImpl::AddPageNode(PageNodeImpl* page_coordination_unit) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!page_coordination_unit_);
page_coordination_unit_ = page_coordination_unit;
}
void FrameNodeImpl::RemovePageNode(PageNodeImpl* page_coordination_unit) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(page_coordination_unit == page_coordination_unit_);
page_coordination_unit_ = nullptr;
}
void FrameNodeImpl::RemoveProcessNode(
ProcessNodeImpl* process_coordination_unit) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(process_coordination_unit == process_coordination_unit_);
process_coordination_unit_ = nullptr;
}
} // namespace performance_manager