| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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 CHROME_BROWSER_MEMORY_TAB_MANAGER_H_ | |
| 6 #define CHROME_BROWSER_MEMORY_TAB_MANAGER_H_ | |
| 7 | |
| 8 #include <stdint.h> | |
| 9 | |
| 10 #include <memory> | |
| 11 #include <string> | |
| 12 #include <utility> | |
| 13 #include <vector> | |
| 14 | |
| 15 #include "base/callback.h" | |
| 16 #include "base/compiler_specific.h" | |
| 17 #include "base/gtest_prod_util.h" | |
| 18 #include "base/macros.h" | |
| 19 #include "base/memory/memory_pressure_listener.h" | |
| 20 #include "base/memory/weak_ptr.h" | |
| 21 #include "base/observer_list.h" | |
| 22 #include "base/strings/string16.h" | |
| 23 #include "base/timer/timer.h" | |
| 24 #include "build/build_config.h" | |
| 25 #include "chrome/browser/memory/tab_manager_observer.h" | |
| 26 #include "chrome/browser/memory/tab_stats.h" | |
| 27 #include "chrome/browser/ui/browser_tab_strip_tracker.h" | |
| 28 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h" | |
| 29 | |
| 30 class BrowserList; | |
| 31 class GURL; | |
| 32 class TabStripModel; | |
| 33 | |
| 34 namespace base { | |
| 35 class TickClock; | |
| 36 } | |
| 37 | |
| 38 namespace content { | |
| 39 class WebContents; | |
| 40 } | |
| 41 | |
| 42 namespace memory { | |
| 43 | |
| 44 #if defined(OS_CHROMEOS) | |
| 45 class TabManagerDelegate; | |
| 46 #endif | |
| 47 | |
| 48 // The TabManager periodically updates (see | |
| 49 // |kAdjustmentIntervalSeconds| in the source) the status of renderers | |
| 50 // which are then used by the algorithm embedded here for priority in being | |
| 51 // killed upon OOM conditions. | |
| 52 // | |
| 53 // The algorithm used favors killing tabs that are not selected, not pinned, | |
| 54 // and have been idle for longest, in that order of priority. | |
| 55 // | |
| 56 // On Chrome OS (via the delegate), the kernel (via /proc/<pid>/oom_score_adj) | |
| 57 // will be informed of each renderer's score, which is based on the status, so | |
| 58 // in case Chrome is not able to relieve the pressure quickly enough and the | |
| 59 // kernel is forced to kill processes, it will be able to do so using the same | |
| 60 // algorithm as the one used here. | |
| 61 // | |
| 62 // Note that the browser tests are only active for platforms that use | |
| 63 // TabManager (CrOS only for now) and need to be adjusted accordingly if | |
| 64 // support for new platforms is added. | |
| 65 class TabManager : public TabStripModelObserver { | |
| 66 public: | |
| 67 // Needs to be public for DEFINE_WEB_CONTENTS_USER_DATA_KEY. | |
| 68 class WebContentsData; | |
| 69 | |
| 70 TabManager(); | |
| 71 ~TabManager() override; | |
| 72 | |
| 73 // Number of discard events since Chrome started. | |
| 74 int discard_count() const { return discard_count_; } | |
| 75 | |
| 76 // See member comment. | |
| 77 bool recent_tab_discard() const { return recent_tab_discard_; } | |
| 78 | |
| 79 // Start/Stop the Tab Manager. | |
| 80 void Start(); | |
| 81 void Stop(); | |
| 82 | |
| 83 // Returns the list of the stats for all renderers. Must be called on the UI | |
| 84 // thread. The returned list is sorted by reversed importance. | |
| 85 TabStatsList GetTabStats() const; | |
| 86 | |
| 87 // Returns true if |contents| is currently discarded. | |
| 88 bool IsTabDiscarded(content::WebContents* contents) const; | |
| 89 | |
| 90 // Goes through a list of checks to see if a tab is allowed to be discarded by | |
| 91 // the automatic tab discarding mechanism. Note that this is not used when | |
| 92 // discarding a particular tab from about:discards. | |
| 93 bool CanDiscardTab(int64_t target_web_contents_id) const; | |
| 94 | |
| 95 // Discards a tab to free the memory occupied by its renderer. The tab still | |
| 96 // exists in the tab-strip; clicking on it will reload it. | |
| 97 void DiscardTab(); | |
| 98 | |
| 99 // Discards a tab with the given unique ID. The tab still exists in the | |
| 100 // tab-strip; clicking on it will reload it. Returns null if the tab cannot | |
| 101 // be found or cannot be discarded. Otherwise returns the new web_contents | |
| 102 // of the discarded tab. | |
| 103 content::WebContents* DiscardTabById(int64_t target_web_contents_id); | |
| 104 | |
| 105 // Method used by the extensions API to discard tabs. If |contents| is null, | |
| 106 // discards the least important tab using DiscardTab(). Otherwise discards | |
| 107 // the given contents. Returns the new web_contents or null if no tab | |
| 108 // was discarded. | |
| 109 content::WebContents* DiscardTabByExtension(content::WebContents* contents); | |
| 110 | |
| 111 // Log memory statistics for the running processes, then discards a tab. | |
| 112 // Tab discard happens sometime later, as collecting the statistics touches | |
| 113 // multiple threads and takes time. | |
| 114 void LogMemoryAndDiscardTab(); | |
| 115 | |
| 116 // Log memory statistics for the running processes, then call the callback. | |
| 117 void LogMemory(const std::string& title, const base::Closure& callback); | |
| 118 | |
| 119 // Used to set the test TickClock, which then gets used by NowTicks(). See | |
| 120 // |test_tick_clock_| for more details. | |
| 121 void set_test_tick_clock(base::TickClock* test_tick_clock); | |
| 122 | |
| 123 // Returns the list of the stats for all renderers. Must be called on the UI | |
| 124 // thread. | |
| 125 TabStatsList GetUnsortedTabStats() const; | |
| 126 | |
| 127 void AddObserver(TabManagerObserver* observer); | |
| 128 void RemoveObserver(TabManagerObserver* observer); | |
| 129 | |
| 130 // Used in tests to change the protection time of the tabs. | |
| 131 void set_minimum_protection_time_for_tests( | |
| 132 base::TimeDelta minimum_protection_time); | |
| 133 | |
| 134 // Returns the auto-discardable state of the tab. When true, the tab is | |
| 135 // eligible to be automatically discarded when critical memory pressure hits, | |
| 136 // otherwise the tab is ignored and will never be automatically discarded. | |
| 137 // Note that this property doesn't block the discarding of the tab via other | |
| 138 // methods (about:discards for instance). | |
| 139 bool IsTabAutoDiscardable(content::WebContents* contents) const; | |
| 140 | |
| 141 // Sets/clears the auto-discardable state of the tab. | |
| 142 void SetTabAutoDiscardableState(content::WebContents* contents, bool state); | |
| 143 | |
| 144 // Returns true when a given renderer can be purged if the specified | |
| 145 // renderer is eligible for purging. | |
| 146 // TODO(tasak): rename this to CanPurgeBackgroundedRenderer. | |
| 147 bool CanSuspendBackgroundedRenderer(int render_process_id) const; | |
| 148 | |
| 149 // Returns true if |first| is considered less desirable to be killed than | |
| 150 // |second|. | |
| 151 static bool CompareTabStats(const TabStats& first, const TabStats& second); | |
| 152 | |
| 153 // Returns a unique ID for a WebContents. Do not cast back to a pointer, as | |
| 154 // the WebContents could be deleted if the user closed the tab. | |
| 155 static int64_t IdFromWebContents(content::WebContents* web_contents); | |
| 156 | |
| 157 private: | |
| 158 FRIEND_TEST_ALL_PREFIXES(TabManagerTest, PurgeBackgroundRenderer); | |
| 159 FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ActivateTabResetPurgeState); | |
| 160 FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ShouldPurgeAtDefaultTime); | |
| 161 FRIEND_TEST_ALL_PREFIXES(TabManagerTest, DefaultTimeToPurgeInCorrectRange); | |
| 162 FRIEND_TEST_ALL_PREFIXES(TabManagerTest, AutoDiscardable); | |
| 163 FRIEND_TEST_ALL_PREFIXES(TabManagerTest, CanOnlyDiscardOnce); | |
| 164 FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ChildProcessNotifications); | |
| 165 FRIEND_TEST_ALL_PREFIXES(TabManagerTest, Comparator); | |
| 166 FRIEND_TEST_ALL_PREFIXES(TabManagerTest, DiscardedTabKeepsLastActiveTime); | |
| 167 FRIEND_TEST_ALL_PREFIXES(TabManagerTest, DiscardWebContentsAt); | |
| 168 FRIEND_TEST_ALL_PREFIXES(TabManagerTest, InvalidOrEmptyURL); | |
| 169 FRIEND_TEST_ALL_PREFIXES(TabManagerTest, IsInternalPage); | |
| 170 FRIEND_TEST_ALL_PREFIXES(TabManagerTest, OomPressureListener); | |
| 171 FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ProtectPDFPages); | |
| 172 FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ProtectRecentlyUsedTabs); | |
| 173 FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ProtectVideoTabs); | |
| 174 FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ReloadDiscardedTabContextMenu); | |
| 175 FRIEND_TEST_ALL_PREFIXES(TabManagerTest, TabManagerBasics); | |
| 176 | |
| 177 // The time of the first purging after a renderer is backgrounded. | |
| 178 // The initial value was chosen because most of users activate backgrounded | |
| 179 // tabs within 30 minutes. (c.f. Tabs.StateTransfer.Time_Inactive_Active) | |
| 180 static constexpr base::TimeDelta kDefaultMinTimeToPurge = | |
| 181 base::TimeDelta::FromMinutes(30); | |
| 182 | |
| 183 // The min/max time to purge ratio. The max time to purge is set to be | |
| 184 // min time to purge times this value. | |
| 185 const int kMinMaxTimeToPurgeRatio = 2; | |
| 186 | |
| 187 // This is needed so WebContentsData can call OnDiscardedStateChange, and | |
| 188 // can use PurgeState. | |
| 189 friend class WebContentsData; | |
| 190 | |
| 191 // Finds TabStripModel which has a WebContents whose id is the given | |
| 192 // web_contents_id, and returns the WebContents index and the TabStripModel. | |
| 193 int FindTabStripModelById(int64_t target_web_contents_id, | |
| 194 TabStripModel** model) const; | |
| 195 | |
| 196 // Called by WebContentsData whenever the discard state of a WebContents | |
| 197 // changes, so that observers can be informed. | |
| 198 void OnDiscardedStateChange(content::WebContents* contents, | |
| 199 bool is_discarded); | |
| 200 | |
| 201 // Called by WebContentsData whenever the auto-discardable state of a | |
| 202 // WebContents changes, so that observers can be informed. | |
| 203 void OnAutoDiscardableStateChange(content::WebContents* contents, | |
| 204 bool is_auto_discardable); | |
| 205 | |
| 206 static void PurgeMemoryAndDiscardTab(); | |
| 207 | |
| 208 // Returns true if the |url| represents an internal Chrome web UI page that | |
| 209 // can be easily reloaded and hence makes a good choice to discard. | |
| 210 static bool IsInternalPage(const GURL& url); | |
| 211 | |
| 212 // Records UMA histogram statistics for a tab discard. Record statistics for | |
| 213 // user triggered discards via chrome://discards/ because that allows to | |
| 214 // manually test the system. | |
| 215 void RecordDiscardStatistics(); | |
| 216 | |
| 217 // Record whether an out of memory occured during a recent time interval. This | |
| 218 // allows the normalization of low memory statistics versus usage. | |
| 219 void RecordRecentTabDiscard(); | |
| 220 | |
| 221 // Purges data structures in the browser that can be easily recomputed. | |
| 222 void PurgeBrowserMemory(); | |
| 223 | |
| 224 // Returns the number of tabs open in all browser instances. | |
| 225 int GetTabCount() const; | |
| 226 | |
| 227 // Adds all the stats of the tabs to |stats_list|. | |
| 228 void AddTabStats(TabStatsList* stats_list) const; | |
| 229 | |
| 230 // Adds all the stats of the tabs in |tab_strip_model| into |stats_list|. | |
| 231 // If |active_model| is true, consider its first tab as being active. | |
| 232 void AddTabStats(const TabStripModel* model, | |
| 233 bool is_app, | |
| 234 bool active_model, | |
| 235 TabStatsList* stats_list) const; | |
| 236 | |
| 237 // Callback for when |update_timer_| fires. Takes care of executing the tasks | |
| 238 // that need to be run periodically (see comment in implementation). | |
| 239 void UpdateTimerCallback(); | |
| 240 | |
| 241 // Returns WebContents whose contents id matches the given tab_contents_id. | |
| 242 content::WebContents* GetWebContentsById(int64_t tab_contents_id) const; | |
| 243 | |
| 244 // Returns a random time-to-purge whose min value is min_time_to_purge and max | |
| 245 // value is min_time_to_purge * kMinMaxTimeToPurgeRatio. | |
| 246 base::TimeDelta GetTimeToPurge(base::TimeDelta min_time_to_purge) const; | |
| 247 | |
| 248 // Returns true if the tab specified by |content| is now eligible to have | |
| 249 // its memory purged. | |
| 250 bool ShouldPurgeNow(content::WebContents* content) const; | |
| 251 | |
| 252 // Purges renderers in backgrounded tabs if the following conditions are | |
| 253 // satisfied: | |
| 254 // - the renderers are not purged yet, | |
| 255 // - the renderers are not playing media, | |
| 256 // (CanPurgeBackgroundedRenderer returns true) | |
| 257 // - the renderers are left inactive and background for time-to-purge. | |
| 258 // If renderers are purged, their internal states become 'purged'. | |
| 259 // The state is reset to be 'not purged' only when they are activated | |
| 260 // (=ActiveTabChanged is invoked). | |
| 261 void PurgeBackgroundedTabsIfNeeded(); | |
| 262 | |
| 263 // Does the actual discard by destroying the WebContents in |model| at |index| | |
| 264 // and replacing it by an empty one. Returns the new WebContents or NULL if | |
| 265 // the operation fails (return value used only in testing). | |
| 266 content::WebContents* DiscardWebContentsAt(int index, TabStripModel* model); | |
| 267 | |
| 268 // Called by the memory pressure listener when the memory pressure rises. | |
| 269 void OnMemoryPressure( | |
| 270 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level); | |
| 271 | |
| 272 // TabStripModelObserver overrides. | |
| 273 void TabChangedAt(content::WebContents* contents, | |
| 274 int index, | |
| 275 TabChangeType change_type) override; | |
| 276 void ActiveTabChanged(content::WebContents* old_contents, | |
| 277 content::WebContents* new_contents, | |
| 278 int index, | |
| 279 int reason) override; | |
| 280 void TabInsertedAt(TabStripModel* tab_strip_model, | |
| 281 content::WebContents* contents, | |
| 282 int index, | |
| 283 bool foreground) override; | |
| 284 | |
| 285 // Returns true if the tab is currently playing audio or has played audio | |
| 286 // recently, or if the tab is currently accessing the camera, microphone or | |
| 287 // mirroring the display. | |
| 288 bool IsMediaTab(content::WebContents* contents) const; | |
| 289 | |
| 290 // Returns the WebContentsData associated with |contents|. Also takes care of | |
| 291 // creating one if needed. | |
| 292 WebContentsData* GetWebContentsData(content::WebContents* contents) const; | |
| 293 | |
| 294 // Returns either the system's clock or the test clock. See |test_tick_clock_| | |
| 295 // for more details. | |
| 296 base::TimeTicks NowTicks() const; | |
| 297 | |
| 298 // Implementation of DiscardTab. Returns null if no tab was discarded. | |
| 299 // Otherwise returns the new web_contents of the discarded tab. | |
| 300 content::WebContents* DiscardTabImpl(); | |
| 301 | |
| 302 // Returns true if tabs can be discarded only once. | |
| 303 bool CanOnlyDiscardOnce() const; | |
| 304 | |
| 305 // Timer to periodically update the stats of the renderers. | |
| 306 base::RepeatingTimer update_timer_; | |
| 307 | |
| 308 // Timer to periodically report whether a tab has been discarded since the | |
| 309 // last time the timer has fired. | |
| 310 base::RepeatingTimer recent_tab_discard_timer_; | |
| 311 | |
| 312 // A listener to global memory pressure events. | |
| 313 std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_; | |
| 314 | |
| 315 // Wall-clock time when the priority manager started running. | |
| 316 base::TimeTicks start_time_; | |
| 317 | |
| 318 // Wall-clock time of last tab discard during this browsing session, or 0 if | |
| 319 // no discard has happened yet. | |
| 320 base::TimeTicks last_discard_time_; | |
| 321 | |
| 322 // Wall-clock time of last priority adjustment, used to correct the above | |
| 323 // times for discontinuities caused by suspend/resume. | |
| 324 base::TimeTicks last_adjust_time_; | |
| 325 | |
| 326 // Number of times a tab has been discarded, for statistics. | |
| 327 int discard_count_; | |
| 328 | |
| 329 // Whether a tab discard event has occurred during the last time interval, | |
| 330 // used for statistics normalized by usage. | |
| 331 bool recent_tab_discard_; | |
| 332 | |
| 333 // Whether a tab can only ever discarded once. | |
| 334 bool discard_once_; | |
| 335 | |
| 336 // This allows protecting tabs for a certain amount of time after being | |
| 337 // backgrounded. | |
| 338 base::TimeDelta minimum_protection_time_; | |
| 339 | |
| 340 // A backgrounded renderer will be purged when this time passes. | |
| 341 base::TimeDelta min_time_to_purge_; | |
| 342 | |
| 343 #if defined(OS_CHROMEOS) | |
| 344 std::unique_ptr<TabManagerDelegate> delegate_; | |
| 345 #endif | |
| 346 | |
| 347 // Responsible for automatically registering this class as an observer of all | |
| 348 // TabStripModels. Automatically tracks browsers as they come and go. | |
| 349 BrowserTabStripTracker browser_tab_strip_tracker_; | |
| 350 | |
| 351 // Pointer to a test clock. If this is set, NowTicks() returns the value of | |
| 352 // this test clock. Otherwise it returns the system clock's value. | |
| 353 base::TickClock* test_tick_clock_; | |
| 354 | |
| 355 // Injected tab strip models. Allows this to be tested end-to-end without | |
| 356 // requiring a full browser environment. If specified these tab strips will be | |
| 357 // crawled as the authoritative source of tabs, otherwise the BrowserList and | |
| 358 // associated Browser objects are crawled. The first of these is considered to | |
| 359 // be the 'active' tab strip model. | |
| 360 // TODO(chrisha): Factor out tab-strip model enumeration to a helper class, | |
| 361 // and make a delegate that centralizes all testing seams. | |
| 362 using TestTabStripModel = std::pair<const TabStripModel*, bool>; | |
| 363 std::vector<TestTabStripModel> test_tab_strip_models_; | |
| 364 | |
| 365 // List of observers that will receive notifications on state changes. | |
| 366 base::ObserverList<TabManagerObserver> observers_; | |
| 367 | |
| 368 // Weak pointer factory used for posting delayed tasks. | |
| 369 base::WeakPtrFactory<TabManager> weak_ptr_factory_; | |
| 370 | |
| 371 DISALLOW_COPY_AND_ASSIGN(TabManager); | |
| 372 }; | |
| 373 | |
| 374 } // namespace memory | |
| 375 | |
| 376 #endif // CHROME_BROWSER_MEMORY_TAB_MANAGER_H_ | |
| OLD | NEW |