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