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/lifetime/application_lifetime.h"
24 #include "chrome/browser/profiles/profile.h"
25 #include "chrome/browser/profiles/profile_info_cache.h"
26 #include "chrome/browser/profiles/profile_manager.h"
27 #include "chrome/browser/status_icons/status_icon.h"
28 #include "chrome/browser/status_icons/status_tray.h"
29 #include "chrome/browser/ui/browser.h"
30 #include "chrome/browser/ui/browser_commands.h"
31 #include "chrome/browser/ui/browser_dialogs.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/browser/ui/user_manager.h"
38 #include "chrome/common/chrome_constants.h"
39 #include "chrome/common/chrome_switches.h"
40 #include "chrome/common/extensions/extension_constants.h"
41 #include "chrome/common/pref_names.h"
42 #include "chrome/grit/chromium_strings.h"
43 #include "chrome/grit/generated_resources.h"
44 #include "content/public/browser/notification_service.h"
45 #include "content/public/browser/user_metrics.h"
46 #include "extensions/browser/extension_system.h"
47 #include "extensions/common/extension.h"
48 #include "extensions/common/manifest_handlers/options_page_info.h"
49 #include "extensions/common/permissions/permission_set.h"
50 #include "grit/chrome_unscaled_resources.h"
51 #include "ui/base/l10n/l10n_util.h"
52 #include "ui/base/resource/resource_bundle.h"
54 using base::UserMetricsAction;
55 using extensions::Extension;
56 using extensions::UpdatedExtensionPermissionsInfo;
59 const int kInvalidExtensionIndex = -1;
62 BackgroundModeManager::BackgroundModeData::BackgroundModeData(
64 CommandIdExtensionVector* command_id_extension_vector)
65 : applications_(new BackgroundApplicationListModel(profile)),
67 command_id_extension_vector_(command_id_extension_vector) {
70 BackgroundModeManager::BackgroundModeData::~BackgroundModeData() {
73 ///////////////////////////////////////////////////////////////////////////////
74 // BackgroundModeManager::BackgroundModeData, StatusIconMenuModel overrides
75 void BackgroundModeManager::BackgroundModeData::ExecuteCommand(
79 case IDC_MinimumLabelValue:
80 // Do nothing. This is just a label.
83 // Launch the app associated with this Command ID.
84 int extension_index = command_id_extension_vector_->at(command_id);
85 if (extension_index != kInvalidExtensionIndex) {
86 const Extension* extension =
87 applications_->GetExtension(extension_index);
88 BackgroundModeManager::LaunchBackgroundApplication(profile_, extension);
94 Browser* BackgroundModeManager::BackgroundModeData::GetBrowserWindow() {
95 chrome::HostDesktopType host_desktop_type = chrome::GetActiveDesktop();
96 Browser* browser = chrome::FindLastActiveWithProfile(profile_,
98 return browser ? browser : chrome::OpenEmptyWindow(profile_,
102 int BackgroundModeManager::BackgroundModeData::GetBackgroundAppCount() const {
103 return applications_->size();
106 void BackgroundModeManager::BackgroundModeData::BuildProfileMenu(
107 StatusIconMenuModel* menu,
108 StatusIconMenuModel* containing_menu) {
110 // When there are no background applications, we want to display
111 // just a label stating that none are running.
112 if (applications_->size() < 1) {
113 menu->AddItemWithStringId(IDC_MinimumLabelValue,
114 IDS_BACKGROUND_APP_NOT_INSTALLED);
115 menu->SetCommandIdEnabled(IDC_MinimumLabelValue, false);
117 for (extensions::ExtensionList::const_iterator cursor =
118 applications_->begin();
119 cursor != applications_->end();
120 ++cursor, ++position) {
121 const gfx::ImageSkia* icon = applications_->GetIcon(cursor->get());
122 DCHECK(position == applications_->GetPosition(cursor->get()));
123 const std::string& name = (*cursor)->name();
124 int command_id = command_id_extension_vector_->size();
125 // Check that the command ID is within the dynamic range.
126 DCHECK(command_id < IDC_MinimumLabelValue);
127 command_id_extension_vector_->push_back(position);
128 menu->AddItem(command_id, base::UTF8ToUTF16(name));
130 menu->SetIcon(menu->GetItemCount() - 1, gfx::Image(*icon));
132 // Component extensions with background that do not have an options page
133 // will cause this menu item to go to the extensions page with an
134 // absent component extension.
136 // Ideally, we would remove this item, but this conflicts with the user
137 // model where this menu shows the extensions with background.
139 // The compromise is to disable the item, avoiding the non-actionable
140 // navigate to the extensions page and preserving the user model.
141 if ((*cursor)->location() == extensions::Manifest::COMPONENT) {
143 extensions::OptionsPageInfo::GetOptionsPage(cursor->get());
144 if (!options_page.is_valid())
145 menu->SetCommandIdEnabled(command_id, false);
149 if (containing_menu) {
150 int menu_command_id = command_id_extension_vector_->size();
151 // Check that the command ID is within the dynamic range.
152 DCHECK(menu_command_id < IDC_MinimumLabelValue);
153 command_id_extension_vector_->push_back(kInvalidExtensionIndex);
154 containing_menu->AddSubMenu(menu_command_id, name_, menu);
158 void BackgroundModeManager::BackgroundModeData::SetName(
159 const base::string16& new_profile_name) {
160 name_ = new_profile_name;
163 base::string16 BackgroundModeManager::BackgroundModeData::name() {
167 std::set<const extensions::Extension*>
168 BackgroundModeManager::BackgroundModeData::GetNewBackgroundApps() {
169 std::set<const extensions::Extension*> new_apps;
171 // Copy all current extensions into our list of |current_extensions_|.
172 for (extensions::ExtensionList::const_iterator it = applications_->begin();
173 it != applications_->end(); ++it) {
174 const extensions::ExtensionId& id = (*it)->id();
175 if (current_extensions_.count(id) == 0) {
176 // Not found in our set yet - add it and maybe return as a previously
178 current_extensions_.insert(id);
179 // If this application has been newly loaded after the initial startup,
181 if (applications_->is_ready()) {
182 const extensions::Extension* extension = (*it).get();
183 new_apps.insert(extension);
191 bool BackgroundModeManager::BackgroundModeData::BackgroundModeDataCompare(
192 const BackgroundModeData* bmd1,
193 const BackgroundModeData* bmd2) {
194 return bmd1->name_ < bmd2->name_;
198 ///////////////////////////////////////////////////////////////////////////////
199 // BackgroundModeManager, public
200 BackgroundModeManager::BackgroundModeManager(
201 CommandLine* command_line,
202 ProfileInfoCache* profile_cache)
203 : profile_cache_(profile_cache),
207 in_background_mode_(false),
208 keep_alive_for_startup_(false),
209 keep_alive_for_test_(false),
210 background_mode_suspended_(false),
211 keeping_alive_(false) {
212 // We should never start up if there is no browser process or if we are
213 // currently quitting.
214 CHECK(g_browser_process != NULL);
215 CHECK(!browser_shutdown::IsTryingToQuit());
217 // Add self as an observer for the profile info cache so we know when profiles
218 // are deleted and their names change.
219 profile_cache_->AddObserver(this);
221 // Listen for the background mode preference changing.
222 if (g_browser_process->local_state()) { // Skip for unit tests
223 pref_registrar_.Init(g_browser_process->local_state());
225 prefs::kBackgroundModeEnabled,
226 base::Bind(&BackgroundModeManager::OnBackgroundModeEnabledPrefChanged,
227 base::Unretained(this)));
230 // Keep the browser alive until extensions are done loading - this is needed
231 // by the --no-startup-window flag. We want to stay alive until we load
232 // extensions, at which point we should either run in background mode (if
233 // there are background apps) or exit if there are none.
234 if (command_line->HasSwitch(switches::kNoStartupWindow)) {
235 keep_alive_for_startup_ = true;
236 chrome::IncrementKeepAliveCount();
238 // Otherwise, start with background mode suspended in case we're launching
239 // in a mode that doesn't open a browser window. It will be resumed when the
240 // first browser window is opened.
241 SuspendBackgroundMode();
244 // If the -keep-alive-for-test flag is passed, then always keep chrome running
245 // in the background until the user explicitly terminates it.
246 if (command_line->HasSwitch(switches::kKeepAliveForTest))
247 keep_alive_for_test_ = true;
249 if (ShouldBeInBackgroundMode())
250 StartBackgroundMode();
252 // Listen for the application shutting down so we can decrement our KeepAlive
254 registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
255 content::NotificationService::AllSources());
256 BrowserList::AddObserver(this);
259 BackgroundModeManager::~BackgroundModeManager() {
260 // Remove ourselves from the application observer list (only needed by unit
261 // tests since APP_TERMINATING is what does this in a real running system).
262 for (BackgroundModeInfoMap::iterator it =
263 background_mode_data_.begin();
264 it != background_mode_data_.end();
266 it->second->applications_->RemoveObserver(this);
268 BrowserList::RemoveObserver(this);
270 // We're going away, so exit background mode (does nothing if we aren't in
271 // background mode currently). This is primarily needed for unit tests,
272 // because in an actual running system we'd get an APP_TERMINATING
273 // notification before being destroyed.
278 void BackgroundModeManager::RegisterPrefs(PrefRegistrySimple* registry) {
279 #if defined(OS_MACOSX)
280 registry->RegisterBooleanPref(prefs::kUserRemovedLoginItem, false);
281 registry->RegisterBooleanPref(prefs::kChromeCreatedLoginItem, false);
282 registry->RegisterBooleanPref(prefs::kMigratedLoginItemPref, false);
284 registry->RegisterBooleanPref(prefs::kBackgroundModeEnabled, true);
288 void BackgroundModeManager::RegisterProfile(Profile* profile) {
289 // We don't want to register multiple times for one profile.
290 DCHECK(background_mode_data_.find(profile) == background_mode_data_.end());
291 BackgroundModeInfo bmd(new BackgroundModeData(profile,
292 &command_id_extension_vector_));
293 background_mode_data_[profile] = bmd;
295 // Initially set the name for this background mode data.
296 size_t index = profile_cache_->GetIndexOfProfileWithPath(profile->GetPath());
297 base::string16 name = l10n_util::GetStringUTF16(IDS_PROFILES_DEFAULT_NAME);
298 if (index != std::string::npos)
299 name = profile_cache_->GetNameOfProfileAtIndex(index);
302 // Check for the presence of background apps after all extensions have been
303 // loaded, to handle the case where an extension has been manually removed
304 // while Chrome was not running.
306 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
307 content::Source<Profile>(profile));
309 bmd->applications_->AddObserver(this);
311 // If we're adding a new profile and running in multi-profile mode, this new
312 // profile should be added to the status icon if one currently exists.
313 if (in_background_mode_ && status_icon_)
314 UpdateStatusTrayIconContextMenu();
318 void BackgroundModeManager::LaunchBackgroundApplication(
320 const Extension* extension) {
321 OpenApplication(AppLaunchParams(profile, extension, NEW_FOREGROUND_TAB));
324 bool BackgroundModeManager::IsBackgroundModeActive() {
325 return in_background_mode_;
328 int BackgroundModeManager::NumberOfBackgroundModeData() {
329 return background_mode_data_.size();
332 ///////////////////////////////////////////////////////////////////////////////
333 // BackgroundModeManager, content::NotificationObserver overrides
334 void BackgroundModeManager::Observe(
336 const content::NotificationSource& source,
337 const content::NotificationDetails& details) {
339 case extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED:
340 // Extensions are loaded, so we don't need to manually keep the browser
341 // process alive any more when running in no-startup-window mode.
342 DecrementKeepAliveCountForStartup();
344 case chrome::NOTIFICATION_APP_TERMINATING:
345 // Make sure we aren't still keeping the app alive (only happens if we
346 // don't receive an EXTENSIONS_READY notification for some reason).
347 DecrementKeepAliveCountForStartup();
348 // Performing an explicit shutdown, so exit background mode (does nothing
349 // if we aren't in background mode currently).
351 // Shutting down, so don't listen for any more notifications so we don't
352 // try to re-enter/exit background mode again.
353 registrar_.RemoveAll();
354 for (BackgroundModeInfoMap::iterator it =
355 background_mode_data_.begin();
356 it != background_mode_data_.end();
358 it->second->applications_->RemoveObserver(this);
367 void BackgroundModeManager::OnBackgroundModeEnabledPrefChanged() {
368 if (IsBackgroundModePrefEnabled())
369 EnableBackgroundMode();
371 DisableBackgroundMode();
374 ///////////////////////////////////////////////////////////////////////////////
375 // BackgroundModeManager, BackgroundApplicationListModel::Observer overrides
376 void BackgroundModeManager::OnApplicationDataChanged(
377 const Extension* extension, Profile* profile) {
378 UpdateStatusTrayIconContextMenu();
381 void BackgroundModeManager::OnApplicationListChanged(Profile* profile) {
382 if (!IsBackgroundModePrefEnabled())
385 // Update the profile cache with the fact whether background apps are running
387 size_t profile_index = profile_cache_->GetIndexOfProfileWithPath(
389 if (profile_index != std::string::npos) {
390 profile_cache_->SetBackgroundStatusOfProfileAtIndex(
391 profile_index, GetBackgroundAppCountForProfile(profile) != 0);
394 if (!ShouldBeInBackgroundMode()) {
395 // We've uninstalled our last background app, make sure we exit background
396 // mode and no longer launch on startup.
397 EnableLaunchOnStartup(false);
400 // We have at least one background app running - make sure we're in
402 if (!in_background_mode_) {
403 // We're entering background mode - make sure we have launch-on-startup
404 // enabled. On Mac, the platform-specific code tracks whether the user
405 // has deleted a login item in the past, and if so, no login item will
406 // be created (to avoid overriding the specific user action).
407 EnableLaunchOnStartup(true);
409 StartBackgroundMode();
411 // List of applications changed so update the UI.
412 UpdateStatusTrayIconContextMenu();
414 // Notify the user about any new applications.
415 BackgroundModeData* bmd = GetBackgroundModeData(profile);
416 std::set<const extensions::Extension*> new_apps =
417 bmd->GetNewBackgroundApps();
418 for (std::set<const extensions::Extension*>::const_iterator it =
419 new_apps.begin(); it != new_apps.end(); ++it) {
420 OnBackgroundAppInstalled(*it);
425 ///////////////////////////////////////////////////////////////////////////////
426 // BackgroundModeManager, ProfileInfoCacheObserver overrides
427 void BackgroundModeManager::OnProfileAdded(const base::FilePath& profile_path) {
428 ProfileInfoCache& cache =
429 g_browser_process->profile_manager()->GetProfileInfoCache();
430 base::string16 profile_name = cache.GetNameOfProfileAtIndex(
431 cache.GetIndexOfProfileWithPath(profile_path));
432 // At this point, the profile should be registered with the background mode
433 // manager, but when it's actually added to the cache is when its name is
434 // set so we need up to update that with the background_mode_data.
435 for (BackgroundModeInfoMap::const_iterator it =
436 background_mode_data_.begin();
437 it != background_mode_data_.end();
439 if (it->first->GetPath() == profile_path) {
440 it->second->SetName(profile_name);
441 UpdateStatusTrayIconContextMenu();
447 void BackgroundModeManager::OnProfileWillBeRemoved(
448 const base::FilePath& profile_path) {
449 ProfileInfoCache& cache =
450 g_browser_process->profile_manager()->GetProfileInfoCache();
451 base::string16 profile_name = cache.GetNameOfProfileAtIndex(
452 cache.GetIndexOfProfileWithPath(profile_path));
453 // Remove the profile from our map of profiles.
454 BackgroundModeInfoMap::iterator it =
455 GetBackgroundModeIterator(profile_name);
456 // If a profile isn't running a background app, it may not be in the map.
457 if (it != background_mode_data_.end()) {
458 it->second->applications_->RemoveObserver(this);
459 background_mode_data_.erase(it);
460 // If there are no background mode profiles any longer, then turn off
462 if (!ShouldBeInBackgroundMode()) {
463 EnableLaunchOnStartup(false);
466 UpdateStatusTrayIconContextMenu();
470 void BackgroundModeManager::OnProfileNameChanged(
471 const base::FilePath& profile_path,
472 const base::string16& old_profile_name) {
473 ProfileInfoCache& cache =
474 g_browser_process->profile_manager()->GetProfileInfoCache();
475 base::string16 new_profile_name = cache.GetNameOfProfileAtIndex(
476 cache.GetIndexOfProfileWithPath(profile_path));
477 BackgroundModeInfoMap::const_iterator it =
478 GetBackgroundModeIterator(old_profile_name);
479 // We check that the returned iterator is valid due to unittests, but really
480 // this should only be called on profiles already known by the background
482 if (it != background_mode_data_.end()) {
483 it->second->SetName(new_profile_name);
484 UpdateStatusTrayIconContextMenu();
488 BackgroundModeManager::BackgroundModeData*
489 BackgroundModeManager::GetBackgroundModeDataForLastProfile() const {
490 Profile* most_recent_profile = g_browser_process->profile_manager()->
491 GetLastUsedProfileAllowedByPolicy();
492 BackgroundModeInfoMap::const_iterator profile_background_data =
493 background_mode_data_.find(most_recent_profile);
495 if (profile_background_data == background_mode_data_.end())
498 // Do not permit a locked profile to be used to open a browser.
499 ProfileInfoCache& cache =
500 g_browser_process->profile_manager()->GetProfileInfoCache();
501 if (cache.ProfileIsSigninRequiredAtIndex(cache.GetIndexOfProfileWithPath(
502 profile_background_data->first->GetPath())))
505 return profile_background_data->second.get();
508 ///////////////////////////////////////////////////////////////////////////////
509 // BackgroundModeManager::BackgroundModeData, StatusIconMenuModel overrides
510 void BackgroundModeManager::ExecuteCommand(int command_id, int event_flags) {
511 BackgroundModeData* bmd = GetBackgroundModeDataForLastProfile();
512 switch (command_id) {
515 chrome::ShowAboutChrome(bmd->GetBrowserWindow());
517 UserManager::Show(base::FilePath(),
518 profiles::USER_MANAGER_NO_TUTORIAL,
519 profiles::USER_MANAGER_SELECT_PROFILE_ABOUT_CHROME);
522 case IDC_TASK_MANAGER:
524 chrome::OpenTaskManager(bmd->GetBrowserWindow());
526 UserManager::Show(base::FilePath(),
527 profiles::USER_MANAGER_NO_TUTORIAL,
528 profiles::USER_MANAGER_SELECT_PROFILE_TASK_MANAGER);
532 content::RecordAction(UserMetricsAction("Exit"));
533 chrome::CloseAllBrowsers();
535 case IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND: {
536 // Background mode must already be enabled (as otherwise this menu would
538 DCHECK(IsBackgroundModePrefEnabled());
539 DCHECK(chrome::WillKeepAlive());
541 // Set the background mode pref to "disabled" - the resulting notification
542 // will result in a call to DisableBackgroundMode().
543 PrefService* service = g_browser_process->local_state();
545 service->SetBoolean(prefs::kBackgroundModeEnabled, false);
550 bmd->ExecuteCommand(command_id, event_flags);
552 UserManager::Show(base::FilePath(),
553 profiles::USER_MANAGER_NO_TUTORIAL,
554 profiles::USER_MANAGER_SELECT_PROFILE_NO_ACTION);
561 ///////////////////////////////////////////////////////////////////////////////
562 // BackgroundModeManager, private
563 void BackgroundModeManager::DecrementKeepAliveCountForStartup() {
564 if (keep_alive_for_startup_) {
565 keep_alive_for_startup_ = false;
566 // We call this via the message queue to make sure we don't try to end
567 // keep-alive (which can shutdown Chrome) before the message loop has
569 base::MessageLoop::current()->PostTask(
570 FROM_HERE, base::Bind(&chrome::DecrementKeepAliveCount));
574 void BackgroundModeManager::StartBackgroundMode() {
575 DCHECK(ShouldBeInBackgroundMode());
576 // Don't bother putting ourselves in background mode if we're already there
577 // or if background mode is disabled.
578 if (in_background_mode_)
581 // Mark ourselves as running in background mode.
582 in_background_mode_ = true;
584 UpdateKeepAliveAndTrayIcon();
586 content::NotificationService::current()->Notify(
587 chrome::NOTIFICATION_BACKGROUND_MODE_CHANGED,
588 content::Source<BackgroundModeManager>(this),
589 content::Details<bool>(&in_background_mode_));
592 void BackgroundModeManager::EndBackgroundMode() {
593 if (!in_background_mode_)
595 in_background_mode_ = false;
597 UpdateKeepAliveAndTrayIcon();
599 content::NotificationService::current()->Notify(
600 chrome::NOTIFICATION_BACKGROUND_MODE_CHANGED,
601 content::Source<BackgroundModeManager>(this),
602 content::Details<bool>(&in_background_mode_));
605 void BackgroundModeManager::EnableBackgroundMode() {
606 DCHECK(IsBackgroundModePrefEnabled());
607 // If background mode should be enabled, but isn't, turn it on.
608 if (!in_background_mode_ && ShouldBeInBackgroundMode()) {
609 StartBackgroundMode();
610 EnableLaunchOnStartup(true);
614 void BackgroundModeManager::DisableBackgroundMode() {
615 DCHECK(!IsBackgroundModePrefEnabled());
616 // If background mode is currently enabled, turn it off.
617 if (in_background_mode_) {
619 EnableLaunchOnStartup(false);
623 void BackgroundModeManager::SuspendBackgroundMode() {
624 background_mode_suspended_ = true;
625 UpdateKeepAliveAndTrayIcon();
628 void BackgroundModeManager::ResumeBackgroundMode() {
629 background_mode_suspended_ = false;
630 UpdateKeepAliveAndTrayIcon();
633 void BackgroundModeManager::UpdateKeepAliveAndTrayIcon() {
634 if (in_background_mode_ && !background_mode_suspended_) {
635 if (!keeping_alive_) {
636 keeping_alive_ = true;
637 chrome::IncrementKeepAliveCount();
639 CreateStatusTrayIcon();
643 RemoveStatusTrayIcon();
644 if (keeping_alive_) {
645 keeping_alive_ = false;
646 chrome::DecrementKeepAliveCount();
650 void BackgroundModeManager::OnBrowserAdded(Browser* browser) {
651 ResumeBackgroundMode();
654 int BackgroundModeManager::GetBackgroundAppCount() const {
656 // Walk the BackgroundModeData for all profiles and count the number of apps.
657 for (BackgroundModeInfoMap::const_iterator it =
658 background_mode_data_.begin();
659 it != background_mode_data_.end();
661 count += it->second->GetBackgroundAppCount();
667 int BackgroundModeManager::GetBackgroundAppCountForProfile(
668 Profile* const profile) const {
669 BackgroundModeData* bmd = GetBackgroundModeData(profile);
670 return bmd->GetBackgroundAppCount();
673 bool BackgroundModeManager::ShouldBeInBackgroundMode() const {
674 return IsBackgroundModePrefEnabled() &&
675 (GetBackgroundAppCount() > 0 || keep_alive_for_test_);
678 void BackgroundModeManager::OnBackgroundAppInstalled(
679 const Extension* extension) {
680 // Background mode is disabled - don't do anything.
681 if (!IsBackgroundModePrefEnabled())
684 // Ensure we have a tray icon (needed so we can display the app-installed
685 // notification below).
686 EnableBackgroundMode();
687 ResumeBackgroundMode();
689 // Notify the user that a background app has been installed.
690 if (extension) { // NULL when called by unit tests.
691 DisplayAppInstalledNotification(extension);
695 void BackgroundModeManager::CreateStatusTrayIcon() {
696 // Only need status icons on windows/linux. ChromeOS doesn't allow exiting
697 // Chrome and Mac can use the dock icon instead.
699 // Since there are multiple profiles which share the status tray, we now
700 // use the browser process to keep track of it.
701 #if !defined(OS_MACOSX) && !defined(OS_CHROMEOS)
703 status_tray_ = g_browser_process->status_tray();
706 // If the platform doesn't support status icons, or we've already created
707 // our status icon, just return.
708 if (!status_tray_ || status_icon_)
711 // TODO(rlp): Status tray icon should have submenus for each profile.
712 gfx::ImageSkia* image_skia = ui::ResourceBundle::GetSharedInstance().
713 GetImageSkiaNamed(IDR_STATUS_TRAY_ICON);
715 status_icon_ = status_tray_->CreateStatusIcon(
716 StatusTray::BACKGROUND_MODE_ICON,
718 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
721 UpdateStatusTrayIconContextMenu();
724 void BackgroundModeManager::UpdateStatusTrayIconContextMenu() {
725 // Ensure we have a tray icon if appropriate.
726 UpdateKeepAliveAndTrayIcon();
728 // If we don't have a status icon or one could not be created succesfully,
729 // then no need to continue the update.
733 // We should only get here if we have a profile loaded, or if we're running
735 if (background_mode_data_.empty()) {
736 DCHECK(keep_alive_for_test_);
740 // We are building a new menu. Reset the Command IDs.
741 command_id_extension_vector_.clear();
743 // Clear the submenus too since we will be creating new ones.
746 // TODO(rlp): Add current profile color or indicator.
747 // Create a context menu item for Chrome.
748 scoped_ptr<StatusIconMenuModel> menu(new StatusIconMenuModel(this));
750 menu->AddItem(IDC_ABOUT, l10n_util::GetStringUTF16(IDS_ABOUT));
751 menu->AddItemWithStringId(IDC_TASK_MANAGER, IDS_TASK_MANAGER);
752 menu->AddSeparator(ui::NORMAL_SEPARATOR);
754 if (profile_cache_->GetNumberOfProfiles() > 1) {
755 std::vector<BackgroundModeData*> bmd_vector;
756 for (BackgroundModeInfoMap::iterator it =
757 background_mode_data_.begin();
758 it != background_mode_data_.end();
760 bmd_vector.push_back(it->second.get());
762 std::sort(bmd_vector.begin(), bmd_vector.end(),
763 &BackgroundModeData::BackgroundModeDataCompare);
764 int profiles_with_apps = 0;
765 for (std::vector<BackgroundModeData*>::const_iterator bmd_it =
767 bmd_it != bmd_vector.end();
769 BackgroundModeData* bmd = *bmd_it;
770 // We should only display the profile in the status icon if it has at
771 // least one background app.
772 if (bmd->GetBackgroundAppCount() > 0) {
773 StatusIconMenuModel* submenu = new StatusIconMenuModel(bmd);
774 // The submenu constructor caller owns the lifetime of the submenu.
775 // The containing menu does not handle the lifetime.
776 submenus.push_back(submenu);
777 bmd->BuildProfileMenu(submenu, menu.get());
778 profiles_with_apps++;
781 // We should only be displaying the status tray icon if there is at least
782 // one profile with a background app.
783 DCHECK_GT(profiles_with_apps, 0);
785 // We should only have one profile in the cache if we are not
786 // using multi-profiles. If keep_alive_for_test_ is set, then we may not
787 // have any profiles in the cache.
788 DCHECK(profile_cache_->GetNumberOfProfiles() == size_t(1) ||
789 keep_alive_for_test_);
790 background_mode_data_.begin()->second->BuildProfileMenu(menu.get(), NULL);
793 menu->AddSeparator(ui::NORMAL_SEPARATOR);
794 menu->AddCheckItemWithStringId(
795 IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND,
796 IDS_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND);
797 menu->SetCommandIdChecked(IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND,
800 PrefService* service = g_browser_process->local_state();
803 service->IsUserModifiablePreference(prefs::kBackgroundModeEnabled);
804 menu->SetCommandIdEnabled(IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND,
807 menu->AddItemWithStringId(IDC_EXIT, IDS_EXIT);
809 context_menu_ = menu.get();
810 status_icon_->SetContextMenu(menu.Pass());
813 void BackgroundModeManager::RemoveStatusTrayIcon() {
815 status_tray_->RemoveStatusIcon(status_icon_);
817 context_menu_ = NULL;
820 BackgroundModeManager::BackgroundModeData*
821 BackgroundModeManager::GetBackgroundModeData(Profile* const profile) const {
822 DCHECK(background_mode_data_.find(profile) != background_mode_data_.end());
823 return background_mode_data_.find(profile)->second.get();
826 BackgroundModeManager::BackgroundModeInfoMap::iterator
827 BackgroundModeManager::GetBackgroundModeIterator(
828 const base::string16& profile_name) {
829 BackgroundModeInfoMap::iterator profile_it =
830 background_mode_data_.end();
831 for (BackgroundModeInfoMap::iterator it =
832 background_mode_data_.begin();
833 it != background_mode_data_.end();
835 if (it->second->name() == profile_name) {
842 bool BackgroundModeManager::IsBackgroundModePrefEnabled() const {
843 PrefService* service = g_browser_process->local_state();
845 return service->GetBoolean(prefs::kBackgroundModeEnabled);