File: session_restore_policy.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 (351 lines) | stat: -rw-r--r-- 14,310 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
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
// Copyright 2018 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_RESOURCE_COORDINATOR_SESSION_RESTORE_POLICY_H_
#define CHROME_BROWSER_RESOURCE_COORDINATOR_SESSION_RESTORE_POLICY_H_

#include <memory>
#include <optional>

#include "base/cancelable_callback.h"
#include "base/containers/flat_map.h"
#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/resource_coordinator/tab_manager_features.h"

namespace content {
class WebContents;
}

namespace resource_coordinator {

// An object that encapsulates session restore policy. For now this is surfaced
// to the TabLoader via TabLoaderDelegate, but eventually TabLoader will be
// merged into TabManager directly.
class SessionRestorePolicy {
 public:
  // Minimum site engagement score for a tab to be restored, if it doesn't
  // communicate in the background.
  static constexpr uint32_t kMinSiteEngagementToRestore = 15;

  // Callback that is used by the policy engine to notify its embedder (the
  // TabLoaderDelegate) of changes to tab priorities as they occur. Zero or one
  // score updates may be delivered for each contents that is added; callbacks
  // associated with an explicit WebContents will only be dispatched while the
  // associated WebContents is still being tracked.
  //
  // A callback with a nullptr WebContents is used to indicate that all tabs
  // have received final scores. This is sent at every transition from "not all
  // tabs have final scores" to "all tabs have final scores". This condition is
  // always invalidated at the addition of a new tab, and restored once (a) all
  // unscored tabs have been removed or (b) all unscored tabs receive a final
  // score. This can happen multiple times over the lifetime of a
  // SessionRestorePolicy object.
  using NotifyTabScoreChangedCallback =
      base::RepeatingCallback<void(content::WebContents*, float)>;

  // Used as a testing seam.
  class Delegate;

  SessionRestorePolicy();

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

  // Overridden for testing.
  virtual ~SessionRestorePolicy();

  size_t simultaneous_tab_loads() const { return simultaneous_tab_loads_; }

  void SetTabScoreChangedCallback(
      NotifyTabScoreChangedCallback notify_tab_score_changed_callback) {
    notify_tab_score_changed_callback_ = notify_tab_score_changed_callback;
  }

  // Notifies the policy engine of a tab (represented by |contents|) that will
  // be restored. It is expected that the |contents| already be attached to the
  // appropriate tab strip model. This returns an initial restore priority
  // score for the |contents|. The score may change asynchronously via a call
  // to the registered NotifyTabScoreChangedCallback.
  float AddTabForScoring(content::WebContents* contents);

  // Notifies the policy engine of a tab that is no longer to be restored. No
  // score change notifications will be sent for this |contents| after it has
  // been removed.
  void RemoveTabForScoring(content::WebContents* contents);

  // Returns true if the given contents should ever be loaded by
  // session restore. If this returns false then session restore should mark the
  // tab load as deferred and move onto the next tab to restore. Note that this
  // always returns true if the policy logic is disabled.
  //
  // Virtual for testing.
  virtual bool ShouldLoad(content::WebContents* contents) const;

  // Intended to be called by the policy client whenever a tab load has been
  // initiated.
  void NotifyTabLoadStarted();

  // Returns the status of the policy logic.
  bool policy_enabled() const { return policy_enabled_; }

  // Direct access to parameters for testing.
  uint32_t& MinSimultaneousTabLoadsForTesting() {
    return min_simultaneous_tab_loads_;
  }
  uint32_t& MaxSimultaneousTabLoadsForTesting() {
    return max_simultaneous_tab_loads_;
  }
  uint32_t& CoresPerSimultaneousTabLoadForTesting() {
    return cores_per_simultaneous_tab_load_;
  }
  uint32_t& MinTabsToRestoreForTesting() { return min_tabs_to_restore_; }
  uint32_t& MaxTabsToRestoreForTesting() { return max_tabs_to_restore_; }
  uint32_t& MbFreeMemoryPerTabToRestoreForTesting() {
    return mb_free_memory_per_tab_to_restore_;
  }
  base::TimeDelta& MaxTimeSinceLastUseToRestoreForTesting() {
    return max_time_since_last_use_to_restore_;
  }
  uint32_t& MinSiteEngagementToRestoreForTesting() {
    return min_site_engagement_to_restore_;
  }
  size_t& SimultaneousTabLoadsForTesting() { return simultaneous_tab_loads_; }
  void CalculateSimultaneousTabLoadsForTesting() {
    simultaneous_tab_loads_ = CalculateSimultaneousTabLoads();
  }

 protected:
  // Protected so can be exposed for unittesting.

  // Full constructor for testing.
  SessionRestorePolicy(bool policy_enabled, const Delegate* delegate);

  // Helper function for computing the number of loading slots to use. All
  // parameters are exposed for testing.
  static size_t CalculateSimultaneousTabLoads(size_t min_loads,
                                              size_t max_loads,
                                              size_t cores_per_load,
                                              size_t num_cores);

  void SetTabLoadsStartedForTesting(size_t tab_loads_started);

  void UpdateSiteEngagementScoreForTesting(content::WebContents* contents,
                                           size_t score);

 protected:
  // Holds a handful of data about a tab which is used to prioritize it during
  // session restore.
  struct TabData {
    TabData();
    TabData(const TabData&) = delete;
    TabData(TabData&&) = delete;
    ~TabData();

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

    // Return true if |used_in_bg| is initialized and set to true, false
    // otherwise.
    bool UsedInBg() const;

    // Indicates whether or not the tab communicates with the user even when it
    // is in the background (tab title changes, favicons, etc).
    // It is initialized to nullopt and set asynchronously to the proper value.
    std::optional<bool> used_in_bg;

    // Indicates whether or not the tab has been pinned by the user. Only
    // applicable on desktop platforms.
    bool is_pinned = false;

    // Indicates whether or not the tab corresponds to a Chrome app (these are
    // deprecated but we still support them for now). Only applicable on
    // desktop platforms.
    bool is_app = false;

    // Indicates whether or not the tab corresponds to an internal chrome://
    // URL. These are considered lower priority for restoring as they can be
    // created locally and usually offline, and have very low latency.
    bool is_internal = false;

    // The site engagement score associated with the tab. Higher values are for
    // sites that see more engagement.
    size_t site_engagement = 0;

    // How long ago since the tab was last made the active tab by the user. This
    // may actually be negative, as it is calculated as a difference between an
    // arbitrary notion of "now" and some time in the past. Lower values still
    // correspond to more recently used and usually more important tabs.
    base::TimeDelta last_active;

    // A higher value here means the tab has higher priority for restoring. This
    // is calculated based on the values of the above properties, which may
    // change as new data becomes available.
    float score = 0.0f;

    struct SiteDataReaderData {
      bool updates_favicon_in_bg = false;
      bool updates_title_in_bg = false;
    };

    // Cancelable callback used to cancel the async initialization of the
    // |used_in_bg| bit.
    base::CancelableOnceCallback<void(SiteDataReaderData)>
        used_in_bg_setter_cancel_callback;
  };

#if !BUILDFLAG(IS_ANDROID)
  // Retrieves the SiteDataReaderData for `contents`, and invokes the provided
  // callback with it.
  void GetSiteDataReaderData(
      content::WebContents* contents,
      base::OnceCallback<void(TabData::SiteDataReaderData)>
          on_site_data_reader_data_received_cb);

  // Initializes the `used_in_bg` bit for the tab data associated with
  // `contents`. Care must be taken to not call this after the tab data has been
  // destroyed.
  void OnSiteDataReaderDataReceived(content::WebContents* contents,
                                    TabData::SiteDataReaderData reader_data);
#endif  // !BUILDFLAG(IS_ANDROID)

  // This is safe to call from the constructor if |delegate_| is already
  // initialized.
  size_t CalculateSimultaneousTabLoads() const;

  // Posts a task to invoke "NotifyAllTabsScored".
  void DispatchNotifyAllTabsScoredIfNeeded();

  // Invokes the |notify_tab_scored_callback_| with a nullptr WebContents,
  // notifying the embedder that all tabs have final scores.
  void NotifyAllTabsScored();

  // This is a testing seam. By default it immediately redirects to ScoreTab.
  // This should return true if the score has changed, false otherwise.
  virtual bool RescoreTabAfterDataLoaded(content::WebContents* contents,
                                         TabData* tab_data);

  // Calculates a |score| for the given tab. Returns true if it has changed from
  // the existing score.
  static bool ScoreTab(TabData* tab_data);

  // Calculates a score for the "age" of the tab. This is a value between 0
  // (inclusive) and 1 (exclusive), where higher values are attributed to newer
  // tabs.
  static float CalculateAgeScore(const TabData* tab_data);

  // Returns true if given tab has had a final score calculated for it.
  static bool HasFinalScore(const TabData* tab_data);

  // Only used in testing to disable the policy.
  const bool policy_enabled_;

  // Delegate for interface with the system. This allows easy testing of only
  // the logic in this class.
  const raw_ptr<const Delegate> delegate_;

  // The minimum number of tabs to ever load simultaneously. This can be
  // exceeded by user actions or load timeouts. See TabLoader for details.
  uint32_t min_simultaneous_tab_loads_ = 1;

  // The maximum number of simultaneous tab loads that should be permitted.
  // Setting to zero means no maximum is applied.
  uint32_t max_simultaneous_tab_loads_ = 4;

  // The number of CPU cores required before per permitted simultaneous tab
  // load. Setting to zero means no CPU core limit applies.
  uint32_t cores_per_simultaneous_tab_load_ = 2;

  // The minimum total number of tabs to restore (if there are even that many).
  uint32_t min_tabs_to_restore_ = 4;

  // The maximum total number of tabs to restore in a session restore. Setting
  // to zero means no maximum is applied.
  uint32_t max_tabs_to_restore_ = 20;

  // The required amount of system free memory per tab to restore. Setting to
  // zero means no memory limit will be applied.
  uint32_t mb_free_memory_per_tab_to_restore_ = 150;

  // The maximum time since last use of a tab in order for it to be restored.
  // Setting to zero means this logic does not apply.
  base::TimeDelta max_time_since_last_use_to_restore_ = base::Days(30);

  // The minimum site engagement score in order for a tab to be restored.
  // Can be overridden for tests. Setting this to zero means all tabs will be
  // restored regardless of the site engagement score.
  uint32_t min_site_engagement_to_restore_ = kMinSiteEngagementToRestore;

  // The number of simultaneous tab loads that are permitted by policy. This
  // is computed based on the number of cores on the machine, except for in
  // tests.
  size_t simultaneous_tab_loads_;

  // The number of tab loads that have started. Every call to ShouldLoad
  // returning to true is assumed to correspond to a tab that starts loading,
  // and increments this value.
  size_t tab_loads_started_ = 0;

  // The number of tabs for which an accurate initial score has been assigned.
  // This is incremented only after the full tab data is available, which
  // may happen asynchronously.
  size_t tabs_scored_ = 0;

  // Used to track the state of the "all tabs scored" notification.
  enum class NotificationState : uint16_t {
    kNotSent = 0,
    kEnRoute = 1,
    kDelivered = 2,
  };
  NotificationState notification_state_ = NotificationState::kDelivered;

  // The collection of tabs being tracked and various data used for scoring
  // them.
  //
  // Note that the value here is a unique_ptr instead of a TabData as this
  // struct isn't movable.
  base::flat_map<content::WebContents*, std::unique_ptr<TabData>> tab_data_;

  // The callback that is invoked in order to update tab restore order.
  NotifyTabScoreChangedCallback notify_tab_score_changed_callback_;

  // The value of "now" to use when calculating time since a tab was used. A
  // constant time is used so that the scores remain constant over the lifetime
  // of a session restore. Note that overlapping session restores may end up
  // having last active times that are *newer* than this value (thus negative
  // ages). CalculateAgeScore can handle this gracefully.
  base::TimeTicks now_;

  // It's possible for this policy object to be destroyed while it has posted
  // notifications in flight. The messages are bound to a weak pointer so that
  // they are not delivered after the policy object is destroyed.
  base::WeakPtrFactory<SessionRestorePolicy> weak_factory_{this};
};

// Abstracts away testing seams for the policy engine. In production code the
// default implementation wraps to base::SysInfo and the
// site_engagement::SiteEngagementService.
class SessionRestorePolicy::Delegate {
 public:
  Delegate();

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

  virtual ~Delegate();

  virtual size_t GetNumberOfCores() const = 0;
  virtual size_t GetFreeMemoryMiB() const = 0;
  virtual base::TimeTicks NowTicks() const = 0;
  virtual size_t GetSiteEngagementScore(
      content::WebContents* contents) const = 0;
};

}  // namespace resource_coordinator

#endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_SESSION_RESTORE_POLICY_H_