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