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