Commit 3eaf67ed authored by dgn's avatar dgn Committed by Commit bot

Add BackgroundModeOptimizer that can restart the browser

Its initialization is behind the BackgroundModeAllowRestart
flag, and is supported only on Windows and Linux

BUG=585080

Review-Url: https://codereview.chromium.org/1931503002
Cr-Commit-Position: refs/heads/master@{#407742}
parent e785d25b
......@@ -23,6 +23,7 @@
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/background/background_application_list_model.h"
#include "chrome/browser/background/background_mode_optimizer.h"
#include "chrome/browser/background/background_trigger.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_shutdown.h"
......@@ -92,6 +93,9 @@ void RecordMenuItemClick(MenuItem item) {
}
} // namespace
// static
bool BackgroundModeManager::should_restart_in_background_ = false;
BackgroundModeManager::BackgroundModeData::BackgroundModeData(
Profile* profile,
CommandIdHandlerVector* command_id_handler_vector)
......@@ -318,6 +322,7 @@ BackgroundModeManager::BackgroundModeManager(
// in a mode that doesn't open a browser window. It will be resumed when the
// first browser window is opened.
SuspendBackgroundMode();
optimizer_ = BackgroundModeOptimizer::Create();
}
// If the -keep-alive-for-test flag is passed, then always keep chrome running
......@@ -671,6 +676,7 @@ void BackgroundModeManager::ExecuteCommand(int command_id, int event_flags) {
// BackgroundModeManager, private
void BackgroundModeManager::ReleaseStartupKeepAliveCallback() {
keep_alive_for_startup_.reset();
optimizer_ = BackgroundModeOptimizer::Create();
}
void BackgroundModeManager::ReleaseStartupKeepAlive() {
......
......@@ -26,6 +26,7 @@
#include "content/public/browser/notification_registrar.h"
#include "extensions/common/extension_id.h"
class BackgroundModeOptimizer;
class BackgroundTrigger;
class Browser;
class PrefRegistrySimple;
......@@ -79,6 +80,16 @@ class BackgroundModeManager
// Opens a new browser window if there isn't one for the active desktop.
static Browser* GetBrowserWindowForProfile(Profile* profile);
// Getter and setter for the flag indicating whether Chrome should start in
// background mode the next time.
static bool should_restart_in_background() {
return should_restart_in_background_;
}
static void set_should_restart_in_background(bool enable) {
should_restart_in_background_ = enable;
}
// Returns true if background mode is active.
virtual bool IsBackgroundModeActive();
......@@ -314,7 +325,7 @@ class BackgroundModeManager
// and has a status bar icon.
void StartBackgroundMode();
// Invoked to take Chrome out of KeepAlive mode - chrome stops running in
// Invoked to take Chrome out of KeepAlive mode - Chrome stops running in
// the background and removes its status bar icon.
void EndBackgroundMode();
......@@ -389,6 +400,11 @@ class BackgroundModeManager
// if the profile isn't locked. Returns NULL otherwise.
BackgroundModeData* GetBackgroundModeDataForLastProfile() const;
// Set to true when the next restart should be done in background mode.
// Static because its value is read after the background mode manager is
// destroyed.
static bool should_restart_in_background_;
// Reference to the ProfileAttributesStorage. It is used to update the
// background app status of profiles when they open/close background apps.
ProfileAttributesStorage* profile_storage_;
......@@ -431,6 +447,10 @@ class BackgroundModeManager
// chrome would immediately exit due to having no open windows.
std::unique_ptr<ScopedKeepAlive> keep_alive_for_startup_;
// Reference to the optimizer to use to reduce Chrome's footprint when in
// background mode. If null, optimizations are disabled.
std::unique_ptr<BackgroundModeOptimizer> optimizer_;
// Set to true when Chrome is running with the --keep-alive-for-test flag
// (used for testing background mode without having to install a background
// app).
......
......@@ -261,12 +261,14 @@ class BackgroundModeManagerWithExtensionsTest : public testing::Test {
// We're getting ready to shutdown the message loop. Clear everything out!
base::RunLoop().RunUntilIdle();
test_keep_alive_.reset();
// TestBackgroundModeManager has dependencies on the infrastructure.
// It should get cleared first.
manager_.reset();
// Now that the background manager is destroyed, the test KeepAlive can be
// cleared without having |manager_| attempt to perform optimizations.
test_keep_alive_.reset();
// The Profile Manager references the Browser Process.
// The Browser Process references the Notification UI Manager.
// The Notification UI Manager references the Message Center.
......
// Copyright 2016 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.
#include "chrome/browser/background/background_mode_optimizer.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/memory/ptr_util.h"
#include "chrome/browser/background/background_mode_manager.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_shutdown.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/lifetime/keep_alive_registry.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_switches.h"
BackgroundModeOptimizer::~BackgroundModeOptimizer() {
KeepAliveRegistry::GetInstance()->RemoveObserver(this);
BrowserList::RemoveObserver(this);
}
// static
std::unique_ptr<BackgroundModeOptimizer> BackgroundModeOptimizer::Create() {
// If the -keep-alive-for-test flag is passed, then always keep chrome running
// in the background until the user explicitly terminates it.
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kKeepAliveForTest))
return nullptr;
#if defined(OS_WIN) || defined(OS_LINUX)
if (base::FeatureList::IsEnabled(features::kBackgroundModeAllowRestart))
return base::WrapUnique(new BackgroundModeOptimizer());
#endif // defined(OS_WIN) || defined(OS_LINUX)
return nullptr;
}
///////////////////////////////////////////////////////////////////////////////
// KeepAliveRegistry implementation
void BackgroundModeOptimizer::OnKeepAliveStateChanged(bool is_keeping_alive) {
// Nothing to do
}
void BackgroundModeOptimizer::OnKeepAliveRestartStateChanged(bool can_restart) {
if (can_restart)
TryBrowserRestart();
}
///////////////////////////////////////////////////////////////////////////////
// BrowserListObserver implementation
void BackgroundModeOptimizer::OnBrowserAdded(Browser* browser) {
browser_was_added_ = true;
}
///////////////////////////////////////////////////////////////////////////////
// private methods
BackgroundModeOptimizer::BackgroundModeOptimizer() : browser_was_added_(false) {
KeepAliveRegistry::GetInstance()->AddObserver(this);
BrowserList::AddObserver(this);
}
void BackgroundModeOptimizer::TryBrowserRestart() {
// Avoid unecessary restarts. Whether a browser window has been shown is
// our current heuristic to determine if it's worth it.
if (!browser_was_added_) {
DVLOG(1) << "TryBrowserRestart: Cancelled because no browser was added "
<< "since the last restart";
return;
}
// If the application is already shutting down, do not turn it into a restart.
if (browser_shutdown::IsTryingToQuit() ||
browser_shutdown::GetShutdownType() != browser_shutdown::NOT_VALID) {
DVLOG(1) << "TryBrowserRestart: Cancelled because we are shutting down.";
return;
}
DVLOG(1) << "TryBrowserRestart: Restarting.";
DoRestart();
}
void BackgroundModeOptimizer::DoRestart() {
BackgroundModeManager::set_should_restart_in_background(true);
chrome::AttemptRestart();
}
// Copyright 2016 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_BACKGROUND_BACKGROUND_MODE_OPTIMIZER_H_
#define CHROME_BROWSER_BACKGROUND_BACKGROUND_MODE_OPTIMIZER_H_
#include <memory>
#include "base/macros.h"
#include "chrome/browser/lifetime/keep_alive_state_observer.h"
#include "chrome/browser/ui/browser_list_observer.h"
class Browser;
// BackgroundModeOptimizer is responsible for applying some optimizations to
// save resources (memory) when Chrome runs in background-only mode.
// It tries to restart the browser to release accumulated memory when it
// is considered non distruptive.
class BackgroundModeOptimizer : public KeepAliveStateObserver,
chrome::BrowserListObserver {
public:
~BackgroundModeOptimizer() override;
// Creates a new BackgroundModeOptimizer. Can return null if optimizations
// are not supported.
static std::unique_ptr<BackgroundModeOptimizer> Create();
// KeepAliveStateObserver implementation
void OnKeepAliveStateChanged(bool is_keeping_alive) override;
void OnKeepAliveRestartStateChanged(bool can_restart) override;
// chrome::BrowserListObserver implementation.
void OnBrowserAdded(Browser* browser) override;
private:
friend class DummyBackgroundModeOptimizer;
BackgroundModeOptimizer();
// Calls DoRestart() if the current state of the process allows it.
void TryBrowserRestart();
// Stop the browser and restart it in background mode.
// Virtual for testing purposes.
virtual void DoRestart();
bool browser_was_added_;
DISALLOW_COPY_AND_ASSIGN(BackgroundModeOptimizer);
};
#endif // CHROME_BROWSER_BACKGROUND_BACKGROUND_MODE_OPTIMIZER_H_
// Copyright 2016 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.
#include <memory>
#include "chrome/browser/background/background_mode_optimizer.h"
#include "chrome/browser/browser_shutdown.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
// Dummy optimizer that skips doing the restart.
// friend with BackgroundModeOptimizer, can't be in the anonymous namespace.
class DummyBackgroundModeOptimizer : public BackgroundModeOptimizer {
public:
DummyBackgroundModeOptimizer() {}
MOCK_METHOD0(DoRestart, void());
};
TEST(BackgroundModeOptimizerTest, Foo) {
// Strict mock, will fail the test if a non expected call is made.
testing::StrictMock<DummyBackgroundModeOptimizer> optimizer;
// No restart until we have at least one browser that got opened
optimizer.OnKeepAliveRestartStateChanged(true);
optimizer.OnBrowserAdded(nullptr);
EXPECT_CALL(optimizer, DoRestart()).RetiresOnSaturation();
optimizer.OnKeepAliveRestartStateChanged(true);
// Restart should not be called when we are trying to quit
browser_shutdown::SetTryingToQuit(true);
optimizer.OnKeepAliveRestartStateChanged(true);
// Restart should check that restart changed to be allowed.
browser_shutdown::SetTryingToQuit(false);
optimizer.OnKeepAliveRestartStateChanged(false);
EXPECT_CALL(optimizer, DoRestart()).RetiresOnSaturation();
optimizer.OnKeepAliveRestartStateChanged(true);
}
......@@ -47,6 +47,10 @@
#include "chrome/browser/first_run/upgrade_util.h"
#endif
#if BUILDFLAG(ENABLE_BACKGROUND)
#include "chrome/browser/background/background_mode_manager.h"
#endif
#if defined(ENABLE_RLZ)
#include "components/rlz/rlz_tracker.h"
#endif
......@@ -194,7 +198,7 @@ bool ShutdownPreThreadsStop() {
return restart_last_session;
}
void ShutdownPostThreadsStop(bool restart_last_session) {
void ShutdownPostThreadsStop(int shutdown_flags) {
delete g_browser_process;
g_browser_process = NULL;
......@@ -214,7 +218,7 @@ void ShutdownPostThreadsStop(bool restart_last_session) {
}
#endif
if (restart_last_session) {
if (shutdown_flags & RESTART_LAST_SESSION) {
#if !defined(OS_CHROMEOS)
// Make sure to relaunch the browser with the original command line plus
// the Restore Last Session flag. Note that Chrome can be launched (ie.
......@@ -239,6 +243,8 @@ void ShutdownPostThreadsStop(bool restart_last_session) {
else
new_cl->AppendSwitch(it.first);
}
if (shutdown_flags & RESTART_IN_BACKGROUND)
new_cl->AppendSwitch(switches::kNoStartupWindow);
#if defined(OS_POSIX) || defined(OS_WIN)
upgrade_util::RelaunchChromeBrowser(*new_cl.get());
......@@ -329,6 +335,24 @@ void ReadLastShutdownInfo() {
void SetTryingToQuit(bool quitting) {
g_trying_to_quit = quitting;
if (quitting)
return;
// Reset the restart-related preferences. They get set unconditionally through
// calls such as chrome::AttemptRestart(), and need to be reset if the restart
// attempt is cancelled.
PrefService* pref_service = g_browser_process->local_state();
if (pref_service) {
#if !defined(OS_ANDROID)
pref_service->ClearPref(prefs::kWasRestarted);
#endif // !defined(OS_ANDROID)
pref_service->ClearPref(prefs::kRestartLastSessionOnShutdown);
}
#if BUILDFLAG(ENABLE_BACKGROUND)
BackgroundModeManager::set_should_restart_in_background(false);
#endif // BUILDFLAG(ENABLE_BACKGROUND)
}
bool IsTryingToQuit() {
......
......@@ -11,6 +11,20 @@ class PrefRegistrySimple;
namespace browser_shutdown {
// Shutdown flags
enum Flags {
NO_FLAGS = 0,
// If |RESTART_LAST_SESSION| is set, the browser will attempt to restart in
// in the last session after the shutdown.
RESTART_LAST_SESSION = 1 << 0,
// Makes a panned restart happen in the background. The browser will just come
// up in the system tray but not open a new window after restarting. This flag
// has no effect if |RESTART_LAST_SESSION| is not set.
RESTART_IN_BACKGROUND = 1 << 1
};
enum ShutdownType {
// an uninitialized value
NOT_VALID = 0,
......@@ -41,9 +55,8 @@ bool ShutdownPreThreadsStop();
// Performs the remaining shutdown tasks after all threads but the
// main thread have been stopped. This includes deleting g_browser_process.
//
// The provided parameter indicates whether a preference to restart
// the session was present.
void ShutdownPostThreadsStop(bool restart_last_session);
// See |browser_shutdown::Flags| for the possible flag values and their effects.
void ShutdownPostThreadsStop(int shutdown_flags);
#endif
// Called at startup to create a histogram from our previous shutdown time.
......
......@@ -235,6 +235,10 @@
#include "components/nacl/browser/nacl_process_host.h"
#endif // !defined(DISABLE_NACL)
#if BUILDFLAG(ENABLE_BACKGROUND)
#include "chrome/browser/background/background_mode_manager.h"
#endif // BUILDFLAG(ENABLE_BACKGROUND)
#if defined(ENABLE_EXTENSIONS)
#include "chrome/browser/extensions/startup_helper.h"
#include "extensions/browser/extension_protocols.h"
......@@ -2035,11 +2039,24 @@ void ChromeBrowserMainParts::PostDestroyThreads() {
// not finish.
NOTREACHED();
#else
int restart_flags = restart_last_session_
? browser_shutdown::RESTART_LAST_SESSION
: browser_shutdown::NO_FLAGS;
#if BUILDFLAG(ENABLE_BACKGROUND)
if (restart_flags) {
restart_flags |= BackgroundModeManager::should_restart_in_background()
? browser_shutdown::RESTART_IN_BACKGROUND
: browser_shutdown::NO_FLAGS;
}
#endif // BUILDFLAG(ENABLE_BACKGROUND)
browser_process_->PostDestroyThreads();
// browser_shutdown takes care of deleting browser_process, so we need to
// release it.
ignore_result(browser_process_.release());
browser_shutdown::ShutdownPostThreadsStop(restart_last_session_);
browser_shutdown::ShutdownPostThreadsStop(restart_flags);
master_prefs_.reset();
process_singleton_.reset();
device_event_log::Shutdown();
......
......@@ -14,6 +14,7 @@
#include "chrome/browser/browser_shutdown.h"
#include "chrome/browser/download/download_service.h"
#include "chrome/browser/download/download_service_factory.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/notifications/notification_ui_manager.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
......
......@@ -92,12 +92,14 @@ void KeepAliveRegistry::Unregister(KeepAliveOrigin origin,
bool new_keeping_alive = IsKeepingAlive();
bool new_restart_allowed = IsRestartAllowed();
if (new_restart_allowed != old_restart_allowed)
OnRestartAllowedChanged(new_restart_allowed);
// Update the KeepAlive state first, so that listeners can check if we are
// trying to shutdown.
if (new_keeping_alive != old_keeping_alive)
OnKeepAliveStateChanged(new_keeping_alive);
if (new_restart_allowed != old_restart_allowed)
OnRestartAllowedChanged(new_restart_allowed);
DVLOG(1) << "New state of the KeepAliveRegistry:" << *this;
}
......
......@@ -195,10 +195,11 @@ TEST_F(BrowserListTest, MAYBE_AttemptRestart) {
testing_browser_process->SetProfileManager(profile_manager);
chrome::AttemptRestart();
EXPECT_TRUE(testing_pref_service.GetBoolean(prefs::kWasRestarted));
// Cancel the effects of us calling chrome::AttemptRestart. Otherwise tests
// ran after this one will fail.
browser_shutdown::SetTryingToQuit(false);
EXPECT_TRUE(testing_pref_service.GetBoolean(prefs::kWasRestarted));
testing_browser_process->SetLocalState(NULL);
}
......@@ -1575,6 +1575,8 @@
'browser/background/background_mode_manager_chromeos.cc',
'browser/background/background_mode_manager_mac.mm',
'browser/background/background_mode_manager_win.cc',
'browser/background/background_mode_optimizer.cc',
'browser/background/background_mode_optimizer.h',
'browser/background/background_trigger.h',
],
'chrome_browser_bookmark_sources': [
......
......@@ -394,6 +394,7 @@
'browser/background/background_application_list_model_unittest.cc',
'browser/background/background_contents_service_unittest.cc',
'browser/background/background_mode_manager_unittest.cc',
'browser/background/background_mode_optimizer_unittest.cc',
],
'chrome_unit_tests_extensions_sources': [
'../apps/saved_files_service_unittest.cc',
......
......@@ -20,6 +20,14 @@ const base::Feature kAutomaticTabDiscarding{"AutomaticTabDiscarding",
base::FEATURE_DISABLED_BY_DEFAULT};
#endif // defined(OS_WIN) || defined(OS_MACOSX)
#if defined(OS_WIN) || defined(OS_LINUX)
// Enables the Restart background mode optimization. When all Chrome UI is
// closed and it goes in the background, allows to restart the browser to
// discard memory.
const base::Feature kBackgroundModeAllowRestart{
"BackgroundModeAllowRestart", base::FEATURE_DISABLED_BY_DEFAULT};
#endif // defined(OS_WIN) || defined(OS_LINUX)
// Experiment to disable small cross-origin content. (http://crbug.com/608886)
const base::Feature kBlockSmallContent{"BlockSmallPluginContent",
base::FEATURE_DISABLED_BY_DEFAULT};
......
......@@ -23,6 +23,10 @@ extern const base::Feature kArcMemoryManagement;
extern const base::Feature kAutomaticTabDiscarding;
#endif // defined(OS_WIN) || defined(OS_MACOSX)
#if defined(OS_WIN) || defined(OS_LINUX)
extern const base::Feature kBackgroundModeAllowRestart;
#endif // defined(OS_WIN) || defined(OS_LINUX)
extern const base::Feature kBlockSmallContent;
extern const base::Feature kBrowserHangFixesExperiment;
......
......@@ -789,6 +789,10 @@ extern const char kRecoveryComponentNeedsElevation[];
extern const char kRegisteredSupervisedUserWhitelists[];
#if BUILDFLAG(ENABLE_BACKGROUND)
extern const char kRestartInBackground[];
#endif
#if defined(ENABLE_EXTENSIONS)
extern const char kAnimationPolicy[];
#endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment