blob: 871e2431cb827c64a1a307294475f0154b8c3873 [file] [log] [blame]
// Copyright (c) 2010 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 "app/l10n_util.h"
#include "app/resource_bundle.h"
#include "base/command_line.h"
#include "chrome/app/chrome_dll_resource.h"
#include "chrome/browser/background_mode_manager.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/metrics/user_metrics.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/status_icons/status_icon.h"
#include "chrome/browser/status_icons/status_tray.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_type.h"
#include "chrome/common/pref_names.h"
#include "grit/browser_resources.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#if defined(TOOLKIT_GTK)
#include "chrome/browser/gtk/gtk_util.h"
#endif
BackgroundModeManager::BackgroundModeManager(Profile* profile)
: profile_(profile),
background_app_count_(0),
status_tray_(NULL),
status_icon_(NULL) {
// If background mode is disabled for unittests, just exit - don't listen for
// any notifications.
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableBackgroundMode))
return;
// If the -keep-alive-for-test flag is passed, then always keep chrome running
// in the background until the user explicitly terminates it, by acting as if
// we loaded a background app.
if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKeepAliveForTest))
OnBackgroundAppLoaded();
// When an extension is installed, make sure launch on startup is properly
// set if appropriate. Likewise, turn off launch on startup when the last
// background app is uninstalled.
registrar_.Add(this, NotificationType::EXTENSION_INSTALLED,
Source<Profile>(profile));
registrar_.Add(this, NotificationType::EXTENSION_UNINSTALLED,
Source<Profile>(profile));
// Listen for when extensions are loaded/unloaded so we can track the
// number of background apps.
registrar_.Add(this, NotificationType::EXTENSION_LOADED,
Source<Profile>(profile));
registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
Source<Profile>(profile));
// Check for the presence of background apps after all extensions have been
// loaded, to handle the case where an extension has been manually removed
// while Chrome was not running.
registrar_.Add(this, NotificationType::EXTENSIONS_READY,
Source<Profile>(profile));
// Listen for the application shutting down so we can decrement our KeepAlive
// count.
registrar_.Add(this, NotificationType::APP_TERMINATING,
NotificationService::AllSources());
}
BackgroundModeManager::~BackgroundModeManager() {
// If we're going away, remove our status tray icon so we don't get any events
// from it.
RemoveStatusTrayIcon();
}
bool BackgroundModeManager::IsBackgroundModeEnabled() {
return profile_->GetPrefs()->GetBoolean(prefs::kBackgroundModeEnabled);
}
void BackgroundModeManager::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
switch (type.value) {
case NotificationType::EXTENSIONS_READY:
EnableLaunchOnStartup(IsBackgroundModeEnabled() &&
background_app_count_ > 0);
break;
case NotificationType::EXTENSION_LOADED:
if (IsBackgroundApp(Details<Extension>(details).ptr()))
OnBackgroundAppLoaded();
break;
case NotificationType::EXTENSION_UNLOADED:
if (IsBackgroundApp(Details<Extension>(details).ptr()))
OnBackgroundAppUnloaded();
break;
case NotificationType::EXTENSION_INSTALLED:
if (IsBackgroundApp(Details<Extension>(details).ptr()))
OnBackgroundAppInstalled();
break;
case NotificationType::EXTENSION_UNINSTALLED:
if (IsBackgroundApp(Details<Extension>(details).ptr()))
OnBackgroundAppUninstalled();
break;
case NotificationType::APP_TERMINATING:
// Performing an explicit shutdown, so exit background mode if we were in
// background mode.
if (background_app_count_ > 0 && IsBackgroundModeEnabled())
EndBackgroundMode();
// Shutting down, so don't listen for any more notifications so we don't
// try to re-enter/exit background mode again.
registrar_.RemoveAll();
break;
default:
NOTREACHED();
break;
}
}
bool BackgroundModeManager::IsBackgroundApp(Extension* extension) {
return extension->HasApiPermission(Extension::kBackgroundPermission);
}
void BackgroundModeManager::OnBackgroundAppLoaded() {
// When a background app loads, increment our count and also enable
// KeepAlive mode if the preference is set.
background_app_count_++;
if (background_app_count_ == 1 && IsBackgroundModeEnabled())
StartBackgroundMode();
}
void BackgroundModeManager::StartBackgroundMode() {
// Put ourselves in KeepAlive mode and create a status tray icon.
BrowserList::StartKeepAlive();
// Display a status icon to exit Chrome.
CreateStatusTrayIcon();
}
void BackgroundModeManager::OnBackgroundAppUnloaded() {
// When a background app unloads, decrement our count and also end
// KeepAlive mode if appropriate.
background_app_count_--;
DCHECK(background_app_count_ == 0);
if (background_app_count_ == 0 && IsBackgroundModeEnabled())
EndBackgroundMode();
}
void BackgroundModeManager::EndBackgroundMode() {
// End KeepAlive mode and blow away our status tray icon.
BrowserList::EndKeepAlive();
RemoveStatusTrayIcon();
}
void BackgroundModeManager::OnBackgroundAppInstalled() {
// We're installing a background app. If this is the first background app
// being installed, make sure we are set to launch on startup.
if (IsBackgroundModeEnabled() && background_app_count_ == 0)
EnableLaunchOnStartup(true);
}
void BackgroundModeManager::OnBackgroundAppUninstalled() {
// When uninstalling a background app, disable launch on startup if it's the
// last one.
if (IsBackgroundModeEnabled() && background_app_count_ == 1)
EnableLaunchOnStartup(false);
}
void BackgroundModeManager::EnableLaunchOnStartup(bool should_launch) {
// TODO(atwilson): Add platform-specific code to enable/disable launch on
// startup.
}
void BackgroundModeManager::CreateStatusTrayIcon() {
// Only need status icons on windows/linux. ChromeOS doesn't allow exiting
// Chrome and Mac can use the dock icon instead.
#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS)
if (!status_tray_)
status_tray_ = profile_->GetStatusTray();
#endif
// If the platform doesn't support status icons, or we've already created
// our status icon, just return.
if (!status_tray_ || status_icon_)
return;
status_icon_ = status_tray_->CreateStatusIcon();
if (!status_icon_)
return;
// Set the image and add ourselves as a click observer on it
SkBitmap* bitmap = ResourceBundle::GetSharedInstance().GetBitmapNamed(
IDR_STATUS_TRAY_ICON);
status_icon_->SetImage(*bitmap);
status_icon_->SetToolTip(l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
// Create a context menu item for Chrome.
menus::SimpleMenuModel* menu = new menus::SimpleMenuModel(this);
menu->AddItem(IDC_ABOUT, l10n_util::GetStringFUTF16(IDS_ABOUT,
l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
#if defined(TOOLKIT_GTK)
string16 preferences = gtk_util::GetStockPreferencesMenuLabel();
if (preferences.empty())
menu->AddItemWithStringId(IDC_OPTIONS, IDS_OPTIONS);
else
menu->AddItem(IDC_OPTIONS, preferences);
#else
menu->AddItemWithStringId(IDC_OPTIONS, IDS_OPTIONS);
#endif
menu->AddSeparator();
menu->AddItemWithStringId(IDC_EXIT, IDS_EXIT);
status_icon_->SetContextMenu(menu);
}
bool BackgroundModeManager::IsCommandIdChecked(int command_id) const {
return false;
}
bool BackgroundModeManager::IsCommandIdEnabled(int command_id) const {
// For now, we do not support disabled items.
return true;
}
bool BackgroundModeManager::GetAcceleratorForCommandId(
int command_id,
menus::Accelerator* accelerator) {
// No accelerators for status icon context menus.
return false;
}
void BackgroundModeManager::RemoveStatusTrayIcon() {
if (status_icon_)
status_tray_->RemoveStatusIcon(status_icon_);
status_icon_ = NULL;
}
void BackgroundModeManager::ExecuteCommand(int item) {
switch (item) {
case IDC_EXIT:
UserMetrics::RecordAction(UserMetricsAction("Exit"), profile_);
BrowserList::CloseAllBrowsersAndExit();
break;
case IDC_ABOUT:
GetBrowserWindow()->OpenAboutChromeDialog();
break;
case IDC_OPTIONS:
GetBrowserWindow()->OpenOptionsDialog();
break;
default:
NOTREACHED();
break;
}
}
Browser* BackgroundModeManager::GetBrowserWindow() {
Browser* browser = BrowserList::GetLastActive();
if (!browser) {
Browser::OpenEmptyWindow(profile_);
browser = BrowserList::GetLastActive();
}
return browser;
}
// static
void BackgroundModeManager::RegisterUserPrefs(PrefService* prefs) {
prefs->RegisterBooleanPref(prefs::kBackgroundModeEnabled, true);
}