1dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 23345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Use of this source code is governed by a BSD-style license that can be 33345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// found in the LICENSE file. 43345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include <string> 672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 73345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/base_paths.h" 83345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/command_line.h" 93345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/logging.h" 10513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch#include "base/utf_string_conversions.h" 11513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch#include "chrome/app/chrome_command_ids.h" 12513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch#include "chrome/browser/background_application_list_model.h" 133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/browser/background_mode_manager.h" 1421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/extensions/extension_service.h" 153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/browser/metrics/user_metrics.h" 1621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/profiles/profile.h" 173345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/browser/status_icons/status_icon.h" 183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/browser/status_icons/status_tray.h" 19ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/ui/browser_list.h" 203345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/common/chrome_switches.h" 213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/common/extensions/extension.h" 223345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/common/pref_names.h" 23ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_service.h" 24ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_type.h" 253345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "grit/chromium_strings.h" 263345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "grit/generated_resources.h" 273345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "grit/theme_resources.h" 2872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/l10n/l10n_util.h" 2972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/resource/resource_bundle.h" 303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 31513209b27ff55e2841eac0e4120199c23acce758Ben Murdochvoid BackgroundModeManager::OnApplicationDataChanged( 32513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const Extension* extension) { 33513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch UpdateContextMenuEntryIcon(extension); 34513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch} 35513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 36513209b27ff55e2841eac0e4120199c23acce758Ben Murdochvoid BackgroundModeManager::OnApplicationListChanged() { 37513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch UpdateStatusTrayIconContextMenu(); 38513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch} 39513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 403345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickBackgroundModeManager::BackgroundModeManager(Profile* profile, 413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick CommandLine* command_line) 423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick : profile_(profile), 43513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch applications_(profile), 443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick background_app_count_(0), 45513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch context_menu_(NULL), 46513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch context_menu_application_offset_(0), 473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick in_background_mode_(false), 48731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick keep_alive_for_startup_(false), 493345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick status_tray_(NULL), 503345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick status_icon_(NULL) { 51201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // If background mode is disabled, just exit - don't listen for any 52201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // notifications. 53201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch if (!IsBackgroundModeEnabled(command_line)) 543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return; 553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 56731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // Keep the browser alive until extensions are done loading - this is needed 57731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // by the --no-startup-window flag. We want to stay alive until we load 58731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // extensions, at which point we should either run in background mode (if 59731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // there are background apps) or exit if there are none. 60731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (command_line->HasSwitch(switches::kNoStartupWindow)) { 61731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick keep_alive_for_startup_ = true; 62731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserList::StartKeepAlive(); 63731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } 64731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 653345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // If the -keep-alive-for-test flag is passed, then always keep chrome running 663345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // in the background until the user explicitly terminates it, by acting as if 673345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // we loaded a background app. 683345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKeepAliveForTest)) 693345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick OnBackgroundAppLoaded(); 703345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 713345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Listen for when extensions are loaded/unloaded so we can track the 7272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // number of background apps and modify our keep-alive and launch-on-startup 7372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // state appropriately. 743345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick registrar_.Add(this, NotificationType::EXTENSION_LOADED, 753345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick Source<Profile>(profile)); 763345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, 773345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick Source<Profile>(profile)); 783345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 793345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Check for the presence of background apps after all extensions have been 803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // loaded, to handle the case where an extension has been manually removed 813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // while Chrome was not running. 823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick registrar_.Add(this, NotificationType::EXTENSIONS_READY, 833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick Source<Profile>(profile)); 843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 853345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Listen for the application shutting down so we can decrement our KeepAlive 863345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // count. 873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick registrar_.Add(this, NotificationType::APP_TERMINATING, 883345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick NotificationService::AllSources()); 893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 90513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch applications_.AddObserver(this); 913345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 933345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickBackgroundModeManager::~BackgroundModeManager() { 94513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch applications_.RemoveObserver(this); 95513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 963345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // We're going away, so exit background mode (does nothing if we aren't in 973345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // background mode currently). This is primarily needed for unit tests, 983345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // because in an actual running system we'd get an APP_TERMINATING 993345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // notification before being destroyed. 1003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick EndBackgroundMode(); 1013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 1023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 1033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid BackgroundModeManager::Observe(NotificationType type, 1043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick const NotificationSource& source, 1053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick const NotificationDetails& details) { 1063345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick switch (type.value) { 1073345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick case NotificationType::EXTENSIONS_READY: 108731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // Extensions are loaded, so we don't need to manually keep the browser 109731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // process alive any more when running in no-startup-window mode. 110731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick EndKeepAliveForStartup(); 111731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 112731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // On a Mac, we use 'login items' mechanism which has user-facing UI so we 113731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // don't want to stomp on user choice every time we start and load 11472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // registered extensions. This means that if a background app is removed 11572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // or added while Chrome is not running, we could leave Chrome in the 11672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // wrong state, but this is better than constantly forcing Chrome to 11772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // launch on startup even after the user removes the LoginItem manually. 1183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#if !defined(OS_MACOSX) 119201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch EnableLaunchOnStartup(background_app_count_ > 0); 1203345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#endif 1213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick break; 122dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen case NotificationType::EXTENSION_LOADED: { 123dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen Extension* extension = Details<Extension>(details).ptr(); 124dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (BackgroundApplicationListModel::IsBackgroundApp(*extension)) { 125dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Extensions loaded after the ExtensionsService is ready should be 126dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // treated as new installs. 127dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (profile_->GetExtensionService()->is_ready()) 128dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen OnBackgroundAppInstalled(extension); 129dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen OnBackgroundAppLoaded(); 130dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 131513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch } 1323345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick break; 1333345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick case NotificationType::EXTENSION_UNLOADED: 134513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch if (BackgroundApplicationListModel::IsBackgroundApp( 13521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen *Details<UnloadedExtensionInfo>(details)->extension)) { 13672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen Details<UnloadedExtensionInfo> info = 13772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen Details<UnloadedExtensionInfo>(details); 13872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // If we already got an unload notification when it was disabled, ignore 13972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // this one. 14072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // TODO(atwilson): Change BackgroundModeManager to use 14172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // BackgroundApplicationListModel instead of tracking the count here. 14272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (info->already_disabled) 14372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return; 1443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick OnBackgroundAppUnloaded(); 1453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick OnBackgroundAppUninstalled(); 146513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch } 1473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick break; 1483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick case NotificationType::APP_TERMINATING: 149731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // Make sure we aren't still keeping the app alive (only happens if we 150731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // don't receive an EXTENSIONS_READY notification for some reason). 151731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick EndKeepAliveForStartup(); 1523345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Performing an explicit shutdown, so exit background mode (does nothing 1533345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // if we aren't in background mode currently). 1543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick EndBackgroundMode(); 1553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Shutting down, so don't listen for any more notifications so we don't 1563345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // try to re-enter/exit background mode again. 1573345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick registrar_.RemoveAll(); 1583345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick break; 1593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick default: 1603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick NOTREACHED(); 1613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick break; 1623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick } 1633345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 1643345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 165731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid BackgroundModeManager::EndKeepAliveForStartup() { 166731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (keep_alive_for_startup_) { 167731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick keep_alive_for_startup_ = false; 168731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // We call this via the message queue to make sure we don't try to end 169731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // keep-alive (which can shutdown Chrome) before the message loop has 170731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // started. 171731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick MessageLoop::current()->PostTask( 172731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick FROM_HERE, NewRunnableFunction(BrowserList::EndKeepAlive)); 173731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } 1743345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 1753345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 1763345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid BackgroundModeManager::OnBackgroundAppLoaded() { 1773345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // When a background app loads, increment our count and also enable 1783345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // KeepAlive mode if the preference is set. 1793345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick background_app_count_++; 180201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch if (background_app_count_ == 1) 1813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick StartBackgroundMode(); 1823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 1833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 1843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid BackgroundModeManager::StartBackgroundMode() { 1853345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Don't bother putting ourselves in background mode if we're already there. 1863345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (in_background_mode_) 1873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return; 1883345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 1893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Mark ourselves as running in background mode. 1903345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick in_background_mode_ = true; 1913345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 1923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Put ourselves in KeepAlive mode and create a status tray icon. 1933345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick BrowserList::StartKeepAlive(); 1943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 1953345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Display a status icon to exit Chrome. 1963345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick CreateStatusTrayIcon(); 1973345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 1983345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 1993345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid BackgroundModeManager::OnBackgroundAppUnloaded() { 2003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // When a background app unloads, decrement our count and also end 2013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // KeepAlive mode if appropriate. 2023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick background_app_count_--; 2033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick DCHECK(background_app_count_ >= 0); 204201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch if (background_app_count_ == 0) 2053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick EndBackgroundMode(); 2063345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 2073345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 2083345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid BackgroundModeManager::EndBackgroundMode() { 2093345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!in_background_mode_) 2103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return; 2113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick in_background_mode_ = false; 2123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 2133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // End KeepAlive mode and blow away our status tray icon. 2143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick BrowserList::EndKeepAlive(); 2153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick RemoveStatusTrayIcon(); 2163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 2173345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 218dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenvoid BackgroundModeManager::OnBackgroundAppInstalled( 219dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const Extension* extension) { 2203345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // We're installing a background app. If this is the first background app 2213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // being installed, make sure we are set to launch on startup. 222201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch if (background_app_count_ == 0) 2233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick EnableLaunchOnStartup(true); 224dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 225dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Notify the user that a background app has been installed. 226dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (extension) // NULL when called by unit tests. 227dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DisplayAppInstalledNotification(extension); 2283345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 2293345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 2303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid BackgroundModeManager::OnBackgroundAppUninstalled() { 231731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // When uninstalling a background app, disable launch on startup if 232731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // we have no more background apps. 233201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch if (background_app_count_ == 0) 2343345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick EnableLaunchOnStartup(false); 2353345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 2363345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 2373345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid BackgroundModeManager::CreateStatusTrayIcon() { 2383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Only need status icons on windows/linux. ChromeOS doesn't allow exiting 2393345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Chrome and Mac can use the dock icon instead. 2403345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS) 2413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!status_tray_) 2423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick status_tray_ = profile_->GetStatusTray(); 2433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#endif 2443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 2453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // If the platform doesn't support status icons, or we've already created 2463345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // our status icon, just return. 2473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!status_tray_ || status_icon_) 2483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return; 2493345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick status_icon_ = status_tray_->CreateStatusIcon(); 2503345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!status_icon_) 2513345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return; 2523345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 2533345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Set the image and add ourselves as a click observer on it 2543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick SkBitmap* bitmap = ResourceBundle::GetSharedInstance().GetBitmapNamed( 2553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick IDR_STATUS_TRAY_ICON); 2563345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick status_icon_->SetImage(*bitmap); 2573345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick status_icon_->SetToolTip(l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)); 258513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch UpdateStatusTrayIconContextMenu(); 259513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch} 260513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 261513209b27ff55e2841eac0e4120199c23acce758Ben Murdochvoid BackgroundModeManager::UpdateContextMenuEntryIcon( 262513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const Extension* extension) { 263513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch if (!context_menu_) 264513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch return; 265513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch context_menu_->SetIcon( 266513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch context_menu_application_offset_ + applications_.GetPosition(extension), 267513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch *(applications_.GetIcon(extension))); 268513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch status_icon_->SetContextMenu(context_menu_); // for Update effect 269513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch} 270513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 271513209b27ff55e2841eac0e4120199c23acce758Ben Murdochvoid BackgroundModeManager::UpdateStatusTrayIconContextMenu() { 272513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch if (!status_icon_) 273513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch return; 2743345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 2753345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Create a context menu item for Chrome. 27672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ui::SimpleMenuModel* menu = new ui::SimpleMenuModel(this); 277513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch // Add About item 278513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch menu->AddItem(IDC_ABOUT, l10n_util::GetStringFUTF16(IDS_ABOUT, 279513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); 28021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen menu->AddItem(IDC_OPTIONS, GetPreferencesMenuLabel()); 281201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch menu->AddItemWithStringId(IDC_TASK_MANAGER, IDS_TASK_MANAGER); 282513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch menu->AddSeparator(); 28321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen int position = 0; 284513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch context_menu_application_offset_ = menu->GetItemCount(); 285513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch for (ExtensionList::const_iterator cursor = applications_.begin(); 286513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch cursor != applications_.end(); 28721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen ++cursor, ++position) { 288513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const SkBitmap* icon = applications_.GetIcon(*cursor); 28921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen DCHECK(position == applications_.GetPosition(*cursor)); 290513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const std::string& name = (*cursor)->name(); 29121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen menu->AddItem(position, UTF8ToUTF16(name)); 292513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch if (icon) 293513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch menu->SetIcon(menu->GetItemCount() - 1, *icon); 294513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch } 29521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen if (applications_.size() > 0) 29621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen menu->AddSeparator(); 2973345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick menu->AddItemWithStringId(IDC_EXIT, IDS_EXIT); 298513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch context_menu_ = menu; 2993345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick status_icon_->SetContextMenu(menu); 3003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 3013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 3023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickbool BackgroundModeManager::IsCommandIdChecked(int command_id) const { 3033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return false; 3043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 3053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 3063345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickbool BackgroundModeManager::IsCommandIdEnabled(int command_id) const { 3073345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // For now, we do not support disabled items. 3083345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return true; 3093345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 3103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 3113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickbool BackgroundModeManager::GetAcceleratorForCommandId( 3123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick int command_id, 31372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ui::Accelerator* accelerator) { 3143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // No accelerators for status icon context menus. 3153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return false; 3163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 3173345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 3183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid BackgroundModeManager::RemoveStatusTrayIcon() { 3193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (status_icon_) 3203345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick status_tray_->RemoveStatusIcon(status_icon_); 3213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick status_icon_ = NULL; 32272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen context_menu_ = NULL; // Do not delete, points within status_icon_ 3233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 3243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 325513209b27ff55e2841eac0e4120199c23acce758Ben Murdochvoid BackgroundModeManager::ExecuteApplication(int item) { 3264a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch DCHECK(item >= 0 && item < static_cast<int>(applications_.size())); 327513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch Browser* browser = BrowserList::GetLastActive(); 328513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch if (!browser) { 329513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch Browser::OpenEmptyWindow(profile_); 330513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch browser = BrowserList::GetLastActive(); 331513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch } 332513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const Extension* extension = applications_.GetExtension(item); 333513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch browser->OpenApplicationTab(profile_, extension, NULL); 334513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch} 3353345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 3363345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid BackgroundModeManager::ExecuteCommand(int item) { 3373345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick switch (item) { 338201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch case IDC_ABOUT: 339201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch GetBrowserWindow()->OpenAboutChromeDialog(); 340201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch break; 3413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick case IDC_EXIT: 3423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick UserMetrics::RecordAction(UserMetricsAction("Exit"), profile_); 3433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick BrowserList::CloseAllBrowsersAndExit(); 3443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick break; 3453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick case IDC_OPTIONS: 3463345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick GetBrowserWindow()->OpenOptionsDialog(); 3473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick break; 348201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch case IDC_TASK_MANAGER: 34972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen GetBrowserWindow()->OpenTaskManager(true); 350201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch break; 3513345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick default: 352513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch ExecuteApplication(item); 3533345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick break; 3543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick } 3553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 3563345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 3573345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickBrowser* BackgroundModeManager::GetBrowserWindow() { 3583345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick Browser* browser = BrowserList::GetLastActive(); 3593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!browser) { 3603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick Browser::OpenEmptyWindow(profile_); 3613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick browser = BrowserList::GetLastActive(); 3623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick } 3633345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return browser; 3643345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 3653345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 3663345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// static 367201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochbool BackgroundModeManager::IsBackgroundModeEnabled( 368201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch const CommandLine* command_line) { 369201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 370201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // Background mode is disabled if the appropriate flag is passed, or if 3713f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen // extensions are disabled. It's always disabled on chromeos since chrome 3723f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen // is always running on that platform, making it superfluous. 3733f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#if defined(OS_CHROMEOS) 3743f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen return false; 3753f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#else 376201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch bool background_mode_enabled = 377201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch !command_line->HasSwitch(switches::kDisableBackgroundMode) && 378201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch !command_line->HasSwitch(switches::kDisableExtensions); 379201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch return background_mode_enabled; 3803f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#endif 381201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch} 382ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 383ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// static 384ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid BackgroundModeManager::RegisterPrefs(PrefService* prefs) { 385ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen prefs->RegisterBooleanPref(prefs::kUserCreatedLoginItem, false); 386ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} 387