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 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_PAGE_SCHEDULER_IMPL_H_ 6 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_PAGE_SCHEDULER_IMPL_H_ 7 8 #include <memory> 9 10 #include "base/macros.h" 11 #include "base/memory/weak_ptr.h" 12 #include "base/observer_list.h" 13 #include "base/optional.h" 14 #include "base/task/sequence_manager/task_queue.h" 15 #include "base/time/time.h" 16 #include "third_party/blink/renderer/platform/platform_export.h" 17 #include "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h" 18 #include "third_party/blink/renderer/platform/scheduler/common/tracing_helper.h" 19 #include "third_party/blink/renderer/platform/scheduler/main_thread/agent_group_scheduler_impl.h" 20 #include "third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.h" 21 #include "third_party/blink/renderer/platform/scheduler/main_thread/page_visibility_state.h" 22 #include "third_party/blink/renderer/platform/scheduler/public/page_lifecycle_state.h" 23 #include "third_party/blink/renderer/platform/scheduler/public/page_scheduler.h" 24 #include "third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h" 25 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" 26 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" 27 #include "third_party/blink/renderer/platform/wtf/hash_set.h" 28 29 namespace base { 30 namespace trace_event { 31 class BlameContext; 32 class TracedValue; 33 } // namespace trace_event 34 } // namespace base 35 36 namespace blink { 37 namespace scheduler { 38 namespace page_scheduler_impl_unittest { 39 class PageSchedulerImplTest; 40 class PageSchedulerImplPageTransitionTest; 41 class 42 PageSchedulerImplPageTransitionTest_PageLifecycleStateTransitionMetric_Test; 43 } // namespace page_scheduler_impl_unittest 44 45 class CPUTimeBudgetPool; 46 class FrameSchedulerImpl; 47 class MainThreadSchedulerImpl; 48 class MainThreadTaskQueue; 49 class WakeUpBudgetPool; 50 51 class PLATFORM_EXPORT PageSchedulerImpl : public PageScheduler { 52 public: 53 // Interval between throttled wake ups, when intensive throttling is disabled. 54 static constexpr base::TimeDelta kDefaultThrottledWakeUpInterval = 55 base::TimeDelta::FromSeconds(1); 56 57 PageSchedulerImpl(PageScheduler::Delegate*, AgentGroupSchedulerImpl&); 58 59 ~PageSchedulerImpl() override; 60 61 // PageScheduler implementation: 62 void OnTitleOrFaviconUpdated() override; 63 void SetPageVisible(bool page_visible) override; 64 void SetPageFrozen(bool) override; 65 void SetPageBackForwardCached(bool) override; 66 void SetKeepActive(bool) override; 67 bool IsMainFrameLocal() const override; 68 void SetIsMainFrameLocal(bool is_local) override; 69 void OnLocalMainFrameNetworkAlmostIdle() override; GetStoredInBackForwardCacheTimestamp()70 base::TimeTicks GetStoredInBackForwardCacheTimestamp() { 71 return stored_in_back_forward_cache_timestamp_; 72 } IsInBackForwardCache()73 bool IsInBackForwardCache() const override { 74 return is_stored_in_back_forward_cache_; 75 } has_ipc_detection_enabled()76 bool has_ipc_detection_enabled() { return has_ipc_detection_enabled_; } 77 78 std::unique_ptr<FrameScheduler> CreateFrameScheduler( 79 FrameScheduler::Delegate* delegate, 80 BlameContext*, 81 FrameScheduler::FrameType) override; 82 base::TimeTicks EnableVirtualTime() override; 83 void DisableVirtualTimeForTesting() override; 84 bool VirtualTimeAllowedToAdvance() const override; 85 void SetVirtualTimePolicy(VirtualTimePolicy) override; 86 void SetInitialVirtualTime(base::Time time) override; 87 void SetInitialVirtualTimeOffset(base::TimeDelta offset) override; 88 void GrantVirtualTimeBudget( 89 base::TimeDelta budget, 90 base::OnceClosure budget_exhausted_callback) override; 91 void SetMaxVirtualTimeTaskStarvationCount( 92 int max_task_starvation_count) override; 93 void AudioStateChanged(bool is_audio_playing) override; 94 bool IsAudioPlaying() const override; 95 bool IsExemptFromBudgetBasedThrottling() const override; 96 bool OptedOutFromAggressiveThrottlingForTest() const override; 97 bool RequestBeginMainFrameNotExpected(bool new_state) override; 98 WebScopedVirtualTimePauser CreateWebScopedVirtualTimePauser( 99 const WTF::String& name, 100 WebScopedVirtualTimePauser::VirtualTaskDuration) override; 101 102 // Virtual for testing. 103 virtual void ReportIntervention(const String& message); 104 105 bool IsPageVisible() const; 106 bool IsFrozen() const; 107 bool OptedOutFromAggressiveThrottling() const; 108 // Returns whether CPU time is throttled for the page. Note: This is 109 // independent from wake up rate throttling. 110 bool IsCPUTimeThrottled() const; 111 bool KeepActive() const; 112 113 bool IsLoading() const; 114 115 // An "ordinary" PageScheduler is responsible for a fully-featured page 116 // owned by a web view. 117 bool IsOrdinary() const; 118 119 MainThreadSchedulerImpl* GetMainThreadScheduler() const; 120 AgentGroupSchedulerImpl& GetAgentGroupScheduler() override; 121 122 void Unregister(FrameSchedulerImpl*); 123 void OnNavigation(); 124 125 void OnThrottlingStatusUpdated(); 126 127 void OnTraceLogEnabled(); 128 129 // Virtual for testing. 130 virtual bool IsWaitingForMainFrameContentfulPaint() const; 131 virtual bool IsWaitingForMainFrameMeaningfulPaint() const; 132 133 // Return a number of child web frame schedulers for this PageScheduler. 134 size_t FrameCount() const; 135 136 PageLifecycleState GetPageLifecycleState() const; 137 138 void SetUpIPCTaskDetection(); 139 // This flag tracks whether or not IPC tasks are tracked if they are posted to 140 // frames or pages that are stored in the back-forward cache 141 bool has_ipc_detection_enabled_ = false; 142 143 // Generally UKMs are associated with the main frame of a page, but the 144 // implementation allows to request a recorder from any local frame with 145 // the same result (e.g. for OOPIF support), therefore we need to select 146 // any frame here. 147 // Note that selecting main frame doesn't work for OOPIFs where the main 148 // frame it not a local one. 149 FrameSchedulerImpl* SelectFrameForUkmAttribution(); 150 151 void AsValueInto(base::trace_event::TracedValue* state) const; 152 GetWeakPtr()153 base::WeakPtr<PageSchedulerImpl> GetWeakPtr() { 154 return weak_factory_.GetWeakPtr(); 155 } 156 157 private: 158 friend class FrameSchedulerImpl; 159 friend class page_scheduler_impl_unittest::PageSchedulerImplTest; 160 friend class page_scheduler_impl_unittest:: 161 PageSchedulerImplPageTransitionTest; 162 friend class page_scheduler_impl_unittest:: 163 PageSchedulerImplPageTransitionTest_PageLifecycleStateTransitionMetric_Test; 164 165 enum class AudioState { 166 kSilent, 167 kAudible, 168 kRecentlyAudible, 169 }; 170 171 enum class NotificationPolicy { kNotifyFrames, kDoNotNotifyFrames }; 172 173 // This enum is used for a histogram and should not be renumbered. 174 // It tracks permissible page state transitions between PageLifecycleStates. 175 // We allow all transitions except for visible to frozen and self transitions. 176 enum class PageLifecycleStateTransition { 177 kActiveToHiddenForegrounded = 0, 178 kActiveToHiddenBackgrounded = 1, 179 kHiddenForegroundedToActive = 2, 180 kHiddenForegroundedToHiddenBackgrounded = 3, 181 kHiddenForegroundedToFrozen = 4, 182 kHiddenBackgroundedToActive = 5, 183 kHiddenBackgroundedToHiddenForegrounded = 6, 184 kHiddenBackgroundedToFrozen = 7, 185 kFrozenToActive = 8, 186 kFrozenToHiddenForegrounded = 9, 187 kFrozenToHiddenBackgrounded = 10, 188 kMaxValue = kFrozenToHiddenBackgrounded, 189 }; 190 191 class PageLifecycleStateTracker { 192 USING_FAST_MALLOC(PageLifecycleStateTracker); 193 194 public: 195 explicit PageLifecycleStateTracker(PageSchedulerImpl*, PageLifecycleState); 196 ~PageLifecycleStateTracker() = default; 197 198 void SetPageLifecycleState(PageLifecycleState); 199 PageLifecycleState GetPageLifecycleState() const; 200 201 private: 202 static base::Optional<PageLifecycleStateTransition> 203 ComputePageLifecycleStateTransition(PageLifecycleState old_state, 204 PageLifecycleState new_state); 205 206 static void RecordPageLifecycleStateTransition( 207 PageLifecycleStateTransition); 208 209 PageSchedulerImpl* page_scheduler_impl_; 210 PageLifecycleState current_state_; 211 212 DISALLOW_COPY_AND_ASSIGN(PageLifecycleStateTracker); 213 }; 214 215 void RegisterFrameSchedulerImpl(FrameSchedulerImpl* frame_scheduler); 216 217 // A page cannot be throttled or frozen 30 seconds after playing audio. 218 // 219 // This used to be 5 seconds, which was barely enough to cover the time of 220 // silence during which a logo and button are shown after a YouTube ad. Since 221 // most pages don't play audio in background, it was decided that the delay 222 // can be increased to 30 seconds without significantly affecting performance. 223 static constexpr base::TimeDelta kRecentAudioDelay = 224 base::TimeDelta::FromSeconds(30); 225 226 static const char kHistogramPageLifecycleStateTransition[]; 227 228 // Support not issuing a notification to frames when we disable freezing as 229 // a part of foregrounding the page. 230 void SetPageFrozenImpl(bool frozen, NotificationPolicy notification_policy); 231 232 // Adds or removes a |task_queue| from the WakeUpBudgetPool associated with 233 // |frame_origin_type|. When the FrameOriginType of a FrameScheduler changes, 234 // it should remove all its TaskQueues from their current WakeUpBudgetPool and 235 // add them back to the WakeUpBudgetPool appropriate for the new 236 // FrameOriginType. 237 void AddQueueToWakeUpBudgetPool(MainThreadTaskQueue* task_queue, 238 FrameOriginType frame_origin_type, 239 base::sequence_manager::LazyNow* lazy_now); 240 void RemoveQueueFromWakeUpBudgetPool( 241 MainThreadTaskQueue* task_queue, 242 FrameOriginType frame_origin_type, 243 base::sequence_manager::LazyNow* lazy_now); 244 // Returns the WakeUpBudgetPool to use for |task_queue| which belongs to a 245 // frame with |frame_origin_type|. 246 WakeUpBudgetPool* GetWakeUpBudgetPool(MainThreadTaskQueue* task_queue, 247 FrameOriginType frame_origin_type); 248 // Initializes WakeUpBudgetPools, if not already initialized. 249 void MaybeInitializeWakeUpBudgetPools( 250 base::sequence_manager::LazyNow* lazy_now); 251 252 CPUTimeBudgetPool* background_cpu_time_budget_pool(); 253 void MaybeInitializeBackgroundCPUTimeBudgetPool( 254 base::sequence_manager::LazyNow* lazy_now); 255 256 void OnThrottlingReported(base::TimeDelta throttling_duration); 257 258 // Depending on page visibility, either turns throttling off, or schedules a 259 // call to enable it after a grace period. 260 void UpdatePolicyOnVisibilityChange(NotificationPolicy notification_policy); 261 262 // Adjusts settings of budget pools depending on current state of the page. 263 void UpdateCPUTimeBudgetPool(base::sequence_manager::LazyNow* lazy_now); 264 void UpdateWakeUpBudgetPools(base::sequence_manager::LazyNow* lazy_now); 265 base::TimeDelta GetIntensiveWakeUpThrottlingDuration(bool is_same_origin); 266 267 // Callback for marking page is silent after a delay since last audible 268 // signal. 269 void OnAudioSilent(); 270 271 // Callbacks for adjusting the settings of a budget pool after a delay. 272 // TODO(altimin): Trigger throttling depending on the loading state 273 // of the page. 274 void DoThrottleCPUTime(); 275 void DoIntensivelyThrottleWakeUps(); 276 void ResetHadRecentTitleOrFaviconUpdate(); 277 278 // Notify frames that the page scheduler state has been updated. 279 void NotifyFrames(); 280 281 void EnableThrottling(); 282 283 // Returns true if the page is backgrounded, false otherwise. A page is 284 // considered backgrounded if it is both not visible and not playing audio. 285 bool IsBackgrounded() const; 286 287 // Returns true if the page should be frozen after delay, which happens if 288 // IsBackgrounded() and freezing is enabled. 289 bool ShouldFreezePage() const; 290 291 // Callback for freezing the page. Freezing must be enabled and the page must 292 // be freezable. 293 void DoFreezePage(); 294 295 // Returns true if WakeUpBudgetPools were initialized. 296 bool HasWakeUpBudgetPools() const; 297 298 // Returns all WakeUpBudgetPools owned by this PageSchedulerImpl. 299 static constexpr int kNumWakeUpBudgetPools = 3; 300 std::array<WakeUpBudgetPool*, kNumWakeUpBudgetPools> AllWakeUpBudgetPools(); 301 302 TraceableVariableController tracing_controller_; 303 HashSet<FrameSchedulerImpl*> frame_schedulers_; 304 MainThreadSchedulerImpl* main_thread_scheduler_; 305 AgentGroupSchedulerImpl& agent_group_scheduler_; 306 307 PageVisibilityState page_visibility_; 308 base::TimeTicks page_visibility_changed_time_; 309 AudioState audio_state_; 310 bool is_frozen_; 311 bool reported_background_throttling_since_navigation_; 312 bool opted_out_from_aggressive_throttling_; 313 bool nested_runloop_; 314 bool is_main_frame_local_; 315 bool is_cpu_time_throttled_; 316 bool are_wake_ups_intensively_throttled_; 317 bool keep_active_; 318 bool had_recent_title_or_favicon_update_; 319 CPUTimeBudgetPool* cpu_time_budget_pool_ = nullptr; 320 321 // Wake up budget pools for each throttling scenario: 322 // 323 // Same-origin frame Cross-origin frame 324 // Normal throttling only 1 1 325 // Normal and intensive throttling 2 3 326 // 327 // 1: This pool allows 1-second aligned wake ups. 328 WakeUpBudgetPool* normal_wake_up_budget_pool_ = nullptr; 329 // 2: This pool allows 1-second aligned wake ups if the page is not 330 // intensively throttled of if there hasn't been a wake up in the last 331 // minute. Otherwise, it allows 1-minute aligned wake ups. 332 WakeUpBudgetPool* same_origin_intensive_wake_up_budget_pool_ = nullptr; 333 // 3: This pool allows 1-second aligned wake ups if the page is not 334 // intensively throttled. Otherwise, it allows 1-minute aligned wake ups. 335 // 336 // Unlike |same_origin_intensive_wake_up_budget_pool_|, this pool does not 337 // allow a 1-second aligned wake up when there hasn't been a wake up in the 338 // last minute. This is to prevent frames from different origins from 339 // learning about each other. Concretely, this means that 340 // MaybeInitializeWakeUpBudgetPools() does not invoke 341 // AllowUnalignedWakeUpIfNoRecentWakeUp() on this pool. 342 WakeUpBudgetPool* cross_origin_intensive_wake_up_budget_pool_ = nullptr; 343 344 PageScheduler::Delegate* delegate_; 345 CancelableClosureHolder do_throttle_cpu_time_callback_; 346 CancelableClosureHolder do_intensively_throttle_wake_ups_callback_; 347 CancelableClosureHolder reset_had_recent_title_or_favicon_update_; 348 CancelableClosureHolder on_audio_silent_closure_; 349 CancelableClosureHolder do_freeze_page_callback_; 350 const base::TimeDelta delay_for_background_tab_freezing_; 351 352 // Whether a background page can be frozen before 353 // |delay_for_background_tab_freezing_| if network is idle. 354 const bool freeze_on_network_idle_enabled_; 355 356 // Delay after which a background page can be frozen if network is idle. 357 const base::TimeDelta delay_for_background_and_network_idle_tab_freezing_; 358 359 bool is_stored_in_back_forward_cache_ = false; 360 TaskHandle set_ipc_posted_handler_task_; 361 base::TimeTicks stored_in_back_forward_cache_timestamp_; 362 363 std::unique_ptr<PageLifecycleStateTracker> page_lifecycle_state_tracker_; 364 base::WeakPtrFactory<PageSchedulerImpl> weak_factory_{this}; 365 366 DISALLOW_COPY_AND_ASSIGN(PageSchedulerImpl); 367 }; 368 369 } // namespace scheduler 370 } // namespace blink 371 372 #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_PAGE_SCHEDULER_IMPL_H_ 373