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