blob: d8c5545189cf2f1e83f1480aeccecd69963265f3 [file] [log] [blame]
// 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.
#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h"
#include <memory>
#include "base/memory/ptr_util.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/task/common/lazy_now.h"
#include "base/task/common/scoped_defer_task_posting.h"
#include "base/task/common/task_annotator.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "components/power_scheduler/power_mode.h"
#include "components/power_scheduler/power_mode_arbiter.h"
#include "components/power_scheduler/power_mode_voter.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/renderer/platform/back_forward_cache_utils.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/scheduler/common/features.h"
#include "third_party/blink/renderer/platform/scheduler/common/task_priority.h"
#include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h"
#include "third_party/blink/renderer/platform/scheduler/common/tracing_helper.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/find_in_page_budget_pool_controller.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_web_scheduling_task_queue_impl.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/page_visibility_state.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.h"
#include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_queue_type.h"
#include "third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_proxy.h"
#include "third_party/perfetto/include/perfetto/tracing/traced_value.h"
namespace blink {
namespace scheduler {
using base::sequence_manager::TaskQueue;
using QueueTraits = MainThreadTaskQueue::QueueTraits;
using perfetto::protos::pbzero::RendererMainThreadTaskExecution;
namespace {
const char* VisibilityStateToString(bool is_visible) {
if (is_visible) {
return "visible";
} else {
return "hidden";
}
}
const char* PausedStateToString(bool is_paused) {
if (is_paused) {
return "paused";
} else {
return "running";
}
}
const char* FrozenStateToString(bool is_frozen) {
if (is_frozen) {
return "frozen";
} else {
return "running";
}
}
// Used to update the priority of task_queue. Note that this function is
// used for queues associated with a frame.
void UpdatePriority(MainThreadTaskQueue* task_queue) {
if (!task_queue)
return;
FrameSchedulerImpl* frame_scheduler = task_queue->GetFrameScheduler();
DCHECK(frame_scheduler);
task_queue->SetQueuePriority(frame_scheduler->ComputePriority(task_queue));
}
} // namespace
FrameSchedulerImpl::PauseSubresourceLoadingHandleImpl::
PauseSubresourceLoadingHandleImpl(
base::WeakPtr<FrameSchedulerImpl> frame_scheduler)
: frame_scheduler_(std::move(frame_scheduler)) {
DCHECK(frame_scheduler_);
frame_scheduler_->AddPauseSubresourceLoadingHandle();
}
FrameSchedulerImpl::PauseSubresourceLoadingHandleImpl::
~PauseSubresourceLoadingHandleImpl() {
if (frame_scheduler_)
frame_scheduler_->RemovePauseSubresourceLoadingHandle();
}
FrameSchedulerImpl::FrameSchedulerImpl(PageSchedulerImpl* parent_page_scheduler,
FrameScheduler::Delegate* delegate,
bool is_in_embedded_frame_tree,
FrameScheduler::FrameType frame_type)
: FrameSchedulerImpl(parent_page_scheduler->GetMainThreadScheduler(),
parent_page_scheduler,
delegate,
is_in_embedded_frame_tree,
frame_type) {}
FrameSchedulerImpl::FrameSchedulerImpl(
MainThreadSchedulerImpl* main_thread_scheduler,
PageSchedulerImpl* parent_page_scheduler,
FrameScheduler::Delegate* delegate,
bool is_in_embedded_frame_tree,
FrameScheduler::FrameType frame_type)
: frame_type_(frame_type),
is_in_embedded_frame_tree_(is_in_embedded_frame_tree),
main_thread_scheduler_(main_thread_scheduler),
parent_page_scheduler_(parent_page_scheduler),
delegate_(delegate),
throttling_state_(SchedulingLifecycleState::kNotThrottled),
frame_visible_(true,
"FrameScheduler.FrameVisible",
&tracing_controller_,
VisibilityStateToString),
frame_paused_(false,
"FrameScheduler.FramePaused",
&tracing_controller_,
PausedStateToString),
frame_origin_type_(frame_type == FrameType::kMainFrame
? FrameOriginType::kMainFrame
: FrameOriginType::kSameOriginToMainFrame,
"FrameScheduler.Origin",
&tracing_controller_,
FrameOriginTypeToString),
subresource_loading_paused_(false,
"FrameScheduler.SubResourceLoadingPaused",
&tracing_controller_,
PausedStateToString),
url_tracer_("FrameScheduler.URL"),
task_queues_throttled_(false,
"FrameScheduler.TaskQueuesThrottled",
&tracing_controller_,
YesNoStateToString),
preempted_for_cooperative_scheduling_(
false,
"FrameScheduler.PreemptedForCooperativeScheduling",
&tracing_controller_,
YesNoStateToString),
aggressive_throttling_opt_out_count_(0),
opted_out_from_aggressive_throttling_(
false,
"FrameScheduler.AggressiveThrottlingDisabled",
&tracing_controller_,
YesNoStateToString),
subresource_loading_pause_count_(0u),
back_forward_cache_disabling_feature_tracker_(&tracing_controller_,
main_thread_scheduler_),
page_frozen_for_tracing_(
parent_page_scheduler_ ? parent_page_scheduler_->IsFrozen() : true,
"FrameScheduler.PageFrozen",
&tracing_controller_,
FrozenStateToString),
page_visibility_for_tracing_(
parent_page_scheduler_ && parent_page_scheduler_->IsPageVisible()
? PageVisibilityState::kVisible
: PageVisibilityState::kHidden,
"FrameScheduler.PageVisibility",
&tracing_controller_,
PageVisibilityStateToString),
waiting_for_contentful_paint_(true,
"FrameScheduler.WaitingForContentfulPaint",
&tracing_controller_,
YesNoStateToString),
waiting_for_meaningful_paint_(true,
"FrameScheduler.WaitingForMeaningfulPaint",
&tracing_controller_,
YesNoStateToString),
loading_power_mode_voter_(
power_scheduler::PowerModeArbiter::GetInstance()->NewVoter(
"PowerModeVoter.Loading")) {
frame_task_queue_controller_ = base::WrapUnique(
new FrameTaskQueueController(main_thread_scheduler_, this, this));
back_forward_cache_disabling_feature_tracker_.SetDelegate(delegate_);
}
FrameSchedulerImpl::FrameSchedulerImpl()
: FrameSchedulerImpl(/*main_thread_scheduler=*/nullptr,
/*parent_page_scheduler=*/nullptr,
/*delegate=*/nullptr,
/*is_in_embedded_frame_tree=*/false,
FrameType::kSubframe) {}
namespace {
void CleanUpQueue(MainThreadTaskQueue* queue) {
DCHECK(queue);
queue->DetachFromMainThreadScheduler();
DCHECK(!queue->GetFrameScheduler());
}
} // namespace
FrameSchedulerImpl::~FrameSchedulerImpl() {
weak_factory_.InvalidateWeakPtrs();
for (const auto& task_queue_and_voter :
frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) {
if (task_queue_and_voter.first->CanBeThrottled()) {
RemoveThrottleableQueueFromBudgetPools(task_queue_and_voter.first);
}
CleanUpQueue(task_queue_and_voter.first);
}
if (parent_page_scheduler_) {
parent_page_scheduler_->Unregister(this);
if (opted_out_from_aggressive_throttling())
parent_page_scheduler_->OnThrottlingStatusUpdated();
}
}
void FrameSchedulerImpl::DetachFromPageScheduler() {
for (const auto& task_queue_and_voter :
frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) {
if (task_queue_and_voter.first->CanBeThrottled()) {
RemoveThrottleableQueueFromBudgetPools(task_queue_and_voter.first);
}
}
parent_page_scheduler_ = nullptr;
}
void FrameSchedulerImpl::RemoveThrottleableQueueFromBudgetPools(
MainThreadTaskQueue* task_queue) {
DCHECK(task_queue);
DCHECK(task_queue->CanBeThrottled());
if (!parent_page_scheduler_)
return;
CPUTimeBudgetPool* cpu_time_budget_pool =
parent_page_scheduler_->background_cpu_time_budget_pool();
// On tests, the scheduler helper might already be shut down and tick is not
// available.
base::LazyNow lazy_now =
main_thread_scheduler_->GetTickClock()
? base::LazyNow(main_thread_scheduler_->GetTickClock())
: base::LazyNow(base::TimeTicks::Now());
if (cpu_time_budget_pool) {
task_queue->RemoveFromBudgetPool(lazy_now.Now(), cpu_time_budget_pool);
}
parent_page_scheduler_->RemoveQueueFromWakeUpBudgetPool(task_queue,
&lazy_now);
}
void FrameSchedulerImpl::MoveTaskQueuesToCorrectWakeUpBudgetPool() {
base::LazyNow lazy_now(main_thread_scheduler_->GetTickClock());
// The WakeUpBudgetPool is selected based on origin state, frame visibility
// and page background state.
//
// For each throttled queue, check if it should be in a different
// WakeUpBudgetPool and make the necessary adjustments.
for (const auto& task_queue_and_voter :
frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) {
auto* task_queue = task_queue_and_voter.first;
if (!task_queue->CanBeThrottled())
continue;
auto* new_wake_up_budget_pool = parent_page_scheduler_->GetWakeUpBudgetPool(
task_queue, frame_origin_type_, frame_visible_);
if (task_queue->GetWakeUpBudgetPool() == new_wake_up_budget_pool) {
continue;
}
parent_page_scheduler_->RemoveQueueFromWakeUpBudgetPool(task_queue,
&lazy_now);
parent_page_scheduler_->AddQueueToWakeUpBudgetPool(
task_queue, frame_origin_type_, frame_visible_, &lazy_now);
}
}
void FrameSchedulerImpl::SetFrameVisible(bool frame_visible) {
DCHECK(parent_page_scheduler_);
if (frame_visible_ == frame_visible)
return;
UMA_HISTOGRAM_BOOLEAN("RendererScheduler.IPC.FrameVisibility", frame_visible);
frame_visible_ = frame_visible;
MoveTaskQueuesToCorrectWakeUpBudgetPool();
UpdatePolicy();
}
bool FrameSchedulerImpl::IsFrameVisible() const {
return frame_visible_;
}
void FrameSchedulerImpl::SetCrossOriginToNearestMainFrame(bool cross_origin) {
DCHECK(parent_page_scheduler_);
if (frame_origin_type_ == FrameOriginType::kMainFrame) {
DCHECK(!cross_origin);
return;
}
if (cross_origin) {
frame_origin_type_ = FrameOriginType::kCrossOriginToMainFrame;
} else {
frame_origin_type_ = FrameOriginType::kSameOriginToMainFrame;
}
MoveTaskQueuesToCorrectWakeUpBudgetPool();
UpdatePolicy();
}
void FrameSchedulerImpl::SetIsAdFrame(bool is_ad_frame) {
is_ad_frame_ = is_ad_frame;
UpdatePolicy();
}
bool FrameSchedulerImpl::IsAdFrame() const {
return is_ad_frame_;
}
bool FrameSchedulerImpl::IsInEmbeddedFrameTree() const {
return is_in_embedded_frame_tree_;
}
bool FrameSchedulerImpl::IsCrossOriginToNearestMainFrame() const {
return frame_origin_type_ == FrameOriginType::kCrossOriginToMainFrame;
}
void FrameSchedulerImpl::TraceUrlChange(const String& url) {
url_tracer_.TraceString(url);
}
void FrameSchedulerImpl::AddTaskTime(base::TimeDelta time) {
// The duration of task time under which AddTaskTime buffers rather than
// sending the task time update to the delegate.
constexpr base::TimeDelta kTaskDurationSendThreshold =
base::Milliseconds(100);
if (!delegate_)
return;
task_time_ += time;
if (task_time_ >= kTaskDurationSendThreshold) {
delegate_->UpdateTaskTime(task_time_);
task_time_ = base::TimeDelta();
}
}
FrameScheduler::FrameType FrameSchedulerImpl::GetFrameType() const {
return frame_type_;
}
// static
QueueTraits FrameSchedulerImpl::CreateQueueTraitsForTaskType(TaskType type) {
// TODO(haraken): Optimize the mapping from TaskTypes to task runners.
// TODO(sreejakshetty): Clean up the PrioritisationType QueueTrait and
// QueueType for kInternalContinueScriptLoading and kInternalContentCapture.
switch (type) {
case TaskType::kInternalContentCapture:
return ThrottleableTaskQueueTraits().SetPrioritisationType(
QueueTraits::PrioritisationType::kBestEffort);
case TaskType::kJavascriptTimerDelayedLowNesting:
return ThrottleableTaskQueueTraits().SetPrioritisationType(
QueueTraits::PrioritisationType::kJavaScriptTimer);
case TaskType::kJavascriptTimerDelayedHighNesting:
return ThrottleableTaskQueueTraits()
.SetPrioritisationType(
QueueTraits::PrioritisationType::kJavaScriptTimer)
.SetCanBeIntensivelyThrottled(IsIntensiveWakeUpThrottlingEnabled());
case TaskType::kJavascriptTimerImmediate: {
// Immediate timers are not throttled.
return DeferrableTaskQueueTraits().SetPrioritisationType(
QueueTraits::PrioritisationType::kJavaScriptTimer);
}
case TaskType::kInternalLoading:
case TaskType::kNetworking:
return LoadingTaskQueueTraits();
case TaskType::kNetworkingUnfreezable:
return IsInflightNetworkRequestBackForwardCacheSupportEnabled()
? UnfreezableLoadingTaskQueueTraits()
: LoadingTaskQueueTraits();
case TaskType::kNetworkingUnfreezableImageLoading: {
QueueTraits queue_traits =
IsInflightNetworkRequestBackForwardCacheSupportEnabled()
? UnfreezableLoadingTaskQueueTraits()
: LoadingTaskQueueTraits();
queue_traits.SetPrioritisationType(
QueueTraits::PrioritisationType::kRenderBlocking);
return queue_traits;
}
case TaskType::kNetworkingControl:
return LoadingControlTaskQueueTraits();
case TaskType::kLowPriorityScriptExecution:
return LoadingTaskQueueTraits().SetPrioritisationType(
QueueTraits::PrioritisationType::kBestEffort);
// Throttling following tasks may break existing web pages, so tentatively
// these are unthrottled.
// TODO(nhiroki): Throttle them again after we're convinced that it's safe
// or provide a mechanism that web pages can opt-out it if throttling is not
// desirable.
case TaskType::kDOMManipulation:
case TaskType::kHistoryTraversal:
case TaskType::kEmbed:
case TaskType::kCanvasBlobSerialization:
case TaskType::kRemoteEvent:
case TaskType::kWebSocket:
case TaskType::kMicrotask:
case TaskType::kUnshippedPortMessage:
case TaskType::kFileReading:
case TaskType::kPresentation:
case TaskType::kSensor:
case TaskType::kPerformanceTimeline:
case TaskType::kWebGL:
case TaskType::kWebGPU:
case TaskType::kIdleTask:
case TaskType::kInternalDefault:
case TaskType::kMiscPlatformAPI:
case TaskType::kFontLoading:
case TaskType::kApplicationLifeCycle:
case TaskType::kBackgroundFetch:
case TaskType::kPermission:
case TaskType::kWakeLock:
case TaskType::kStorage:
// TODO(altimin): Move appropriate tasks to throttleable task queue.
return DeferrableTaskQueueTraits();
// PostedMessage can be used for navigation, so we shouldn't defer it
// when expecting a user gesture.
case TaskType::kPostedMessage:
case TaskType::kServiceWorkerClientMessage:
case TaskType::kWorkerAnimation:
// UserInteraction tasks should be run even when expecting a user gesture.
case TaskType::kUserInteraction:
// Media events should not be deferred to ensure that media playback is
// smooth.
case TaskType::kMediaElementEvent:
case TaskType::kInternalWebCrypto:
case TaskType::kInternalMedia:
case TaskType::kInternalMediaRealTime:
case TaskType::kInternalUserInteraction:
case TaskType::kInternalIntersectionObserver:
return PausableTaskQueueTraits();
case TaskType::kInternalFindInPage:
return FindInPageTaskQueueTraits();
case TaskType::kInternalHighPriorityLocalFrame:
return QueueTraits().SetPrioritisationType(
QueueTraits::PrioritisationType::kHighPriorityLocalFrame);
case TaskType::kInternalContinueScriptLoading:
return PausableTaskQueueTraits().SetPrioritisationType(
QueueTraits::PrioritisationType::kInternalScriptContinuation);
case TaskType::kDatabaseAccess:
if (base::FeatureList::IsEnabled(kHighPriorityDatabaseTaskType)) {
return PausableTaskQueueTraits().SetPrioritisationType(
QueueTraits::PrioritisationType::kExperimentalDatabase);
} else {
return PausableTaskQueueTraits();
}
case TaskType::kInternalNavigationAssociated:
return FreezableTaskQueueTraits();
case TaskType::kInternalNavigationCancellation:
return FreezableTaskQueueTraits().SetPrioritisationType(
QueueTraits::PrioritisationType::kInternalNavigationCancellation);
case TaskType::kInternalInputBlocking:
return InputBlockingQueueTraits();
// Some tasks in the tests need to run when objects are paused e.g. to hook
// when recovering from debugger JavaScript statetment.
case TaskType::kInternalTest:
// kWebLocks can be frozen if for entire page, but not for individual
// frames. See https://crrev.com/c/1687716
case TaskType::kWebLocks:
case TaskType::kInternalFrameLifecycleControl:
return UnpausableTaskQueueTraits();
case TaskType::kInternalTranslation:
return ForegroundOnlyTaskQueueTraits();
// The TaskType of Inspector tasks need to be unpausable and should not use
// virtual time because they need to run on a paused page or when virtual
// time is paused.
case TaskType::kInternalInspector:
// Navigation IPCs do not run using virtual time to avoid hanging.
case TaskType::kInternalNavigationAssociatedUnfreezable:
return CanRunWhenVirtualTimePausedTaskQueueTraits();
case TaskType::kInternalPostMessageForwarding:
// postMessages to remote frames hop through the scheduler so that any
// IPCs generated in the same task arrive first. These tasks must be
// pausable in order to maintain this invariant, otherwise they might run
// in a nested event loop before the task completes, e.g. debugger
// breakpoints or javascript dialogs.
//
// Freezing this task type would prevent transmission of postMessages to
// remote frames that occurred in unfreezable tasks or from tasks that ran
// prior to being frozen (e.g. freeze event handler), which is not
// desirable. The messages are still queued on the receiving side, which
// is where frozenness should be assessed.
return PausableTaskQueueTraits()
.SetCanBeFrozen(false)
.SetPrioritisationType(
QueueTraits::PrioritisationType::kPostMessageForwarding);
case TaskType::kDeprecatedNone:
case TaskType::kMainThreadTaskQueueV8:
case TaskType::kMainThreadTaskQueueCompositor:
case TaskType::kMainThreadTaskQueueDefault:
case TaskType::kMainThreadTaskQueueInput:
case TaskType::kMainThreadTaskQueueIdle:
case TaskType::kMainThreadTaskQueueControl:
case TaskType::kMainThreadTaskQueueMemoryPurge:
case TaskType::kMainThreadTaskQueueIPCTracking:
case TaskType::kCompositorThreadTaskQueueDefault:
case TaskType::kCompositorThreadTaskQueueInput:
case TaskType::kWorkerThreadTaskQueueDefault:
case TaskType::kWorkerThreadTaskQueueV8:
case TaskType::kWorkerThreadTaskQueueCompositor:
case TaskType::kMainThreadTaskQueueNonWaking:
// The web scheduling API task types are used by WebSchedulingTaskQueues.
// The associated TaskRunner should be obtained by creating a
// WebSchedulingTaskQueue with CreateWebSchedulingTaskQueue().
case TaskType::kWebSchedulingPostedTask:
// Not a valid frame-level TaskType.
NOTREACHED();
return QueueTraits();
}
// This method is called for all values between 0 and kCount. TaskType,
// however, has numbering gaps, so even though all enumerated TaskTypes are
// handled in the switch and return a value, we fall through for some values
// of |type|.
NOTREACHED();
return QueueTraits();
}
scoped_refptr<base::SingleThreadTaskRunner> FrameSchedulerImpl::GetTaskRunner(
TaskType type) {
scoped_refptr<MainThreadTaskQueue> task_queue = GetTaskQueue(type);
DCHECK(task_queue);
return task_queue->CreateTaskRunner(type);
}
scoped_refptr<MainThreadTaskQueue> FrameSchedulerImpl::GetTaskQueue(
TaskType type) {
QueueTraits queue_traits = CreateQueueTraitsForTaskType(type);
return frame_task_queue_controller_->GetTaskQueue(queue_traits);
}
scoped_refptr<base::SingleThreadTaskRunner>
FrameSchedulerImpl::ControlTaskRunner() {
DCHECK(parent_page_scheduler_);
return main_thread_scheduler_->ControlTaskRunner();
}
AgentGroupScheduler* FrameSchedulerImpl::GetAgentGroupScheduler() {
return parent_page_scheduler_
? &parent_page_scheduler_->GetAgentGroupScheduler()
: nullptr;
}
blink::PageScheduler* FrameSchedulerImpl::GetPageScheduler() const {
return parent_page_scheduler_;
}
void FrameSchedulerImpl::DidStartProvisionalLoad() {
main_thread_scheduler_->DidStartProvisionalLoad(
frame_type_ == FrameScheduler::FrameType::kMainFrame &&
!is_in_embedded_frame_tree_);
}
void FrameSchedulerImpl::DidCommitProvisionalLoad(
bool is_web_history_inert_commit,
NavigationType navigation_type) {
bool is_outermost_main_frame =
GetFrameType() == FrameType::kMainFrame && !is_in_embedded_frame_tree_;
bool is_same_document = navigation_type == NavigationType::kSameDocument;
if (!is_same_document) {
loading_power_mode_voter_->VoteFor(power_scheduler::PowerMode::kLoading);
loading_power_mode_voter_->ResetVoteAfterTimeout(
power_scheduler::PowerModeVoter::kStuckLoadingTimeout);
waiting_for_contentful_paint_ = true;
waiting_for_meaningful_paint_ = true;
}
if (is_outermost_main_frame && !is_same_document) {
task_time_ = base::TimeDelta();
}
main_thread_scheduler_->DidCommitProvisionalLoad(
is_web_history_inert_commit, navigation_type == NavigationType::kReload,
is_outermost_main_frame);
if (!is_same_document)
ResetForNavigation();
}
WebScopedVirtualTimePauser FrameSchedulerImpl::CreateWebScopedVirtualTimePauser(
const WTF::String& name,
WebScopedVirtualTimePauser::VirtualTaskDuration duration) {
return WebScopedVirtualTimePauser(main_thread_scheduler_, duration, name);
}
scoped_refptr<base::SingleThreadTaskRunner>
FrameSchedulerImpl::CompositorTaskRunner() {
return parent_page_scheduler_->GetAgentGroupScheduler()
.CompositorTaskRunner();
}
void FrameSchedulerImpl::ResetForNavigation() {
document_bound_weak_factory_.InvalidateWeakPtrs();
back_forward_cache_disabling_feature_tracker_.Reset();
}
void FrameSchedulerImpl::OnStartedUsingNonStickyFeature(
SchedulingPolicy::Feature feature,
const SchedulingPolicy& policy,
std::unique_ptr<SourceLocation> source_location,
SchedulingAffectingFeatureHandle* handle) {
if (policy.disable_aggressive_throttling)
OnAddedAggressiveThrottlingOptOut();
if (policy.disable_back_forward_cache) {
back_forward_cache_disabling_feature_tracker_.AddNonStickyFeature(
feature, std::move(source_location), handle);
}
if (policy.disable_align_wake_ups)
DisableAlignWakeUpsForProcess();
}
void FrameSchedulerImpl::OnStartedUsingStickyFeature(
SchedulingPolicy::Feature feature,
const SchedulingPolicy& policy,
std::unique_ptr<SourceLocation> source_location) {
if (policy.disable_aggressive_throttling)
OnAddedAggressiveThrottlingOptOut();
if (policy.disable_back_forward_cache) {
back_forward_cache_disabling_feature_tracker_.AddStickyFeature(
feature, std::move(source_location));
}
if (policy.disable_align_wake_ups)
DisableAlignWakeUpsForProcess();
}
void FrameSchedulerImpl::OnStoppedUsingNonStickyFeature(
SchedulingAffectingFeatureHandle* handle) {
if (handle->GetPolicy().disable_aggressive_throttling)
OnRemovedAggressiveThrottlingOptOut();
if (handle->GetPolicy().disable_back_forward_cache) {
back_forward_cache_disabling_feature_tracker_.Remove(
handle->GetFeatureAndJSLocationBlockingBFCache());
}
}
base::WeakPtr<FrameScheduler> FrameSchedulerImpl::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
base::WeakPtr<const FrameSchedulerImpl> FrameSchedulerImpl::GetWeakPtr() const {
return weak_factory_.GetWeakPtr();
}
void FrameSchedulerImpl::ReportActiveSchedulerTrackedFeatures() {
back_forward_cache_disabling_feature_tracker_.ReportFeaturesToDelegate();
}
base::WeakPtr<FrameSchedulerImpl>
FrameSchedulerImpl::GetInvalidatingOnBFCacheRestoreWeakPtr() {
return invalidating_on_bfcache_restore_weak_factory_.GetWeakPtr();
}
void FrameSchedulerImpl::OnAddedAggressiveThrottlingOptOut() {
++aggressive_throttling_opt_out_count_;
opted_out_from_aggressive_throttling_ =
static_cast<bool>(aggressive_throttling_opt_out_count_);
if (parent_page_scheduler_)
parent_page_scheduler_->OnThrottlingStatusUpdated();
}
void FrameSchedulerImpl::OnRemovedAggressiveThrottlingOptOut() {
DCHECK_GT(aggressive_throttling_opt_out_count_, 0);
--aggressive_throttling_opt_out_count_;
opted_out_from_aggressive_throttling_ =
static_cast<bool>(aggressive_throttling_opt_out_count_);
if (parent_page_scheduler_)
parent_page_scheduler_->OnThrottlingStatusUpdated();
}
void FrameSchedulerImpl::OnTaskCompleted(
TaskQueue::TaskTiming* timing,
base::TimeTicks desired_execution_time) {
if (delegate_) {
delegate_->OnTaskCompleted(timing->start_time(), timing->end_time(),
desired_execution_time);
}
}
void FrameSchedulerImpl::WriteIntoTrace(perfetto::TracedValue context) const {
auto dict = std::move(context).WriteDictionary();
dict.Add("frame_visible", frame_visible_);
dict.Add("page_visible", parent_page_scheduler_->IsPageVisible());
dict.Add("cross_origin_to_main_frame", IsCrossOriginToNearestMainFrame());
dict.Add("frame_type", frame_type_ == FrameScheduler::FrameType::kMainFrame
? "MainFrame"
: "Subframe");
dict.Add("disable_background_timer_throttling",
!RuntimeEnabledFeatures::TimerThrottlingForBackgroundTabsEnabled());
dict.Add("frame_task_queue_controller", frame_task_queue_controller_);
}
void FrameSchedulerImpl::WriteIntoTrace(
perfetto::TracedProto<
perfetto::protos::pbzero::RendererMainThreadTaskExecution> proto)
const {
proto->set_frame_visible(frame_visible_);
proto->set_page_visible(parent_page_scheduler_->IsPageVisible());
proto->set_frame_type(
frame_type_ == FrameScheduler::FrameType::kMainFrame
? RendererMainThreadTaskExecution::FRAME_TYPE_MAIN_FRAME
: IsCrossOriginToNearestMainFrame()
? RendererMainThreadTaskExecution::FRAME_TYPE_CROSS_ORIGIN_SUBFRAME
: RendererMainThreadTaskExecution::FRAME_TYPE_SAME_ORIGIN_SUBFRAME);
proto->set_is_ad_frame(is_ad_frame_);
}
void FrameSchedulerImpl::SetPageVisibilityForTracing(
PageVisibilityState page_visibility) {
page_visibility_for_tracing_ = page_visibility;
}
bool FrameSchedulerImpl::IsPageVisible() const {
return parent_page_scheduler_ ? parent_page_scheduler_->IsPageVisible()
: true;
}
void FrameSchedulerImpl::SetPaused(bool frame_paused) {
DCHECK(parent_page_scheduler_);
if (frame_paused_ == frame_paused)
return;
frame_paused_ = frame_paused;
UpdatePolicy();
}
void FrameSchedulerImpl::SetShouldReportPostedTasksWhenDisabled(
bool should_report) {
// Forward this to all the task queues associated with this frame.
for (const auto& task_queue_and_voter :
frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) {
auto* task_queue = task_queue_and_voter.first;
if (task_queue->CanBeFrozen()) {
task_queue->SetShouldReportPostedTasksWhenDisabled(should_report);
}
}
}
void FrameSchedulerImpl::SetPageFrozenForTracing(bool frozen) {
page_frozen_for_tracing_ = frozen;
}
void FrameSchedulerImpl::UpdatePolicy() {
bool task_queues_were_throttled = task_queues_throttled_;
task_queues_throttled_ = ShouldThrottleTaskQueues();
if (!task_queues_throttled_)
throttled_task_queue_handles_.clear();
for (const auto& task_queue_and_voter :
frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) {
UpdateQueuePolicy(task_queue_and_voter.first, task_queue_and_voter.second);
if (task_queues_throttled_ && !task_queues_were_throttled &&
task_queue_and_voter.first->CanBeThrottled()) {
MainThreadTaskQueue::ThrottleHandle handle =
task_queue_and_voter.first->Throttle();
throttled_task_queue_handles_.push_back(std::move(handle));
}
}
NotifyLifecycleObservers();
}
void FrameSchedulerImpl::UpdateQueuePolicy(
MainThreadTaskQueue* queue,
TaskQueue::QueueEnabledVoter* voter) {
DCHECK(queue);
UpdatePriority(queue);
DCHECK(voter);
DCHECK(parent_page_scheduler_);
bool queue_disabled = false;
queue_disabled |= frame_paused_ && queue->CanBePaused();
queue_disabled |= preempted_for_cooperative_scheduling_;
// Per-frame freezable task queues will be frozen after 5 mins in background
// on Android, and if the browser freezes the page in the background. They
// will be resumed when the page is visible.
bool queue_frozen =
parent_page_scheduler_->IsFrozen() && queue->CanBeFrozen();
queue_disabled |= queue_frozen;
// Per-frame freezable queues of tasks which are specified as getting frozen
// immediately when their frame becomes invisible get frozen. They will be
// resumed when the frame becomes visible again.
queue_disabled |= !frame_visible_ && !queue->CanRunInBackground();
if (queue_disabled) {
TRACE_EVENT_INSTANT("renderer.scheduler",
"FrameSchedulerImpl::UpdateQueuePolicy_QueueDisabled");
} else {
TRACE_EVENT_INSTANT("renderer.scheduler",
"FrameSchedulerImpl::UpdateQueuePolicy_QueueEnabled");
}
voter->SetVoteToEnable(!queue_disabled);
}
SchedulingLifecycleState FrameSchedulerImpl::CalculateLifecycleState(
ObserverType type) const {
// Detached frames are not throttled.
if (!parent_page_scheduler_)
return SchedulingLifecycleState::kNotThrottled;
if (parent_page_scheduler_->IsFrozen()) {
DCHECK(!parent_page_scheduler_->IsPageVisible());
return SchedulingLifecycleState::kStopped;
}
if (subresource_loading_paused_ && type == ObserverType::kLoader)
return SchedulingLifecycleState::kStopped;
if (type == ObserverType::kLoader &&
parent_page_scheduler_->OptedOutFromAggressiveThrottling()) {
return SchedulingLifecycleState::kNotThrottled;
}
// Note: The scheduling lifecycle state ignores wake up rate throttling.
if (parent_page_scheduler_->IsCPUTimeThrottled())
return SchedulingLifecycleState::kThrottled;
if (!parent_page_scheduler_->IsPageVisible())
return SchedulingLifecycleState::kHidden;
return SchedulingLifecycleState::kNotThrottled;
}
void FrameSchedulerImpl::OnFirstContentfulPaintInMainFrame() {
waiting_for_contentful_paint_ = false;
DCHECK_EQ(GetFrameType(), FrameScheduler::FrameType::kMainFrame);
main_thread_scheduler_->OnMainFramePaint();
}
void FrameSchedulerImpl::OnMainFrameInteractive() {
if (delegate_) {
return delegate_->MainFrameInteractive();
}
}
void FrameSchedulerImpl::OnFirstMeaningfulPaint() {
waiting_for_meaningful_paint_ = false;
if (GetFrameType() != FrameScheduler::FrameType::kMainFrame ||
is_in_embedded_frame_tree_) {
return;
}
main_thread_scheduler_->OnMainFramePaint();
}
void FrameSchedulerImpl::OnLoad() {
loading_power_mode_voter_->ResetVoteAfterTimeout(
power_scheduler::PowerModeVoter::kLoadingTimeout);
}
bool FrameSchedulerImpl::IsWaitingForContentfulPaint() const {
return waiting_for_contentful_paint_;
}
bool FrameSchedulerImpl::IsWaitingForMeaningfulPaint() const {
return waiting_for_meaningful_paint_;
}
bool FrameSchedulerImpl::IsOrdinary() const {
if (!parent_page_scheduler_)
return true;
return parent_page_scheduler_->IsOrdinary();
}
bool FrameSchedulerImpl::ShouldThrottleTaskQueues() const {
DCHECK(parent_page_scheduler_);
if (parent_page_scheduler_->ThrottleForegroundTimers())
return true;
if (!RuntimeEnabledFeatures::TimerThrottlingForBackgroundTabsEnabled())
return false;
if (parent_page_scheduler_->IsAudioPlaying())
return false;
if (!parent_page_scheduler_->IsPageVisible())
return true;
return !frame_visible_ && IsCrossOriginToNearestMainFrame();
}
bool FrameSchedulerImpl::IsExemptFromBudgetBasedThrottling() const {
return opted_out_from_aggressive_throttling();
}
TaskPriority FrameSchedulerImpl::ComputePriority(
MainThreadTaskQueue* task_queue) const {
DCHECK(task_queue);
FrameScheduler* frame_scheduler = task_queue->GetFrameScheduler();
// Checks the task queue is associated with this frame scheduler.
DCHECK_EQ(frame_scheduler, this);
// TODO(crbug.com/986569): Ordering here is relative to the experiments below.
// Cleanup unused experiment logic so that this switch can be merged with the
// prioritisation type decisions below.
switch (task_queue->GetPrioritisationType()) {
case MainThreadTaskQueue::QueueTraits::PrioritisationType::
kInternalScriptContinuation:
return TaskPriority::kVeryHighPriority;
case MainThreadTaskQueue::QueueTraits::PrioritisationType::kBestEffort:
return TaskPriority::kBestEffortPriority;
case MainThreadTaskQueue::QueueTraits::PrioritisationType::
kPostMessageForwarding:
return TaskPriority::kVeryHighPriority;
case MainThreadTaskQueue::QueueTraits::PrioritisationType::
kInternalNavigationCancellation:
return TaskPriority::kVeryHighPriority;
default:
break;
}
// TODO(shaseley): This should use lower priorities if the frame is
// deprioritized. Change this once we refactor and add frame policy/priorities
// and add a range of new priorities less than low.
if (absl::optional<WebSchedulingQueueType> queue_type =
task_queue->GetWebSchedulingQueueType()) {
bool is_continuation =
*queue_type == WebSchedulingQueueType::kContinuationQueue;
switch (*task_queue->GetWebSchedulingPriority()) {
case WebSchedulingPriority::kUserBlockingPriority:
return is_continuation ? TaskPriority::kHighPriorityContinuation
: TaskPriority::kHighPriority;
case WebSchedulingPriority::kUserVisiblePriority:
return is_continuation ? TaskPriority::kNormalPriorityContinuation
: TaskPriority::kNormalPriority;
case WebSchedulingPriority::kBackgroundPriority:
return is_continuation ? TaskPriority::kLowPriorityContinuation
: TaskPriority::kLowPriority;
}
}
if (!parent_page_scheduler_) {
// Frame might be detached during its shutdown. Return a default priority
// in that case.
return TaskPriority::kNormalPriority;
}
if (task_queue->GetPrioritisationType() ==
MainThreadTaskQueue::QueueTraits::PrioritisationType::kLoadingControl) {
return TaskPriority::kHighPriority;
}
if (task_queue->GetPrioritisationType() ==
MainThreadTaskQueue::QueueTraits::PrioritisationType::kFindInPage) {
return main_thread_scheduler_->find_in_page_priority();
}
if (task_queue->GetPrioritisationType() ==
MainThreadTaskQueue::QueueTraits::PrioritisationType::
kHighPriorityLocalFrame) {
return TaskPriority::kHighestPriority;
}
if (task_queue->GetPrioritisationType() ==
MainThreadTaskQueue::QueueTraits::PrioritisationType::kInput &&
base::FeatureList::IsEnabled(
::blink::features::kInputTargetClientHighPriority)) {
return TaskPriority::kHighestPriority;
}
if (task_queue->GetPrioritisationType() ==
MainThreadTaskQueue::QueueTraits::PrioritisationType::
kExperimentalDatabase) {
// TODO(shaseley): This decision should probably be based on Agent
// visibility. Consider changing this before shipping anything.
return parent_page_scheduler_->IsPageVisible()
? TaskPriority::kHighPriority
: TaskPriority::kNormalPriority;
}
if (task_queue->GetPrioritisationType() ==
MainThreadTaskQueue::QueueTraits::PrioritisationType::kRenderBlocking) {
return parent_page_scheduler_->IsPageVisible()
? TaskPriority::kExtremelyHighPriority
: TaskPriority::kNormalPriority;
}
return TaskPriority::kNormalPriority;
}
std::unique_ptr<blink::mojom::blink::PauseSubresourceLoadingHandle>
FrameSchedulerImpl::GetPauseSubresourceLoadingHandle() {
return std::make_unique<PauseSubresourceLoadingHandleImpl>(
weak_factory_.GetWeakPtr());
}
void FrameSchedulerImpl::AddPauseSubresourceLoadingHandle() {
++subresource_loading_pause_count_;
if (subresource_loading_pause_count_ != 1) {
DCHECK(subresource_loading_paused_);
return;
}
DCHECK(!subresource_loading_paused_);
subresource_loading_paused_ = true;
UpdatePolicy();
}
void FrameSchedulerImpl::RemovePauseSubresourceLoadingHandle() {
DCHECK_LT(0u, subresource_loading_pause_count_);
--subresource_loading_pause_count_;
DCHECK(subresource_loading_paused_);
if (subresource_loading_pause_count_ == 0) {
subresource_loading_paused_ = false;
UpdatePolicy();
}
}
ukm::UkmRecorder* FrameSchedulerImpl::GetUkmRecorder() {
if (!delegate_)
return nullptr;
return delegate_->GetUkmRecorder();
}
ukm::SourceId FrameSchedulerImpl::GetUkmSourceId() {
if (!delegate_)
return ukm::kInvalidSourceId;
return delegate_->GetUkmSourceId();
}
void FrameSchedulerImpl::OnTaskQueueCreated(
MainThreadTaskQueue* task_queue,
base::sequence_manager::TaskQueue::QueueEnabledVoter* voter) {
DCHECK(parent_page_scheduler_);
UpdateQueuePolicy(task_queue, voter);
if (task_queue->CanBeThrottled()) {
base::LazyNow lazy_now(main_thread_scheduler_->GetTickClock());
CPUTimeBudgetPool* cpu_time_budget_pool =
parent_page_scheduler_->background_cpu_time_budget_pool();
if (cpu_time_budget_pool) {
task_queue->AddToBudgetPool(lazy_now.Now(), cpu_time_budget_pool);
}
parent_page_scheduler_->AddQueueToWakeUpBudgetPool(
task_queue, frame_origin_type_, frame_visible_, &lazy_now);
if (task_queues_throttled_) {
MainThreadTaskQueue::ThrottleHandle handle = task_queue->Throttle();
throttled_task_queue_handles_.push_back(std::move(handle));
}
}
}
void FrameSchedulerImpl::SetOnIPCTaskPostedWhileInBackForwardCacheHandler() {
DCHECK(parent_page_scheduler_->IsInBackForwardCache());
for (const auto& task_queue_and_voter :
frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) {
task_queue_and_voter.first->SetOnIPCTaskPosted(base::BindRepeating(
[](scoped_refptr<base::SingleThreadTaskRunner> task_runner,
base::WeakPtr<FrameSchedulerImpl> frame_scheduler,
const base::sequence_manager::Task& task) {
// Only log IPC tasks. IPC tasks are only logged currently as IPC
// hash can be mapped back to a function name, and IPC tasks may
// potentially post sensitive information.
if (!task.ipc_hash && !task.ipc_interface_name) {
return;
}
base::ScopedDeferTaskPosting::PostOrDefer(
task_runner, FROM_HERE,
base::BindOnce(
&FrameSchedulerImpl::OnIPCTaskPostedWhileInBackForwardCache,
frame_scheduler, task.ipc_hash, task.ipc_interface_name),
base::Seconds(1));
},
main_thread_scheduler_->BackForwardCacheIpcTrackingTaskRunner(),
GetInvalidatingOnBFCacheRestoreWeakPtr()));
}
}
void FrameSchedulerImpl::DetachOnIPCTaskPostedWhileInBackForwardCacheHandler() {
for (const auto& task_queue_and_voter :
frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) {
task_queue_and_voter.first->DetachOnIPCTaskPostedWhileInBackForwardCache();
}
invalidating_on_bfcache_restore_weak_factory_.InvalidateWeakPtrs();
}
void FrameSchedulerImpl::OnIPCTaskPostedWhileInBackForwardCache(
uint32_t ipc_hash,
const char* ipc_interface_name) {
// IPC tasks may have an IPC interface name in addition to, or instead of an
// IPC hash. IPC hash is known from the mojo Accept method. When IPC hash is
// 0, then the IPC hash must be calculated from the IPC interface name
// instead.
if (!ipc_hash) {
// base::HashMetricName produces a uint64; however, the MD5 hash calculation
// for an IPC interface name is always calculated as uint32; the IPC hash on
// a task is also a uint32. The calculation here is meant to mimic the
// calculation used in base::MD5Hash32Constexpr.
ipc_hash = base::TaskAnnotator::ScopedSetIpcHash::MD5HashMetricName(
ipc_interface_name);
}
DCHECK(parent_page_scheduler_->IsInBackForwardCache());
base::UmaHistogramSparse(
"BackForwardCache.Experimental.UnexpectedIPCMessagePostedToCachedFrame."
"MethodHash",
static_cast<int32_t>(ipc_hash));
base::TimeDelta duration =
main_thread_scheduler_->NowTicks() -
parent_page_scheduler_->GetStoredInBackForwardCacheTimestamp();
base::UmaHistogramCustomTimes(
"BackForwardCache.Experimental.UnexpectedIPCMessagePostedToCachedFrame."
"TimeUntilIPCReceived",
duration, base::TimeDelta(), base::Minutes(5), 100);
}
WTF::HashSet<SchedulingPolicy::Feature>
FrameSchedulerImpl::GetActiveFeaturesTrackedForBackForwardCacheMetrics() {
return back_forward_cache_disabling_feature_tracker_
.GetActiveFeaturesTrackedForBackForwardCacheMetrics();
}
uint64_t
FrameSchedulerImpl::GetActiveFeaturesTrackedForBackForwardCacheMetricsMask()
const {
return back_forward_cache_disabling_feature_tracker_
.GetActiveFeaturesTrackedForBackForwardCacheMetricsMask();
}
base::WeakPtr<FrameOrWorkerScheduler>
FrameSchedulerImpl::GetFrameOrWorkerSchedulerWeakPtr() {
// We reset feature sets upon frame navigation, so having a document-bound
// weak pointer ensures that the feature handle associated with previous
// document can't influence the new one.
return document_bound_weak_factory_.GetWeakPtr();
}
std::unique_ptr<WebSchedulingTaskQueue>
FrameSchedulerImpl::CreateWebSchedulingTaskQueue(
WebSchedulingQueueType queue_type,
WebSchedulingPriority priority) {
// The QueueTraits for scheduler.postTask() are similar to those of
// setTimeout() (deferrable queue traits + throttling for delayed tasks), with
// the following differences:
// 1. All delayed tasks are intensively throttled (no nesting-level exception
// or policy/flag opt-out)
// 2. There is no separate PrioritisationType (prioritization is based on the
// WebSchedulingPriority, which is only set for these task queues)
scoped_refptr<MainThreadTaskQueue> immediate_task_queue =
frame_task_queue_controller_->NewWebSchedulingTaskQueue(
DeferrableTaskQueueTraits(), queue_type, priority);
// Continuation task queues can only be used for immediate tasks since there
// the yield API doesn't support delayed continuations.
if (queue_type == WebSchedulingQueueType::kContinuationQueue) {
return std::make_unique<MainThreadWebSchedulingTaskQueueImpl>(
immediate_task_queue->AsWeakPtr(), nullptr);
}
scoped_refptr<MainThreadTaskQueue> delayed_task_queue =
frame_task_queue_controller_->NewWebSchedulingTaskQueue(
DeferrableTaskQueueTraits()
.SetCanBeThrottled(true)
.SetCanBeIntensivelyThrottled(true),
queue_type, priority);
return std::make_unique<MainThreadWebSchedulingTaskQueueImpl>(
immediate_task_queue->AsWeakPtr(), delayed_task_queue->AsWeakPtr());
}
void FrameSchedulerImpl::OnWebSchedulingTaskQueuePriorityChanged(
MainThreadTaskQueue* queue) {
UpdateQueuePolicy(queue,
frame_task_queue_controller_->GetQueueEnabledVoter(queue));
}
void FrameSchedulerImpl::OnWebSchedulingTaskQueueDestroyed(
MainThreadTaskQueue* queue) {
if (queue->CanBeThrottled())
RemoveThrottleableQueueFromBudgetPools(queue);
CleanUpQueue(queue);
// After this is called, the queue will be destroyed. Do not attempt
// to use it further.
frame_task_queue_controller_->RemoveWebSchedulingTaskQueue(queue);
}
const base::UnguessableToken& FrameSchedulerImpl::GetAgentClusterId() const {
if (!delegate_)
return base::UnguessableToken::Null();
return delegate_->GetAgentClusterId();
}
// static
MainThreadTaskQueue::QueueTraits
FrameSchedulerImpl::ThrottleableTaskQueueTraits() {
return QueueTraits()
.SetCanBeThrottled(true)
.SetCanBeFrozen(true)
.SetCanBeDeferred(true)
.SetCanBePaused(true)
.SetCanRunWhenVirtualTimePaused(false)
.SetCanBePausedForAndroidWebview(true);
}
// static
MainThreadTaskQueue::QueueTraits
FrameSchedulerImpl::DeferrableTaskQueueTraits() {
return QueueTraits()
.SetCanBeDeferred(true)
.SetCanBeFrozen(true)
.SetCanBePaused(true)
.SetCanRunWhenVirtualTimePaused(false)
.SetCanBePausedForAndroidWebview(true);
}
// static
MainThreadTaskQueue::QueueTraits FrameSchedulerImpl::PausableTaskQueueTraits() {
return QueueTraits()
.SetCanBeFrozen(true)
.SetCanBePaused(true)
.SetCanRunWhenVirtualTimePaused(false)
.SetCanBePausedForAndroidWebview(true);
}
// static
MainThreadTaskQueue::QueueTraits
FrameSchedulerImpl::FreezableTaskQueueTraits() {
// Should not use VirtualTime because using VirtualTime would make the task
// execution non-deterministic and produce timeouts failures.
return QueueTraits().SetCanBeFrozen(true);
}
// static
MainThreadTaskQueue::QueueTraits
FrameSchedulerImpl::UnpausableTaskQueueTraits() {
return QueueTraits().SetCanRunWhenVirtualTimePaused(false);
}
MainThreadTaskQueue::QueueTraits
FrameSchedulerImpl::ForegroundOnlyTaskQueueTraits() {
return ThrottleableTaskQueueTraits()
.SetCanRunInBackground(false)
.SetCanRunWhenVirtualTimePaused(false);
}
MainThreadTaskQueue::QueueTraits
FrameSchedulerImpl::CanRunWhenVirtualTimePausedTaskQueueTraits() {
return QueueTraits().SetCanRunWhenVirtualTimePaused(true);
}
void FrameSchedulerImpl::SetPreemptedForCooperativeScheduling(
Preempted preempted) {
DCHECK_NE(preempted.value(), preempted_for_cooperative_scheduling_);
preempted_for_cooperative_scheduling_ = preempted.value();
UpdatePolicy();
}
MainThreadTaskQueue::QueueTraits FrameSchedulerImpl::LoadingTaskQueueTraits() {
return QueueTraits()
.SetCanBePaused(true)
.SetCanBeFrozen(true)
.SetCanBeDeferred(true)
.SetPrioritisationType(QueueTraits::PrioritisationType::kLoading);
}
MainThreadTaskQueue::QueueTraits
FrameSchedulerImpl::UnfreezableLoadingTaskQueueTraits() {
return LoadingTaskQueueTraits().SetCanBeFrozen(false);
}
MainThreadTaskQueue::QueueTraits
FrameSchedulerImpl::LoadingControlTaskQueueTraits() {
return QueueTraits()
.SetCanBePaused(true)
.SetCanBeFrozen(true)
.SetCanBeDeferred(true)
.SetPrioritisationType(QueueTraits::PrioritisationType::kLoadingControl);
}
MainThreadTaskQueue::QueueTraits
FrameSchedulerImpl::FindInPageTaskQueueTraits() {
return PausableTaskQueueTraits().SetPrioritisationType(
QueueTraits::PrioritisationType::kFindInPage);
}
MainThreadTaskQueue::QueueTraits
FrameSchedulerImpl::InputBlockingQueueTraits() {
return QueueTraits().SetPrioritisationType(
QueueTraits::PrioritisationType::kInput);
}
} // namespace scheduler
} // namespace blink