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/page_scheduler_impl.h"
6 #include <memory>
7 
8 #include "base/bind.h"
9 #include "base/check_op.h"
10 #include "base/metrics/field_trial_params.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/notreached.h"
13 #include "base/optional.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/time/time.h"
16 #include "third_party/blink/public/common/features.h"
17 #include "third_party/blink/public/common/switches.h"
18 #include "third_party/blink/public/platform/platform.h"
19 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
20 #include "third_party/blink/renderer/platform/scheduler/common/features.h"
21 #include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h"
22 #include "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h"
23 #include "third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h"
24 #include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h"
25 #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
26 #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
27 #include "third_party/blink/renderer/platform/scheduler/main_thread/page_visibility_state.h"
28 #include "third_party/blink/renderer/platform/scheduler/main_thread/use_case.h"
29 #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
30 #include "third_party/blink/renderer/platform/scheduler/public/page_lifecycle_state.h"
31 
32 namespace blink {
33 namespace scheduler {
34 
35 namespace {
36 
37 using blink::FrameScheduler;
38 
39 constexpr double kDefaultBackgroundBudgetAsCPUFraction = .01;
40 constexpr double kDefaultMaxBackgroundBudgetLevelInSeconds = 3;
41 constexpr double kDefaultInitialBackgroundBudgetInSeconds = 1;
42 constexpr double kDefaultMaxBackgroundThrottlingDelayInSeconds = 0;
43 
44 // Given that we already align timers to 1Hz, do not report throttling if
45 // it is under 3s.
46 constexpr base::TimeDelta kMinimalBackgroundThrottlingDurationToReport =
47     base::TimeDelta::FromSeconds(3);
48 
49 // Delay for fully throttling the page after backgrounding.
50 constexpr base::TimeDelta kThrottlingDelayAfterBackgrounding =
51     base::TimeDelta::FromSeconds(10);
52 
53 // The amount of time to wait before suspending shared timers, and loading
54 // etc. after the renderer has been backgrounded. This is used only if
55 // background suspension is enabled.
56 constexpr base::TimeDelta kDefaultDelayForBackgroundTabFreezing =
57     base::TimeDelta::FromMinutes(5);
58 
59 // The amount of time to wait before checking network idleness
60 // after the page has been backgrounded. If network is idle,
61 // suspend shared timers, and loading etc. This is used only if
62 // freeze-background-tab-on-network-idle feature is enabled.
63 // This value should be smaller than kDefaultDelayForBackgroundTabFreezing.
64 constexpr base::TimeDelta kDefaultDelayForBackgroundAndNetworkIdleTabFreezing =
65     base::TimeDelta::FromMinutes(1);
66 
67 // Duration of a throttled wake up.
68 constexpr base::TimeDelta kThrottledWakeUpDuration =
69     base::TimeDelta::FromMilliseconds(3);
70 
71 constexpr base::TimeDelta kDefaultDelayForTrackingIPCsPostedToCachedFrames =
72     base::TimeDelta::FromSeconds(15);
73 
74 // Values coming from the field trial config are interpreted as follows:
75 //   -1 is "not set". Scheduler should use a reasonable default.
76 //   0 corresponds to base::nullopt.
77 //   Other values are left without changes.
78 
79 struct BackgroundThrottlingSettings {
80   double budget_recovery_rate;
81   base::Optional<base::TimeDelta> max_budget_level;
82   base::Optional<base::TimeDelta> max_throttling_delay;
83   base::Optional<base::TimeDelta> initial_budget;
84 };
85 
GetDoubleParameterFromMap(const base::FieldTrialParams & settings,const std::string & setting_name,double default_value)86 double GetDoubleParameterFromMap(const base::FieldTrialParams& settings,
87                                  const std::string& setting_name,
88                                  double default_value) {
89   const auto& find_it = settings.find(setting_name);
90   if (find_it == settings.end())
91     return default_value;
92   double parsed_value;
93   if (!base::StringToDouble(find_it->second, &parsed_value))
94     return default_value;
95   if (parsed_value == -1)
96     return default_value;
97   return parsed_value;
98 }
99 
DoubleToOptionalTime(double value)100 base::Optional<base::TimeDelta> DoubleToOptionalTime(double value) {
101   if (value == 0)
102     return base::nullopt;
103   return base::TimeDelta::FromSecondsD(value);
104 }
105 
GetBackgroundThrottlingSettings()106 BackgroundThrottlingSettings GetBackgroundThrottlingSettings() {
107   base::FieldTrialParams background_throttling_settings;
108   base::GetFieldTrialParams("ExpensiveBackgroundTimerThrottling",
109                             &background_throttling_settings);
110 
111   BackgroundThrottlingSettings settings;
112 
113   settings.budget_recovery_rate =
114       GetDoubleParameterFromMap(background_throttling_settings, "cpu_budget",
115                                 kDefaultBackgroundBudgetAsCPUFraction);
116 
117   settings.max_budget_level = DoubleToOptionalTime(
118       GetDoubleParameterFromMap(background_throttling_settings, "max_budget",
119                                 kDefaultMaxBackgroundBudgetLevelInSeconds));
120 
121   settings.max_throttling_delay = DoubleToOptionalTime(
122       GetDoubleParameterFromMap(background_throttling_settings, "max_delay",
123                                 kDefaultMaxBackgroundThrottlingDelayInSeconds));
124 
125   settings.initial_budget = DoubleToOptionalTime(GetDoubleParameterFromMap(
126       background_throttling_settings, "initial_budget",
127       kDefaultInitialBackgroundBudgetInSeconds));
128 
129   return settings;
130 }
131 
GetDelayForBackgroundTabFreezing()132 base::TimeDelta GetDelayForBackgroundTabFreezing() {
133   static const base::FeatureParam<int> kDelayForBackgroundTabFreezingMillis{
134       &features::kStopInBackground, "DelayForBackgroundTabFreezingMills",
135       static_cast<int>(kDefaultDelayForBackgroundTabFreezing.InMilliseconds())};
136   return base::TimeDelta::FromMilliseconds(
137       kDelayForBackgroundTabFreezingMillis.Get());
138 }
139 
GetDelayForBackgroundAndNetworkIdleTabFreezing()140 base::TimeDelta GetDelayForBackgroundAndNetworkIdleTabFreezing() {
141   static const base::FeatureParam<int>
142       kDelayForBackgroundAndNetworkIdleTabFreezingMillis{
143           &features::kFreezeBackgroundTabOnNetworkIdle,
144           "DelayForBackgroundAndNetworkIdleTabFreezingMills",
145           static_cast<int>(kDefaultDelayForBackgroundAndNetworkIdleTabFreezing
146                                .InMilliseconds())};
147   return base::TimeDelta::FromMilliseconds(
148       kDelayForBackgroundAndNetworkIdleTabFreezingMillis.Get());
149 }
150 
GetTimeToDelayIPCTrackingWhileStoredInBackForwardCache()151 base::TimeDelta GetTimeToDelayIPCTrackingWhileStoredInBackForwardCache() {
152   if (base::FeatureList::IsEnabled(
153           features::kLogUnexpectedIPCPostedToBackForwardCachedDocuments)) {
154     static const base::FeatureParam<int>
155         kDelayForLoggingUnexpectedIPCPostedToBckForwardCacheMillis{
156             &features::kLogUnexpectedIPCPostedToBackForwardCachedDocuments,
157             "delay_before_tracking_ms",
158             static_cast<int>(kDefaultDelayForTrackingIPCsPostedToCachedFrames
159                                  .InMilliseconds())};
160     return base::TimeDelta::FromMilliseconds(
161         kDelayForLoggingUnexpectedIPCPostedToBckForwardCacheMillis.Get());
162   }
163   return kDefaultDelayForTrackingIPCsPostedToCachedFrames;
164 }
165 
166 }  // namespace
167 
168 constexpr base::TimeDelta PageSchedulerImpl::kDefaultThrottledWakeUpInterval;
169 
PageSchedulerImpl(PageScheduler::Delegate * delegate,AgentGroupSchedulerImpl & agent_group_scheduler)170 PageSchedulerImpl::PageSchedulerImpl(
171     PageScheduler::Delegate* delegate,
172     AgentGroupSchedulerImpl& agent_group_scheduler)
173     : main_thread_scheduler_(&agent_group_scheduler.GetMainThreadScheduler()),
174       agent_group_scheduler_(agent_group_scheduler),
175       page_visibility_(kDefaultPageVisibility),
176       page_visibility_changed_time_(
177           agent_group_scheduler.GetMainThreadScheduler()
178               .GetTickClock()
179               ->NowTicks()),
180       audio_state_(AudioState::kSilent),
181       is_frozen_(false),
182       reported_background_throttling_since_navigation_(false),
183       opted_out_from_aggressive_throttling_(false),
184       nested_runloop_(false),
185       is_main_frame_local_(false),
186       is_cpu_time_throttled_(false),
187       are_wake_ups_intensively_throttled_(false),
188       keep_active_(
189           agent_group_scheduler.GetMainThreadScheduler().SchedulerKeepActive()),
190       had_recent_title_or_favicon_update_(false),
191       delegate_(delegate),
192       delay_for_background_tab_freezing_(GetDelayForBackgroundTabFreezing()),
193       freeze_on_network_idle_enabled_(base::FeatureList::IsEnabled(
194           blink::features::kFreezeBackgroundTabOnNetworkIdle)),
195       delay_for_background_and_network_idle_tab_freezing_(
196           GetDelayForBackgroundAndNetworkIdleTabFreezing()) {
197   page_lifecycle_state_tracker_.reset(new PageLifecycleStateTracker(
198       this, kDefaultPageVisibility == PageVisibilityState::kVisible
199                 ? PageLifecycleState::kActive
200                 : PageLifecycleState::kHiddenBackgrounded));
201   do_throttle_cpu_time_callback_.Reset(base::BindRepeating(
202       &PageSchedulerImpl::DoThrottleCPUTime, base::Unretained(this)));
203   do_intensively_throttle_wake_ups_callback_.Reset(
204       base::BindRepeating(&PageSchedulerImpl::DoIntensivelyThrottleWakeUps,
205                           base::Unretained(this)));
206   reset_had_recent_title_or_favicon_update_.Reset(base::BindRepeating(
207       &PageSchedulerImpl::ResetHadRecentTitleOrFaviconUpdate,
208       base::Unretained(this)));
209   on_audio_silent_closure_.Reset(base::BindRepeating(
210       &PageSchedulerImpl::OnAudioSilent, base::Unretained(this)));
211   do_freeze_page_callback_.Reset(base::BindRepeating(
212       &PageSchedulerImpl::DoFreezePage, base::Unretained(this)));
213 }
214 
~PageSchedulerImpl()215 PageSchedulerImpl::~PageSchedulerImpl() {
216   // TODO(alexclarke): Find out why we can't rely on the web view outliving the
217   // frame.
218   for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_) {
219     frame_scheduler->DetachFromPageScheduler();
220   }
221   main_thread_scheduler_->RemovePageScheduler(this);
222 
223   if (cpu_time_budget_pool_)
224     cpu_time_budget_pool_->Close();
225   if (HasWakeUpBudgetPools()) {
226     for (WakeUpBudgetPool* pool : AllWakeUpBudgetPools()) {
227       DCHECK(pool);
228       pool->Close();
229     }
230   }
231 }
232 
233 // static
234 // kRecentAudioDelay is defined in the header for use in unit tests and requires
235 // storage for linking to succeed with some compiler toolchains.
236 constexpr base::TimeDelta PageSchedulerImpl::kRecentAudioDelay;
237 
SetPageVisible(bool page_visible)238 void PageSchedulerImpl::SetPageVisible(bool page_visible) {
239   PageVisibilityState page_visibility = page_visible
240                                             ? PageVisibilityState::kVisible
241                                             : PageVisibilityState::kHidden;
242 
243   if (page_visibility_ == page_visibility)
244     return;
245   page_visibility_ = page_visibility;
246   page_visibility_changed_time_ =
247       main_thread_scheduler_->GetTickClock()->NowTicks();
248 
249   switch (page_visibility_) {
250     case PageVisibilityState::kVisible:
251       // Visible pages should not be frozen.
252       SetPageFrozenImpl(false, NotificationPolicy::kDoNotNotifyFrames);
253       page_lifecycle_state_tracker_->SetPageLifecycleState(
254           PageLifecycleState::kActive);
255       break;
256     case PageVisibilityState::kHidden:
257       page_lifecycle_state_tracker_->SetPageLifecycleState(
258           IsBackgrounded() ? PageLifecycleState::kHiddenBackgrounded
259                            : PageLifecycleState::kHiddenForegrounded);
260       break;
261   }
262 
263   if (ShouldFreezePage()) {
264     main_thread_scheduler_->ControlTaskRunner()->PostDelayedTask(
265         FROM_HERE, do_freeze_page_callback_.GetCallback(),
266         freeze_on_network_idle_enabled_
267             ? delay_for_background_and_network_idle_tab_freezing_
268             : delay_for_background_tab_freezing_);
269   }
270 
271   for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_)
272     frame_scheduler->SetPageVisibilityForTracing(page_visibility_);
273 
274   UpdatePolicyOnVisibilityChange(NotificationPolicy::kDoNotNotifyFrames);
275 
276   NotifyFrames();
277 }
278 
SetPageFrozen(bool frozen)279 void PageSchedulerImpl::SetPageFrozen(bool frozen) {
280   // Only transitions from HIDDEN to FROZEN are allowed for pages (see
281   // https://github.com/WICG/page-lifecycle).
282   // This is the page freezing path we expose via WebView, which is how
283   // embedders freeze pages. Visibility is also controlled by the embedder,
284   // through [WebView|WebViewFrameWidget]::SetVisibilityState(). The following
285   // happens if the embedder attempts to freeze a page that it set to visible.
286   // We check for this illegal state transition later on this code path in page
287   // scheduler and frame scheduler when computing the new lifecycle state, but
288   // it is desirable to reject the page freeze to prevent the scheduler from
289   // being put in a bad state. See https://crbug.com/873214 for context of how
290   // this can happen on the browser side.
291   if (frozen && IsPageVisible()) {
292     DCHECK(false);
293     return;
294   }
295   SetPageFrozenImpl(frozen, NotificationPolicy::kNotifyFrames);
296 }
297 
SetPageFrozenImpl(bool frozen,PageSchedulerImpl::NotificationPolicy notification_policy)298 void PageSchedulerImpl::SetPageFrozenImpl(
299     bool frozen,
300     PageSchedulerImpl::NotificationPolicy notification_policy) {
301   // Only pages owned by web views can be frozen.
302   DCHECK(IsOrdinary());
303 
304   do_freeze_page_callback_.Cancel();
305   if (is_frozen_ == frozen)
306     return;
307   is_frozen_ = frozen;
308   for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_) {
309     frame_scheduler->SetPageFrozenForTracing(frozen);
310     frame_scheduler->SetShouldReportPostedTasksWhenDisabled(frozen);
311   }
312   if (notification_policy ==
313       PageSchedulerImpl::NotificationPolicy::kNotifyFrames)
314     NotifyFrames();
315   if (frozen) {
316     page_lifecycle_state_tracker_->SetPageLifecycleState(
317         PageLifecycleState::kFrozen);
318     main_thread_scheduler_->OnPageFrozen();
319   } else {
320     // The new state may have already been set if unfreezing through the
321     // renderer, but that's okay - duplicate state changes won't be recorded.
322     if (page_visibility_ == PageVisibilityState::kVisible) {
323       page_lifecycle_state_tracker_->SetPageLifecycleState(
324           PageLifecycleState::kActive);
325     } else if (IsBackgrounded()) {
326       page_lifecycle_state_tracker_->SetPageLifecycleState(
327           PageLifecycleState::kHiddenBackgrounded);
328     } else {
329       page_lifecycle_state_tracker_->SetPageLifecycleState(
330           PageLifecycleState::kHiddenForegrounded);
331     }
332     main_thread_scheduler_->OnPageResumed();
333   }
334 
335   if (delegate_)
336     delegate_->OnSetPageFrozen(frozen);
337 }
338 
SetPageBackForwardCached(bool is_in_back_forward_cache)339 void PageSchedulerImpl::SetPageBackForwardCached(
340     bool is_in_back_forward_cache) {
341   is_stored_in_back_forward_cache_ = is_in_back_forward_cache;
342 
343   if (!is_stored_in_back_forward_cache_) {
344     set_ipc_posted_handler_task_.Cancel();
345     has_ipc_detection_enabled_ = false;
346     main_thread_scheduler_->UpdateIpcTracking();
347     for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_) {
348       frame_scheduler->DetachOnIPCTaskPostedWhileInBackForwardCacheHandler();
349     }
350     stored_in_back_forward_cache_timestamp_ = base::TimeTicks();
351   } else {
352     stored_in_back_forward_cache_timestamp_ =
353         main_thread_scheduler_->tick_clock()->NowTicks();
354 
355     // Incorporate a delay of 15 seconds to allow for caching operations to
356     // complete before tasks are logged.
357     set_ipc_posted_handler_task_ = PostDelayedCancellableTask(
358         *main_thread_scheduler_->ControlTaskRunner(), FROM_HERE,
359         base::BindRepeating(&PageSchedulerImpl::SetUpIPCTaskDetection,
360                             GetWeakPtr()),
361         GetTimeToDelayIPCTrackingWhileStoredInBackForwardCache());
362   }
363 }
364 
SetUpIPCTaskDetection()365 void PageSchedulerImpl::SetUpIPCTaskDetection() {
366   DCHECK(is_stored_in_back_forward_cache_);
367   has_ipc_detection_enabled_ = true;
368   main_thread_scheduler_->UpdateIpcTracking();
369   for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_) {
370     frame_scheduler->SetOnIPCTaskPostedWhileInBackForwardCacheHandler();
371   }
372 }
373 
SetKeepActive(bool keep_active)374 void PageSchedulerImpl::SetKeepActive(bool keep_active) {
375   if (keep_active_ == keep_active)
376     return;
377   keep_active_ = keep_active;
378 
379   for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_)
380     frame_scheduler->SetPageKeepActiveForTracing(keep_active);
381 
382   NotifyFrames();
383 }
384 
KeepActive() const385 bool PageSchedulerImpl::KeepActive() const {
386   return keep_active_;
387 }
388 
IsMainFrameLocal() const389 bool PageSchedulerImpl::IsMainFrameLocal() const {
390   return is_main_frame_local_;
391 }
392 
IsLoading() const393 bool PageSchedulerImpl::IsLoading() const {
394   return main_thread_scheduler_->current_use_case() == UseCase::kEarlyLoading ||
395          main_thread_scheduler_->current_use_case() == UseCase::kLoading;
396 }
397 
IsOrdinary() const398 bool PageSchedulerImpl::IsOrdinary() const {
399   if (!delegate_)
400     return true;
401   return delegate_->IsOrdinary();
402 }
403 
SetIsMainFrameLocal(bool is_local)404 void PageSchedulerImpl::SetIsMainFrameLocal(bool is_local) {
405   is_main_frame_local_ = is_local;
406 }
407 
RegisterFrameSchedulerImpl(FrameSchedulerImpl * frame_scheduler)408 void PageSchedulerImpl::RegisterFrameSchedulerImpl(
409     FrameSchedulerImpl* frame_scheduler) {
410   base::sequence_manager::LazyNow lazy_now(
411       main_thread_scheduler_->tick_clock());
412 
413   MaybeInitializeWakeUpBudgetPools(&lazy_now);
414   MaybeInitializeBackgroundCPUTimeBudgetPool(&lazy_now);
415 
416   frame_schedulers_.insert(frame_scheduler);
417   main_thread_scheduler_->OnFrameAdded(*frame_scheduler);
418   frame_scheduler->UpdatePolicy();
419 }
420 
CreateFrameScheduler(FrameScheduler::Delegate * delegate,blink::BlameContext * blame_context,FrameScheduler::FrameType frame_type)421 std::unique_ptr<blink::FrameScheduler> PageSchedulerImpl::CreateFrameScheduler(
422     FrameScheduler::Delegate* delegate,
423     blink::BlameContext* blame_context,
424     FrameScheduler::FrameType frame_type) {
425   auto frame_scheduler = std::make_unique<FrameSchedulerImpl>(
426       this, delegate, blame_context, frame_type);
427   RegisterFrameSchedulerImpl(frame_scheduler.get());
428   return frame_scheduler;
429 }
430 
Unregister(FrameSchedulerImpl * frame_scheduler)431 void PageSchedulerImpl::Unregister(FrameSchedulerImpl* frame_scheduler) {
432   DCHECK(frame_schedulers_.find(frame_scheduler) != frame_schedulers_.end());
433   frame_schedulers_.erase(frame_scheduler);
434   main_thread_scheduler_->OnFrameRemoved(*frame_scheduler);
435 }
436 
OnNavigation()437 void PageSchedulerImpl::OnNavigation() {
438   reported_background_throttling_since_navigation_ = false;
439 }
440 
ReportIntervention(const String & message)441 void PageSchedulerImpl::ReportIntervention(const String& message) {
442   delegate_->ReportIntervention(message);
443 }
444 
EnableVirtualTime()445 base::TimeTicks PageSchedulerImpl::EnableVirtualTime() {
446   return main_thread_scheduler_->EnableVirtualTime();
447 }
448 
DisableVirtualTimeForTesting()449 void PageSchedulerImpl::DisableVirtualTimeForTesting() {
450   main_thread_scheduler_->DisableVirtualTimeForTesting();
451 }
452 
SetVirtualTimePolicy(VirtualTimePolicy policy)453 void PageSchedulerImpl::SetVirtualTimePolicy(VirtualTimePolicy policy) {
454   main_thread_scheduler_->SetVirtualTimePolicy(policy);
455 }
456 
SetInitialVirtualTime(base::Time time)457 void PageSchedulerImpl::SetInitialVirtualTime(base::Time time) {
458   main_thread_scheduler_->SetInitialVirtualTime(time);
459 }
460 
SetInitialVirtualTimeOffset(base::TimeDelta offset)461 void PageSchedulerImpl::SetInitialVirtualTimeOffset(base::TimeDelta offset) {
462   main_thread_scheduler_->SetInitialVirtualTimeOffset(offset);
463 }
464 
VirtualTimeAllowedToAdvance() const465 bool PageSchedulerImpl::VirtualTimeAllowedToAdvance() const {
466   return main_thread_scheduler_->VirtualTimeAllowedToAdvance();
467 }
468 
GrantVirtualTimeBudget(base::TimeDelta budget,base::OnceClosure budget_exhausted_callback)469 void PageSchedulerImpl::GrantVirtualTimeBudget(
470     base::TimeDelta budget,
471     base::OnceClosure budget_exhausted_callback) {
472   main_thread_scheduler_->VirtualTimeControlTaskRunner()->PostDelayedTask(
473       FROM_HERE, std::move(budget_exhausted_callback), budget);
474   // This can shift time forwards if there's a pending MaybeAdvanceVirtualTime,
475   // so it's important this is called second.
476   main_thread_scheduler_->GetVirtualTimeDomain()->SetVirtualTimeFence(
477       main_thread_scheduler_->GetVirtualTimeDomain()->Now() + budget);
478 }
479 
AudioStateChanged(bool is_audio_playing)480 void PageSchedulerImpl::AudioStateChanged(bool is_audio_playing) {
481   if (is_audio_playing) {
482     audio_state_ = AudioState::kAudible;
483     on_audio_silent_closure_.Cancel();
484     if (page_visibility_ == PageVisibilityState::kHidden) {
485       page_lifecycle_state_tracker_->SetPageLifecycleState(
486           PageLifecycleState::kHiddenForegrounded);
487     }
488     // Pages with audio playing should not be frozen.
489     SetPageFrozenImpl(false, NotificationPolicy::kDoNotNotifyFrames);
490     NotifyFrames();
491     main_thread_scheduler_->OnAudioStateChanged();
492   } else {
493     if (audio_state_ != AudioState::kAudible)
494       return;
495     on_audio_silent_closure_.Cancel();
496 
497     audio_state_ = AudioState::kRecentlyAudible;
498     main_thread_scheduler_->ControlTaskRunner()->PostDelayedTask(
499         FROM_HERE, on_audio_silent_closure_.GetCallback(), kRecentAudioDelay);
500     // No need to call NotifyFrames or
501     // MainThreadScheduler::OnAudioStateChanged here, as for outside world
502     // kAudible and kRecentlyAudible are the same thing.
503   }
504 }
505 
OnAudioSilent()506 void PageSchedulerImpl::OnAudioSilent() {
507   DCHECK_EQ(audio_state_, AudioState::kRecentlyAudible);
508   audio_state_ = AudioState::kSilent;
509   NotifyFrames();
510   main_thread_scheduler_->OnAudioStateChanged();
511   if (IsBackgrounded()) {
512     page_lifecycle_state_tracker_->SetPageLifecycleState(
513         PageLifecycleState::kHiddenBackgrounded);
514   }
515   if (ShouldFreezePage()) {
516     main_thread_scheduler_->ControlTaskRunner()->PostDelayedTask(
517         FROM_HERE, do_freeze_page_callback_.GetCallback(),
518         freeze_on_network_idle_enabled_
519             ? delay_for_background_and_network_idle_tab_freezing_
520             : delay_for_background_tab_freezing_);
521   }
522 }
523 
IsExemptFromBudgetBasedThrottling() const524 bool PageSchedulerImpl::IsExemptFromBudgetBasedThrottling() const {
525   return opted_out_from_aggressive_throttling_;
526 }
527 
OptedOutFromAggressiveThrottlingForTest() const528 bool PageSchedulerImpl::OptedOutFromAggressiveThrottlingForTest() const {
529   return OptedOutFromAggressiveThrottling();
530 }
531 
OptedOutFromAggressiveThrottling() const532 bool PageSchedulerImpl::OptedOutFromAggressiveThrottling() const {
533   return opted_out_from_aggressive_throttling_;
534 }
535 
RequestBeginMainFrameNotExpected(bool new_state)536 bool PageSchedulerImpl::RequestBeginMainFrameNotExpected(bool new_state) {
537   if (!delegate_)
538     return false;
539   return delegate_->RequestBeginMainFrameNotExpected(new_state);
540 }
541 
IsAudioPlaying() const542 bool PageSchedulerImpl::IsAudioPlaying() const {
543   return audio_state_ == AudioState::kAudible ||
544          audio_state_ == AudioState::kRecentlyAudible;
545 }
546 
IsPageVisible() const547 bool PageSchedulerImpl::IsPageVisible() const {
548   return page_visibility_ == PageVisibilityState::kVisible;
549 }
550 
IsFrozen() const551 bool PageSchedulerImpl::IsFrozen() const {
552   return is_frozen_;
553 }
554 
IsCPUTimeThrottled() const555 bool PageSchedulerImpl::IsCPUTimeThrottled() const {
556   return is_cpu_time_throttled_;
557 }
558 
OnThrottlingStatusUpdated()559 void PageSchedulerImpl::OnThrottlingStatusUpdated() {
560   bool opted_out_from_aggressive_throttling = false;
561   for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_) {
562     opted_out_from_aggressive_throttling |=
563         frame_scheduler->opted_out_from_aggressive_throttling();
564   }
565 
566   if (opted_out_from_aggressive_throttling_ !=
567       opted_out_from_aggressive_throttling) {
568     opted_out_from_aggressive_throttling_ =
569         opted_out_from_aggressive_throttling;
570     base::sequence_manager::LazyNow lazy_now(
571         main_thread_scheduler_->tick_clock());
572     UpdateCPUTimeBudgetPool(&lazy_now);
573     UpdateWakeUpBudgetPools(&lazy_now);
574   }
575 }
576 
OnTraceLogEnabled()577 void PageSchedulerImpl::OnTraceLogEnabled() {
578   tracing_controller_.OnTraceLogEnabled();
579   for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_) {
580     frame_scheduler->OnTraceLogEnabled();
581   }
582 }
583 
IsWaitingForMainFrameContentfulPaint() const584 bool PageSchedulerImpl::IsWaitingForMainFrameContentfulPaint() const {
585   return std::any_of(frame_schedulers_.begin(), frame_schedulers_.end(),
586                      [](const FrameSchedulerImpl* fs) {
587                        return fs->IsWaitingForContentfulPaint() &&
588                               fs->GetFrameType() ==
589                                   FrameScheduler::FrameType::kMainFrame;
590                      });
591 }
592 
IsWaitingForMainFrameMeaningfulPaint() const593 bool PageSchedulerImpl::IsWaitingForMainFrameMeaningfulPaint() const {
594   return std::any_of(frame_schedulers_.begin(), frame_schedulers_.end(),
595                      [](const FrameSchedulerImpl* fs) {
596                        return fs->IsWaitingForMeaningfulPaint() &&
597                               fs->GetFrameType() ==
598                                   FrameScheduler::FrameType::kMainFrame;
599                      });
600 }
601 
AsValueInto(base::trace_event::TracedValue * state) const602 void PageSchedulerImpl::AsValueInto(
603     base::trace_event::TracedValue* state) const {
604   state->SetBoolean("page_visible",
605                     page_visibility_ == PageVisibilityState::kVisible);
606   state->SetBoolean("is_audio_playing", IsAudioPlaying());
607   state->SetBoolean("is_frozen", is_frozen_);
608   state->SetBoolean("reported_background_throttling_since_navigation",
609                     reported_background_throttling_since_navigation_);
610   state->SetBoolean("is_page_freezable", IsBackgrounded());
611 
612   {
613     auto dictionary_scope = state->BeginDictionaryScoped("frame_schedulers");
614     for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_) {
615       auto inner_dictionary = state->BeginDictionaryScopedWithCopiedName(
616           PointerToString(frame_scheduler));
617       frame_scheduler->AsValueInto(state);
618     }
619   }
620 }
621 
AddQueueToWakeUpBudgetPool(MainThreadTaskQueue * task_queue,FrameOriginType frame_origin_type,base::sequence_manager::LazyNow * lazy_now)622 void PageSchedulerImpl::AddQueueToWakeUpBudgetPool(
623     MainThreadTaskQueue* task_queue,
624     FrameOriginType frame_origin_type,
625     base::sequence_manager::LazyNow* lazy_now) {
626   GetWakeUpBudgetPool(task_queue, frame_origin_type)
627       ->AddQueue(lazy_now->Now(), task_queue->GetTaskQueue());
628 }
629 
RemoveQueueFromWakeUpBudgetPool(MainThreadTaskQueue * task_queue,FrameOriginType frame_origin_type,base::sequence_manager::LazyNow * lazy_now)630 void PageSchedulerImpl::RemoveQueueFromWakeUpBudgetPool(
631     MainThreadTaskQueue* task_queue,
632     FrameOriginType frame_origin_type,
633     base::sequence_manager::LazyNow* lazy_now) {
634   GetWakeUpBudgetPool(task_queue, frame_origin_type)
635       ->RemoveQueue(lazy_now->Now(), task_queue->GetTaskQueue());
636 }
637 
GetWakeUpBudgetPool(MainThreadTaskQueue * task_queue,FrameOriginType frame_origin_type)638 WakeUpBudgetPool* PageSchedulerImpl::GetWakeUpBudgetPool(
639     MainThreadTaskQueue* task_queue,
640     FrameOriginType frame_origin_type) {
641   if (!task_queue->CanBeIntensivelyThrottled())
642     return normal_wake_up_budget_pool_;
643 
644   switch (frame_origin_type) {
645     case FrameOriginType::kMainFrame:
646     case FrameOriginType::kSameOriginToMainFrame:
647       return same_origin_intensive_wake_up_budget_pool_;
648     case FrameOriginType::kCrossOriginToMainFrame:
649       return cross_origin_intensive_wake_up_budget_pool_;
650     case FrameOriginType::kCount:
651       NOTREACHED();
652       return nullptr;
653   }
654 }
655 
background_cpu_time_budget_pool()656 CPUTimeBudgetPool* PageSchedulerImpl::background_cpu_time_budget_pool() {
657   return cpu_time_budget_pool_;
658 }
659 
MaybeInitializeBackgroundCPUTimeBudgetPool(base::sequence_manager::LazyNow * lazy_now)660 void PageSchedulerImpl::MaybeInitializeBackgroundCPUTimeBudgetPool(
661     base::sequence_manager::LazyNow* lazy_now) {
662   if (cpu_time_budget_pool_)
663     return;
664 
665   if (!RuntimeEnabledFeatures::ExpensiveBackgroundTimerThrottlingEnabled())
666     return;
667 
668   cpu_time_budget_pool_ =
669       main_thread_scheduler_->task_queue_throttler()->CreateCPUTimeBudgetPool(
670           "background");
671 
672   BackgroundThrottlingSettings settings = GetBackgroundThrottlingSettings();
673 
674   cpu_time_budget_pool_->SetMaxBudgetLevel(lazy_now->Now(),
675                                            settings.max_budget_level);
676   cpu_time_budget_pool_->SetMaxThrottlingDelay(lazy_now->Now(),
677                                                settings.max_throttling_delay);
678 
679   cpu_time_budget_pool_->SetTimeBudgetRecoveryRate(
680       lazy_now->Now(), settings.budget_recovery_rate);
681 
682   if (settings.initial_budget) {
683     cpu_time_budget_pool_->GrantAdditionalBudget(
684         lazy_now->Now(), settings.initial_budget.value());
685   }
686 
687   UpdateCPUTimeBudgetPool(lazy_now);
688 }
689 
MaybeInitializeWakeUpBudgetPools(base::sequence_manager::LazyNow * lazy_now)690 void PageSchedulerImpl::MaybeInitializeWakeUpBudgetPools(
691     base::sequence_manager::LazyNow* lazy_now) {
692   if (HasWakeUpBudgetPools())
693     return;
694 
695   normal_wake_up_budget_pool_ =
696       main_thread_scheduler_->task_queue_throttler()->CreateWakeUpBudgetPool(
697           "Page - Normal Wake Up Throttling");
698   same_origin_intensive_wake_up_budget_pool_ =
699       main_thread_scheduler_->task_queue_throttler()->CreateWakeUpBudgetPool(
700           "Page - Intensive Wake Up Throttling - Same-Origin as Main Frame");
701   cross_origin_intensive_wake_up_budget_pool_ =
702       main_thread_scheduler_->task_queue_throttler()->CreateWakeUpBudgetPool(
703           "Page - Intensive Wake Up Throttling - Cross-Origin to Main Frame");
704 
705   // The Wake Up Duration and Unaligned Wake Ups Allowance are constant and set
706   // here. The Wake Up Interval is set in UpdateWakeUpBudgetPools(), based on
707   // current state.
708   for (WakeUpBudgetPool* pool : AllWakeUpBudgetPools())
709     pool->SetWakeUpDuration(kThrottledWakeUpDuration);
710 
711   if (IsIntensiveWakeUpThrottlingEnabled()) {
712     same_origin_intensive_wake_up_budget_pool_
713         ->AllowUnalignedWakeUpIfNoRecentWakeUp();
714   }
715 
716   UpdateWakeUpBudgetPools(lazy_now);
717 }
718 
OnThrottlingReported(base::TimeDelta throttling_duration)719 void PageSchedulerImpl::OnThrottlingReported(
720     base::TimeDelta throttling_duration) {
721   if (throttling_duration < kMinimalBackgroundThrottlingDurationToReport)
722     return;
723 
724   if (reported_background_throttling_since_navigation_)
725     return;
726   reported_background_throttling_since_navigation_ = true;
727 
728   String message = String::Format(
729       "Timer tasks have taken too much time while the page was in the "
730       "background. "
731       "As a result, they have been deferred for %.3f seconds. "
732       "See https://www.chromestatus.com/feature/6172836527865856 "
733       "for more details",
734       throttling_duration.InSecondsF());
735 
736   delegate_->ReportIntervention(message);
737 }
738 
UpdatePolicyOnVisibilityChange(NotificationPolicy notification_policy)739 void PageSchedulerImpl::UpdatePolicyOnVisibilityChange(
740     NotificationPolicy notification_policy) {
741   base::sequence_manager::LazyNow lazy_now(
742       main_thread_scheduler_->tick_clock());
743 
744   if (page_visibility_ == PageVisibilityState::kVisible) {
745     is_cpu_time_throttled_ = false;
746     do_throttle_cpu_time_callback_.Cancel();
747     UpdateCPUTimeBudgetPool(&lazy_now);
748 
749     are_wake_ups_intensively_throttled_ = false;
750     do_intensively_throttle_wake_ups_callback_.Cancel();
751     UpdateWakeUpBudgetPools(&lazy_now);
752   } else {
753     if (cpu_time_budget_pool_) {
754       main_thread_scheduler_->ControlTaskRunner()->PostDelayedTask(
755           FROM_HERE, do_throttle_cpu_time_callback_.GetCallback(),
756           kThrottlingDelayAfterBackgrounding);
757     }
758     if (IsIntensiveWakeUpThrottlingEnabled()) {
759       main_thread_scheduler_->ControlTaskRunner()->PostDelayedTask(
760           FROM_HERE, do_intensively_throttle_wake_ups_callback_.GetCallback(),
761           GetIntensiveWakeUpThrottlingGracePeriod());
762     }
763   }
764   if (notification_policy == NotificationPolicy::kNotifyFrames)
765     NotifyFrames();
766 }
767 
DoThrottleCPUTime()768 void PageSchedulerImpl::DoThrottleCPUTime() {
769   do_throttle_cpu_time_callback_.Cancel();
770   is_cpu_time_throttled_ = true;
771 
772   base::sequence_manager::LazyNow lazy_now(
773       main_thread_scheduler_->tick_clock());
774   UpdateCPUTimeBudgetPool(&lazy_now);
775   NotifyFrames();
776 }
777 
DoIntensivelyThrottleWakeUps()778 void PageSchedulerImpl::DoIntensivelyThrottleWakeUps() {
779   DCHECK(IsIntensiveWakeUpThrottlingEnabled());
780 
781   do_intensively_throttle_wake_ups_callback_.Cancel();
782   are_wake_ups_intensively_throttled_ = true;
783 
784   base::sequence_manager::LazyNow lazy_now(
785       main_thread_scheduler_->tick_clock());
786   UpdateWakeUpBudgetPools(&lazy_now);
787   NotifyFrames();
788 }
789 
UpdateCPUTimeBudgetPool(base::sequence_manager::LazyNow * lazy_now)790 void PageSchedulerImpl::UpdateCPUTimeBudgetPool(
791     base::sequence_manager::LazyNow* lazy_now) {
792   if (!cpu_time_budget_pool_)
793     return;
794 
795   if (is_cpu_time_throttled_ && !opted_out_from_aggressive_throttling_) {
796     cpu_time_budget_pool_->EnableThrottling(lazy_now);
797   } else {
798     cpu_time_budget_pool_->DisableThrottling(lazy_now);
799   }
800 }
801 
OnTitleOrFaviconUpdated()802 void PageSchedulerImpl::OnTitleOrFaviconUpdated() {
803   if (!HasWakeUpBudgetPools())
804     return;
805 
806   if (are_wake_ups_intensively_throttled_ &&
807       !opted_out_from_aggressive_throttling_) {
808     // When the title of favicon is updated, intensive throttling is inhibited
809     // for same-origin frames. This enables alternating effects meant to grab
810     // the user's attention. Cross-origin frames are not affected, since they
811     // shouldn't be able to observe that the page title or favicon was updated.
812     base::TimeDelta time_to_inhibit_intensive_throttling =
813         GetTimeToInhibitIntensiveThrottlingOnTitleOrFaviconUpdate();
814 
815     if (time_to_inhibit_intensive_throttling.is_zero()) {
816       // No inhibiting to be done.
817       return;
818     }
819 
820     had_recent_title_or_favicon_update_ = true;
821     base::sequence_manager::LazyNow lazy_now(
822         main_thread_scheduler_->tick_clock());
823     UpdateWakeUpBudgetPools(&lazy_now);
824 
825     // Re-enable intensive throttling from a delayed task.
826     reset_had_recent_title_or_favicon_update_.Cancel();
827     main_thread_scheduler_->ControlTaskRunner()->PostDelayedTask(
828         FROM_HERE, reset_had_recent_title_or_favicon_update_.GetCallback(),
829         time_to_inhibit_intensive_throttling);
830   }
831 }
832 
ResetHadRecentTitleOrFaviconUpdate()833 void PageSchedulerImpl::ResetHadRecentTitleOrFaviconUpdate() {
834   had_recent_title_or_favicon_update_ = false;
835 
836   base::sequence_manager::LazyNow lazy_now(
837       main_thread_scheduler_->tick_clock());
838   UpdateWakeUpBudgetPools(&lazy_now);
839 
840   NotifyFrames();
841 }
842 
GetIntensiveWakeUpThrottlingDuration(bool is_same_origin)843 base::TimeDelta PageSchedulerImpl::GetIntensiveWakeUpThrottlingDuration(
844     bool is_same_origin) {
845   // Title and favicon changes only affect the same_origin wake up budget pool.
846   if (is_same_origin && had_recent_title_or_favicon_update_)
847     return kDefaultThrottledWakeUpInterval;
848 
849   if (are_wake_ups_intensively_throttled_ &&
850       !opted_out_from_aggressive_throttling_)
851     return GetIntensiveWakeUpThrottlingDurationBetweenWakeUps();
852   else
853     return kDefaultThrottledWakeUpInterval;
854 }
855 
UpdateWakeUpBudgetPools(base::sequence_manager::LazyNow * lazy_now)856 void PageSchedulerImpl::UpdateWakeUpBudgetPools(
857     base::sequence_manager::LazyNow* lazy_now) {
858   if (!same_origin_intensive_wake_up_budget_pool_)
859     return;
860 
861   same_origin_intensive_wake_up_budget_pool_->SetWakeUpInterval(
862       lazy_now->Now(), GetIntensiveWakeUpThrottlingDuration(true));
863   cross_origin_intensive_wake_up_budget_pool_->SetWakeUpInterval(
864       lazy_now->Now(), GetIntensiveWakeUpThrottlingDuration(false));
865 }
866 
NotifyFrames()867 void PageSchedulerImpl::NotifyFrames() {
868   for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_) {
869     frame_scheduler->UpdatePolicy();
870   }
871 }
872 
FrameCount() const873 size_t PageSchedulerImpl::FrameCount() const {
874   return frame_schedulers_.size();
875 }
876 
SetMaxVirtualTimeTaskStarvationCount(int max_task_starvation_count)877 void PageSchedulerImpl::SetMaxVirtualTimeTaskStarvationCount(
878     int max_task_starvation_count) {
879   main_thread_scheduler_->SetMaxVirtualTimeTaskStarvationCount(
880       max_task_starvation_count);
881 }
882 
GetMainThreadScheduler() const883 MainThreadSchedulerImpl* PageSchedulerImpl::GetMainThreadScheduler() const {
884   return main_thread_scheduler_;
885 }
886 
GetAgentGroupScheduler()887 AgentGroupSchedulerImpl& PageSchedulerImpl::GetAgentGroupScheduler() {
888   return agent_group_scheduler_;
889 }
890 
IsBackgrounded() const891 bool PageSchedulerImpl::IsBackgrounded() const {
892   return page_visibility_ == PageVisibilityState::kHidden && !IsAudioPlaying();
893 }
894 
ShouldFreezePage() const895 bool PageSchedulerImpl::ShouldFreezePage() const {
896   if (!base::FeatureList::IsEnabled(blink::features::kStopInBackground))
897     return false;
898   return IsBackgrounded();
899 }
900 
OnLocalMainFrameNetworkAlmostIdle()901 void PageSchedulerImpl::OnLocalMainFrameNetworkAlmostIdle() {
902   if (!freeze_on_network_idle_enabled_)
903     return;
904 
905   if (!ShouldFreezePage())
906     return;
907 
908   if (IsFrozen())
909     return;
910 
911   // If delay_for_background_and_network_idle_tab_freezing_ passes after
912   // the page is not visible, we should freeze the page.
913   base::TimeDelta passed = main_thread_scheduler_->GetTickClock()->NowTicks() -
914                            page_visibility_changed_time_;
915   if (passed < delay_for_background_and_network_idle_tab_freezing_)
916     return;
917 
918   SetPageFrozenImpl(true, NotificationPolicy::kNotifyFrames);
919 }
920 
DoFreezePage()921 void PageSchedulerImpl::DoFreezePage() {
922   DCHECK(ShouldFreezePage());
923 
924   if (freeze_on_network_idle_enabled_) {
925     DCHECK(delegate_);
926     base::TimeDelta passed =
927         main_thread_scheduler_->GetTickClock()->NowTicks() -
928         page_visibility_changed_time_;
929     // The page will be frozen if:
930     // (1) the main frame is remote, or,
931     // (2) the local main frame's network is almost idle, or,
932     // (3) delay_for_background_tab passes after the page is not visible.
933     if (!delegate_->LocalMainFrameNetworkIsAlmostIdle() &&
934         passed < delay_for_background_tab_freezing_) {
935       main_thread_scheduler_->ControlTaskRunner()->PostDelayedTask(
936           FROM_HERE, do_freeze_page_callback_.GetCallback(),
937           delay_for_background_tab_freezing_ - passed);
938       return;
939     }
940   }
941 
942   SetPageFrozenImpl(true, NotificationPolicy::kNotifyFrames);
943 }
944 
GetPageLifecycleState() const945 PageLifecycleState PageSchedulerImpl::GetPageLifecycleState() const {
946   return page_lifecycle_state_tracker_->GetPageLifecycleState();
947 }
948 
PageLifecycleStateTracker(PageSchedulerImpl * page_scheduler_impl,PageLifecycleState state)949 PageSchedulerImpl::PageLifecycleStateTracker::PageLifecycleStateTracker(
950     PageSchedulerImpl* page_scheduler_impl,
951     PageLifecycleState state)
952     : page_scheduler_impl_(page_scheduler_impl),
953       current_state_(kDefaultPageLifecycleState) {
954   SetPageLifecycleState(state);
955 }
956 
SetPageLifecycleState(PageLifecycleState new_state)957 void PageSchedulerImpl::PageLifecycleStateTracker::SetPageLifecycleState(
958     PageLifecycleState new_state) {
959   if (new_state == current_state_)
960     return;
961   base::Optional<PageLifecycleStateTransition> transition =
962       ComputePageLifecycleStateTransition(current_state_, new_state);
963   if (transition) {
964     UMA_HISTOGRAM_ENUMERATION(
965         kHistogramPageLifecycleStateTransition,
966         static_cast<PageLifecycleStateTransition>(transition.value()));
967   }
968   current_state_ = new_state;
969 }
970 
971 PageLifecycleState
GetPageLifecycleState() const972 PageSchedulerImpl::PageLifecycleStateTracker::GetPageLifecycleState() const {
973   return current_state_;
974 }
975 
976 // static
977 base::Optional<PageSchedulerImpl::PageLifecycleStateTransition>
978 PageSchedulerImpl::PageLifecycleStateTracker::
ComputePageLifecycleStateTransition(PageLifecycleState old_state,PageLifecycleState new_state)979     ComputePageLifecycleStateTransition(PageLifecycleState old_state,
980                                         PageLifecycleState new_state) {
981   switch (old_state) {
982     case PageLifecycleState::kUnknown:
983       // We don't track the initial transition.
984       return base::nullopt;
985     case PageLifecycleState::kActive:
986       switch (new_state) {
987         case PageLifecycleState::kHiddenForegrounded:
988           return PageLifecycleStateTransition::kActiveToHiddenForegrounded;
989         case PageLifecycleState::kHiddenBackgrounded:
990           return PageLifecycleStateTransition::kActiveToHiddenBackgrounded;
991         default:
992           NOTREACHED();
993           return base::nullopt;
994       }
995     case PageLifecycleState::kHiddenForegrounded:
996       switch (new_state) {
997         case PageLifecycleState::kActive:
998           return PageLifecycleStateTransition::kHiddenForegroundedToActive;
999         case PageLifecycleState::kHiddenBackgrounded:
1000           return PageLifecycleStateTransition::
1001               kHiddenForegroundedToHiddenBackgrounded;
1002         case PageLifecycleState::kFrozen:
1003           return PageLifecycleStateTransition::kHiddenForegroundedToFrozen;
1004         default:
1005           NOTREACHED();
1006           return base::nullopt;
1007       }
1008     case PageLifecycleState::kHiddenBackgrounded:
1009       switch (new_state) {
1010         case PageLifecycleState::kActive:
1011           return PageLifecycleStateTransition::kHiddenBackgroundedToActive;
1012         case PageLifecycleState::kHiddenForegrounded:
1013           return PageLifecycleStateTransition::
1014               kHiddenBackgroundedToHiddenForegrounded;
1015         case PageLifecycleState::kFrozen:
1016           return PageLifecycleStateTransition::kHiddenBackgroundedToFrozen;
1017         default:
1018           NOTREACHED();
1019           return base::nullopt;
1020       }
1021     case PageLifecycleState::kFrozen:
1022       switch (new_state) {
1023         case PageLifecycleState::kActive:
1024           return PageLifecycleStateTransition::kFrozenToActive;
1025         case PageLifecycleState::kHiddenForegrounded:
1026           return PageLifecycleStateTransition::kFrozenToHiddenForegrounded;
1027         case PageLifecycleState::kHiddenBackgrounded:
1028           return PageLifecycleStateTransition::kFrozenToHiddenBackgrounded;
1029         default:
1030           NOTREACHED();
1031           return base::nullopt;
1032       }
1033   }
1034 }
1035 
SelectFrameForUkmAttribution()1036 FrameSchedulerImpl* PageSchedulerImpl::SelectFrameForUkmAttribution() {
1037   for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_) {
1038     if (frame_scheduler->GetUkmRecorder())
1039       return frame_scheduler;
1040   }
1041   return nullptr;
1042 }
1043 
CreateWebScopedVirtualTimePauser(const String & name,WebScopedVirtualTimePauser::VirtualTaskDuration duration)1044 WebScopedVirtualTimePauser PageSchedulerImpl::CreateWebScopedVirtualTimePauser(
1045     const String& name,
1046     WebScopedVirtualTimePauser::VirtualTaskDuration duration) {
1047   return WebScopedVirtualTimePauser(main_thread_scheduler_, duration, name);
1048 }
1049 
HasWakeUpBudgetPools() const1050 bool PageSchedulerImpl::HasWakeUpBudgetPools() const {
1051   // All WakeUpBudgetPools should be initialized together.
1052   DCHECK_EQ(!!normal_wake_up_budget_pool_,
1053             !!same_origin_intensive_wake_up_budget_pool_);
1054   DCHECK_EQ(!!normal_wake_up_budget_pool_,
1055             !!cross_origin_intensive_wake_up_budget_pool_);
1056 
1057   return !!normal_wake_up_budget_pool_;
1058 }
1059 
1060 std::array<WakeUpBudgetPool*, PageSchedulerImpl::kNumWakeUpBudgetPools>
AllWakeUpBudgetPools()1061 PageSchedulerImpl::AllWakeUpBudgetPools() {
1062   return {normal_wake_up_budget_pool_,
1063           same_origin_intensive_wake_up_budget_pool_,
1064           cross_origin_intensive_wake_up_budget_pool_};
1065 }
1066 
1067 // static
1068 const char PageSchedulerImpl::kHistogramPageLifecycleStateTransition[] =
1069     "PageScheduler.PageLifecycleStateTransition";
1070 
1071 }  // namespace scheduler
1072 }  // namespace blink
1073