File: tab_loader.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (321 lines) | stat: -rw-r--r-- 14,010 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_SESSIONS_TAB_LOADER_H_
#define CHROME_BROWSER_SESSIONS_TAB_LOADER_H_

#include <utility>
#include <vector>

#include "base/containers/flat_set.h"
#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/memory_pressure_listener.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "base/time/tick_clock.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chrome/browser/resource_coordinator/tab_load_tracker.h"
#include "chrome/browser/sessions/session_restore_delegate.h"
#include "chrome/browser/sessions/tab_loader_delegate.h"

class TabLoaderTester;

// TabLoader is responsible for loading tabs after session restore has finished
// creating all the tabs. Tabs are loaded after a previously started tab
// finishes loading or a timeout is reached. If the timeout is reached before a
// tab finishes loading the timeout delay is doubled.
//
// TabLoader keeps a reference to itself when it's loading. When it has finished
// loading, it drops the reference. If another profile is restored while the
// TabLoader is loading, it will schedule its tabs to get loaded by the same
// TabLoader. When doing the scheduling, it holds a reference to the TabLoader.
// This is not part of SessionRestoreImpl so that synchronous destruction
// of SessionRestoreImpl doesn't have timing problems.
//
// TabLoader is effectively a state machine that guides session/tab restored
// tabs through being unloaded, to loading and finally to their loaded state. It
// does this while respecting memory pressure, a maximum simultaneous number of
// tabs loading in parallel, and a maximum tab load timeouts. At most one
// TabLoader exists at a moment; it owns itself and destroys itself once all
// tabs posted to it have been loaded.
//
// Beyond requesting tabs to load TabLoader maintains the following invariant:
//
// - If loads are ongoing and there are future tabs to load, then a timeout
//   timer is running.
//
// The general principle is that before returning control to the caller,
// the invariant is maintained. Extra care is taken in functions that may
// can cause reentrancy as they need to ensure the invariant is satisfied before
// passing control to the external code.
//
// Since the conditions for self-destroying can occur while deeply nested in our
// own code an entrance count is maintained to ensure it only happens on the way
// out of the outermost function.
class TabLoader : public base::RefCounted<TabLoader>,
                  public TabLoaderCallback,
                  public resource_coordinator::TabLoadTracker::Observer {
 public:
  // Helper class used for tracking reentrancy and performing lifetime
  // management. See implementation for full details.
  class ReentrancyHelper;

  using RestoredTab = SessionRestoreDelegate::RestoredTab;

  TabLoader(const TabLoader&) = delete;
  TabLoader& operator=(const TabLoader&) = delete;

  // Called to start restoring tabs.
  static void RestoreTabs(const std::vector<RestoredTab>& tabs,
                          const base::TimeTicks& restore_started);

 private:
  friend class base::RefCounted<TabLoader>;
  friend class ReentrancyHelper;

  // Allows access from various unittests.
  friend class TabLoaderTester;

  // Used for storing tabs under our control that have started loading. The
  // set of these is sorted by |loading_start_time| and used to manage the
  // loading timeout timer.
  struct LoadingTab {
    base::TimeTicks loading_start_time;
    // This field is not a raw_ptr<> because it was filtered by the rewriter
    // for: #union
    RAW_PTR_EXCLUSION content::WebContents* contents;

    // For use with sorted STL containers.
    bool operator<(const LoadingTab& rhs) const {
      return std::tie(loading_start_time, contents) <
             std::tie(rhs.loading_start_time, rhs.contents);
    }
  };

  using LoadingTabSet = base::flat_set<LoadingTab>;
  using TabSet = base::flat_set<raw_ptr<content::WebContents, CtnExperimental>>;
  using TabVector = std::vector<std::pair<float, content::WebContents*>>;

  TabLoader();
  ~TabLoader() override;

  // TabLoaderCallback:
  void SetTabLoadingEnabled(bool loading_enabled) override;
  void NotifyTabScoreChanged(content::WebContents* contents,
                             float score) override;

  // This is invoked once by RestoreTabs to start loading.
  void StartLoading(const std::vector<RestoredTab>& tabs);

  // resource_coordinator::TabLoadTracker::Observer implementation:
  void OnLoadingStateChange(content::WebContents* contents,
                            LoadingState old_loading_state,
                            LoadingState new_loading_state) override;
  void OnStopTracking(content::WebContents* contents,
                      LoadingState loading_state) override;

  // React to memory pressure by stopping to load any more tabs.
  void OnMemoryPressure(
      base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);

  // Determines whether or not tab loading should stop early due to external
  // factors.
  bool ShouldStopLoadingTabs() const;

  // Determines the number of tab loads that can safely be started at the
  // moment.
  size_t GetMaxNewTabLoads() const;

  // Adds a tab that we are responsible for to one of the |tabs_*| containers.
  // Can invalidate self-destroy and timer invariants.
  void AddTab(content::WebContents* contents);

  // Removes the tab from the set of tabs to load and list of tabs we're waiting
  // to get a load from. Can invalidate self-destroy and timer invariants.
  void RemoveTab(content::WebContents* contents);

  // Moves the tab from |tabs_to_load_| to |tabs_load_initiated_|. Can
  // invalidate self-destroy and timer invariants.
  void MarkTabAsLoadInitiated(content::WebContents* contents);

  // Moves the tab from |tabs_to_load_| or |tabs_load_initiated_| to
  // |tabs_loading_|. Can invalidate self-destroy and timer invariants.
  void MarkTabAsLoading(content::WebContents* contents);

  // Stops tracking the tab, marking its load as deferred. This will remove it
  // from all tab tracking containers and notify the stats delegate of the
  // deferred load.
  void MarkTabAsDeferred(content::WebContents* contents);

  // Maybes loads one of more tabs. This will cause one or more tabs (up to the
  // number of open loading slots) to load, while respecting the loading slot
  // cap.
  void MaybeLoadSomeTabs();

  // Invoked from |force_load_timer_|. Doubles |force_load_delay_multiplier_|
  // and invokes |LoadNextTab| to load the next tab
  void ForceLoadTimerFired();

  // Stops loading tabs.
  void StopLoadingTabs();

  // Gets the next tab to load, returning nullptr if there are none. Note that
  // this can cause |tabs_to_load_| to be drained due to policy decision made by
  // the TabLoaderDelegate.
  content::WebContents* GetNextTabToLoad();

  // Loads the next tab and restores invariants. This should only be called if
  // there is a next tab to load. This will always start loading a next tab even
  // if the number of simultaneously loading tabs is exceeded.
  void LoadNextTab(bool due_to_timeout);

  // Returns the current load timeout period.
  base::TimeDelta GetLoadTimeoutPeriod() const;

  // Can do nothing, start a timer, or cancel a previously started timer
  // depending on whether or not one needs to be running.
  void StartTimerIfNeeded();

  // Limit the number of loaded tabs.
  // Value of 0 restores default behavior. In test mode command line flags and
  // free memory size are not taken into account.
  static void SetMaxLoadedTabCountForTesting(size_t value);

  // Sets an on construction callback for testing.
  static void SetConstructionCallbackForTesting(
      base::RepeatingCallback<void(TabLoader*)>* callback);

  // Sets the number of simultaneous loads for testing.
  void SetMaxSimultaneousLoadsForTesting(size_t loading_slots);

  // Sets the tick clock.
  void SetTickClockForTesting(base::TickClock* tick_clock);

  // Calls MaybeLoadSomeTabs, but wrapped with entry count management.
  void MaybeLoadSomeTabsForTesting();

  // Sets a tab loading enabled callback for testing.
  void SetTabLoadingEnabledCallbackForTesting(
      base::RepeatingCallback<void(bool)>* callback);

  // Returns true if loading is currently enabled, false otherwise. This checks
  // the value of |loading_enabled_| and |all_tabs_scored_|, which are each
  // independent mechanisms for disabling tab loading.
  bool IsLoadingEnabled() const;

  // Starts or stops loading as necessary, depending on the current value of
  // IsLoadingEnabled. Should only be called if IsLoadingEnabled has changed
  // values. This is called via SetAllTabsScored or SetTabLoadingEnabled.
  void OnIsLoadingEnabledChanged();

  void SetAllTabsScored(bool all_tabs_scored);

  TabVector::iterator FindTabToLoad(content::WebContents* contents);
  TabVector::const_iterator FindTabToLoad(content::WebContents* contents) const;

  // Given a position in |tabs_to_load_|, moves that element into its sorted
  // position using a bubble sort. This assumes that only data associated with
  // the particular element has changed, and that the vector is otherwise
  // sorted.
  void MoveToSortedPosition(TabVector::iterator it);

  // The number of tabs to load simultaneously. This is a soft cap in that it
  // can be exceeded by tabs that timeout, visible tabs, and user interactions
  // forcing a tab load. However, normal session restore tab loads will not kick
  // off a new load unless there is room below this cap.
  size_t MaxSimultaneousLoads() const;

  // The OS specific delegate of the TabLoader.
  std::unique_ptr<TabLoaderDelegate> delegate_;

  // Listens for system under memory pressure notifications and stops loading
  // of tabs when we start running out of memory.
  base::MemoryPressureListener memory_pressure_listener_;

  // Used for selecting which timeout to use, and to prevent additional
  // non-active tabs from being scheduled to load initially.
  bool did_one_tab_load_ = false;

  // Overrides the value of max simultaneous loads that is normally provided by
  // the policy engine.
  size_t max_simultaneous_loads_for_testing_ = 0;

  // The delay timer multiplier. See class description for details.
  size_t force_load_delay_multiplier_ = 1;

  // These two variables determine whether or not tab loading is enabled.
  bool loading_enabled_ = true;
  bool all_tabs_scored_ = true;

  // The following 3 containers are mutually exclusive. A tab will be in at most
  // one of them at any moment.

  // The tabs that have been restored for which we need to schedule loads. This
  // does not include "active" tabs. Tabs transition from this container to
  // |tabs_load_initiated_|, or are removed from this container.
  TabVector tabs_to_load_;

  // The set of tabs that we have initiated loading, but for which we're
  // waiting for TabLoadTracker to tell us has actually commenced (network
  // activity). This is used to ensure we don't start loading too many tabs.
  // Tabs are removed from this container in two ways: if they were observed to
  // start loading they transition to |tabs_loading_|. Otherwise (closed before
  // loading starts) they stop being tracked by this TabLoader.
  TabSet tabs_load_initiated_;

  // The set of tabs that we have started loading, along with the times at which
  // their loads started. This is used to drive load timeout logic. Tabs
  // eventually transition out of this container. When the 3 tab containers are
  // empty the TabLoader detaches from being the shared TabLoader and destroys
  // itself.
  LoadingTabSet tabs_loading_;

  // The number of tabs that were passed into this TabLoader that have been
  // observed starting to load, or for which we explicitly initiated the load.
  // This is monotonically increasing, and can never exceed the combined number
  // of tabs passed into this TabLoader via StartLoading(). This is only used in
  // order to support a combined maximum total number of tab loads for testing.
  size_t scheduled_to_load_count_ = 0;

  // Timer used to force progress despite tabs that take too long to load.
  base::OneShotTimer force_load_timer_;

  // The time at which the timer is scheduled to fire. Used to minimize
  // restarts of the timer. This should be default initialized when the timer is
  // not running.
  base::TimeTicks force_load_time_;

  // The time at which tab loading was last disabled. This is used to extend
  // time outs across "tab loading disabled" time periods (tab loading is
  // disabled due to loss of network connection, or while waiting for tab
  // ordering scores to be calculated).
  base::TimeTicks tab_loading_disabled_time_;

  // For keeping TabLoader alive while it's loading even if no
  // SessionRestoreImpls reference it.
  scoped_refptr<TabLoader> this_retainer_;

  // The tick clock used by this class. This is used as a testing seam. If not
  // overridden it defaults to a base::DefaultTickClock.
  raw_ptr<const base::TickClock> clock_;

  // Holds a pointer to the active tab loader, if one exists. Overlapping
  // session restores will be handled by the same tab loader.
  static TabLoader* shared_tab_loader_;

  // Used to prevent self-destroys while in nested calls, and to initiate
  // self-destroying from the outermost scope only. This is managed by the
  // ReentrancyHelper, and indicates the number of times that the current object
  // has been reentered. Only functions that are directly invoked by external
  // callers are counted.
  size_t reentry_depth_ = 0;

  // Callback that is invoked by calls to SetTabLoadingEnabled.
  raw_ptr<base::RepeatingCallback<void(bool)>> tab_loading_enabled_callback_ =
      nullptr;
};

#endif  // CHROME_BROWSER_SESSIONS_TAB_LOADER_H_