blob: 181e2bffff5f50ece2ad1dd44416bc5d1a2f6db8 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_MEMORY_TAB_MANAGER_H_
#define CHROME_BROWSER_MEMORY_TAB_MANAGER_H_
#include <stdint.h>
#include <utility>
#include <vector>
#include "base/compiler_specific.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/memory_pressure_listener.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string16.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
#include "chrome/browser/memory/tab_stats.h"
#include "chrome/browser/ui/browser_tab_strip_tracker.h"
#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
class BrowserList;
class GURL;
namespace base {
class TickClock;
}
namespace content {
class WebContents;
}
namespace memory {
#if defined(OS_CHROMEOS)
class TabManagerDelegate;
#endif
// The TabManager periodically updates (see
// |kAdjustmentIntervalSeconds| in the source) the status of renderers
// which are then used by the algorithm embedded here for priority in being
// killed upon OOM conditions.
//
// The algorithm used favors killing tabs that are not selected, not pinned,
// and have been idle for longest, in that order of priority.
//
// On Chrome OS (via the delegate), the kernel (via /proc/<pid>/oom_score_adj)
// will be informed of each renderer's score, which is based on the status, so
// in case Chrome is not able to relieve the pressure quickly enough and the
// kernel is forced to kill processes, it will be able to do so using the same
// algorithm as the one used here.
//
// Note that the browser tests are only active for platforms that use
// TabManager (CrOS only for now) and need to be adjusted accordingly if
// support for new platforms is added.
class TabManager : public TabStripModelObserver {
public:
// Needs to be public for DEFINE_WEB_CONTENTS_USER_DATA_KEY.
class WebContentsData;
TabManager();
~TabManager() override;
// Number of discard events since Chrome started.
int discard_count() const { return discard_count_; }
// See member comment.
bool recent_tab_discard() const { return recent_tab_discard_; }
// Start/Stop the Tab Manager.
void Start();
void Stop();
// Returns the list of the stats for all renderers. Must be called on the UI
// thread.
TabStatsList GetTabStats();
// Returns true if |contents| is currently discarded.
bool IsTabDiscarded(content::WebContents* contents) const;
// Discards a tab to free the memory occupied by its renderer. The tab still
// exists in the tab-strip; clicking on it will reload it. Returns true if it
// successfully found a tab and discarded it.
bool DiscardTab();
// Discards a tab with the given unique ID. The tab still exists in the
// tab-strip; clicking on it will reload it. Returns true if it successfully
// found a tab and discarded it.
content::WebContents* DiscardTabById(int64_t target_web_contents_id);
// Log memory statistics for the running processes, then discards a tab.
// Tab discard happens sometime later, as collecting the statistics touches
// multiple threads and takes time.
void LogMemoryAndDiscardTab();
// Log memory statistics for the running processes, then call the callback.
void LogMemory(const std::string& title, const base::Closure& callback);
// Used to set the test TickClock, which then gets used by NowTicks(). See
// |test_tick_clock_| for more details.
void set_test_tick_clock(base::TickClock* test_tick_clock);
private:
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, Comparator);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, DiscardedTabKeepsLastActiveTime);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, DiscardWebContentsAt);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, IsInternalPage);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ProtectRecentlyUsedTabs);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ReloadDiscardedTabContextMenu);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, TabManagerBasics);
static void PurgeMemoryAndDiscardTab();
// Returns true if the |url| represents an internal Chrome web UI page that
// can be easily reloaded and hence makes a good choice to discard.
static bool IsInternalPage(const GURL& url);
// Records UMA histogram statistics for a tab discard. Record statistics for
// user triggered discards via chrome://discards/ because that allows to
// manually test the system.
void RecordDiscardStatistics();
// Record whether an out of memory occured during a recent time interval. This
// allows the normalization of low memory statistics versus usage.
void RecordRecentTabDiscard();
// Purges data structures in the browser that can be easily recomputed.
void PurgeBrowserMemory();
// Returns the number of tabs open in all browser instances.
int GetTabCount() const;
// Adds all the stats of the tabs to |stats_list|.
void AddTabStats(TabStatsList* stats_list);
// Callback for when |update_timer_| fires. Takes care of executing the tasks
// that need to be run periodically (see comment in implementation).
void UpdateTimerCallback();
// Goes through a list of checks to see if a tab is allowed to be discarded by
// the automatic tab discarding mechanism. Note that this is not used when
// discarding a particular tab from about:discards.
bool CanDiscardTab(int64_t target_web_contents_id) const;
// Does the actual discard by destroying the WebContents in |model| at |index|
// and replacing it by an empty one. Returns the new WebContents or NULL if
// the operation fails (return value used only in testing).
content::WebContents* DiscardWebContentsAt(int index, TabStripModel* model);
// Called by the memory pressure listener when the memory pressure rises.
void OnMemoryPressure(
base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
// TabStripModelObserver overrides.
void TabChangedAt(content::WebContents* contents,
int index,
TabChangeType change_type) override;
void ActiveTabChanged(content::WebContents* old_contents,
content::WebContents* new_contents,
int index,
int reason) override;
// Returns true if the tab is currently playing audio or has played audio
// recently, or if the tab is currently accessing the camera, microphone or
// mirroring the display.
bool IsMediaTab(content::WebContents* contents) const;
// Returns the WebContentsData associated with |contents|. Also takes care of
// creating one if needed.
WebContentsData* GetWebContentsData(content::WebContents* contents) const;
// Returns true if |first| is considered less desirable to be killed than
// |second|.
static bool CompareTabStats(TabStats first, TabStats second);
// Returns either the system's clock or the test clock. See |test_tick_clock_|
// for more details.
base::TimeTicks NowTicks() const;
// Timer to periodically update the stats of the renderers.
base::RepeatingTimer update_timer_;
// Timer to periodically report whether a tab has been discarded since the
// last time the timer has fired.
base::RepeatingTimer recent_tab_discard_timer_;
// A listener to global memory pressure events.
scoped_ptr<base::MemoryPressureListener> memory_pressure_listener_;
// Wall-clock time when the priority manager started running.
base::TimeTicks start_time_;
// Wall-clock time of last tab discard during this browsing session, or 0 if
// no discard has happened yet.
base::TimeTicks last_discard_time_;
// Wall-clock time of last priority adjustment, used to correct the above
// times for discontinuities caused by suspend/resume.
base::TimeTicks last_adjust_time_;
// Number of times a tab has been discarded, for statistics.
int discard_count_;
// Whether a tab discard event has occurred during the last time interval,
// used for statistics normalized by usage.
bool recent_tab_discard_;
// Whether a tab can only ever discarded once.
bool discard_once_;
// This allows protecting tabs for a certain amount of time after being
// backgrounded.
base::TimeDelta minimum_protection_time_;
#if defined(OS_CHROMEOS)
scoped_ptr<TabManagerDelegate> delegate_;
#endif
BrowserTabStripTracker browser_tab_strip_tracker_;
// Pointer to a test clock. If this is set, NowTicks() returns the value of
// this test clock. Otherwise it returns the system clock's value.
base::TickClock* test_tick_clock_;
DISALLOW_COPY_AND_ASSIGN(TabManager);
};
} // namespace memory
#endif // CHROME_BROWSER_MEMORY_TAB_MANAGER_H_