1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h"
6
7 #include <memory>
8
9 #include "base/metrics/field_trial_params.h"
10 #include "base/metrics/histogram_functions.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/task/common/scoped_defer_task_posting.h"
13 #include "base/task/common/task_annotator.h"
14 #include "base/task/sequence_manager/lazy_now.h"
15 #include "base/time/time.h"
16 #include "base/trace_event/blame_context.h"
17 #include "third_party/blink/public/common/features.h"
18 #include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h"
19 #include "third_party/blink/public/platform/blame_context.h"
20 #include "third_party/blink/public/platform/web_string.h"
21 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
22 #include "third_party/blink/renderer/platform/scheduler/common/features.h"
23 #include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h"
24 #include "third_party/blink/renderer/platform/scheduler/common/tracing_helper.h"
25 #include "third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h"
26 #include "third_party/blink/renderer/platform/scheduler/main_thread/find_in_page_budget_pool_controller.h"
27 #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
28 #include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
29 #include "third_party/blink/renderer/platform/scheduler/main_thread/page_visibility_state.h"
30 #include "third_party/blink/renderer/platform/scheduler/main_thread/resource_loading_task_runner_handle_impl.h"
31 #include "third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.h"
32 #include "third_party/blink/renderer/platform/scheduler/main_thread/web_scheduling_task_queue_impl.h"
33 #include "third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_proxy.h"
34
35 namespace blink {
36
37 namespace scheduler {
38
39 using base::sequence_manager::TaskQueue;
40 using QueueTraits = MainThreadTaskQueue::QueueTraits;
41
42 namespace {
43
VisibilityStateToString(bool is_visible)44 const char* VisibilityStateToString(bool is_visible) {
45 if (is_visible) {
46 return "visible";
47 } else {
48 return "hidden";
49 }
50 }
51
PausedStateToString(bool is_paused)52 const char* PausedStateToString(bool is_paused) {
53 if (is_paused) {
54 return "paused";
55 } else {
56 return "running";
57 }
58 }
59
FrozenStateToString(bool is_frozen)60 const char* FrozenStateToString(bool is_frozen) {
61 if (is_frozen) {
62 return "frozen";
63 } else {
64 return "running";
65 }
66 }
67
KeepActiveStateToString(bool keep_active)68 const char* KeepActiveStateToString(bool keep_active) {
69 if (keep_active) {
70 return "keep_active";
71 } else {
72 return "no_keep_active";
73 }
74 }
75
76 // Used to update the priority of task_queue. Note that this function is
77 // used for queues associated with a frame.
UpdatePriority(MainThreadTaskQueue * task_queue)78 void UpdatePriority(MainThreadTaskQueue* task_queue) {
79 if (!task_queue)
80 return;
81
82 FrameSchedulerImpl* frame_scheduler = task_queue->GetFrameScheduler();
83 DCHECK(frame_scheduler);
84 task_queue->GetTaskQueue()->SetQueuePriority(
85 frame_scheduler->ComputePriority(task_queue));
86 }
87
88 } // namespace
89
90 FrameSchedulerImpl::PauseSubresourceLoadingHandleImpl::
PauseSubresourceLoadingHandleImpl(base::WeakPtr<FrameSchedulerImpl> frame_scheduler)91 PauseSubresourceLoadingHandleImpl(
92 base::WeakPtr<FrameSchedulerImpl> frame_scheduler)
93 : frame_scheduler_(std::move(frame_scheduler)) {
94 DCHECK(frame_scheduler_);
95 frame_scheduler_->AddPauseSubresourceLoadingHandle();
96 }
97
98 FrameSchedulerImpl::PauseSubresourceLoadingHandleImpl::
~PauseSubresourceLoadingHandleImpl()99 ~PauseSubresourceLoadingHandleImpl() {
100 if (frame_scheduler_)
101 frame_scheduler_->RemovePauseSubresourceLoadingHandle();
102 }
103
FrameSchedulerImpl(PageSchedulerImpl * parent_page_scheduler,FrameScheduler::Delegate * delegate,base::trace_event::BlameContext * blame_context,FrameScheduler::FrameType frame_type)104 FrameSchedulerImpl::FrameSchedulerImpl(
105 PageSchedulerImpl* parent_page_scheduler,
106 FrameScheduler::Delegate* delegate,
107 base::trace_event::BlameContext* blame_context,
108 FrameScheduler::FrameType frame_type)
109 : FrameSchedulerImpl(parent_page_scheduler->GetMainThreadScheduler(),
110 parent_page_scheduler,
111 delegate,
112 blame_context,
113 frame_type) {}
114
FrameSchedulerImpl(MainThreadSchedulerImpl * main_thread_scheduler,PageSchedulerImpl * parent_page_scheduler,FrameScheduler::Delegate * delegate,base::trace_event::BlameContext * blame_context,FrameScheduler::FrameType frame_type)115 FrameSchedulerImpl::FrameSchedulerImpl(
116 MainThreadSchedulerImpl* main_thread_scheduler,
117 PageSchedulerImpl* parent_page_scheduler,
118 FrameScheduler::Delegate* delegate,
119 base::trace_event::BlameContext* blame_context,
120 FrameScheduler::FrameType frame_type)
121 : frame_type_(frame_type),
122 is_ad_frame_(false),
123 main_thread_scheduler_(main_thread_scheduler),
124 parent_page_scheduler_(parent_page_scheduler),
125 delegate_(delegate),
126 blame_context_(blame_context),
127 throttling_state_(SchedulingLifecycleState::kNotThrottled),
128 frame_visible_(true,
129 "FrameScheduler.FrameVisible",
130 &tracing_controller_,
131 VisibilityStateToString),
132 frame_paused_(false,
133 "FrameScheduler.FramePaused",
134 &tracing_controller_,
135 PausedStateToString),
136 frame_origin_type_(frame_type == FrameType::kMainFrame
137 ? FrameOriginType::kMainFrame
138 : FrameOriginType::kSameOriginToMainFrame,
139 "FrameScheduler.Origin",
140 &tracing_controller_,
141 FrameOriginTypeToString),
142 subresource_loading_paused_(false,
143 "FrameScheduler.SubResourceLoadingPaused",
144 &tracing_controller_,
145 PausedStateToString),
146 url_tracer_("FrameScheduler.URL"),
147 task_queues_throttled_(false,
148 "FrameScheduler.TaskQueuesThrottled",
149 &tracing_controller_,
150 YesNoStateToString),
151 preempted_for_cooperative_scheduling_(
152 false,
153 "FrameScheduler.PreemptedForCooperativeScheduling",
154 &tracing_controller_,
155 YesNoStateToString),
156 aggressive_throttling_opt_out_count_(0),
157 opted_out_from_aggressive_throttling_(
158 false,
159 "FrameScheduler.AggressiveThrottlingDisabled",
160 &tracing_controller_,
161 YesNoStateToString),
162 subresource_loading_pause_count_(0u),
163 opted_out_from_back_forward_cache_(
164 false,
165 "FrameScheduler.OptedOutFromBackForwardCache",
166 &tracing_controller_,
167 YesNoStateToString),
168 page_frozen_for_tracing_(
169 parent_page_scheduler_ ? parent_page_scheduler_->IsFrozen() : true,
170 "FrameScheduler.PageFrozen",
171 &tracing_controller_,
172 FrozenStateToString),
173 page_visibility_for_tracing_(
174 parent_page_scheduler_ && parent_page_scheduler_->IsPageVisible()
175 ? PageVisibilityState::kVisible
176 : PageVisibilityState::kHidden,
177 "FrameScheduler.PageVisibility",
178 &tracing_controller_,
179 PageVisibilityStateToString),
180 page_keep_active_for_tracing_(
181 parent_page_scheduler_ ? parent_page_scheduler_->KeepActive() : false,
182 "FrameScheduler.KeepActive",
183 &tracing_controller_,
184 KeepActiveStateToString),
185 waiting_for_contentful_paint_(true,
186 "FrameScheduler.WaitingForContentfulPaint",
187 &tracing_controller_,
188 YesNoStateToString),
189 waiting_for_meaningful_paint_(true,
190 "FrameScheduler.WaitingForMeaningfulPaint",
191 &tracing_controller_,
192 YesNoStateToString) {
193 frame_task_queue_controller_.reset(
194 new FrameTaskQueueController(main_thread_scheduler_, this, this));
195 }
196
FrameSchedulerImpl()197 FrameSchedulerImpl::FrameSchedulerImpl()
198 : FrameSchedulerImpl(/*main_thread_scheduler=*/nullptr,
199 /*parent_page_scheduler=*/nullptr,
200 /*delegate=*/nullptr,
201 /*blame_context=*/nullptr,
202 FrameType::kSubframe) {}
203
204 namespace {
205
CleanUpQueue(MainThreadTaskQueue * queue)206 void CleanUpQueue(MainThreadTaskQueue* queue) {
207 DCHECK(queue);
208
209 queue->DetachFromMainThreadScheduler();
210 DCHECK(!queue->GetFrameScheduler());
211 queue->GetTaskQueue()->SetBlameContext(nullptr);
212 }
213
214 } // namespace
215
~FrameSchedulerImpl()216 FrameSchedulerImpl::~FrameSchedulerImpl() {
217 weak_factory_.InvalidateWeakPtrs();
218
219 for (const auto& task_queue_and_voter :
220 frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) {
221 if (task_queue_and_voter.first->CanBeThrottled()) {
222 RemoveThrottleableQueueFromBudgetPools(task_queue_and_voter.first);
223 }
224 CleanUpQueue(task_queue_and_voter.first);
225 }
226
227 if (parent_page_scheduler_) {
228 parent_page_scheduler_->Unregister(this);
229
230 if (opted_out_from_aggressive_throttling())
231 parent_page_scheduler_->OnThrottlingStatusUpdated();
232 }
233 }
234
DetachFromPageScheduler()235 void FrameSchedulerImpl::DetachFromPageScheduler() {
236 for (const auto& task_queue_and_voter :
237 frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) {
238 if (task_queue_and_voter.first->CanBeThrottled()) {
239 RemoveThrottleableQueueFromBudgetPools(task_queue_and_voter.first);
240 }
241 }
242
243 parent_page_scheduler_ = nullptr;
244 }
245
RemoveThrottleableQueueFromBudgetPools(MainThreadTaskQueue * task_queue)246 void FrameSchedulerImpl::RemoveThrottleableQueueFromBudgetPools(
247 MainThreadTaskQueue* task_queue) {
248 DCHECK(task_queue);
249 DCHECK(task_queue->CanBeThrottled());
250
251 if (!parent_page_scheduler_)
252 return;
253
254 CPUTimeBudgetPool* cpu_time_budget_pool =
255 parent_page_scheduler_->background_cpu_time_budget_pool();
256
257 // On tests, the scheduler helper might already be shut down and tick is not
258 // available.
259 base::sequence_manager::LazyNow lazy_now =
260 main_thread_scheduler_->tick_clock()
261 ? base::sequence_manager::LazyNow(
262 main_thread_scheduler_->tick_clock())
263 : base::sequence_manager::LazyNow(base::TimeTicks::Now());
264
265 if (cpu_time_budget_pool) {
266 cpu_time_budget_pool->RemoveQueue(lazy_now.Now(),
267 task_queue->GetTaskQueue());
268 }
269
270 parent_page_scheduler_->RemoveQueueFromWakeUpBudgetPool(
271 task_queue, frame_origin_type_, &lazy_now);
272 }
273
SetFrameVisible(bool frame_visible)274 void FrameSchedulerImpl::SetFrameVisible(bool frame_visible) {
275 DCHECK(parent_page_scheduler_);
276 if (frame_visible_ == frame_visible)
277 return;
278 UMA_HISTOGRAM_BOOLEAN("RendererScheduler.IPC.FrameVisibility", frame_visible);
279 frame_visible_ = frame_visible;
280 UpdatePolicy();
281 }
282
IsFrameVisible() const283 bool FrameSchedulerImpl::IsFrameVisible() const {
284 return frame_visible_;
285 }
286
SetCrossOriginToMainFrame(bool cross_origin)287 void FrameSchedulerImpl::SetCrossOriginToMainFrame(bool cross_origin) {
288 DCHECK(parent_page_scheduler_);
289 if (frame_origin_type_ == FrameOriginType::kMainFrame) {
290 DCHECK(!cross_origin);
291 return;
292 }
293
294 base::sequence_manager::LazyNow lazy_now(
295 main_thread_scheduler_->tick_clock());
296
297 // Remove throttleable TaskQueues from their current WakeUpBudgetPool.
298 //
299 // The WakeUpBudgetPool is selected based on origin. TaskQueues are reinserted
300 // in the appropriate WakeUpBudgetPool at the end of this method, after the
301 // |frame_origin_type_| is updated.
302 for (const auto& task_queue_and_voter :
303 frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) {
304 if (task_queue_and_voter.first->CanBeThrottled()) {
305 parent_page_scheduler_->RemoveQueueFromWakeUpBudgetPool(
306 task_queue_and_voter.first, frame_origin_type_, &lazy_now);
307 }
308 }
309
310 // Update the FrameOriginType.
311 if (cross_origin) {
312 frame_origin_type_ = FrameOriginType::kCrossOriginToMainFrame;
313 } else {
314 frame_origin_type_ = FrameOriginType::kSameOriginToMainFrame;
315 }
316
317 // Add throttleable TaskQueues to WakeUpBudgetPool that corresponds to the
318 // updated |frame_origin_type_|.
319 for (const auto& task_queue_and_voter :
320 frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) {
321 if (task_queue_and_voter.first->CanBeThrottled()) {
322 parent_page_scheduler_->AddQueueToWakeUpBudgetPool(
323 task_queue_and_voter.first, frame_origin_type_, &lazy_now);
324 }
325 }
326
327 UpdatePolicy();
328 }
329
SetIsAdFrame()330 void FrameSchedulerImpl::SetIsAdFrame() {
331 is_ad_frame_ = true;
332 UpdatePolicy();
333 }
334
IsAdFrame() const335 bool FrameSchedulerImpl::IsAdFrame() const {
336 return is_ad_frame_;
337 }
338
IsCrossOriginToMainFrame() const339 bool FrameSchedulerImpl::IsCrossOriginToMainFrame() const {
340 return frame_origin_type_ == FrameOriginType::kCrossOriginToMainFrame;
341 }
342
TraceUrlChange(const String & url)343 void FrameSchedulerImpl::TraceUrlChange(const String& url) {
344 url_tracer_.TraceString(url);
345 }
346
AddTaskTime(base::TimeDelta time)347 void FrameSchedulerImpl::AddTaskTime(base::TimeDelta time) {
348 // The duration of task time under which AddTaskTime buffers rather than
349 // sending the task time update to the delegate.
350 constexpr base::TimeDelta kTaskDurationSendThreshold =
351 base::TimeDelta::FromMilliseconds(100);
352 if (!delegate_)
353 return;
354 task_time_ += time;
355 if (task_time_ >= kTaskDurationSendThreshold) {
356 delegate_->UpdateTaskTime(task_time_);
357 task_time_ = base::TimeDelta();
358 }
359 }
360
GetFrameType() const361 FrameScheduler::FrameType FrameSchedulerImpl::GetFrameType() const {
362 return frame_type_;
363 }
364
365 // static
CreateQueueTraitsForTaskType(TaskType type)366 QueueTraits FrameSchedulerImpl::CreateQueueTraitsForTaskType(TaskType type) {
367 // TODO(haraken): Optimize the mapping from TaskTypes to task runners.
368 // TODO(sreejakshetty): Clean up the PrioritisationType QueueTrait and
369 // QueueType for kInternalContinueScriptLoading and kInternalContentCapture.
370 switch (type) {
371 case TaskType::kInternalContentCapture:
372 return ThrottleableTaskQueueTraits().SetPrioritisationType(
373 QueueTraits::PrioritisationType::kBestEffort);
374 case TaskType::kJavascriptTimerDelayedLowNesting:
375 return ThrottleableTaskQueueTraits()
376 .SetPrioritisationType(
377 QueueTraits::PrioritisationType::kJavaScriptTimer)
378 .SetCanBeIntensivelyThrottled(
379 IsIntensiveWakeUpThrottlingEnabled() &&
380 CanIntensivelyThrottleLowNestingLevel());
381 case TaskType::kJavascriptTimerDelayedHighNesting:
382 return ThrottleableTaskQueueTraits()
383 .SetPrioritisationType(
384 QueueTraits::PrioritisationType::kJavaScriptTimer)
385 .SetCanBeIntensivelyThrottled(IsIntensiveWakeUpThrottlingEnabled());
386 case TaskType::kJavascriptTimerImmediate: {
387 return DeferrableTaskQueueTraits()
388 .SetPrioritisationType(
389 QueueTraits::PrioritisationType::kJavaScriptTimer)
390 .SetCanBeThrottled(!base::FeatureList::IsEnabled(
391 features::kOptOutZeroTimeoutTimersFromThrottling));
392 }
393 case TaskType::kInternalLoading:
394 case TaskType::kNetworking:
395 case TaskType::kNetworkingWithURLLoaderAnnotation:
396 return LoadingTaskQueueTraits();
397 case TaskType::kNetworkingUnfreezable:
398 return base::FeatureList::IsEnabled(features::kLoadingTasksUnfreezable)
399 ? UnfreezableLoadingTaskQueueTraits()
400 : LoadingTaskQueueTraits();
401 case TaskType::kNetworkingControl:
402 return LoadingControlTaskQueueTraits();
403 // Throttling following tasks may break existing web pages, so tentatively
404 // these are unthrottled.
405 // TODO(nhiroki): Throttle them again after we're convinced that it's safe
406 // or provide a mechanism that web pages can opt-out it if throttling is not
407 // desirable.
408 case TaskType::kDOMManipulation:
409 case TaskType::kHistoryTraversal:
410 case TaskType::kEmbed:
411 case TaskType::kCanvasBlobSerialization:
412 case TaskType::kRemoteEvent:
413 case TaskType::kWebSocket:
414 case TaskType::kMicrotask:
415 case TaskType::kUnshippedPortMessage:
416 case TaskType::kFileReading:
417 case TaskType::kPresentation:
418 case TaskType::kSensor:
419 case TaskType::kPerformanceTimeline:
420 case TaskType::kWebGL:
421 case TaskType::kIdleTask:
422 case TaskType::kInternalDefault:
423 case TaskType::kMiscPlatformAPI:
424 case TaskType::kFontLoading:
425 case TaskType::kApplicationLifeCycle:
426 case TaskType::kBackgroundFetch:
427 case TaskType::kPermission:
428 // TODO(altimin): Move appropriate tasks to throttleable task queue.
429 return DeferrableTaskQueueTraits();
430 // PostedMessage can be used for navigation, so we shouldn't defer it
431 // when expecting a user gesture.
432 case TaskType::kPostedMessage:
433 case TaskType::kServiceWorkerClientMessage:
434 case TaskType::kWorkerAnimation:
435 // UserInteraction tasks should be run even when expecting a user gesture.
436 case TaskType::kUserInteraction:
437 // Media events should not be deferred to ensure that media playback is
438 // smooth.
439 case TaskType::kMediaElementEvent:
440 case TaskType::kInternalWebCrypto:
441 case TaskType::kInternalMedia:
442 case TaskType::kInternalMediaRealTime:
443 case TaskType::kInternalUserInteraction:
444 case TaskType::kInternalIntersectionObserver:
445 return PausableTaskQueueTraits();
446 case TaskType::kInternalFindInPage:
447 return FindInPageTaskQueueTraits();
448 case TaskType::kInternalHighPriorityLocalFrame:
449 return QueueTraits().SetPrioritisationType(
450 QueueTraits::PrioritisationType::kHighPriorityLocalFrame);
451 case TaskType::kInternalContinueScriptLoading:
452 return PausableTaskQueueTraits().SetPrioritisationType(
453 QueueTraits::PrioritisationType::kInternalScriptContinuation);
454 case TaskType::kDatabaseAccess:
455 if (base::FeatureList::IsEnabled(kHighPriorityDatabaseTaskType)) {
456 return PausableTaskQueueTraits().SetPrioritisationType(
457 QueueTraits::PrioritisationType::kExperimentalDatabase);
458 } else {
459 return PausableTaskQueueTraits();
460 }
461 case TaskType::kInternalNavigationAssociated:
462 return FreezableTaskQueueTraits();
463 // Some tasks in the tests need to run when objects are paused e.g. to hook
464 // when recovering from debugger JavaScript statetment.
465 case TaskType::kInternalTest:
466 // kWebLocks can be frozen if for entire page, but not for individual
467 // frames. See https://crrev.com/c/1687716
468 case TaskType::kWebLocks:
469 case TaskType::kInternalFrameLifecycleControl:
470 return UnpausableTaskQueueTraits();
471 case TaskType::kInternalTranslation:
472 return ForegroundOnlyTaskQueueTraits();
473 // The TaskType of Inspector tasks need to be unpausable and should not use
474 // virtual time because they need to run on a paused page or when virtual
475 // time is paused.
476 case TaskType::kInternalInspector:
477 // Navigation IPCs do not run using virtual time to avoid hanging.
478 case TaskType::kInternalNavigationAssociatedUnfreezable:
479 return DoesNotUseVirtualTimeTaskQueueTraits();
480 case TaskType::kDeprecatedNone:
481 case TaskType::kMainThreadTaskQueueV8:
482 case TaskType::kMainThreadTaskQueueCompositor:
483 case TaskType::kMainThreadTaskQueueDefault:
484 case TaskType::kMainThreadTaskQueueInput:
485 case TaskType::kMainThreadTaskQueueIdle:
486 case TaskType::kMainThreadTaskQueueControl:
487 case TaskType::kMainThreadTaskQueueMemoryPurge:
488 case TaskType::kMainThreadTaskQueueIPCTracking:
489 case TaskType::kCompositorThreadTaskQueueDefault:
490 case TaskType::kCompositorThreadTaskQueueInput:
491 case TaskType::kWorkerThreadTaskQueueDefault:
492 case TaskType::kWorkerThreadTaskQueueV8:
493 case TaskType::kWorkerThreadTaskQueueCompositor:
494 case TaskType::kMainThreadTaskQueueNonWaking:
495 // The web scheduling API task types are used by WebSchedulingTaskQueues.
496 // The associated TaskRunner should be obtained by creating a
497 // WebSchedulingTaskQueue with CreateWebSchedulingTaskQueue().
498 case TaskType::kExperimentalWebScheduling:
499 case TaskType::kCount:
500 // Not a valid frame-level TaskType.
501 NOTREACHED();
502 return QueueTraits();
503 }
504 // This method is called for all values between 0 and kCount. TaskType,
505 // however, has numbering gaps, so even though all enumerated TaskTypes are
506 // handled in the switch and return a value, we fall through for some values
507 // of |type|.
508 NOTREACHED();
509 return QueueTraits();
510 }
511
GetTaskRunner(TaskType type)512 scoped_refptr<base::SingleThreadTaskRunner> FrameSchedulerImpl::GetTaskRunner(
513 TaskType type) {
514 scoped_refptr<MainThreadTaskQueue> task_queue = GetTaskQueue(type);
515 DCHECK(task_queue);
516 return task_queue->CreateTaskRunner(type);
517 }
518
GetTaskQueue(TaskType type)519 scoped_refptr<MainThreadTaskQueue> FrameSchedulerImpl::GetTaskQueue(
520 TaskType type) {
521 QueueTraits queue_traits = CreateQueueTraitsForTaskType(type);
522 return frame_task_queue_controller_->GetTaskQueue(queue_traits);
523 }
524
525 std::unique_ptr<WebResourceLoadingTaskRunnerHandle>
CreateResourceLoadingTaskRunnerHandle()526 FrameSchedulerImpl::CreateResourceLoadingTaskRunnerHandle() {
527 return CreateResourceLoadingTaskRunnerHandleImpl();
528 }
529
530 std::unique_ptr<WebResourceLoadingTaskRunnerHandle>
CreateResourceLoadingMaybeUnfreezableTaskRunnerHandle()531 FrameSchedulerImpl::CreateResourceLoadingMaybeUnfreezableTaskRunnerHandle() {
532 return ResourceLoadingTaskRunnerHandleImpl::WrapTaskRunner(
533 GetTaskQueue(TaskType::kNetworkingUnfreezable));
534 }
535
536 std::unique_ptr<ResourceLoadingTaskRunnerHandleImpl>
CreateResourceLoadingTaskRunnerHandleImpl()537 FrameSchedulerImpl::CreateResourceLoadingTaskRunnerHandleImpl() {
538 if (main_thread_scheduler_->scheduling_settings()
539 .use_resource_fetch_priority ||
540 (parent_page_scheduler_ && parent_page_scheduler_->IsLoading() &&
541 main_thread_scheduler_->scheduling_settings()
542 .use_resource_priorities_only_during_loading)) {
543 scoped_refptr<MainThreadTaskQueue> task_queue =
544 frame_task_queue_controller_->NewResourceLoadingTaskQueue();
545 resource_loading_task_queue_priorities_.insert(
546 task_queue, task_queue->GetTaskQueue()->GetQueuePriority());
547 return ResourceLoadingTaskRunnerHandleImpl::WrapTaskRunner(task_queue);
548 }
549
550 return ResourceLoadingTaskRunnerHandleImpl::WrapTaskRunner(
551 GetTaskQueue(TaskType::kNetworkingWithURLLoaderAnnotation));
552 }
553
DidChangeResourceLoadingPriority(scoped_refptr<MainThreadTaskQueue> task_queue,net::RequestPriority priority)554 void FrameSchedulerImpl::DidChangeResourceLoadingPriority(
555 scoped_refptr<MainThreadTaskQueue> task_queue,
556 net::RequestPriority priority) {
557 // This check is done since in some cases (when kUseResourceFetchPriority
558 // feature isn't enabled) we use the loading task queue for resource loading
559 // and the priority of this queue shouldn't be affected by resource
560 // priorities.
561 auto queue_priority_pair =
562 resource_loading_task_queue_priorities_.find(task_queue);
563 if (queue_priority_pair != resource_loading_task_queue_priorities_.end()) {
564 task_queue->SetNetRequestPriority(priority);
565 queue_priority_pair->value = main_thread_scheduler_->scheduling_settings()
566 .net_to_blink_priority[priority];
567 auto* voter =
568 frame_task_queue_controller_->GetQueueEnabledVoter(task_queue);
569 UpdateQueuePolicy(task_queue.get(), voter);
570 }
571 }
572
OnShutdownResourceLoadingTaskQueue(scoped_refptr<MainThreadTaskQueue> task_queue)573 void FrameSchedulerImpl::OnShutdownResourceLoadingTaskQueue(
574 scoped_refptr<MainThreadTaskQueue> task_queue) {
575 // This check is done since in some cases (when kUseResourceFetchPriority
576 // feature isn't enabled) we use the loading task queue for resource loading,
577 // and the lifetime of this queue isn't bound to one resource.
578 auto iter = resource_loading_task_queue_priorities_.find(task_queue);
579 if (iter != resource_loading_task_queue_priorities_.end()) {
580 resource_loading_task_queue_priorities_.erase(iter);
581 bool removed = frame_task_queue_controller_->RemoveResourceLoadingTaskQueue(
582 task_queue);
583 DCHECK(removed);
584 CleanUpQueue(task_queue.get());
585 }
586 }
587
588 scoped_refptr<base::SingleThreadTaskRunner>
ControlTaskRunner()589 FrameSchedulerImpl::ControlTaskRunner() {
590 DCHECK(parent_page_scheduler_);
591 return main_thread_scheduler_->ControlTaskRunner();
592 }
593
GetAgentGroupScheduler()594 WebAgentGroupScheduler* FrameSchedulerImpl::GetAgentGroupScheduler() {
595 return parent_page_scheduler_
596 ? &parent_page_scheduler_->GetAgentGroupScheduler()
597 : nullptr;
598 }
599
GetPageScheduler() const600 blink::PageScheduler* FrameSchedulerImpl::GetPageScheduler() const {
601 return parent_page_scheduler_;
602 }
603
DidStartProvisionalLoad(bool is_main_frame)604 void FrameSchedulerImpl::DidStartProvisionalLoad(bool is_main_frame) {
605 main_thread_scheduler_->DidStartProvisionalLoad(is_main_frame);
606 }
607
DidCommitProvisionalLoad(bool is_web_history_inert_commit,NavigationType navigation_type)608 void FrameSchedulerImpl::DidCommitProvisionalLoad(
609 bool is_web_history_inert_commit,
610 NavigationType navigation_type) {
611 bool is_main_frame = GetFrameType() == FrameType::kMainFrame;
612 bool is_same_document = navigation_type == NavigationType::kSameDocument;
613
614 if (!is_same_document) {
615 waiting_for_contentful_paint_ = true;
616 waiting_for_meaningful_paint_ = true;
617 }
618 if (is_main_frame && !is_same_document) {
619 task_time_ = base::TimeDelta();
620 // Ignore result here, based on the assumption that
621 // MTSI::DidCommitProvisionalLoad will trigger an update policy.
622 ignore_result(main_thread_scheduler_->agent_scheduling_strategy()
623 .OnDocumentChangedInMainFrame(*this));
624 }
625
626 main_thread_scheduler_->DidCommitProvisionalLoad(
627 is_web_history_inert_commit, navigation_type == NavigationType::kReload,
628 is_main_frame);
629 if (!is_same_document)
630 ResetForNavigation();
631 }
632
CreateWebScopedVirtualTimePauser(const WTF::String & name,WebScopedVirtualTimePauser::VirtualTaskDuration duration)633 WebScopedVirtualTimePauser FrameSchedulerImpl::CreateWebScopedVirtualTimePauser(
634 const WTF::String& name,
635 WebScopedVirtualTimePauser::VirtualTaskDuration duration) {
636 return WebScopedVirtualTimePauser(main_thread_scheduler_, duration, name);
637 }
638
ResetForNavigation()639 void FrameSchedulerImpl::ResetForNavigation() {
640 document_bound_weak_factory_.InvalidateWeakPtrs();
641
642 for (const auto& it : back_forward_cache_opt_out_counts_) {
643 TRACE_EVENT_NESTABLE_ASYNC_END0(
644 "renderer.scheduler", "ActiveSchedulerTrackedFeature",
645 TRACE_ID_LOCAL(reinterpret_cast<intptr_t>(this) ^
646 static_cast<int>(it.first)));
647 }
648
649 back_forward_cache_opt_out_counts_.clear();
650 back_forward_cache_opt_outs_.reset();
651 last_uploaded_active_features_ = 0;
652 }
653
OnStartedUsingFeature(SchedulingPolicy::Feature feature,const SchedulingPolicy & policy)654 void FrameSchedulerImpl::OnStartedUsingFeature(
655 SchedulingPolicy::Feature feature,
656 const SchedulingPolicy& policy) {
657 uint64_t old_mask = GetActiveFeaturesTrackedForBackForwardCacheMetricsMask();
658
659 if (policy.disable_aggressive_throttling)
660 OnAddedAggressiveThrottlingOptOut();
661 if (policy.disable_back_forward_cache) {
662 OnAddedBackForwardCacheOptOut(feature);
663 }
664
665 uint64_t new_mask = GetActiveFeaturesTrackedForBackForwardCacheMetricsMask();
666
667 if (old_mask != new_mask) {
668 NotifyDelegateAboutFeaturesAfterCurrentTask();
669 TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(
670 "renderer.scheduler", "ActiveSchedulerTrackedFeature",
671 TRACE_ID_LOCAL(reinterpret_cast<intptr_t>(this) ^
672 static_cast<int>(feature)),
673 "feature", FeatureToString(feature));
674 }
675 }
676
OnStoppedUsingFeature(SchedulingPolicy::Feature feature,const SchedulingPolicy & policy)677 void FrameSchedulerImpl::OnStoppedUsingFeature(
678 SchedulingPolicy::Feature feature,
679 const SchedulingPolicy& policy) {
680 uint64_t old_mask = GetActiveFeaturesTrackedForBackForwardCacheMetricsMask();
681
682 if (policy.disable_aggressive_throttling)
683 OnRemovedAggressiveThrottlingOptOut();
684 if (policy.disable_back_forward_cache)
685 OnRemovedBackForwardCacheOptOut(feature);
686
687 uint64_t new_mask = GetActiveFeaturesTrackedForBackForwardCacheMetricsMask();
688
689 if (old_mask != new_mask) {
690 NotifyDelegateAboutFeaturesAfterCurrentTask();
691 TRACE_EVENT_NESTABLE_ASYNC_END0(
692 "renderer.scheduler", "ActiveSchedulerTrackedFeature",
693 TRACE_ID_LOCAL(reinterpret_cast<intptr_t>(this) ^
694 static_cast<int>(feature)));
695 }
696 }
697
NotifyDelegateAboutFeaturesAfterCurrentTask()698 void FrameSchedulerImpl::NotifyDelegateAboutFeaturesAfterCurrentTask() {
699 if (!delegate_)
700 return;
701 if (feature_report_scheduled_)
702 return;
703 feature_report_scheduled_ = true;
704
705 main_thread_scheduler_->ExecuteAfterCurrentTask(
706 base::BindOnce(&FrameSchedulerImpl::ReportFeaturesToDelegate,
707 document_bound_weak_factory_.GetWeakPtr()));
708 }
709
ReportFeaturesToDelegate()710 void FrameSchedulerImpl::ReportFeaturesToDelegate() {
711 DCHECK(delegate_);
712 feature_report_scheduled_ = false;
713 uint64_t mask = GetActiveFeaturesTrackedForBackForwardCacheMetricsMask();
714 if (mask == last_uploaded_active_features_)
715 return;
716 last_uploaded_active_features_ = mask;
717 delegate_->UpdateActiveSchedulerTrackedFeatures(mask);
718 }
719
GetWeakPtr()720 base::WeakPtr<FrameScheduler> FrameSchedulerImpl::GetWeakPtr() {
721 return weak_factory_.GetWeakPtr();
722 }
723
GetWeakPtr() const724 base::WeakPtr<const FrameSchedulerImpl> FrameSchedulerImpl::GetWeakPtr() const {
725 return weak_factory_.GetWeakPtr();
726 }
727
ReportActiveSchedulerTrackedFeatures()728 void FrameSchedulerImpl::ReportActiveSchedulerTrackedFeatures() {
729 if (delegate_)
730 ReportFeaturesToDelegate();
731 }
732
733 base::WeakPtr<FrameSchedulerImpl>
GetInvalidatingOnBFCacheRestoreWeakPtr()734 FrameSchedulerImpl::GetInvalidatingOnBFCacheRestoreWeakPtr() {
735 return invalidating_on_bfcache_restore_weak_factory_.GetWeakPtr();
736 }
737
OnAddedAggressiveThrottlingOptOut()738 void FrameSchedulerImpl::OnAddedAggressiveThrottlingOptOut() {
739 ++aggressive_throttling_opt_out_count_;
740 opted_out_from_aggressive_throttling_ =
741 static_cast<bool>(aggressive_throttling_opt_out_count_);
742 if (parent_page_scheduler_)
743 parent_page_scheduler_->OnThrottlingStatusUpdated();
744 }
745
OnRemovedAggressiveThrottlingOptOut()746 void FrameSchedulerImpl::OnRemovedAggressiveThrottlingOptOut() {
747 DCHECK_GT(aggressive_throttling_opt_out_count_, 0);
748 --aggressive_throttling_opt_out_count_;
749 opted_out_from_aggressive_throttling_ =
750 static_cast<bool>(aggressive_throttling_opt_out_count_);
751 if (parent_page_scheduler_)
752 parent_page_scheduler_->OnThrottlingStatusUpdated();
753 }
754
OnAddedBackForwardCacheOptOut(SchedulingPolicy::Feature feature)755 void FrameSchedulerImpl::OnAddedBackForwardCacheOptOut(
756 SchedulingPolicy::Feature feature) {
757 ++back_forward_cache_opt_out_counts_[feature];
758 back_forward_cache_opt_outs_.set(static_cast<size_t>(feature));
759 opted_out_from_back_forward_cache_ = true;
760 }
761
OnRemovedBackForwardCacheOptOut(SchedulingPolicy::Feature feature)762 void FrameSchedulerImpl::OnRemovedBackForwardCacheOptOut(
763 SchedulingPolicy::Feature feature) {
764 DCHECK_GT(back_forward_cache_opt_out_counts_[feature], 0);
765 auto it = back_forward_cache_opt_out_counts_.find(feature);
766 if (it->second == 1) {
767 back_forward_cache_opt_out_counts_.erase(it);
768 back_forward_cache_opt_outs_.reset(static_cast<size_t>(feature));
769 } else {
770 --it->second;
771 }
772 opted_out_from_back_forward_cache_ =
773 !back_forward_cache_opt_out_counts_.empty();
774 }
775
AsValueInto(base::trace_event::TracedValue * state) const776 void FrameSchedulerImpl::AsValueInto(
777 base::trace_event::TracedValue* state) const {
778 state->SetBoolean("frame_visible", frame_visible_);
779 state->SetBoolean("page_visible", parent_page_scheduler_->IsPageVisible());
780 state->SetBoolean("cross_origin_to_main_frame", IsCrossOriginToMainFrame());
781 state->SetString("frame_type",
782 frame_type_ == FrameScheduler::FrameType::kMainFrame
783 ? "MainFrame"
784 : "Subframe");
785 state->SetBoolean(
786 "disable_background_timer_throttling",
787 !RuntimeEnabledFeatures::TimerThrottlingForBackgroundTabsEnabled());
788
789 {
790 auto dictionary_scope =
791 state->BeginDictionaryScoped("frame_task_queue_controller");
792 frame_task_queue_controller_->AsValueInto(state);
793 }
794
795 if (blame_context_) {
796 auto dictionary_scope = state->BeginDictionaryScoped("blame_context");
797 state->SetString(
798 "id_ref",
799 PointerToString(reinterpret_cast<void*>(blame_context_->id())));
800 state->SetString("scope", blame_context_->scope());
801 }
802 }
803
SetPageVisibilityForTracing(PageVisibilityState page_visibility)804 void FrameSchedulerImpl::SetPageVisibilityForTracing(
805 PageVisibilityState page_visibility) {
806 page_visibility_for_tracing_ = page_visibility;
807 }
808
IsPageVisible() const809 bool FrameSchedulerImpl::IsPageVisible() const {
810 return parent_page_scheduler_ ? parent_page_scheduler_->IsPageVisible()
811 : true;
812 }
813
SetPaused(bool frame_paused)814 void FrameSchedulerImpl::SetPaused(bool frame_paused) {
815 DCHECK(parent_page_scheduler_);
816 if (frame_paused_ == frame_paused)
817 return;
818
819 frame_paused_ = frame_paused;
820 UpdatePolicy();
821 }
822
SetShouldReportPostedTasksWhenDisabled(bool should_report)823 void FrameSchedulerImpl::SetShouldReportPostedTasksWhenDisabled(
824 bool should_report) {
825 // Forward this to all the task queues associated with this frame.
826 for (const auto& task_queue_and_voter :
827 frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) {
828 auto* task_queue = task_queue_and_voter.first;
829 if (task_queue->CanBeFrozen()) {
830 task_queue->GetTaskQueue()->SetShouldReportPostedTasksWhenDisabled(
831 should_report);
832 }
833 }
834 }
835
SetPageFrozenForTracing(bool frozen)836 void FrameSchedulerImpl::SetPageFrozenForTracing(bool frozen) {
837 page_frozen_for_tracing_ = frozen;
838 }
839
SetPageKeepActiveForTracing(bool keep_active)840 void FrameSchedulerImpl::SetPageKeepActiveForTracing(bool keep_active) {
841 page_keep_active_for_tracing_ = keep_active;
842 }
843
UpdatePolicy()844 void FrameSchedulerImpl::UpdatePolicy() {
845 bool task_queues_were_throttled = task_queues_throttled_;
846 task_queues_throttled_ = ShouldThrottleTaskQueues();
847
848 for (const auto& task_queue_and_voter :
849 frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) {
850 UpdateQueuePolicy(task_queue_and_voter.first, task_queue_and_voter.second);
851 if (task_queues_were_throttled != task_queues_throttled_) {
852 UpdateTaskQueueThrottling(task_queue_and_voter.first,
853 task_queues_throttled_);
854 }
855 }
856
857 NotifyLifecycleObservers();
858 }
859
UpdateQueuePolicy(MainThreadTaskQueue * queue,TaskQueue::QueueEnabledVoter * voter)860 void FrameSchedulerImpl::UpdateQueuePolicy(
861 MainThreadTaskQueue* queue,
862 TaskQueue::QueueEnabledVoter* voter) {
863 DCHECK(queue);
864 UpdatePriority(queue);
865 if (!voter)
866 return;
867 DCHECK(parent_page_scheduler_);
868 bool queue_disabled = false;
869 queue_disabled |= frame_paused_ && queue->CanBePaused();
870 queue_disabled |= preempted_for_cooperative_scheduling_;
871 // Per-frame freezable task queues will be frozen after 5 mins in background
872 // on Android, and if the browser freezes the page in the background. They
873 // will be resumed when the page is visible.
874 bool queue_frozen =
875 parent_page_scheduler_->IsFrozen() && queue->CanBeFrozen();
876 // Override freezing if keep-active is true.
877 if (queue_frozen && !queue->FreezeWhenKeepActive())
878 queue_frozen = !parent_page_scheduler_->KeepActive();
879 queue_disabled |= queue_frozen;
880 // Per-frame freezable queues of tasks which are specified as getting frozen
881 // immediately when their frame becomes invisible get frozen. They will be
882 // resumed when the frame becomes visible again.
883 queue_disabled |= !frame_visible_ && !queue->CanRunInBackground();
884 voter->SetVoteToEnable(!queue_disabled);
885 }
886
CalculateLifecycleState(ObserverType type) const887 SchedulingLifecycleState FrameSchedulerImpl::CalculateLifecycleState(
888 ObserverType type) const {
889 // Detached frames are not throttled.
890 if (!parent_page_scheduler_)
891 return SchedulingLifecycleState::kNotThrottled;
892
893 if (parent_page_scheduler_->IsFrozen() &&
894 !parent_page_scheduler_->KeepActive()) {
895 DCHECK(!parent_page_scheduler_->IsPageVisible());
896 return SchedulingLifecycleState::kStopped;
897 }
898 if (subresource_loading_paused_ && type == ObserverType::kLoader)
899 return SchedulingLifecycleState::kStopped;
900 if (type == ObserverType::kLoader &&
901 parent_page_scheduler_->OptedOutFromAggressiveThrottling()) {
902 return SchedulingLifecycleState::kNotThrottled;
903 }
904 // Note: The scheduling lifecycle state ignores wake up rate throttling.
905 if (parent_page_scheduler_->IsCPUTimeThrottled())
906 return SchedulingLifecycleState::kThrottled;
907 if (!parent_page_scheduler_->IsPageVisible())
908 return SchedulingLifecycleState::kHidden;
909 return SchedulingLifecycleState::kNotThrottled;
910 }
911
OnFirstContentfulPaint()912 void FrameSchedulerImpl::OnFirstContentfulPaint() {
913 waiting_for_contentful_paint_ = false;
914 if (GetFrameType() == FrameScheduler::FrameType::kMainFrame)
915 main_thread_scheduler_->OnMainFramePaint();
916 }
917
OnFirstMeaningfulPaint()918 void FrameSchedulerImpl::OnFirstMeaningfulPaint() {
919 waiting_for_meaningful_paint_ = false;
920
921 if (GetFrameType() != FrameScheduler::FrameType::kMainFrame)
922 return;
923
924 main_thread_scheduler_->OnMainFramePaint();
925
926 if (main_thread_scheduler_->agent_scheduling_strategy()
927 .OnMainFrameFirstMeaningfulPaint(*this) ==
928 AgentSchedulingStrategy::ShouldUpdatePolicy::kYes) {
929 main_thread_scheduler_->OnAgentStrategyUpdated();
930 }
931 }
932
OnLoad()933 void FrameSchedulerImpl::OnLoad() {
934 if (GetFrameType() == FrameScheduler::FrameType::kMainFrame) {
935 // TODO(talp): Once MTSI::UpdatePolicyLocked is refactored, this can notify
936 // the agent strategy directly and, if necessary, trigger the queue priority
937 // update.
938 main_thread_scheduler_->OnMainFrameLoad(*this);
939 }
940 }
941
IsWaitingForContentfulPaint() const942 bool FrameSchedulerImpl::IsWaitingForContentfulPaint() const {
943 return waiting_for_contentful_paint_;
944 }
945
IsWaitingForMeaningfulPaint() const946 bool FrameSchedulerImpl::IsWaitingForMeaningfulPaint() const {
947 return waiting_for_meaningful_paint_;
948 }
949
IsOrdinary() const950 bool FrameSchedulerImpl::IsOrdinary() const {
951 if (!parent_page_scheduler_)
952 return true;
953 return parent_page_scheduler_->IsOrdinary();
954 }
955
ShouldThrottleTaskQueues() const956 bool FrameSchedulerImpl::ShouldThrottleTaskQueues() const {
957 DCHECK(parent_page_scheduler_);
958
959 if (!RuntimeEnabledFeatures::TimerThrottlingForBackgroundTabsEnabled())
960 return false;
961 if (parent_page_scheduler_->IsAudioPlaying())
962 return false;
963 if (!parent_page_scheduler_->IsPageVisible())
964 return true;
965 return RuntimeEnabledFeatures::TimerThrottlingForHiddenFramesEnabled() &&
966 !frame_visible_ && IsCrossOriginToMainFrame();
967 }
968
UpdateTaskQueueThrottling(MainThreadTaskQueue * task_queue,bool should_throttle)969 void FrameSchedulerImpl::UpdateTaskQueueThrottling(
970 MainThreadTaskQueue* task_queue,
971 bool should_throttle) {
972 if (!task_queue->CanBeThrottled())
973 return;
974 if (should_throttle) {
975 main_thread_scheduler_->task_queue_throttler()->IncreaseThrottleRefCount(
976 task_queue->GetTaskQueue());
977 } else {
978 main_thread_scheduler_->task_queue_throttler()->DecreaseThrottleRefCount(
979 task_queue->GetTaskQueue());
980 }
981 }
982
IsExemptFromBudgetBasedThrottling() const983 bool FrameSchedulerImpl::IsExemptFromBudgetBasedThrottling() const {
984 return opted_out_from_aggressive_throttling();
985 }
986
ComputePriority(MainThreadTaskQueue * task_queue) const987 TaskQueue::QueuePriority FrameSchedulerImpl::ComputePriority(
988 MainThreadTaskQueue* task_queue) const {
989 DCHECK(task_queue);
990
991 FrameScheduler* frame_scheduler = task_queue->GetFrameScheduler();
992
993 // Checks the task queue is associated with this frame scheduler.
994 DCHECK_EQ(frame_scheduler, this);
995
996 auto queue_priority_pair =
997 resource_loading_task_queue_priorities_.find(task_queue);
998 if (queue_priority_pair != resource_loading_task_queue_priorities_.end())
999 return queue_priority_pair->value;
1000
1001 // TODO(kdillon): Ordering here is relative to the experiments below. Cleanup
1002 // unused experiment logic so that this switch can be merged with the
1003 // prioritisation type decisions below.
1004 switch (task_queue->GetPrioritisationType()) {
1005 case MainThreadTaskQueue::QueueTraits::PrioritisationType::
1006 kInternalScriptContinuation:
1007 return TaskQueue::QueuePriority::kVeryHighPriority;
1008 case MainThreadTaskQueue::QueueTraits::PrioritisationType::kBestEffort:
1009 return TaskQueue::QueuePriority::kBestEffortPriority;
1010 default:
1011 break;
1012 }
1013
1014 // TODO(shaseley): This should use lower priorities if the frame is
1015 // deprioritized. Change this once we refactor and add frame policy/priorities
1016 // and add a range of new priorities less than low.
1017 if (task_queue->web_scheduling_priority()) {
1018 switch (task_queue->web_scheduling_priority().value()) {
1019 case WebSchedulingPriority::kUserBlockingPriority:
1020 return TaskQueue::QueuePriority::kHighPriority;
1021 case WebSchedulingPriority::kUserVisiblePriority:
1022 return TaskQueue::QueuePriority::kNormalPriority;
1023 case WebSchedulingPriority::kBackgroundPriority:
1024 return TaskQueue::QueuePriority::kLowPriority;
1025 }
1026 }
1027
1028 if (!parent_page_scheduler_) {
1029 // Frame might be detached during its shutdown. Return a default priority
1030 // in that case.
1031 return TaskQueue::QueuePriority::kNormalPriority;
1032 }
1033
1034 // A hidden page with no audio.
1035 if (parent_page_scheduler_->IsBackgrounded()) {
1036 if (main_thread_scheduler_->scheduling_settings()
1037 .low_priority_background_page) {
1038 return TaskQueue::QueuePriority::kLowPriority;
1039 }
1040
1041 if (main_thread_scheduler_->scheduling_settings()
1042 .best_effort_background_page) {
1043 return TaskQueue::QueuePriority::kBestEffortPriority;
1044 }
1045 }
1046
1047 // If the page is loading or if the priority experiments should take place at
1048 // all times.
1049 if (parent_page_scheduler_->IsLoading() ||
1050 !main_thread_scheduler_->scheduling_settings()
1051 .use_frame_priorities_only_during_loading) {
1052 // Low priority feature enabled for hidden frame.
1053 if (main_thread_scheduler_->scheduling_settings()
1054 .low_priority_hidden_frame &&
1055 !IsFrameVisible()) {
1056 return TaskQueue::QueuePriority::kLowPriority;
1057 }
1058
1059 bool is_subframe = GetFrameType() == FrameScheduler::FrameType::kSubframe;
1060 bool is_throttleable_task_queue =
1061 task_queue->queue_type() ==
1062 MainThreadTaskQueue::QueueType::kFrameThrottleable;
1063
1064 // Low priority feature enabled for sub-frame.
1065 if (main_thread_scheduler_->scheduling_settings().low_priority_subframe &&
1066 is_subframe) {
1067 return TaskQueue::QueuePriority::kLowPriority;
1068 }
1069
1070 // Low priority feature enabled for sub-frame throttleable task queues.
1071 if (main_thread_scheduler_->scheduling_settings()
1072 .low_priority_subframe_throttleable &&
1073 is_subframe && is_throttleable_task_queue) {
1074 return TaskQueue::QueuePriority::kLowPriority;
1075 }
1076
1077 // Low priority feature enabled for throttleable task queues.
1078 if (main_thread_scheduler_->scheduling_settings()
1079 .low_priority_throttleable &&
1080 is_throttleable_task_queue) {
1081 return TaskQueue::QueuePriority::kLowPriority;
1082 }
1083 }
1084
1085 // Ad frame experiment.
1086 if (IsAdFrame() && (parent_page_scheduler_->IsLoading() ||
1087 !main_thread_scheduler_->scheduling_settings()
1088 .use_adframe_priorities_only_during_loading)) {
1089 if (main_thread_scheduler_->scheduling_settings().low_priority_ad_frame) {
1090 return TaskQueue::QueuePriority::kLowPriority;
1091 }
1092
1093 if (main_thread_scheduler_->scheduling_settings().best_effort_ad_frame) {
1094 return TaskQueue::QueuePriority::kBestEffortPriority;
1095 }
1096 }
1097
1098 // Frame origin type experiment.
1099 if (IsCrossOriginToMainFrame()) {
1100 if (main_thread_scheduler_->scheduling_settings()
1101 .low_priority_cross_origin ||
1102 (main_thread_scheduler_->scheduling_settings()
1103 .low_priority_cross_origin_only_during_loading &&
1104 parent_page_scheduler_->IsLoading())) {
1105 return TaskQueue::QueuePriority::kLowPriority;
1106 }
1107 }
1108
1109 // Consult per-agent scheduling strategy to see if it wants to affect queue
1110 // priority. Done here to avoid interfering with other policy decisions.
1111 base::Optional<TaskQueue::QueuePriority> per_agent_priority =
1112 main_thread_scheduler_->agent_scheduling_strategy().QueuePriority(
1113 *task_queue);
1114 if (per_agent_priority.has_value())
1115 return per_agent_priority.value();
1116
1117 if (task_queue->GetPrioritisationType() ==
1118 MainThreadTaskQueue::QueueTraits::PrioritisationType::kLoadingControl) {
1119 return main_thread_scheduler_->should_prioritize_loading_with_compositing()
1120 ? TaskQueue::QueuePriority::kVeryHighPriority
1121 : TaskQueue::QueuePriority::kHighPriority;
1122 }
1123
1124 if (task_queue->GetPrioritisationType() ==
1125 MainThreadTaskQueue::QueueTraits::PrioritisationType::kLoading &&
1126 main_thread_scheduler_->should_prioritize_loading_with_compositing()) {
1127 return main_thread_scheduler_->compositor_priority();
1128 }
1129
1130 if (task_queue->GetPrioritisationType() ==
1131 MainThreadTaskQueue::QueueTraits::PrioritisationType::kFindInPage) {
1132 return main_thread_scheduler_->find_in_page_priority();
1133 }
1134
1135 if (task_queue->GetPrioritisationType() ==
1136 MainThreadTaskQueue::QueueTraits::PrioritisationType::
1137 kHighPriorityLocalFrame) {
1138 return TaskQueue::QueuePriority::kHighestPriority;
1139 }
1140
1141 if (task_queue->GetPrioritisationType() ==
1142 MainThreadTaskQueue::QueueTraits::PrioritisationType::
1143 kExperimentalDatabase) {
1144 // TODO(shaseley): This decision should probably be based on Agent
1145 // visibility. Consider changing this before shipping anything.
1146 return parent_page_scheduler_->IsPageVisible()
1147 ? TaskQueue::QueuePriority::kHighPriority
1148 : TaskQueue::QueuePriority::kNormalPriority;
1149 }
1150
1151 return TaskQueue::QueuePriority::kNormalPriority;
1152 }
1153
1154 std::unique_ptr<blink::mojom::blink::PauseSubresourceLoadingHandle>
GetPauseSubresourceLoadingHandle()1155 FrameSchedulerImpl::GetPauseSubresourceLoadingHandle() {
1156 return std::make_unique<PauseSubresourceLoadingHandleImpl>(
1157 weak_factory_.GetWeakPtr());
1158 }
1159
AddPauseSubresourceLoadingHandle()1160 void FrameSchedulerImpl::AddPauseSubresourceLoadingHandle() {
1161 ++subresource_loading_pause_count_;
1162 if (subresource_loading_pause_count_ != 1) {
1163 DCHECK(subresource_loading_paused_);
1164 return;
1165 }
1166
1167 DCHECK(!subresource_loading_paused_);
1168 subresource_loading_paused_ = true;
1169 UpdatePolicy();
1170 }
1171
RemovePauseSubresourceLoadingHandle()1172 void FrameSchedulerImpl::RemovePauseSubresourceLoadingHandle() {
1173 DCHECK_LT(0u, subresource_loading_pause_count_);
1174 --subresource_loading_pause_count_;
1175 DCHECK(subresource_loading_paused_);
1176 if (subresource_loading_pause_count_ == 0) {
1177 subresource_loading_paused_ = false;
1178 UpdatePolicy();
1179 }
1180 }
1181
GetUkmRecorder()1182 ukm::UkmRecorder* FrameSchedulerImpl::GetUkmRecorder() {
1183 if (!delegate_)
1184 return nullptr;
1185 return delegate_->GetUkmRecorder();
1186 }
1187
GetUkmSourceId()1188 ukm::SourceId FrameSchedulerImpl::GetUkmSourceId() {
1189 if (!delegate_)
1190 return ukm::kInvalidSourceId;
1191 return delegate_->GetUkmSourceId();
1192 }
1193
OnTaskQueueCreated(MainThreadTaskQueue * task_queue,base::sequence_manager::TaskQueue::QueueEnabledVoter * voter)1194 void FrameSchedulerImpl::OnTaskQueueCreated(
1195 MainThreadTaskQueue* task_queue,
1196 base::sequence_manager::TaskQueue::QueueEnabledVoter* voter) {
1197 DCHECK(parent_page_scheduler_);
1198
1199 task_queue->GetTaskQueue()->SetBlameContext(blame_context_);
1200 UpdateQueuePolicy(task_queue, voter);
1201
1202 if (task_queue->CanBeThrottled()) {
1203 base::sequence_manager::LazyNow lazy_now(
1204 main_thread_scheduler_->tick_clock());
1205
1206 CPUTimeBudgetPool* cpu_time_budget_pool =
1207 parent_page_scheduler_->background_cpu_time_budget_pool();
1208 if (cpu_time_budget_pool) {
1209 cpu_time_budget_pool->AddQueue(lazy_now.Now(),
1210 task_queue->GetTaskQueue());
1211 }
1212
1213 parent_page_scheduler_->AddQueueToWakeUpBudgetPool(
1214 task_queue, frame_origin_type_, &lazy_now);
1215
1216 if (task_queues_throttled_) {
1217 UpdateTaskQueueThrottling(task_queue, true);
1218 }
1219 }
1220 }
1221
SetOnIPCTaskPostedWhileInBackForwardCacheHandler()1222 void FrameSchedulerImpl::SetOnIPCTaskPostedWhileInBackForwardCacheHandler() {
1223 DCHECK(parent_page_scheduler_->IsInBackForwardCache());
1224 for (const auto& task_queue_and_voter :
1225 frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) {
1226 task_queue_and_voter.first->SetOnIPCTaskPosted(base::BindRepeating(
1227 [](scoped_refptr<base::SingleThreadTaskRunner> task_runner,
1228 base::WeakPtr<FrameSchedulerImpl> frame_scheduler,
1229 const base::sequence_manager::Task& task) {
1230 // Only log IPC tasks. IPC tasks are only logged currently as IPC
1231 // hash can be mapped back to a function name, and IPC tasks may
1232 // potentially post sensitive information.
1233 if (!task.ipc_hash && !task.ipc_interface_name) {
1234 return;
1235 }
1236 base::ScopedDeferTaskPosting::PostOrDefer(
1237 task_runner, FROM_HERE,
1238 base::BindOnce(
1239 &FrameSchedulerImpl::OnIPCTaskPostedWhileInBackForwardCache,
1240 frame_scheduler, task.ipc_hash, task.ipc_interface_name),
1241 base::TimeDelta());
1242 },
1243 main_thread_scheduler_->BackForwardCacheIpcTrackingTaskRunner(),
1244 GetInvalidatingOnBFCacheRestoreWeakPtr()));
1245 }
1246 }
1247
DetachOnIPCTaskPostedWhileInBackForwardCacheHandler()1248 void FrameSchedulerImpl::DetachOnIPCTaskPostedWhileInBackForwardCacheHandler() {
1249 for (const auto& task_queue_and_voter :
1250 frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) {
1251 task_queue_and_voter.first->DetachOnIPCTaskPostedWhileInBackForwardCache();
1252 }
1253
1254 invalidating_on_bfcache_restore_weak_factory_.InvalidateWeakPtrs();
1255 }
1256
OnIPCTaskPostedWhileInBackForwardCache(uint32_t ipc_hash,const char * ipc_interface_name)1257 void FrameSchedulerImpl::OnIPCTaskPostedWhileInBackForwardCache(
1258 uint32_t ipc_hash,
1259 const char* ipc_interface_name) {
1260 // IPC tasks may have an IPC interface name in addition to, or instead of an
1261 // IPC hash. IPC hash is known from the mojo Accept method. When IPC hash is
1262 // 0, then the IPC hash must be calculated from the IPC interface name
1263 // instead.
1264 if (!ipc_hash) {
1265 // base::HashMetricName produces a uint64; however, the MD5 hash calculation
1266 // for an IPC interface name is always calculated as uint32; the IPC hash on
1267 // a task is also a uint32. The calculation here is meant to mimic the
1268 // calculation used in base::MD5Hash32Constexpr.
1269 ipc_hash = base::TaskAnnotator::ScopedSetIpcHash::MD5HashMetricName(
1270 ipc_interface_name);
1271 }
1272
1273 DCHECK(parent_page_scheduler_->IsInBackForwardCache());
1274 base::UmaHistogramSparse(
1275 "BackForwardCache.Experimental.UnexpectedIPCMessagePostedToCachedFrame."
1276 "MethodHash",
1277 static_cast<int32_t>(ipc_hash));
1278
1279 base::TimeDelta duration =
1280 main_thread_scheduler_->tick_clock()->NowTicks() -
1281 parent_page_scheduler_->GetStoredInBackForwardCacheTimestamp();
1282 base::UmaHistogramCustomTimes(
1283 "BackForwardCache.Experimental.UnexpectedIPCMessagePostedToCachedFrame."
1284 "TimeUntilIPCReceived",
1285 duration, base::TimeDelta(), base::TimeDelta::FromMinutes(5), 100);
1286 }
1287
1288 WTF::HashSet<SchedulingPolicy::Feature>
GetActiveFeaturesTrackedForBackForwardCacheMetrics()1289 FrameSchedulerImpl::GetActiveFeaturesTrackedForBackForwardCacheMetrics() {
1290 WTF::HashSet<SchedulingPolicy::Feature> result;
1291 for (const auto& it : back_forward_cache_opt_out_counts_)
1292 result.insert(it.first);
1293 return result;
1294 }
1295
1296 uint64_t
GetActiveFeaturesTrackedForBackForwardCacheMetricsMask() const1297 FrameSchedulerImpl::GetActiveFeaturesTrackedForBackForwardCacheMetricsMask()
1298 const {
1299 auto result = back_forward_cache_opt_outs_.to_ullong();
1300 static_assert(static_cast<size_t>(SchedulingPolicy::Feature::kMaxValue) <
1301 sizeof(result) * 8,
1302 "Number of the features should allow a bitmask to fit into "
1303 "64-bit integer");
1304 return result;
1305 }
1306
1307 base::WeakPtr<FrameOrWorkerScheduler>
GetDocumentBoundWeakPtr()1308 FrameSchedulerImpl::GetDocumentBoundWeakPtr() {
1309 return document_bound_weak_factory_.GetWeakPtr();
1310 }
1311
1312 std::unique_ptr<WebSchedulingTaskQueue>
CreateWebSchedulingTaskQueue(WebSchedulingPriority priority)1313 FrameSchedulerImpl::CreateWebSchedulingTaskQueue(
1314 WebSchedulingPriority priority) {
1315 // Use QueueTraits here that are the same as postMessage, which is one current
1316 // method for scheduling script.
1317 scoped_refptr<MainThreadTaskQueue> task_queue =
1318 frame_task_queue_controller_->NewWebSchedulingTaskQueue(
1319 PausableTaskQueueTraits(), priority);
1320 return std::make_unique<WebSchedulingTaskQueueImpl>(task_queue->AsWeakPtr());
1321 }
1322
OnWebSchedulingTaskQueuePriorityChanged(MainThreadTaskQueue * queue)1323 void FrameSchedulerImpl::OnWebSchedulingTaskQueuePriorityChanged(
1324 MainThreadTaskQueue* queue) {
1325 UpdateQueuePolicy(queue,
1326 frame_task_queue_controller_->GetQueueEnabledVoter(queue));
1327 }
1328
GetAgentClusterId() const1329 const base::UnguessableToken& FrameSchedulerImpl::GetAgentClusterId() const {
1330 if (!delegate_)
1331 return base::UnguessableToken::Null();
1332 return delegate_->GetAgentClusterId();
1333 }
1334
1335 // static
1336 MainThreadTaskQueue::QueueTraits
ThrottleableTaskQueueTraits()1337 FrameSchedulerImpl::ThrottleableTaskQueueTraits() {
1338 return QueueTraits()
1339 .SetCanBeThrottled(true)
1340 .SetCanBeFrozen(true)
1341 .SetCanBeDeferred(true)
1342 .SetCanBePaused(true)
1343 .SetCanRunWhenVirtualTimePaused(false)
1344 .SetCanBePausedForAndroidWebview(true);
1345 }
1346
1347 // static
1348 MainThreadTaskQueue::QueueTraits
DeferrableTaskQueueTraits()1349 FrameSchedulerImpl::DeferrableTaskQueueTraits() {
1350 return QueueTraits()
1351 .SetCanBeDeferred(true)
1352 .SetCanBeFrozen(base::FeatureList::IsEnabled(
1353 blink::features::kStopNonTimersInBackground))
1354 .SetCanBePaused(true)
1355 .SetCanRunWhenVirtualTimePaused(false)
1356 .SetCanBePausedForAndroidWebview(true);
1357 }
1358
1359 // static
PausableTaskQueueTraits()1360 MainThreadTaskQueue::QueueTraits FrameSchedulerImpl::PausableTaskQueueTraits() {
1361 return QueueTraits()
1362 .SetCanBeFrozen(base::FeatureList::IsEnabled(
1363 blink::features::kStopNonTimersInBackground))
1364 .SetCanBePaused(true)
1365 .SetCanRunWhenVirtualTimePaused(false)
1366 .SetCanBePausedForAndroidWebview(true);
1367 }
1368
1369 // static
1370 MainThreadTaskQueue::QueueTraits
FreezableTaskQueueTraits()1371 FrameSchedulerImpl::FreezableTaskQueueTraits() {
1372 // Should not use VirtualTime because using VirtualTime would make the task
1373 // execution non-deterministic and produce timeouts failures.
1374 return QueueTraits().SetCanBeFrozen(true);
1375 }
1376
1377 // static
1378 MainThreadTaskQueue::QueueTraits
UnpausableTaskQueueTraits()1379 FrameSchedulerImpl::UnpausableTaskQueueTraits() {
1380 return QueueTraits().SetCanRunWhenVirtualTimePaused(false);
1381 }
1382
1383 MainThreadTaskQueue::QueueTraits
ForegroundOnlyTaskQueueTraits()1384 FrameSchedulerImpl::ForegroundOnlyTaskQueueTraits() {
1385 return ThrottleableTaskQueueTraits()
1386 .SetCanRunInBackground(false)
1387 .SetCanRunWhenVirtualTimePaused(false);
1388 }
1389
1390 MainThreadTaskQueue::QueueTraits
DoesNotUseVirtualTimeTaskQueueTraits()1391 FrameSchedulerImpl::DoesNotUseVirtualTimeTaskQueueTraits() {
1392 return QueueTraits().SetCanRunWhenVirtualTimePaused(true);
1393 }
1394
SetPreemptedForCooperativeScheduling(Preempted preempted)1395 void FrameSchedulerImpl::SetPreemptedForCooperativeScheduling(
1396 Preempted preempted) {
1397 DCHECK_NE(preempted.value(), preempted_for_cooperative_scheduling_);
1398 preempted_for_cooperative_scheduling_ = preempted.value();
1399 UpdatePolicy();
1400 }
1401
LoadingTaskQueueTraits()1402 MainThreadTaskQueue::QueueTraits FrameSchedulerImpl::LoadingTaskQueueTraits() {
1403 return QueueTraits()
1404 .SetCanBePaused(true)
1405 .SetCanBeFrozen(true)
1406 .SetCanBeDeferred(true)
1407 .SetPrioritisationType(QueueTraits::PrioritisationType::kLoading);
1408 }
1409
1410 MainThreadTaskQueue::QueueTraits
UnfreezableLoadingTaskQueueTraits()1411 FrameSchedulerImpl::UnfreezableLoadingTaskQueueTraits() {
1412 return LoadingTaskQueueTraits().SetCanBeFrozen(false);
1413 }
1414
1415 MainThreadTaskQueue::QueueTraits
LoadingControlTaskQueueTraits()1416 FrameSchedulerImpl::LoadingControlTaskQueueTraits() {
1417 return QueueTraits()
1418 .SetCanBePaused(true)
1419 .SetCanBeFrozen(true)
1420 .SetCanBeDeferred(true)
1421 .SetPrioritisationType(QueueTraits::PrioritisationType::kLoadingControl);
1422 }
1423
1424 MainThreadTaskQueue::QueueTraits
FindInPageTaskQueueTraits()1425 FrameSchedulerImpl::FindInPageTaskQueueTraits() {
1426 return PausableTaskQueueTraits().SetPrioritisationType(
1427 QueueTraits::PrioritisationType::kFindInPage);
1428 }
1429 } // namespace scheduler
1430 } // namespace blink
1431