| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <string> | 5 #include <string> |
| 6 | 6 |
| 7 #include "base/base_paths.h" | 7 #include "base/base_paths.h" |
| 8 #include "base/command_line.h" | 8 #include "base/command_line.h" |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/utf_string_conversions.h" | 10 #include "base/utf_string_conversions.h" |
| (...skipping 11 matching lines...) Expand all Loading... |
| 22 #include "chrome/common/pref_names.h" | 22 #include "chrome/common/pref_names.h" |
| 23 #include "content/browser/user_metrics.h" | 23 #include "content/browser/user_metrics.h" |
| 24 #include "content/common/notification_service.h" | 24 #include "content/common/notification_service.h" |
| 25 #include "content/common/notification_type.h" | 25 #include "content/common/notification_type.h" |
| 26 #include "grit/chromium_strings.h" | 26 #include "grit/chromium_strings.h" |
| 27 #include "grit/generated_resources.h" | 27 #include "grit/generated_resources.h" |
| 28 #include "grit/theme_resources.h" | 28 #include "grit/theme_resources.h" |
| 29 #include "ui/base/l10n/l10n_util.h" | 29 #include "ui/base/l10n/l10n_util.h" |
| 30 #include "ui/base/resource/resource_bundle.h" | 30 #include "ui/base/resource/resource_bundle.h" |
| 31 | 31 |
| 32 void BackgroundModeManager::OnApplicationDataChanged( | 32 |
| 33 const Extension* extension) { | 33 BackgroundModeManager::BackgroundModeData::BackgroundModeData( |
| 34 UpdateContextMenuEntryIcon(extension); | 34 Profile* profile, |
| 35 BackgroundModeManager* background_mode_manager) |
| 36 : applications_(new BackgroundApplicationListModel(profile)), |
| 37 status_icon_(NULL), |
| 38 context_menu_(NULL), |
| 39 context_menu_application_offset_(0), |
| 40 profile_(profile), |
| 41 background_mode_manager_(background_mode_manager) { |
| 35 } | 42 } |
| 36 | 43 |
| 37 void BackgroundModeManager::OnApplicationListChanged() { | 44 BackgroundModeManager::BackgroundModeData::~BackgroundModeData() { |
| 38 UpdateStatusTrayIconContextMenu(); | |
| 39 } | 45 } |
| 40 | 46 |
| 41 BackgroundModeManager::BackgroundModeManager(Profile* profile, | 47 /////////////////////////////////////////////////////////////////////////////// |
| 42 CommandLine* command_line) | 48 // BackgroundModeManager::BackgroundModeData, ui::SimpleMenuModel overrides |
| 43 : profile_(profile), | 49 bool BackgroundModeManager::BackgroundModeData::IsCommandIdChecked( |
| 44 applications_(profile), | 50 int command_id) const { |
| 51 DCHECK(command_id == IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND); |
| 52 return true; |
| 53 } |
| 54 |
| 55 bool BackgroundModeManager::BackgroundModeData::IsCommandIdEnabled( |
| 56 int command_id) const { |
| 57 // For now, we do not support disabled items. |
| 58 return true; |
| 59 } |
| 60 |
| 61 bool BackgroundModeManager::BackgroundModeData::GetAcceleratorForCommandId( |
| 62 int command_id, ui::Accelerator* accelerator) { |
| 63 // No accelerators for status icon context menus. |
| 64 return false; |
| 65 } |
| 66 |
| 67 void BackgroundModeManager::BackgroundModeData::ExecuteCommand(int item) { |
| 68 switch (item) { |
| 69 case IDC_ABOUT: |
| 70 GetBrowserWindow()->OpenAboutChromeDialog(); |
| 71 break; |
| 72 case IDC_EXIT: |
| 73 UserMetrics::RecordAction(UserMetricsAction("Exit")); |
| 74 BrowserList::CloseAllBrowsersAndExit(); |
| 75 break; |
| 76 case IDC_OPTIONS: |
| 77 GetBrowserWindow()->OpenOptionsDialog(); |
| 78 break; |
| 79 case IDC_TASK_MANAGER: |
| 80 GetBrowserWindow()->OpenTaskManager(true); |
| 81 break; |
| 82 case IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND: { |
| 83 // Background mode must already be enabled (as otherwise this menu would |
| 84 // not be visible). |
| 85 DCHECK(background_mode_manager_->IsBackgroundModePrefEnabled()); |
| 86 DCHECK(BrowserList::WillKeepAlive()); |
| 87 |
| 88 // Set the background mode pref to "disabled" - the resulting notification |
| 89 // will result in a call to DisableBackgroundMode(). |
| 90 PrefService* service = g_browser_process->local_state(); |
| 91 DCHECK(service); |
| 92 service->SetBoolean(prefs::kBackgroundModeEnabled, false); |
| 93 break; |
| 94 } |
| 95 default: |
| 96 ExecuteApplication(item); |
| 97 break; |
| 98 } |
| 99 } |
| 100 |
| 101 void BackgroundModeManager::BackgroundModeData::ExecuteApplication( |
| 102 int item) { |
| 103 Browser* browser = GetBrowserWindow(); |
| 104 const Extension* extension = applications_->GetExtension(item); |
| 105 browser->OpenApplicationTab(profile_, extension, NEW_FOREGROUND_TAB); |
| 106 } |
| 107 |
| 108 Browser* BackgroundModeManager::BackgroundModeData::GetBrowserWindow() { |
| 109 Browser* browser = BrowserList::GetLastActiveWithProfile(profile_); |
| 110 if (!browser) { |
| 111 Browser::OpenEmptyWindow(profile_); |
| 112 browser = BrowserList::GetLastActiveWithProfile(profile_); |
| 113 } |
| 114 return browser; |
| 115 } |
| 116 |
| 117 void BackgroundModeManager::BackgroundModeData::UpdateContextMenuEntryIcon( |
| 118 const Extension* extension) { |
| 119 if (!context_menu_) |
| 120 return; |
| 121 context_menu_->SetIcon( |
| 122 context_menu_application_offset_ + |
| 123 applications_->GetPosition(extension), |
| 124 *(applications_->GetIcon(extension))); |
| 125 |
| 126 status_icon_->SetContextMenu(context_menu_); // for Update effect |
| 127 } |
| 128 |
| 129 bool BackgroundModeManager::BackgroundModeData::HasBackgroundApp() { |
| 130 return (applications_->size() > 0); |
| 131 } |
| 132 |
| 133 /////////////////////////////////////////////////////////////////////////////// |
| 134 // BackgroundModeManager, public |
| 135 BackgroundModeManager::BackgroundModeManager(CommandLine* command_line) |
| 136 : status_tray_(NULL), |
| 45 background_app_count_(0), | 137 background_app_count_(0), |
| 46 context_menu_(NULL), | |
| 47 context_menu_application_offset_(0), | |
| 48 in_background_mode_(false), | 138 in_background_mode_(false), |
| 49 keep_alive_for_startup_(false), | 139 keep_alive_for_startup_(false) { |
| 50 status_tray_(NULL), | |
| 51 status_icon_(NULL) { | |
| 52 // If background mode is currently disabled, just exit - don't listen for any | 140 // If background mode is currently disabled, just exit - don't listen for any |
| 53 // notifications. | 141 // notifications. |
| 54 if (IsBackgroundModePermanentlyDisabled(command_line)) | 142 if (IsBackgroundModePermanentlyDisabled(command_line)) |
| 55 return; | 143 return; |
| 56 | 144 |
| 57 // Listen for the background mode preference changing. | 145 // Listen for the background mode preference changing. |
| 58 if (g_browser_process->local_state()) { // Skip for unit tests | 146 if (g_browser_process->local_state()) { // Skip for unit tests |
| 59 pref_registrar_.Init(g_browser_process->local_state()); | 147 pref_registrar_.Init(g_browser_process->local_state()); |
| 60 pref_registrar_.Add(prefs::kBackgroundModeEnabled, this); | 148 pref_registrar_.Add(prefs::kBackgroundModeEnabled, this); |
| 61 } | 149 } |
| 62 | 150 |
| 63 // Keep the browser alive until extensions are done loading - this is needed | 151 // Keep the browser alive until extensions are done loading - this is needed |
| 64 // by the --no-startup-window flag. We want to stay alive until we load | 152 // by the --no-startup-window flag. We want to stay alive until we load |
| 65 // extensions, at which point we should either run in background mode (if | 153 // extensions, at which point we should either run in background mode (if |
| 66 // there are background apps) or exit if there are none. | 154 // there are background apps) or exit if there are none. |
| 67 if (command_line->HasSwitch(switches::kNoStartupWindow)) { | 155 if (command_line->HasSwitch(switches::kNoStartupWindow)) { |
| 68 keep_alive_for_startup_ = true; | 156 keep_alive_for_startup_ = true; |
| 69 BrowserList::StartKeepAlive(); | 157 BrowserList::StartKeepAlive(); |
| 70 } | 158 } |
| 71 | 159 |
| 72 // If the -keep-alive-for-test flag is passed, then always keep chrome running | 160 // If the -keep-alive-for-test flag is passed, then always keep chrome running |
| 73 // in the background until the user explicitly terminates it, by acting as if | 161 // in the background until the user explicitly terminates it, by acting as if |
| 74 // we loaded a background app. | 162 // we loaded a background app. |
| 75 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKeepAliveForTest)) | 163 if (command_line->HasSwitch(switches::kKeepAliveForTest)) |
| 76 OnBackgroundAppLoaded(); | 164 OnBackgroundAppLoaded(); |
| 77 | 165 |
| 166 // Listen for the application shutting down so we can decrement our KeepAlive |
| 167 // count. |
| 168 registrar_.Add(this, NotificationType::APP_TERMINATING, |
| 169 NotificationService::AllSources()); |
| 170 } |
| 171 |
| 172 BackgroundModeManager::~BackgroundModeManager() { |
| 173 for (std::map<Profile*, BackgroundModeInfo>::iterator it = |
| 174 background_mode_data_.begin(); |
| 175 it != background_mode_data_.end(); |
| 176 ++it) { |
| 177 it->second->applications_->RemoveObserver(this); |
| 178 } |
| 179 |
| 180 // We're going away, so exit background mode (does nothing if we aren't in |
| 181 // background mode currently). This is primarily needed for unit tests, |
| 182 // because in an actual running system we'd get an APP_TERMINATING |
| 183 // notification before being destroyed. |
| 184 EndBackgroundMode(); |
| 185 } |
| 186 |
| 187 // static |
| 188 void BackgroundModeManager::RegisterPrefs(PrefService* prefs) { |
| 189 prefs->RegisterBooleanPref(prefs::kUserCreatedLoginItem, false); |
| 190 prefs->RegisterBooleanPref(prefs::kBackgroundModeEnabled, true); |
| 191 } |
| 192 |
| 193 |
| 194 void BackgroundModeManager::RegisterProfile(Profile* profile) { |
| 195 // We don't want to register multiple times for one profile. |
| 196 DCHECK(background_mode_data_.find(profile) == background_mode_data_.end()); |
| 197 BackgroundModeInfo bmd(new BackgroundModeData(profile, this)); |
| 198 background_mode_data_[profile] = bmd; |
| 199 |
| 78 // Listen for when extensions are loaded/unloaded so we can track the | 200 // Listen for when extensions are loaded/unloaded so we can track the |
| 79 // number of background apps and modify our keep-alive and launch-on-startup | 201 // number of background apps and modify our keep-alive and launch-on-startup |
| 80 // state appropriately. | 202 // state appropriately. |
| 81 registrar_.Add(this, NotificationType::EXTENSION_LOADED, | 203 registrar_.Add(this, NotificationType::EXTENSION_LOADED, |
| 82 Source<Profile>(profile)); | 204 Source<Profile>(profile)); |
| 83 registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, | 205 registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, |
| 84 Source<Profile>(profile)); | 206 Source<Profile>(profile)); |
| 85 | 207 |
| 86 // Check for the presence of background apps after all extensions have been | 208 // Check for the presence of background apps after all extensions have been |
| 87 // loaded, to handle the case where an extension has been manually removed | 209 // loaded, to handle the case where an extension has been manually removed |
| 88 // while Chrome was not running. | 210 // while Chrome was not running. |
| 89 registrar_.Add(this, NotificationType::EXTENSIONS_READY, | 211 registrar_.Add(this, NotificationType::EXTENSIONS_READY, |
| 90 Source<Profile>(profile)); | 212 Source<Profile>(profile)); |
| 91 | 213 |
| 92 // Listen for the application shutting down so we can decrement our KeepAlive | 214 background_mode_data_[profile]->applications_->AddObserver(this); |
| 93 // count. | |
| 94 registrar_.Add(this, NotificationType::APP_TERMINATING, | |
| 95 NotificationService::AllSources()); | |
| 96 | |
| 97 applications_.AddObserver(this); | |
| 98 } | 215 } |
| 99 | 216 |
| 100 BackgroundModeManager::~BackgroundModeManager() { | 217 /////////////////////////////////////////////////////////////////////////////// |
| 101 applications_.RemoveObserver(this); | 218 // BackgroundModeManager, NotificationObserver overrides |
| 102 | |
| 103 // We're going away, so exit background mode (does nothing if we aren't in | |
| 104 // background mode currently). This is primarily needed for unit tests, | |
| 105 // because in an actual running system we'd get an APP_TERMINATING | |
| 106 // notification before being destroyed. | |
| 107 EndBackgroundMode(); | |
| 108 } | |
| 109 | |
| 110 void BackgroundModeManager::Observe(NotificationType type, | 219 void BackgroundModeManager::Observe(NotificationType type, |
| 111 const NotificationSource& source, | 220 const NotificationSource& source, |
| 112 const NotificationDetails& details) { | 221 const NotificationDetails& details) { |
| 113 switch (type.value) { | 222 switch (type.value) { |
| 114 case NotificationType::PREF_CHANGED: | 223 case NotificationType::PREF_CHANGED: |
| 115 DCHECK(*Details<std::string>(details).ptr() == | 224 DCHECK(*Details<std::string>(details).ptr() == |
| 116 prefs::kBackgroundModeEnabled); | 225 prefs::kBackgroundModeEnabled); |
| 117 if (IsBackgroundModePrefEnabled()) | 226 if (IsBackgroundModePrefEnabled()) |
| 118 EnableBackgroundMode(); | 227 EnableBackgroundMode(); |
| 119 else | 228 else |
| (...skipping 12 matching lines...) Expand all Loading... |
| 132 // launch on startup even after the user removes the LoginItem manually. | 241 // launch on startup even after the user removes the LoginItem manually. |
| 133 #if !defined(OS_MACOSX) | 242 #if !defined(OS_MACOSX) |
| 134 EnableLaunchOnStartup(background_app_count_ > 0); | 243 EnableLaunchOnStartup(background_app_count_ > 0); |
| 135 #endif | 244 #endif |
| 136 break; | 245 break; |
| 137 case NotificationType::EXTENSION_LOADED: { | 246 case NotificationType::EXTENSION_LOADED: { |
| 138 Extension* extension = Details<Extension>(details).ptr(); | 247 Extension* extension = Details<Extension>(details).ptr(); |
| 139 if (BackgroundApplicationListModel::IsBackgroundApp(*extension)) { | 248 if (BackgroundApplicationListModel::IsBackgroundApp(*extension)) { |
| 140 // Extensions loaded after the ExtensionsService is ready should be | 249 // Extensions loaded after the ExtensionsService is ready should be |
| 141 // treated as new installs. | 250 // treated as new installs. |
| 142 if (profile_->GetExtensionService()->is_ready()) | 251 Profile* profile = Source<Profile>(source).ptr(); |
| 143 OnBackgroundAppInstalled(extension); | 252 if (profile->GetExtensionService()->is_ready()) |
| 253 OnBackgroundAppInstalled(extension, profile); |
| 144 OnBackgroundAppLoaded(); | 254 OnBackgroundAppLoaded(); |
| 145 } | 255 } |
| 146 } | 256 } |
| 147 break; | 257 break; |
| 148 case NotificationType::EXTENSION_UNLOADED: | 258 case NotificationType::EXTENSION_UNLOADED: |
| 149 if (BackgroundApplicationListModel::IsBackgroundApp( | 259 if (BackgroundApplicationListModel::IsBackgroundApp( |
| 150 *Details<UnloadedExtensionInfo>(details)->extension)) { | 260 *Details<UnloadedExtensionInfo>(details)->extension)) { |
| 151 Details<UnloadedExtensionInfo> info = | 261 Details<UnloadedExtensionInfo> info = |
| 152 Details<UnloadedExtensionInfo>(details); | 262 Details<UnloadedExtensionInfo>(details); |
| 153 // If we already got an unload notification when it was disabled, ignore | 263 // If we already got an unload notification when it was disabled, ignore |
| 154 // this one. | 264 // this one. |
| 155 // TODO(atwilson): Change BackgroundModeManager to use | 265 // TODO(atwilson): Change BackgroundModeManager to use |
| 156 // BackgroundApplicationListModel instead of tracking the count here. | 266 // BackgroundApplicationListModel instead of tracking the count here. |
| 157 if (info->already_disabled) | 267 if (info->already_disabled) |
| 158 return; | 268 return; |
| 159 OnBackgroundAppUnloaded(); | 269 OnBackgroundAppUnloaded(); |
| 160 OnBackgroundAppUninstalled(); | 270 Profile* profile = Source<Profile>(source).ptr(); |
| 271 OnBackgroundAppUninstalled(profile); |
| 161 } | 272 } |
| 162 break; | 273 break; |
| 163 case NotificationType::APP_TERMINATING: | 274 case NotificationType::APP_TERMINATING: |
| 164 // Make sure we aren't still keeping the app alive (only happens if we | 275 // Make sure we aren't still keeping the app alive (only happens if we |
| 165 // don't receive an EXTENSIONS_READY notification for some reason). | 276 // don't receive an EXTENSIONS_READY notification for some reason). |
| 166 EndKeepAliveForStartup(); | 277 EndKeepAliveForStartup(); |
| 167 // Performing an explicit shutdown, so exit background mode (does nothing | 278 // Performing an explicit shutdown, so exit background mode (does nothing |
| 168 // if we aren't in background mode currently). | 279 // if we aren't in background mode currently). |
| 169 EndBackgroundMode(); | 280 EndBackgroundMode(); |
| 170 // Shutting down, so don't listen for any more notifications so we don't | 281 // Shutting down, so don't listen for any more notifications so we don't |
| 171 // try to re-enter/exit background mode again. | 282 // try to re-enter/exit background mode again. |
| 172 registrar_.RemoveAll(); | 283 registrar_.RemoveAll(); |
| 173 break; | 284 break; |
| 174 default: | 285 default: |
| 175 NOTREACHED(); | 286 NOTREACHED(); |
| 176 break; | 287 break; |
| 177 } | 288 } |
| 178 } | 289 } |
| 179 | 290 |
| 291 /////////////////////////////////////////////////////////////////////////////// |
| 292 // BackgroundModeManager, BackgroundApplicationListModel::Observer overrides |
| 293 void BackgroundModeManager::OnApplicationDataChanged( |
| 294 const Extension* extension, Profile* profile) { |
| 295 UpdateContextMenuEntryIcon(extension, profile); |
| 296 } |
| 297 |
| 298 void BackgroundModeManager::OnApplicationListChanged(Profile* profile) { |
| 299 UpdateStatusTrayIconContextMenu(profile); |
| 300 } |
| 301 |
| 302 |
| 303 /////////////////////////////////////////////////////////////////////////////// |
| 304 // BackgroundModeManager, private |
| 180 void BackgroundModeManager::EndKeepAliveForStartup() { | 305 void BackgroundModeManager::EndKeepAliveForStartup() { |
| 181 if (keep_alive_for_startup_) { | 306 if (keep_alive_for_startup_) { |
| 182 keep_alive_for_startup_ = false; | 307 keep_alive_for_startup_ = false; |
| 183 // We call this via the message queue to make sure we don't try to end | 308 // We call this via the message queue to make sure we don't try to end |
| 184 // keep-alive (which can shutdown Chrome) before the message loop has | 309 // keep-alive (which can shutdown Chrome) before the message loop has |
| 185 // started. | 310 // started. |
| 186 MessageLoop::current()->PostTask( | 311 MessageLoop::current()->PostTask( |
| 187 FROM_HERE, NewRunnableFunction(BrowserList::EndKeepAlive)); | 312 FROM_HERE, NewRunnableFunction(BrowserList::EndKeepAlive)); |
| 188 } | 313 } |
| 189 } | 314 } |
| 190 | 315 |
| 191 void BackgroundModeManager::OnBackgroundAppLoaded() { | 316 void BackgroundModeManager::OnBackgroundAppLoaded() { |
| 192 // When a background app loads, increment our count and also enable | 317 // When a background app loads, increment our count and also enable |
| 193 // KeepAlive mode if the preference is set. | 318 // KeepAlive mode if the preference is set. |
| 319 // The count here is across all profiles since we must have background |
| 320 // mode if there is even one. |
| 194 background_app_count_++; | 321 background_app_count_++; |
| 195 if (background_app_count_ == 1) | 322 if (background_app_count_ == 1) |
| 196 StartBackgroundMode(); | 323 StartBackgroundMode(); |
| 197 } | 324 } |
| 198 | 325 |
| 199 void BackgroundModeManager::StartBackgroundMode() { | 326 void BackgroundModeManager::StartBackgroundMode() { |
| 200 // Don't bother putting ourselves in background mode if we're already there | 327 // Don't bother putting ourselves in background mode if we're already there |
| 201 // or if background mode is disabled. | 328 // or if background mode is disabled. |
| 202 if (in_background_mode_ || !IsBackgroundModePrefEnabled()) | 329 if (in_background_mode_ || !IsBackgroundModePrefEnabled()) |
| 203 return; | 330 return; |
| 204 | 331 |
| 205 // Mark ourselves as running in background mode. | 332 // Mark ourselves as running in background mode. |
| 206 in_background_mode_ = true; | 333 in_background_mode_ = true; |
| 207 | 334 |
| 208 // Put ourselves in KeepAlive mode and create a status tray icon. | 335 // Put ourselves in KeepAlive mode and create a status tray icon. |
| 209 BrowserList::StartKeepAlive(); | 336 BrowserList::StartKeepAlive(); |
| 210 | 337 |
| 211 // Display a status icon to exit Chrome. | 338 // Display a status icon to exit Chrome. |
| 212 CreateStatusTrayIcon(); | 339 InitStatusTrayIcons(); |
| 340 } |
| 341 |
| 342 void BackgroundModeManager::InitStatusTrayIcons() { |
| 343 // Only initialize status tray icons for those profiles which actually |
| 344 // have a background app running. |
| 345 for (std::map<Profile*, BackgroundModeInfo>::iterator it = |
| 346 background_mode_data_.begin(); |
| 347 it != background_mode_data_.end(); |
| 348 ++it) { |
| 349 if (it->second->HasBackgroundApp()) |
| 350 CreateStatusTrayIcon(it->first); |
| 351 } |
| 213 } | 352 } |
| 214 | 353 |
| 215 void BackgroundModeManager::OnBackgroundAppUnloaded() { | 354 void BackgroundModeManager::OnBackgroundAppUnloaded() { |
| 216 // When a background app unloads, decrement our count and also end | 355 // When a background app unloads, decrement our count and also end |
| 217 // KeepAlive mode if appropriate. | 356 // KeepAlive mode if appropriate. |
| 218 background_app_count_--; | 357 background_app_count_--; |
| 219 DCHECK_GE(background_app_count_, 0); | 358 DCHECK_GE(background_app_count_, 0); |
| 220 if (background_app_count_ == 0) | 359 if (background_app_count_ == 0) |
| 221 EndBackgroundMode(); | 360 EndBackgroundMode(); |
| 222 } | 361 } |
| 223 | 362 |
| 224 void BackgroundModeManager::EndBackgroundMode() { | 363 void BackgroundModeManager::EndBackgroundMode() { |
| 225 if (!in_background_mode_) | 364 if (!in_background_mode_) |
| 226 return; | 365 return; |
| 227 in_background_mode_ = false; | 366 in_background_mode_ = false; |
| 228 | 367 |
| 229 // End KeepAlive mode and blow away our status tray icon. | 368 // End KeepAlive mode and blow away our status tray icon. |
| 230 BrowserList::EndKeepAlive(); | 369 BrowserList::EndKeepAlive(); |
| 231 RemoveStatusTrayIcon(); | 370 // There is a status tray icon for each profile. Blow them all away. |
| 371 for (std::map<Profile*, BackgroundModeInfo>::iterator it = |
| 372 background_mode_data_.begin(); |
| 373 it != background_mode_data_.end(); |
| 374 ++it) { |
| 375 RemoveStatusTrayIcon(it->first); |
| 376 } |
| 232 } | 377 } |
| 233 | 378 |
| 234 void BackgroundModeManager::EnableBackgroundMode() { | 379 void BackgroundModeManager::EnableBackgroundMode() { |
| 235 DCHECK(IsBackgroundModePrefEnabled()); | 380 DCHECK(IsBackgroundModePrefEnabled()); |
| 236 // If background mode should be enabled, but isn't, turn it on. | 381 // If background mode should be enabled, but isn't, turn it on. |
| 237 if (background_app_count_ > 0 && !in_background_mode_) { | 382 if (background_app_count_ > 0 && !in_background_mode_) { |
| 238 StartBackgroundMode(); | 383 StartBackgroundMode(); |
| 239 EnableLaunchOnStartup(true); | 384 EnableLaunchOnStartup(true); |
| 240 } | 385 } |
| 241 } | 386 } |
| 242 | 387 |
| 243 void BackgroundModeManager::DisableBackgroundMode() { | 388 void BackgroundModeManager::DisableBackgroundMode() { |
| 244 DCHECK(!IsBackgroundModePrefEnabled()); | 389 DCHECK(!IsBackgroundModePrefEnabled()); |
| 245 // If background mode is currently enabled, turn it off. | 390 // If background mode is currently enabled, turn it off. |
| 246 if (in_background_mode_) { | 391 if (in_background_mode_) { |
| 247 EndBackgroundMode(); | 392 EndBackgroundMode(); |
| 248 EnableLaunchOnStartup(false); | 393 EnableLaunchOnStartup(false); |
| 249 } | 394 } |
| 250 } | 395 } |
| 251 | 396 |
| 252 void BackgroundModeManager::OnBackgroundAppInstalled( | 397 void BackgroundModeManager::OnBackgroundAppInstalled( |
| 253 const Extension* extension) { | 398 const Extension* extension, Profile* profile) { |
| 254 // Background mode is disabled - don't do anything. | 399 // Background mode is disabled - don't do anything. |
| 255 if (!IsBackgroundModePrefEnabled()) | 400 if (!IsBackgroundModePrefEnabled()) |
| 256 return; | 401 return; |
| 257 | 402 |
| 258 // We're installing a background app. If this is the first background app | 403 // We're installing a background app. If this is the first background app |
| 259 // being installed, make sure we are set to launch on startup. | 404 // being installed, make sure we are set to launch on startup. |
| 260 if (background_app_count_ == 0) | 405 if (background_app_count_ == 0) |
| 261 EnableLaunchOnStartup(true); | 406 EnableLaunchOnStartup(true); |
| 262 | 407 |
| 408 // Check if we need a status tray icon and make one if we do. |
| 409 CreateStatusTrayIcon(profile); |
| 410 |
| 263 // Notify the user that a background app has been installed. | 411 // Notify the user that a background app has been installed. |
| 264 if (extension) // NULL when called by unit tests. | 412 if (extension) // NULL when called by unit tests. |
| 265 DisplayAppInstalledNotification(extension); | 413 DisplayAppInstalledNotification(extension, profile); |
| 266 } | 414 } |
| 267 | 415 |
| 268 void BackgroundModeManager::OnBackgroundAppUninstalled() { | 416 void BackgroundModeManager::OnBackgroundAppUninstalled(Profile* profile) { |
| 417 // Check if we need to remove the status tray icon if there are no |
| 418 // more background apps. |
| 419 BackgroundModeInfo bmd = GetBackgroundModeInfo(profile); |
| 420 DCHECK(bmd.get()); |
| 421 // If there are still background apps for this profile, don't remove |
| 422 // the status tray icon. |
| 423 if (!bmd->HasBackgroundApp()) |
| 424 RemoveStatusTrayIcon(profile); |
| 425 |
| 269 // When uninstalling a background app, disable launch on startup if | 426 // When uninstalling a background app, disable launch on startup if |
| 270 // we have no more background apps. | 427 // we have no more background apps. |
| 271 if (background_app_count_ == 0) | 428 if (background_app_count_ == 0) |
| 272 EnableLaunchOnStartup(false); | 429 EnableLaunchOnStartup(false); |
| 273 } | 430 } |
| 274 | 431 |
| 275 void BackgroundModeManager::CreateStatusTrayIcon() { | 432 void BackgroundModeManager::CreateStatusTrayIcon(Profile* profile) { |
| 276 // Only need status icons on windows/linux. ChromeOS doesn't allow exiting | 433 // Only need status icons on windows/linux. ChromeOS doesn't allow exiting |
| 277 // Chrome and Mac can use the dock icon instead. | 434 // Chrome and Mac can use the dock icon instead. |
| 435 |
| 436 // Since there are multiple profiles which share the status tray, we now |
| 437 // use the browser process to keep track of it. |
| 278 #if !defined(OS_MACOSX) && !defined(OS_CHROMEOS) | 438 #if !defined(OS_MACOSX) && !defined(OS_CHROMEOS) |
| 279 if (!status_tray_) | 439 if (!status_tray_) |
| 280 status_tray_ = profile_->GetStatusTray(); | 440 status_tray_ = g_browser_process->status_tray(); |
| 281 #endif | 441 #endif |
| 282 | 442 |
| 283 // If the platform doesn't support status icons, or we've already created | 443 // If the platform doesn't support status icons, or we've already created |
| 284 // our status icon, just return. | 444 // our status icon, just return. |
| 285 if (!status_tray_ || status_icon_) | 445 BackgroundModeInfo bmd = GetBackgroundModeInfo(profile); |
| 286 return; | 446 if (!status_tray_ || bmd->status_icon_) |
| 287 status_icon_ = status_tray_->CreateStatusIcon(); | |
| 288 if (!status_icon_) | |
| 289 return; | 447 return; |
| 290 | 448 |
| 291 // Set the image and add ourselves as a click observer on it | 449 bmd->status_icon_ = status_tray_->CreateStatusIcon(); |
| 450 if (!bmd->status_icon_) |
| 451 return; |
| 452 |
| 453 // Set the image and add ourselves as a click observer on it. |
| 454 // TODO(rlp): Status tray icon should have submenus for each profile. |
| 292 SkBitmap* bitmap = ResourceBundle::GetSharedInstance().GetBitmapNamed( | 455 SkBitmap* bitmap = ResourceBundle::GetSharedInstance().GetBitmapNamed( |
| 293 IDR_STATUS_TRAY_ICON); | 456 IDR_STATUS_TRAY_ICON); |
| 294 status_icon_->SetImage(*bitmap); | 457 bmd->status_icon_->SetImage(*bitmap); |
| 295 status_icon_->SetToolTip(l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)); | 458 bmd->status_icon_->SetToolTip(l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)); |
| 296 UpdateStatusTrayIconContextMenu(); | 459 UpdateStatusTrayIconContextMenu(profile); |
| 297 } | 460 } |
| 298 | 461 |
| 299 void BackgroundModeManager::UpdateContextMenuEntryIcon( | 462 void BackgroundModeManager::UpdateContextMenuEntryIcon( |
| 300 const Extension* extension) { | 463 const Extension* extension, Profile* profile) { |
| 301 if (!context_menu_) | 464 BackgroundModeInfo bmd = GetBackgroundModeInfo(profile); |
| 302 return; | 465 DCHECK(bmd.get()); |
| 303 context_menu_->SetIcon( | 466 bmd->UpdateContextMenuEntryIcon(extension); |
| 304 context_menu_application_offset_ + applications_.GetPosition(extension), | |
| 305 *(applications_.GetIcon(extension))); | |
| 306 status_icon_->SetContextMenu(context_menu_); // for Update effect | |
| 307 } | 467 } |
| 308 | 468 |
| 309 void BackgroundModeManager::UpdateStatusTrayIconContextMenu() { | 469 void BackgroundModeManager::UpdateStatusTrayIconContextMenu(Profile* profile) { |
| 310 if (!status_icon_) | 470 BackgroundModeInfo bmd = GetBackgroundModeInfo(profile); |
| 311 return; | 471 DCHECK(bmd.get()); |
| 472 if (!bmd->status_icon_) { |
| 473 // If no status icon exists, it's either because one wasn't created when |
| 474 // it should have been which can happen when extensions load after the |
| 475 // profile has already been registered with the background mode manager. |
| 476 // The other case is if we aren't in background mode. |
| 477 if (in_background_mode_) |
| 478 CreateStatusTrayIcon(profile); |
| 479 else |
| 480 return; |
| 481 } |
| 312 | 482 |
| 483 // TODO(rlp): Add current profile color. |
| 313 // Create a context menu item for Chrome. | 484 // Create a context menu item for Chrome. |
| 314 ui::SimpleMenuModel* menu = new ui::SimpleMenuModel(this); | 485 ui::SimpleMenuModel* menu = new ui::SimpleMenuModel(bmd.get()); |
| 315 // Add About item | 486 // Add About item |
| 316 menu->AddItem(IDC_ABOUT, l10n_util::GetStringFUTF16(IDS_ABOUT, | 487 menu->AddItem(IDC_ABOUT, l10n_util::GetStringFUTF16(IDS_ABOUT, |
| 317 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); | 488 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); |
| 318 menu->AddItem(IDC_OPTIONS, GetPreferencesMenuLabel()); | 489 menu->AddItem(IDC_OPTIONS, GetPreferencesMenuLabel()); |
| 319 menu->AddItemWithStringId(IDC_TASK_MANAGER, IDS_TASK_MANAGER); | 490 menu->AddItemWithStringId(IDC_TASK_MANAGER, IDS_TASK_MANAGER); |
| 320 menu->AddSeparator(); | 491 menu->AddSeparator(); |
| 321 int position = 0; | 492 int position = 0; |
| 322 context_menu_application_offset_ = menu->GetItemCount(); | 493 bmd->context_menu_application_offset_ = menu->GetItemCount(); |
| 323 for (ExtensionList::const_iterator cursor = applications_.begin(); | 494 for (ExtensionList::const_iterator cursor = bmd->applications_->begin(); |
| 324 cursor != applications_.end(); | 495 cursor != bmd->applications_->end(); |
| 325 ++cursor, ++position) { | 496 ++cursor, ++position) { |
| 326 const SkBitmap* icon = applications_.GetIcon(*cursor); | 497 const SkBitmap* icon = bmd->applications_->GetIcon(*cursor); |
| 327 DCHECK(position == applications_.GetPosition(*cursor)); | 498 DCHECK(position == bmd->applications_->GetPosition(*cursor)); |
| 328 const std::string& name = (*cursor)->name(); | 499 const std::string& name = (*cursor)->name(); |
| 329 menu->AddItem(position, UTF8ToUTF16(name)); | 500 menu->AddItem(position, UTF8ToUTF16(name)); |
| 330 if (icon) | 501 if (icon) |
| 331 menu->SetIcon(menu->GetItemCount() - 1, *icon); | 502 menu->SetIcon(menu->GetItemCount() - 1, *icon); |
| 332 } | 503 } |
| 333 if (applications_.size() > 0) | 504 if (bmd->applications_->size() > 0) |
| 334 menu->AddSeparator(); | 505 menu->AddSeparator(); |
| 335 menu->AddCheckItemWithStringId( | 506 menu->AddCheckItemWithStringId( |
| 336 IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND, | 507 IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND, |
| 337 IDS_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND); | 508 IDS_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND); |
| 338 menu->AddItemWithStringId(IDC_EXIT, IDS_EXIT); | 509 menu->AddItemWithStringId(IDC_EXIT, IDS_EXIT); |
| 339 context_menu_ = menu; | 510 bmd->context_menu_ = menu; |
| 340 status_icon_->SetContextMenu(menu); | 511 bmd->status_icon_->SetContextMenu(menu); |
| 341 } | 512 } |
| 342 | 513 |
| 343 bool BackgroundModeManager::IsCommandIdChecked(int command_id) const { | 514 void BackgroundModeManager::RemoveStatusTrayIcon(Profile* profile) { |
| 344 DCHECK(command_id == IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND); | 515 BackgroundModeInfo bmd = GetBackgroundModeInfo(profile); |
| 345 return true; | 516 DCHECK(bmd.get()); |
| 517 |
| 518 if (bmd->status_icon_) |
| 519 status_tray_->RemoveStatusIcon(bmd->status_icon_); |
| 520 bmd->status_icon_ = NULL; |
| 521 bmd->context_menu_ = NULL; // Do not delete, points within |status_icon_|. |
| 346 } | 522 } |
| 347 | 523 |
| 348 bool BackgroundModeManager::IsCommandIdEnabled(int command_id) const { | 524 BackgroundModeManager::BackgroundModeInfo |
| 349 // For now, we do not support disabled items. | 525 BackgroundModeManager::GetBackgroundModeInfo(Profile* profile) { |
| 350 return true; | 526 DCHECK(background_mode_data_.find(profile) != background_mode_data_.end()); |
| 351 } | 527 return background_mode_data_[profile]; |
| 352 | |
| 353 bool BackgroundModeManager::GetAcceleratorForCommandId( | |
| 354 int command_id, | |
| 355 ui::Accelerator* accelerator) { | |
| 356 // No accelerators for status icon context menus. | |
| 357 return false; | |
| 358 } | |
| 359 | |
| 360 void BackgroundModeManager::RemoveStatusTrayIcon() { | |
| 361 if (status_icon_) | |
| 362 status_tray_->RemoveStatusIcon(status_icon_); | |
| 363 status_icon_ = NULL; | |
| 364 context_menu_ = NULL; // Do not delete, points within |status_icon_|. | |
| 365 } | |
| 366 | |
| 367 void BackgroundModeManager::ExecuteApplication(int item) { | |
| 368 DCHECK(item >= 0 && item < static_cast<int>(applications_.size())); | |
| 369 Browser* browser = BrowserList::GetLastActive(); | |
| 370 if (!browser) { | |
| 371 Browser::OpenEmptyWindow(profile_); | |
| 372 browser = BrowserList::GetLastActive(); | |
| 373 } | |
| 374 const Extension* extension = applications_.GetExtension(item); | |
| 375 browser->OpenApplicationTab(profile_, extension, NEW_FOREGROUND_TAB); | |
| 376 } | |
| 377 | |
| 378 void BackgroundModeManager::ExecuteCommand(int item) { | |
| 379 switch (item) { | |
| 380 case IDC_ABOUT: | |
| 381 GetBrowserWindow()->OpenAboutChromeDialog(); | |
| 382 break; | |
| 383 case IDC_EXIT: | |
| 384 UserMetrics::RecordAction(UserMetricsAction("Exit")); | |
| 385 BrowserList::CloseAllBrowsersAndExit(); | |
| 386 break; | |
| 387 case IDC_OPTIONS: | |
| 388 GetBrowserWindow()->OpenOptionsDialog(); | |
| 389 break; | |
| 390 case IDC_TASK_MANAGER: | |
| 391 GetBrowserWindow()->OpenTaskManager(true); | |
| 392 break; | |
| 393 case IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND: { | |
| 394 // Background mode must already be enabled (as otherwise this menu would | |
| 395 // not be visible). | |
| 396 DCHECK(IsBackgroundModePrefEnabled()); | |
| 397 DCHECK(BrowserList::WillKeepAlive()); | |
| 398 | |
| 399 // Set the background mode pref to "disabled" - the resulting notification | |
| 400 // will result in a call to DisableBackgroundMode(). | |
| 401 PrefService* service = g_browser_process->local_state(); | |
| 402 DCHECK(service); | |
| 403 service->SetBoolean(prefs::kBackgroundModeEnabled, false); | |
| 404 break; | |
| 405 } | |
| 406 default: | |
| 407 ExecuteApplication(item); | |
| 408 break; | |
| 409 } | |
| 410 } | |
| 411 | |
| 412 Browser* BackgroundModeManager::GetBrowserWindow() { | |
| 413 Browser* browser = BrowserList::GetLastActive(); | |
| 414 if (!browser) { | |
| 415 Browser::OpenEmptyWindow(profile_); | |
| 416 browser = BrowserList::GetLastActive(); | |
| 417 } | |
| 418 return browser; | |
| 419 } | 528 } |
| 420 | 529 |
| 421 // static | 530 // static |
| 422 bool BackgroundModeManager::IsBackgroundModePermanentlyDisabled( | 531 bool BackgroundModeManager::IsBackgroundModePermanentlyDisabled( |
| 423 const CommandLine* command_line) { | 532 const CommandLine* command_line) { |
| 424 | 533 |
| 425 // Background mode is disabled if the appropriate flag is passed, or if | 534 // Background mode is disabled if the appropriate flag is passed, or if |
| 426 // extensions are disabled, or if the associated preference is unset. It's | 535 // extensions are disabled, or if the associated preference is unset. It's |
| 427 // always disabled on chromeos since chrome is always running on that | 536 // always disabled on chromeos since chrome is always running on that |
| 428 // platform, making it superfluous. | 537 // platform, making it superfluous. |
| 429 #if defined(OS_CHROMEOS) | 538 #if defined(OS_CHROMEOS) |
| 430 return true; | 539 return true; |
| 431 #else | 540 #else |
| 432 bool background_mode_disabled = | 541 bool background_mode_disabled = |
| 433 command_line->HasSwitch(switches::kDisableBackgroundMode) || | 542 command_line->HasSwitch(switches::kDisableBackgroundMode) || |
| 434 command_line->HasSwitch(switches::kDisableExtensions); | 543 command_line->HasSwitch(switches::kDisableExtensions); |
| 435 return background_mode_disabled; | 544 return background_mode_disabled; |
| 436 #endif | 545 #endif |
| 437 } | 546 } |
| 438 | 547 |
| 439 bool BackgroundModeManager::IsBackgroundModePrefEnabled() { | 548 bool BackgroundModeManager::IsBackgroundModePrefEnabled() { |
| 440 PrefService* service = g_browser_process->local_state(); | 549 PrefService* service = g_browser_process->local_state(); |
| 441 DCHECK(service); | 550 DCHECK(service); |
| 442 return service->GetBoolean(prefs::kBackgroundModeEnabled); | 551 return service->GetBoolean(prefs::kBackgroundModeEnabled); |
| 443 } | 552 } |
| 444 | |
| 445 // static | |
| 446 void BackgroundModeManager::RegisterPrefs(PrefService* prefs) { | |
| 447 prefs->RegisterBooleanPref(prefs::kUserCreatedLoginItem, false); | |
| 448 prefs->RegisterBooleanPref(prefs::kBackgroundModeEnabled, true); | |
| 449 } | |
| OLD | NEW |