1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
9 #include "base/base_paths.h"
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/logging.h"
13 #include "base/prefs/pref_registry_simple.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/app/chrome_command_ids.h"
17 #include "chrome/browser/background/background_application_list_model.h"
18 #include "chrome/browser/background/background_mode_manager.h"
19 #include "chrome/browser/browser_process.h"
20 #include "chrome/browser/browser_shutdown.h"
21 #include "chrome/browser/chrome_notification_types.h"
22 #include "chrome/browser/extensions/extension_service.h"
23 #include "chrome/browser/extensions/extension_system.h"
24 #include "chrome/browser/lifetime/application_lifetime.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/browser/profiles/profile_info_cache.h"
27 #include "chrome/browser/profiles/profile_manager.h"
28 #include "chrome/browser/status_icons/status_icon.h"
29 #include "chrome/browser/status_icons/status_tray.h"
30 #include "chrome/browser/ui/browser.h"
31 #include "chrome/browser/ui/browser_commands.h"
32 #include "chrome/browser/ui/browser_finder.h"
33 #include "chrome/browser/ui/browser_list.h"
34 #include "chrome/browser/ui/chrome_pages.h"
35 #include "chrome/browser/ui/extensions/application_launch.h"
36 #include "chrome/browser/ui/host_desktop.h"
37 #include "chrome/common/chrome_constants.h"
38 #include "chrome/common/chrome_switches.h"
39 #include "chrome/common/extensions/extension.h"
40 #include "chrome/common/extensions/extension_constants.h"
41 #include "chrome/common/extensions/manifest_url_handler.h"
42 #include "chrome/common/pref_names.h"
43 #include "content/public/browser/notification_service.h"
44 #include "content/public/browser/user_metrics.h"
45 #include "extensions/common/permissions/permission_set.h"
46 #include "grit/chrome_unscaled_resources.h"
47 #include "grit/chromium_strings.h"
48 #include "grit/generated_resources.h"
49 #include "ui/base/l10n/l10n_util.h"
50 #include "ui/base/resource/resource_bundle.h"
52 using content::UserMetricsAction;
53 using extensions::Extension;
54 using extensions::UpdatedExtensionPermissionsInfo;
56 BackgroundModeManager::BackgroundModeData::BackgroundModeData(
59 : applications_(new BackgroundApplicationListModel(profile)),
60 command_id_(command_id),
64 BackgroundModeManager::BackgroundModeData::~BackgroundModeData() {
67 ///////////////////////////////////////////////////////////////////////////////
68 // BackgroundModeManager::BackgroundModeData, StatusIconMenuModel overrides
69 void BackgroundModeManager::BackgroundModeData::ExecuteCommand(
73 case IDC_MinimumLabelValue:
74 // Do nothing. This is just a label.
77 // Launch the app associated with this item.
78 const Extension* extension = applications_->
80 BackgroundModeManager::LaunchBackgroundApplication(profile_, extension);
85 Browser* BackgroundModeManager::BackgroundModeData::GetBrowserWindow() {
86 chrome::HostDesktopType host_desktop_type = chrome::GetActiveDesktop();
87 Browser* browser = chrome::FindLastActiveWithProfile(profile_,
89 return browser ? browser : chrome::OpenEmptyWindow(profile_,
93 int BackgroundModeManager::BackgroundModeData::GetBackgroundAppCount() const {
94 return applications_->size();
97 void BackgroundModeManager::BackgroundModeData::BuildProfileMenu(
98 StatusIconMenuModel* menu,
99 StatusIconMenuModel* containing_menu) {
101 // When there are no background applications, we want to display
102 // just a label stating that none are running.
103 if (applications_->size() < 1) {
104 menu->AddItemWithStringId(IDC_MinimumLabelValue,
105 IDS_BACKGROUND_APP_NOT_INSTALLED);
106 menu->SetCommandIdEnabled(IDC_MinimumLabelValue, false);
108 for (extensions::ExtensionList::const_iterator cursor =
109 applications_->begin();
110 cursor != applications_->end();
111 ++cursor, ++position) {
112 const gfx::ImageSkia* icon = applications_->GetIcon(cursor->get());
113 DCHECK(position == applications_->GetPosition(cursor->get()));
114 const std::string& name = (*cursor)->name();
115 menu->AddItem(position, UTF8ToUTF16(name));
117 menu->SetIcon(menu->GetItemCount() - 1, gfx::Image(*icon));
119 // Component extensions with background that do not have an options page
120 // will cause this menu item to go to the extensions page with an
121 // absent component extension.
123 // Ideally, we would remove this item, but this conflicts with the user
124 // model where this menu shows the extensions with background.
126 // The compromise is to disable the item, avoiding the non-actionable
127 // navigate to the extensions page and preserving the user model.
128 if ((*cursor)->location() == extensions::Manifest::COMPONENT) {
129 GURL options_page = extensions::ManifestURL::GetOptionsPage(*cursor);
130 if (!options_page.is_valid())
131 menu->SetCommandIdEnabled(position, false);
136 containing_menu->AddSubMenu(command_id_, name_, menu);
139 void BackgroundModeManager::BackgroundModeData::SetName(
140 const string16& new_profile_name) {
141 name_ = new_profile_name;
144 string16 BackgroundModeManager::BackgroundModeData::name() {
149 bool BackgroundModeManager::BackgroundModeData::BackgroundModeDataCompare(
150 const BackgroundModeData* bmd1,
151 const BackgroundModeData* bmd2) {
152 return bmd1->name_ < bmd2->name_;
156 ///////////////////////////////////////////////////////////////////////////////
157 // BackgroundModeManager, public
158 BackgroundModeManager::BackgroundModeManager(
159 CommandLine* command_line,
160 ProfileInfoCache* profile_cache)
161 : profile_cache_(profile_cache),
165 in_background_mode_(false),
166 keep_alive_for_startup_(false),
167 keep_alive_for_test_(false),
168 background_mode_suspended_(false),
169 keeping_alive_(false),
170 current_command_id_(0) {
171 // We should never start up if there is no browser process or if we are
172 // currently quitting.
173 CHECK(g_browser_process != NULL);
174 CHECK(!browser_shutdown::IsTryingToQuit());
176 // Add self as an observer for the profile info cache so we know when profiles
177 // are deleted and their names change.
178 profile_cache_->AddObserver(this);
180 // Listen for the background mode preference changing.
181 if (g_browser_process->local_state()) { // Skip for unit tests
182 pref_registrar_.Init(g_browser_process->local_state());
184 prefs::kBackgroundModeEnabled,
185 base::Bind(&BackgroundModeManager::OnBackgroundModeEnabledPrefChanged,
186 base::Unretained(this)));
189 // Keep the browser alive until extensions are done loading - this is needed
190 // by the --no-startup-window flag. We want to stay alive until we load
191 // extensions, at which point we should either run in background mode (if
192 // there are background apps) or exit if there are none.
193 if (command_line->HasSwitch(switches::kNoStartupWindow)) {
194 keep_alive_for_startup_ = true;
195 chrome::StartKeepAlive();
197 // Otherwise, start with background mode suspended in case we're launching
198 // in a mode that doesn't open a browser window. It will be resumed when the
199 // first browser window is opened.
200 SuspendBackgroundMode();
203 // If the -keep-alive-for-test flag is passed, then always keep chrome running
204 // in the background until the user explicitly terminates it.
205 if (command_line->HasSwitch(switches::kKeepAliveForTest))
206 keep_alive_for_test_ = true;
208 if (ShouldBeInBackgroundMode())
209 StartBackgroundMode();
211 // Listen for the application shutting down so we can decrement our KeepAlive
213 registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
214 content::NotificationService::AllSources());
215 BrowserList::AddObserver(this);
218 BackgroundModeManager::~BackgroundModeManager() {
219 // Remove ourselves from the application observer list (only needed by unit
220 // tests since APP_TERMINATING is what does this in a real running system).
221 for (BackgroundModeInfoMap::iterator it =
222 background_mode_data_.begin();
223 it != background_mode_data_.end();
225 it->second->applications_->RemoveObserver(this);
227 BrowserList::RemoveObserver(this);
229 // We're going away, so exit background mode (does nothing if we aren't in
230 // background mode currently). This is primarily needed for unit tests,
231 // because in an actual running system we'd get an APP_TERMINATING
232 // notification before being destroyed.
237 void BackgroundModeManager::RegisterPrefs(PrefRegistrySimple* registry) {
238 #if defined(OS_MACOSX)
239 registry->RegisterBooleanPref(prefs::kUserRemovedLoginItem, false);
240 registry->RegisterBooleanPref(prefs::kChromeCreatedLoginItem, false);
241 registry->RegisterBooleanPref(prefs::kMigratedLoginItemPref, false);
243 registry->RegisterBooleanPref(prefs::kBackgroundModeEnabled, true);
247 void BackgroundModeManager::RegisterProfile(Profile* profile) {
248 // We don't want to register multiple times for one profile.
249 DCHECK(background_mode_data_.find(profile) == background_mode_data_.end());
250 BackgroundModeInfo bmd(new BackgroundModeData(current_command_id_++,
252 background_mode_data_[profile] = bmd;
254 // Initially set the name for this background mode data.
255 size_t index = profile_cache_->GetIndexOfProfileWithPath(profile->GetPath());
256 string16 name = l10n_util::GetStringUTF16(IDS_PROFILES_DEFAULT_NAME);
257 if (index != std::string::npos)
258 name = profile_cache_->GetNameOfProfileAtIndex(index);
261 // Listen for when extensions are loaded or add the background permission so
262 // we can display a "background app installed" notification and enter
263 // "launch on login" mode on the Mac.
264 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
265 content::Source<Profile>(profile));
266 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
267 content::Source<Profile>(profile));
270 // Check for the presence of background apps after all extensions have been
271 // loaded, to handle the case where an extension has been manually removed
272 // while Chrome was not running.
273 registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY,
274 content::Source<Profile>(profile));
276 bmd->applications_->AddObserver(this);
278 // If we're adding a new profile and running in multi-profile mode, this new
279 // profile should be added to the status icon if one currently exists.
280 if (in_background_mode_ && status_icon_)
281 UpdateStatusTrayIconContextMenu();
285 void BackgroundModeManager::LaunchBackgroundApplication(
287 const Extension* extension) {
288 OpenApplication(AppLaunchParams(profile, extension, NEW_FOREGROUND_TAB));
291 bool BackgroundModeManager::IsBackgroundModeActive() {
292 return in_background_mode_;
295 int BackgroundModeManager::NumberOfBackgroundModeData() {
296 return background_mode_data_.size();
299 ///////////////////////////////////////////////////////////////////////////////
300 // BackgroundModeManager, content::NotificationObserver overrides
301 void BackgroundModeManager::Observe(
303 const content::NotificationSource& source,
304 const content::NotificationDetails& details) {
306 case chrome::NOTIFICATION_EXTENSIONS_READY:
307 // Extensions are loaded, so we don't need to manually keep the browser
308 // process alive any more when running in no-startup-window mode.
309 EndKeepAliveForStartup();
312 case chrome::NOTIFICATION_EXTENSION_LOADED: {
313 Extension* extension = content::Details<Extension>(details).ptr();
314 Profile* profile = content::Source<Profile>(source).ptr();
315 if (BackgroundApplicationListModel::IsBackgroundApp(
316 *extension, profile)) {
317 // Extensions loaded after the ExtensionsService is ready should be
318 // treated as new installs.
319 if (extensions::ExtensionSystem::Get(profile)->extension_service()->
321 bool is_being_reloaded = false;
322 CheckReloadStatus(extension, &is_being_reloaded);
323 // No need to show the notification if we showed to the user
324 // previously for this app.
325 if (!is_being_reloaded)
326 OnBackgroundAppInstalled(extension);
331 case chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED: {
332 UpdatedExtensionPermissionsInfo* info =
333 content::Details<UpdatedExtensionPermissionsInfo>(details).ptr();
334 if (info->permissions->HasAPIPermission(
335 extensions::APIPermission::kBackground) &&
336 info->reason == UpdatedExtensionPermissionsInfo::ADDED) {
337 // Turned on background permission, so treat this as a new install.
338 OnBackgroundAppInstalled(info->extension);
342 case chrome::NOTIFICATION_APP_TERMINATING:
343 // Make sure we aren't still keeping the app alive (only happens if we
344 // don't receive an EXTENSIONS_READY notification for some reason).
345 EndKeepAliveForStartup();
346 // Performing an explicit shutdown, so exit background mode (does nothing
347 // if we aren't in background mode currently).
349 // Shutting down, so don't listen for any more notifications so we don't
350 // try to re-enter/exit background mode again.
351 registrar_.RemoveAll();
352 for (BackgroundModeInfoMap::iterator it =
353 background_mode_data_.begin();
354 it != background_mode_data_.end();
356 it->second->applications_->RemoveObserver(this);
365 void BackgroundModeManager::OnBackgroundModeEnabledPrefChanged() {
366 if (IsBackgroundModePrefEnabled())
367 EnableBackgroundMode();
369 DisableBackgroundMode();
372 ///////////////////////////////////////////////////////////////////////////////
373 // BackgroundModeManager, BackgroundApplicationListModel::Observer overrides
374 void BackgroundModeManager::OnApplicationDataChanged(
375 const Extension* extension, Profile* profile) {
376 UpdateStatusTrayIconContextMenu();
379 void BackgroundModeManager::OnApplicationListChanged(Profile* profile) {
380 if (!IsBackgroundModePrefEnabled())
383 // Update the profile cache with the fact whether background apps are running
385 size_t profile_index = profile_cache_->GetIndexOfProfileWithPath(
387 if (profile_index != std::string::npos) {
388 profile_cache_->SetBackgroundStatusOfProfileAtIndex(
389 profile_index, GetBackgroundAppCountForProfile(profile) != 0);
392 if (!ShouldBeInBackgroundMode()) {
393 // We've uninstalled our last background app, make sure we exit background
394 // mode and no longer launch on startup.
395 EnableLaunchOnStartup(false);
398 // We have at least one background app running - make sure we're in
400 if (!in_background_mode_) {
401 // We're entering background mode - make sure we have launch-on-startup
402 // enabled. On Mac, the platform-specific code tracks whether the user
403 // has deleted a login item in the past, and if so, no login item will
404 // be created (to avoid overriding the specific user action).
405 EnableLaunchOnStartup(true);
407 StartBackgroundMode();
409 // List of applications changed so update the UI.
410 UpdateStatusTrayIconContextMenu();
414 ///////////////////////////////////////////////////////////////////////////////
415 // BackgroundModeManager, ProfileInfoCacheObserver overrides
416 void BackgroundModeManager::OnProfileAdded(const base::FilePath& profile_path) {
417 ProfileInfoCache& cache =
418 g_browser_process->profile_manager()->GetProfileInfoCache();
419 string16 profile_name = cache.GetNameOfProfileAtIndex(
420 cache.GetIndexOfProfileWithPath(profile_path));
421 // At this point, the profile should be registered with the background mode
422 // manager, but when it's actually added to the cache is when its name is
423 // set so we need up to update that with the background_mode_data.
424 for (BackgroundModeInfoMap::const_iterator it =
425 background_mode_data_.begin();
426 it != background_mode_data_.end();
428 if (it->first->GetPath() == profile_path) {
429 it->second->SetName(profile_name);
430 UpdateStatusTrayIconContextMenu();
436 void BackgroundModeManager::OnProfileWillBeRemoved(
437 const base::FilePath& profile_path) {
438 ProfileInfoCache& cache =
439 g_browser_process->profile_manager()->GetProfileInfoCache();
440 string16 profile_name = cache.GetNameOfProfileAtIndex(
441 cache.GetIndexOfProfileWithPath(profile_path));
442 // Remove the profile from our map of profiles.
443 BackgroundModeInfoMap::iterator it =
444 GetBackgroundModeIterator(profile_name);
445 // If a profile isn't running a background app, it may not be in the map.
446 if (it != background_mode_data_.end()) {
447 background_mode_data_.erase(it);
448 UpdateStatusTrayIconContextMenu();
452 void BackgroundModeManager::OnProfileNameChanged(
453 const base::FilePath& profile_path,
454 const string16& old_profile_name) {
455 ProfileInfoCache& cache =
456 g_browser_process->profile_manager()->GetProfileInfoCache();
457 string16 new_profile_name = cache.GetNameOfProfileAtIndex(
458 cache.GetIndexOfProfileWithPath(profile_path));
459 BackgroundModeInfoMap::const_iterator it =
460 GetBackgroundModeIterator(old_profile_name);
461 // We check that the returned iterator is valid due to unittests, but really
462 // this should only be called on profiles already known by the background
464 if (it != background_mode_data_.end()) {
465 it->second->SetName(new_profile_name);
466 UpdateStatusTrayIconContextMenu();
470 ///////////////////////////////////////////////////////////////////////////////
471 // BackgroundModeManager::BackgroundModeData, StatusIconMenuModel overrides
472 void BackgroundModeManager::ExecuteCommand(int command_id, int event_flags) {
473 // When a browser window is necessary, we use the first profile. The windows
474 // opened for these commands are not profile-specific, so any profile would
475 // work and the first is convenient.
476 BackgroundModeData* bmd = background_mode_data_.begin()->second.get();
477 switch (command_id) {
479 chrome::ShowAboutChrome(bmd->GetBrowserWindow());
481 case IDC_TASK_MANAGER:
482 chrome::OpenTaskManager(bmd->GetBrowserWindow());
485 content::RecordAction(UserMetricsAction("Exit"));
486 chrome::CloseAllBrowsers();
488 case IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND: {
489 // Background mode must already be enabled (as otherwise this menu would
491 DCHECK(IsBackgroundModePrefEnabled());
492 DCHECK(chrome::WillKeepAlive());
494 // Set the background mode pref to "disabled" - the resulting notification
495 // will result in a call to DisableBackgroundMode().
496 PrefService* service = g_browser_process->local_state();
498 service->SetBoolean(prefs::kBackgroundModeEnabled, false);
502 bmd->ExecuteCommand(command_id, event_flags);
508 ///////////////////////////////////////////////////////////////////////////////
509 // BackgroundModeManager, private
510 void BackgroundModeManager::EndKeepAliveForStartup() {
511 if (keep_alive_for_startup_) {
512 keep_alive_for_startup_ = false;
513 // We call this via the message queue to make sure we don't try to end
514 // keep-alive (which can shutdown Chrome) before the message loop has
516 base::MessageLoop::current()->PostTask(FROM_HERE,
517 base::Bind(&chrome::EndKeepAlive));
521 void BackgroundModeManager::StartBackgroundMode() {
522 DCHECK(ShouldBeInBackgroundMode());
523 // Don't bother putting ourselves in background mode if we're already there
524 // or if background mode is disabled.
525 if (in_background_mode_)
528 // Mark ourselves as running in background mode.
529 in_background_mode_ = true;
531 UpdateKeepAliveAndTrayIcon();
533 content::NotificationService::current()->Notify(
534 chrome::NOTIFICATION_BACKGROUND_MODE_CHANGED,
535 content::Source<BackgroundModeManager>(this),
536 content::Details<bool>(&in_background_mode_));
539 void BackgroundModeManager::EndBackgroundMode() {
540 if (!in_background_mode_)
542 in_background_mode_ = false;
544 UpdateKeepAliveAndTrayIcon();
546 content::NotificationService::current()->Notify(
547 chrome::NOTIFICATION_BACKGROUND_MODE_CHANGED,
548 content::Source<BackgroundModeManager>(this),
549 content::Details<bool>(&in_background_mode_));
552 void BackgroundModeManager::EnableBackgroundMode() {
553 DCHECK(IsBackgroundModePrefEnabled());
554 // If background mode should be enabled, but isn't, turn it on.
555 if (!in_background_mode_ && ShouldBeInBackgroundMode()) {
556 StartBackgroundMode();
557 EnableLaunchOnStartup(true);
561 void BackgroundModeManager::DisableBackgroundMode() {
562 DCHECK(!IsBackgroundModePrefEnabled());
563 // If background mode is currently enabled, turn it off.
564 if (in_background_mode_) {
566 EnableLaunchOnStartup(false);
570 void BackgroundModeManager::SuspendBackgroundMode() {
571 background_mode_suspended_ = true;
572 UpdateKeepAliveAndTrayIcon();
575 void BackgroundModeManager::ResumeBackgroundMode() {
576 background_mode_suspended_ = false;
577 UpdateKeepAliveAndTrayIcon();
580 void BackgroundModeManager::UpdateKeepAliveAndTrayIcon() {
581 if (in_background_mode_ && !background_mode_suspended_) {
582 if (!keeping_alive_) {
583 keeping_alive_ = true;
584 chrome::StartKeepAlive();
586 CreateStatusTrayIcon();
590 RemoveStatusTrayIcon();
591 if (keeping_alive_) {
592 keeping_alive_ = false;
593 chrome::EndKeepAlive();
597 void BackgroundModeManager::OnBrowserAdded(Browser* browser) {
598 ResumeBackgroundMode();
601 int BackgroundModeManager::GetBackgroundAppCount() const {
603 // Walk the BackgroundModeData for all profiles and count the number of apps.
604 for (BackgroundModeInfoMap::const_iterator it =
605 background_mode_data_.begin();
606 it != background_mode_data_.end();
608 count += it->second->GetBackgroundAppCount();
614 int BackgroundModeManager::GetBackgroundAppCountForProfile(
615 Profile* const profile) const {
616 BackgroundModeData* bmd = GetBackgroundModeData(profile);
617 return bmd->GetBackgroundAppCount();
620 bool BackgroundModeManager::ShouldBeInBackgroundMode() const {
621 return IsBackgroundModePrefEnabled() &&
622 (GetBackgroundAppCount() > 0 || keep_alive_for_test_);
625 void BackgroundModeManager::OnBackgroundAppInstalled(
626 const Extension* extension) {
627 // Background mode is disabled - don't do anything.
628 if (!IsBackgroundModePrefEnabled())
631 // Ensure we have a tray icon (needed so we can display the app-installed
632 // notification below).
633 EnableBackgroundMode();
634 ResumeBackgroundMode();
636 // Notify the user that a background app has been installed.
637 if (extension) { // NULL when called by unit tests.
638 DisplayAppInstalledNotification(extension);
642 void BackgroundModeManager::CheckReloadStatus(
643 const Extension* extension,
644 bool* is_being_reloaded) {
645 // Walk the BackgroundModeData for all profiles to see if one of their
646 // extensions is being reloaded.
647 for (BackgroundModeInfoMap::const_iterator it =
648 background_mode_data_.begin();
649 it != background_mode_data_.end();
651 Profile* profile = it->first;
652 // If the extension is being reloaded, no need to show a notification.
653 if (profile->GetExtensionService()->IsBeingReloaded(extension->id()))
654 *is_being_reloaded = true;
658 void BackgroundModeManager::CreateStatusTrayIcon() {
659 // Only need status icons on windows/linux. ChromeOS doesn't allow exiting
660 // Chrome and Mac can use the dock icon instead.
662 // Since there are multiple profiles which share the status tray, we now
663 // use the browser process to keep track of it.
664 #if !defined(OS_MACOSX) && !defined(OS_CHROMEOS)
666 status_tray_ = g_browser_process->status_tray();
669 // If the platform doesn't support status icons, or we've already created
670 // our status icon, just return.
671 if (!status_tray_ || status_icon_)
674 // TODO(rlp): Status tray icon should have submenus for each profile.
675 gfx::ImageSkia* image_skia = ui::ResourceBundle::GetSharedInstance().
676 GetImageSkiaNamed(IDR_STATUS_TRAY_ICON);
678 status_icon_ = status_tray_->CreateStatusIcon(
679 StatusTray::BACKGROUND_MODE_ICON,
681 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
684 UpdateStatusTrayIconContextMenu();
687 void BackgroundModeManager::UpdateStatusTrayIconContextMenu() {
688 // Ensure we have a tray icon if appropriate.
689 UpdateKeepAliveAndTrayIcon();
691 // If we don't have a status icon or one could not be created succesfully,
692 // then no need to continue the update.
696 // We should only get here if we have a profile loaded, or if we're running
698 if (background_mode_data_.empty()) {
699 DCHECK(keep_alive_for_test_);
703 // TODO(rlp): Add current profile color or indicator.
704 // Create a context menu item for Chrome.
705 scoped_ptr<StatusIconMenuModel> menu(new StatusIconMenuModel(this));
707 menu->AddItem(IDC_ABOUT, l10n_util::GetStringUTF16(IDS_ABOUT));
708 menu->AddItemWithStringId(IDC_TASK_MANAGER, IDS_TASK_MANAGER);
709 menu->AddSeparator(ui::NORMAL_SEPARATOR);
711 if (profile_cache_->GetNumberOfProfiles() > 1) {
712 std::vector<BackgroundModeData*> bmd_vector;
713 for (BackgroundModeInfoMap::iterator it =
714 background_mode_data_.begin();
715 it != background_mode_data_.end();
717 bmd_vector.push_back(it->second.get());
719 std::sort(bmd_vector.begin(), bmd_vector.end(),
720 &BackgroundModeData::BackgroundModeDataCompare);
721 int profiles_with_apps = 0;
722 for (std::vector<BackgroundModeData*>::const_iterator bmd_it =
724 bmd_it != bmd_vector.end();
726 BackgroundModeData* bmd = *bmd_it;
727 // We should only display the profile in the status icon if it has at
728 // least one background app.
729 if (bmd->GetBackgroundAppCount() > 0) {
730 StatusIconMenuModel* submenu = new StatusIconMenuModel(bmd);
731 bmd->BuildProfileMenu(submenu, menu.get());
732 profiles_with_apps++;
735 // We should only be displaying the status tray icon if there is at least
736 // one profile with a background app.
737 DCHECK_GT(profiles_with_apps, 0);
739 // We should only have one profile in the cache if we are not
740 // using multi-profiles. If keep_alive_for_test_ is set, then we may not
741 // have any profiles in the cache.
742 DCHECK(profile_cache_->GetNumberOfProfiles() == size_t(1) ||
743 keep_alive_for_test_);
744 background_mode_data_.begin()->second->BuildProfileMenu(menu.get(), NULL);
747 menu->AddSeparator(ui::NORMAL_SEPARATOR);
748 menu->AddCheckItemWithStringId(
749 IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND,
750 IDS_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND);
751 menu->SetCommandIdChecked(IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND,
754 PrefService* service = g_browser_process->local_state();
757 service->IsUserModifiablePreference(prefs::kBackgroundModeEnabled);
758 menu->SetCommandIdEnabled(IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND,
761 menu->AddItemWithStringId(IDC_EXIT, IDS_EXIT);
763 context_menu_ = menu.get();
764 status_icon_->SetContextMenu(menu.Pass());
767 void BackgroundModeManager::RemoveStatusTrayIcon() {
769 status_tray_->RemoveStatusIcon(status_icon_);
771 context_menu_ = NULL;
774 BackgroundModeManager::BackgroundModeData*
775 BackgroundModeManager::GetBackgroundModeData(Profile* const profile) const {
776 DCHECK(background_mode_data_.find(profile) != background_mode_data_.end());
777 return background_mode_data_.find(profile)->second.get();
780 BackgroundModeManager::BackgroundModeInfoMap::iterator
781 BackgroundModeManager::GetBackgroundModeIterator(
782 const string16& profile_name) {
783 BackgroundModeInfoMap::iterator profile_it =
784 background_mode_data_.end();
785 for (BackgroundModeInfoMap::iterator it =
786 background_mode_data_.begin();
787 it != background_mode_data_.end();
789 if (it->second->name() == profile_name) {
796 bool BackgroundModeManager::IsBackgroundModePrefEnabled() const {
797 PrefService* service = g_browser_process->local_state();
799 return service->GetBoolean(prefs::kBackgroundModeEnabled);