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_finder.h"
32 #include "chrome/browser/ui/browser_list.h"
33 #include "chrome/browser/ui/chrome_pages.h"
34 #include "chrome/browser/ui/extensions/application_launch.h"
35 #include "chrome/browser/ui/host_desktop.h"
36 #include "chrome/common/chrome_constants.h"
37 #include "chrome/common/chrome_switches.h"
38 #include "chrome/common/extensions/extension_constants.h"
39 #include "chrome/common/extensions/manifest_url_handler.h"
40 #include "chrome/common/pref_names.h"
41 #include "content/public/browser/notification_service.h"
42 #include "content/public/browser/user_metrics.h"
43 #include "extensions/browser/extension_system.h"
44 #include "extensions/common/extension.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 base::UserMetricsAction
;
53 using extensions::Extension
;
54 using extensions::UpdatedExtensionPermissionsInfo
;
57 const int kInvalidExtensionIndex
= -1;
60 BackgroundModeManager::BackgroundModeData::BackgroundModeData(
62 CommandIdExtensionVector
* command_id_extension_vector
)
63 : applications_(new BackgroundApplicationListModel(profile
)),
65 command_id_extension_vector_(command_id_extension_vector
) {
68 BackgroundModeManager::BackgroundModeData::~BackgroundModeData() {
71 ///////////////////////////////////////////////////////////////////////////////
72 // BackgroundModeManager::BackgroundModeData, StatusIconMenuModel overrides
73 void BackgroundModeManager::BackgroundModeData::ExecuteCommand(
77 case IDC_MinimumLabelValue
:
78 // Do nothing. This is just a label.
81 // Launch the app associated with this Command ID.
82 int extension_index
= command_id_extension_vector_
->at(command_id
);
83 if (extension_index
!= kInvalidExtensionIndex
) {
84 const Extension
* extension
=
85 applications_
->GetExtension(extension_index
);
86 BackgroundModeManager::LaunchBackgroundApplication(profile_
, extension
);
92 Browser
* BackgroundModeManager::BackgroundModeData::GetBrowserWindow() {
93 chrome::HostDesktopType host_desktop_type
= chrome::GetActiveDesktop();
94 Browser
* browser
= chrome::FindLastActiveWithProfile(profile_
,
96 return browser
? browser
: chrome::OpenEmptyWindow(profile_
,
100 int BackgroundModeManager::BackgroundModeData::GetBackgroundAppCount() const {
101 return applications_
->size();
104 void BackgroundModeManager::BackgroundModeData::BuildProfileMenu(
105 StatusIconMenuModel
* menu
,
106 StatusIconMenuModel
* containing_menu
) {
108 // When there are no background applications, we want to display
109 // just a label stating that none are running.
110 if (applications_
->size() < 1) {
111 menu
->AddItemWithStringId(IDC_MinimumLabelValue
,
112 IDS_BACKGROUND_APP_NOT_INSTALLED
);
113 menu
->SetCommandIdEnabled(IDC_MinimumLabelValue
, false);
115 for (extensions::ExtensionList::const_iterator cursor
=
116 applications_
->begin();
117 cursor
!= applications_
->end();
118 ++cursor
, ++position
) {
119 const gfx::ImageSkia
* icon
= applications_
->GetIcon(cursor
->get());
120 DCHECK(position
== applications_
->GetPosition(cursor
->get()));
121 const std::string
& name
= (*cursor
)->name();
122 int command_id
= command_id_extension_vector_
->size();
123 // Check that the command ID is within the dynamic range.
124 DCHECK(command_id
< IDC_MinimumLabelValue
);
125 command_id_extension_vector_
->push_back(position
);
126 menu
->AddItem(command_id
, base::UTF8ToUTF16(name
));
128 menu
->SetIcon(menu
->GetItemCount() - 1, gfx::Image(*icon
));
130 // Component extensions with background that do not have an options page
131 // will cause this menu item to go to the extensions page with an
132 // absent component extension.
134 // Ideally, we would remove this item, but this conflicts with the user
135 // model where this menu shows the extensions with background.
137 // The compromise is to disable the item, avoiding the non-actionable
138 // navigate to the extensions page and preserving the user model.
139 if ((*cursor
)->location() == extensions::Manifest::COMPONENT
) {
140 GURL options_page
= extensions::ManifestURL::GetOptionsPage(*cursor
);
141 if (!options_page
.is_valid())
142 menu
->SetCommandIdEnabled(command_id
, false);
146 if (containing_menu
) {
147 int menu_command_id
= command_id_extension_vector_
->size();
148 // Check that the command ID is within the dynamic range.
149 DCHECK(menu_command_id
< IDC_MinimumLabelValue
);
150 command_id_extension_vector_
->push_back(kInvalidExtensionIndex
);
151 containing_menu
->AddSubMenu(menu_command_id
, name_
, menu
);
155 void BackgroundModeManager::BackgroundModeData::SetName(
156 const base::string16
& new_profile_name
) {
157 name_
= new_profile_name
;
160 base::string16
BackgroundModeManager::BackgroundModeData::name() {
165 bool BackgroundModeManager::BackgroundModeData::BackgroundModeDataCompare(
166 const BackgroundModeData
* bmd1
,
167 const BackgroundModeData
* bmd2
) {
168 return bmd1
->name_
< bmd2
->name_
;
172 ///////////////////////////////////////////////////////////////////////////////
173 // BackgroundModeManager, public
174 BackgroundModeManager::BackgroundModeManager(
175 CommandLine
* command_line
,
176 ProfileInfoCache
* profile_cache
)
177 : profile_cache_(profile_cache
),
181 in_background_mode_(false),
182 keep_alive_for_startup_(false),
183 keep_alive_for_test_(false),
184 background_mode_suspended_(false),
185 keeping_alive_(false) {
186 // We should never start up if there is no browser process or if we are
187 // currently quitting.
188 CHECK(g_browser_process
!= NULL
);
189 CHECK(!browser_shutdown::IsTryingToQuit());
191 // Add self as an observer for the profile info cache so we know when profiles
192 // are deleted and their names change.
193 profile_cache_
->AddObserver(this);
195 // Listen for the background mode preference changing.
196 if (g_browser_process
->local_state()) { // Skip for unit tests
197 pref_registrar_
.Init(g_browser_process
->local_state());
199 prefs::kBackgroundModeEnabled
,
200 base::Bind(&BackgroundModeManager::OnBackgroundModeEnabledPrefChanged
,
201 base::Unretained(this)));
204 // Keep the browser alive until extensions are done loading - this is needed
205 // by the --no-startup-window flag. We want to stay alive until we load
206 // extensions, at which point we should either run in background mode (if
207 // there are background apps) or exit if there are none.
208 if (command_line
->HasSwitch(switches::kNoStartupWindow
)) {
209 keep_alive_for_startup_
= true;
210 chrome::IncrementKeepAliveCount();
212 // Otherwise, start with background mode suspended in case we're launching
213 // in a mode that doesn't open a browser window. It will be resumed when the
214 // first browser window is opened.
215 SuspendBackgroundMode();
218 // If the -keep-alive-for-test flag is passed, then always keep chrome running
219 // in the background until the user explicitly terminates it.
220 if (command_line
->HasSwitch(switches::kKeepAliveForTest
))
221 keep_alive_for_test_
= true;
223 if (ShouldBeInBackgroundMode())
224 StartBackgroundMode();
226 // Listen for the application shutting down so we can decrement our KeepAlive
228 registrar_
.Add(this, chrome::NOTIFICATION_APP_TERMINATING
,
229 content::NotificationService::AllSources());
230 BrowserList::AddObserver(this);
233 BackgroundModeManager::~BackgroundModeManager() {
234 // Remove ourselves from the application observer list (only needed by unit
235 // tests since APP_TERMINATING is what does this in a real running system).
236 for (BackgroundModeInfoMap::iterator it
=
237 background_mode_data_
.begin();
238 it
!= background_mode_data_
.end();
240 it
->second
->applications_
->RemoveObserver(this);
242 BrowserList::RemoveObserver(this);
244 // We're going away, so exit background mode (does nothing if we aren't in
245 // background mode currently). This is primarily needed for unit tests,
246 // because in an actual running system we'd get an APP_TERMINATING
247 // notification before being destroyed.
252 void BackgroundModeManager::RegisterPrefs(PrefRegistrySimple
* registry
) {
253 #if defined(OS_MACOSX)
254 registry
->RegisterBooleanPref(prefs::kUserRemovedLoginItem
, false);
255 registry
->RegisterBooleanPref(prefs::kChromeCreatedLoginItem
, false);
256 registry
->RegisterBooleanPref(prefs::kMigratedLoginItemPref
, false);
258 registry
->RegisterBooleanPref(prefs::kBackgroundModeEnabled
, true);
262 void BackgroundModeManager::RegisterProfile(Profile
* profile
) {
263 // We don't want to register multiple times for one profile.
264 DCHECK(background_mode_data_
.find(profile
) == background_mode_data_
.end());
265 BackgroundModeInfo
bmd(new BackgroundModeData(profile
,
266 &command_id_extension_vector_
));
267 background_mode_data_
[profile
] = bmd
;
269 // Initially set the name for this background mode data.
270 size_t index
= profile_cache_
->GetIndexOfProfileWithPath(profile
->GetPath());
271 base::string16 name
= l10n_util::GetStringUTF16(IDS_PROFILES_DEFAULT_NAME
);
272 if (index
!= std::string::npos
)
273 name
= profile_cache_
->GetNameOfProfileAtIndex(index
);
276 // Listen for when extensions are loaded or add the background permission so
277 // we can display a "background app installed" notification and enter
278 // "launch on login" mode on the Mac.
280 chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED
,
281 content::Source
<Profile
>(profile
));
282 registrar_
.Add(this, chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED
,
283 content::Source
<Profile
>(profile
));
286 // Check for the presence of background apps after all extensions have been
287 // loaded, to handle the case where an extension has been manually removed
288 // while Chrome was not running.
289 registrar_
.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY
,
290 content::Source
<Profile
>(profile
));
292 bmd
->applications_
->AddObserver(this);
294 // If we're adding a new profile and running in multi-profile mode, this new
295 // profile should be added to the status icon if one currently exists.
296 if (in_background_mode_
&& status_icon_
)
297 UpdateStatusTrayIconContextMenu();
301 void BackgroundModeManager::LaunchBackgroundApplication(
303 const Extension
* extension
) {
304 OpenApplication(AppLaunchParams(profile
, extension
, NEW_FOREGROUND_TAB
));
307 bool BackgroundModeManager::IsBackgroundModeActive() {
308 return in_background_mode_
;
311 int BackgroundModeManager::NumberOfBackgroundModeData() {
312 return background_mode_data_
.size();
315 ///////////////////////////////////////////////////////////////////////////////
316 // BackgroundModeManager, content::NotificationObserver overrides
317 void BackgroundModeManager::Observe(
319 const content::NotificationSource
& source
,
320 const content::NotificationDetails
& details
) {
322 case chrome::NOTIFICATION_EXTENSIONS_READY
:
323 // Extensions are loaded, so we don't need to manually keep the browser
324 // process alive any more when running in no-startup-window mode.
325 DecrementKeepAliveCountForStartup();
328 case chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED
: {
329 Extension
* extension
= content::Details
<Extension
>(details
).ptr();
330 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
331 if (BackgroundApplicationListModel::IsBackgroundApp(
332 *extension
, profile
)) {
333 // Extensions loaded after the ExtensionsService is ready should be
334 // treated as new installs.
335 if (extensions::ExtensionSystem::Get(profile
)->extension_service()->
337 bool is_being_reloaded
= false;
338 CheckReloadStatus(extension
, &is_being_reloaded
);
339 // No need to show the notification if we showed to the user
340 // previously for this app.
341 if (!is_being_reloaded
)
342 OnBackgroundAppInstalled(extension
);
347 case chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED
: {
348 UpdatedExtensionPermissionsInfo
* info
=
349 content::Details
<UpdatedExtensionPermissionsInfo
>(details
).ptr();
350 if (info
->permissions
->HasAPIPermission(
351 extensions::APIPermission::kBackground
) &&
352 info
->reason
== UpdatedExtensionPermissionsInfo::ADDED
) {
353 // Turned on background permission, so treat this as a new install.
354 OnBackgroundAppInstalled(info
->extension
);
358 case chrome::NOTIFICATION_APP_TERMINATING
:
359 // Make sure we aren't still keeping the app alive (only happens if we
360 // don't receive an EXTENSIONS_READY notification for some reason).
361 DecrementKeepAliveCountForStartup();
362 // Performing an explicit shutdown, so exit background mode (does nothing
363 // if we aren't in background mode currently).
365 // Shutting down, so don't listen for any more notifications so we don't
366 // try to re-enter/exit background mode again.
367 registrar_
.RemoveAll();
368 for (BackgroundModeInfoMap::iterator it
=
369 background_mode_data_
.begin();
370 it
!= background_mode_data_
.end();
372 it
->second
->applications_
->RemoveObserver(this);
381 void BackgroundModeManager::OnBackgroundModeEnabledPrefChanged() {
382 if (IsBackgroundModePrefEnabled())
383 EnableBackgroundMode();
385 DisableBackgroundMode();
388 ///////////////////////////////////////////////////////////////////////////////
389 // BackgroundModeManager, BackgroundApplicationListModel::Observer overrides
390 void BackgroundModeManager::OnApplicationDataChanged(
391 const Extension
* extension
, Profile
* profile
) {
392 UpdateStatusTrayIconContextMenu();
395 void BackgroundModeManager::OnApplicationListChanged(Profile
* profile
) {
396 if (!IsBackgroundModePrefEnabled())
399 // Update the profile cache with the fact whether background apps are running
401 size_t profile_index
= profile_cache_
->GetIndexOfProfileWithPath(
403 if (profile_index
!= std::string::npos
) {
404 profile_cache_
->SetBackgroundStatusOfProfileAtIndex(
405 profile_index
, GetBackgroundAppCountForProfile(profile
) != 0);
408 if (!ShouldBeInBackgroundMode()) {
409 // We've uninstalled our last background app, make sure we exit background
410 // mode and no longer launch on startup.
411 EnableLaunchOnStartup(false);
414 // We have at least one background app running - make sure we're in
416 if (!in_background_mode_
) {
417 // We're entering background mode - make sure we have launch-on-startup
418 // enabled. On Mac, the platform-specific code tracks whether the user
419 // has deleted a login item in the past, and if so, no login item will
420 // be created (to avoid overriding the specific user action).
421 EnableLaunchOnStartup(true);
423 StartBackgroundMode();
425 // List of applications changed so update the UI.
426 UpdateStatusTrayIconContextMenu();
430 ///////////////////////////////////////////////////////////////////////////////
431 // BackgroundModeManager, ProfileInfoCacheObserver overrides
432 void BackgroundModeManager::OnProfileAdded(const base::FilePath
& profile_path
) {
433 ProfileInfoCache
& cache
=
434 g_browser_process
->profile_manager()->GetProfileInfoCache();
435 base::string16 profile_name
= cache
.GetNameOfProfileAtIndex(
436 cache
.GetIndexOfProfileWithPath(profile_path
));
437 // At this point, the profile should be registered with the background mode
438 // manager, but when it's actually added to the cache is when its name is
439 // set so we need up to update that with the background_mode_data.
440 for (BackgroundModeInfoMap::const_iterator it
=
441 background_mode_data_
.begin();
442 it
!= background_mode_data_
.end();
444 if (it
->first
->GetPath() == profile_path
) {
445 it
->second
->SetName(profile_name
);
446 UpdateStatusTrayIconContextMenu();
452 void BackgroundModeManager::OnProfileWillBeRemoved(
453 const base::FilePath
& profile_path
) {
454 ProfileInfoCache
& cache
=
455 g_browser_process
->profile_manager()->GetProfileInfoCache();
456 base::string16 profile_name
= cache
.GetNameOfProfileAtIndex(
457 cache
.GetIndexOfProfileWithPath(profile_path
));
458 // Remove the profile from our map of profiles.
459 BackgroundModeInfoMap::iterator it
=
460 GetBackgroundModeIterator(profile_name
);
461 // If a profile isn't running a background app, it may not be in the map.
462 if (it
!= background_mode_data_
.end()) {
463 it
->second
->applications_
->RemoveObserver(this);
464 background_mode_data_
.erase(it
);
465 // If there are no background mode profiles any longer, then turn off
467 if (!ShouldBeInBackgroundMode()) {
468 EnableLaunchOnStartup(false);
471 UpdateStatusTrayIconContextMenu();
475 void BackgroundModeManager::OnProfileNameChanged(
476 const base::FilePath
& profile_path
,
477 const base::string16
& old_profile_name
) {
478 ProfileInfoCache
& cache
=
479 g_browser_process
->profile_manager()->GetProfileInfoCache();
480 base::string16 new_profile_name
= cache
.GetNameOfProfileAtIndex(
481 cache
.GetIndexOfProfileWithPath(profile_path
));
482 BackgroundModeInfoMap::const_iterator it
=
483 GetBackgroundModeIterator(old_profile_name
);
484 // We check that the returned iterator is valid due to unittests, but really
485 // this should only be called on profiles already known by the background
487 if (it
!= background_mode_data_
.end()) {
488 it
->second
->SetName(new_profile_name
);
489 UpdateStatusTrayIconContextMenu();
493 ///////////////////////////////////////////////////////////////////////////////
494 // BackgroundModeManager::BackgroundModeData, StatusIconMenuModel overrides
495 void BackgroundModeManager::ExecuteCommand(int command_id
, int event_flags
) {
496 // When a browser window is necessary, we use the first profile. The windows
497 // opened for these commands are not profile-specific, so any profile would
498 // work and the first is convenient.
499 BackgroundModeData
* bmd
= background_mode_data_
.begin()->second
.get();
500 switch (command_id
) {
502 chrome::ShowAboutChrome(bmd
->GetBrowserWindow());
504 case IDC_TASK_MANAGER
:
505 chrome::OpenTaskManager(bmd
->GetBrowserWindow());
508 content::RecordAction(UserMetricsAction("Exit"));
509 chrome::CloseAllBrowsers();
511 case IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND
: {
512 // Background mode must already be enabled (as otherwise this menu would
514 DCHECK(IsBackgroundModePrefEnabled());
515 DCHECK(chrome::WillKeepAlive());
517 // Set the background mode pref to "disabled" - the resulting notification
518 // will result in a call to DisableBackgroundMode().
519 PrefService
* service
= g_browser_process
->local_state();
521 service
->SetBoolean(prefs::kBackgroundModeEnabled
, false);
525 bmd
->ExecuteCommand(command_id
, event_flags
);
531 ///////////////////////////////////////////////////////////////////////////////
532 // BackgroundModeManager, private
533 void BackgroundModeManager::DecrementKeepAliveCountForStartup() {
534 if (keep_alive_for_startup_
) {
535 keep_alive_for_startup_
= false;
536 // We call this via the message queue to make sure we don't try to end
537 // keep-alive (which can shutdown Chrome) before the message loop has
539 base::MessageLoop::current()->PostTask(
540 FROM_HERE
, base::Bind(&chrome::DecrementKeepAliveCount
));
544 void BackgroundModeManager::StartBackgroundMode() {
545 DCHECK(ShouldBeInBackgroundMode());
546 // Don't bother putting ourselves in background mode if we're already there
547 // or if background mode is disabled.
548 if (in_background_mode_
)
551 // Mark ourselves as running in background mode.
552 in_background_mode_
= true;
554 UpdateKeepAliveAndTrayIcon();
556 content::NotificationService::current()->Notify(
557 chrome::NOTIFICATION_BACKGROUND_MODE_CHANGED
,
558 content::Source
<BackgroundModeManager
>(this),
559 content::Details
<bool>(&in_background_mode_
));
562 void BackgroundModeManager::EndBackgroundMode() {
563 if (!in_background_mode_
)
565 in_background_mode_
= false;
567 UpdateKeepAliveAndTrayIcon();
569 content::NotificationService::current()->Notify(
570 chrome::NOTIFICATION_BACKGROUND_MODE_CHANGED
,
571 content::Source
<BackgroundModeManager
>(this),
572 content::Details
<bool>(&in_background_mode_
));
575 void BackgroundModeManager::EnableBackgroundMode() {
576 DCHECK(IsBackgroundModePrefEnabled());
577 // If background mode should be enabled, but isn't, turn it on.
578 if (!in_background_mode_
&& ShouldBeInBackgroundMode()) {
579 StartBackgroundMode();
580 EnableLaunchOnStartup(true);
584 void BackgroundModeManager::DisableBackgroundMode() {
585 DCHECK(!IsBackgroundModePrefEnabled());
586 // If background mode is currently enabled, turn it off.
587 if (in_background_mode_
) {
589 EnableLaunchOnStartup(false);
593 void BackgroundModeManager::SuspendBackgroundMode() {
594 background_mode_suspended_
= true;
595 UpdateKeepAliveAndTrayIcon();
598 void BackgroundModeManager::ResumeBackgroundMode() {
599 background_mode_suspended_
= false;
600 UpdateKeepAliveAndTrayIcon();
603 void BackgroundModeManager::UpdateKeepAliveAndTrayIcon() {
604 if (in_background_mode_
&& !background_mode_suspended_
) {
605 if (!keeping_alive_
) {
606 keeping_alive_
= true;
607 chrome::IncrementKeepAliveCount();
609 CreateStatusTrayIcon();
613 RemoveStatusTrayIcon();
614 if (keeping_alive_
) {
615 keeping_alive_
= false;
616 chrome::DecrementKeepAliveCount();
620 void BackgroundModeManager::OnBrowserAdded(Browser
* browser
) {
621 ResumeBackgroundMode();
624 int BackgroundModeManager::GetBackgroundAppCount() const {
626 // Walk the BackgroundModeData for all profiles and count the number of apps.
627 for (BackgroundModeInfoMap::const_iterator it
=
628 background_mode_data_
.begin();
629 it
!= background_mode_data_
.end();
631 count
+= it
->second
->GetBackgroundAppCount();
637 int BackgroundModeManager::GetBackgroundAppCountForProfile(
638 Profile
* const profile
) const {
639 BackgroundModeData
* bmd
= GetBackgroundModeData(profile
);
640 return bmd
->GetBackgroundAppCount();
643 bool BackgroundModeManager::ShouldBeInBackgroundMode() const {
644 return IsBackgroundModePrefEnabled() &&
645 (GetBackgroundAppCount() > 0 || keep_alive_for_test_
);
648 void BackgroundModeManager::OnBackgroundAppInstalled(
649 const Extension
* extension
) {
650 // Background mode is disabled - don't do anything.
651 if (!IsBackgroundModePrefEnabled())
654 // Ensure we have a tray icon (needed so we can display the app-installed
655 // notification below).
656 EnableBackgroundMode();
657 ResumeBackgroundMode();
659 // Notify the user that a background app has been installed.
660 if (extension
) { // NULL when called by unit tests.
661 DisplayAppInstalledNotification(extension
);
665 void BackgroundModeManager::CheckReloadStatus(
666 const Extension
* extension
,
667 bool* is_being_reloaded
) {
668 // Walk the BackgroundModeData for all profiles to see if one of their
669 // extensions is being reloaded.
670 for (BackgroundModeInfoMap::const_iterator it
=
671 background_mode_data_
.begin();
672 it
!= background_mode_data_
.end();
674 Profile
* profile
= it
->first
;
675 // If the extension is being reloaded, no need to show a notification.
676 if (profile
->GetExtensionService()->IsBeingReloaded(extension
->id()))
677 *is_being_reloaded
= true;
681 void BackgroundModeManager::CreateStatusTrayIcon() {
682 // Only need status icons on windows/linux. ChromeOS doesn't allow exiting
683 // Chrome and Mac can use the dock icon instead.
685 // Since there are multiple profiles which share the status tray, we now
686 // use the browser process to keep track of it.
687 #if !defined(OS_MACOSX) && !defined(OS_CHROMEOS)
689 status_tray_
= g_browser_process
->status_tray();
692 // If the platform doesn't support status icons, or we've already created
693 // our status icon, just return.
694 if (!status_tray_
|| status_icon_
)
697 // TODO(rlp): Status tray icon should have submenus for each profile.
698 gfx::ImageSkia
* image_skia
= ui::ResourceBundle::GetSharedInstance().
699 GetImageSkiaNamed(IDR_STATUS_TRAY_ICON
);
701 status_icon_
= status_tray_
->CreateStatusIcon(
702 StatusTray::BACKGROUND_MODE_ICON
,
704 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME
));
707 UpdateStatusTrayIconContextMenu();
710 void BackgroundModeManager::UpdateStatusTrayIconContextMenu() {
711 // Ensure we have a tray icon if appropriate.
712 UpdateKeepAliveAndTrayIcon();
714 // If we don't have a status icon or one could not be created succesfully,
715 // then no need to continue the update.
719 // We should only get here if we have a profile loaded, or if we're running
721 if (background_mode_data_
.empty()) {
722 DCHECK(keep_alive_for_test_
);
726 // We are building a new menu. Reset the Command IDs.
727 command_id_extension_vector_
.clear();
729 // Clear the submenus too since we will be creating new ones.
732 // TODO(rlp): Add current profile color or indicator.
733 // Create a context menu item for Chrome.
734 scoped_ptr
<StatusIconMenuModel
> menu(new StatusIconMenuModel(this));
736 menu
->AddItem(IDC_ABOUT
, l10n_util::GetStringUTF16(IDS_ABOUT
));
737 menu
->AddItemWithStringId(IDC_TASK_MANAGER
, IDS_TASK_MANAGER
);
738 menu
->AddSeparator(ui::NORMAL_SEPARATOR
);
740 if (profile_cache_
->GetNumberOfProfiles() > 1) {
741 std::vector
<BackgroundModeData
*> bmd_vector
;
742 for (BackgroundModeInfoMap::iterator it
=
743 background_mode_data_
.begin();
744 it
!= background_mode_data_
.end();
746 bmd_vector
.push_back(it
->second
.get());
748 std::sort(bmd_vector
.begin(), bmd_vector
.end(),
749 &BackgroundModeData::BackgroundModeDataCompare
);
750 int profiles_with_apps
= 0;
751 for (std::vector
<BackgroundModeData
*>::const_iterator bmd_it
=
753 bmd_it
!= bmd_vector
.end();
755 BackgroundModeData
* bmd
= *bmd_it
;
756 // We should only display the profile in the status icon if it has at
757 // least one background app.
758 if (bmd
->GetBackgroundAppCount() > 0) {
759 StatusIconMenuModel
* submenu
= new StatusIconMenuModel(bmd
);
760 // The submenu constructor caller owns the lifetime of the submenu.
761 // The containing menu does not handle the lifetime.
762 submenus
.push_back(submenu
);
763 bmd
->BuildProfileMenu(submenu
, menu
.get());
764 profiles_with_apps
++;
767 // We should only be displaying the status tray icon if there is at least
768 // one profile with a background app.
769 DCHECK_GT(profiles_with_apps
, 0);
771 // We should only have one profile in the cache if we are not
772 // using multi-profiles. If keep_alive_for_test_ is set, then we may not
773 // have any profiles in the cache.
774 DCHECK(profile_cache_
->GetNumberOfProfiles() == size_t(1) ||
775 keep_alive_for_test_
);
776 background_mode_data_
.begin()->second
->BuildProfileMenu(menu
.get(), NULL
);
779 menu
->AddSeparator(ui::NORMAL_SEPARATOR
);
780 menu
->AddCheckItemWithStringId(
781 IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND
,
782 IDS_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND
);
783 menu
->SetCommandIdChecked(IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND
,
786 PrefService
* service
= g_browser_process
->local_state();
789 service
->IsUserModifiablePreference(prefs::kBackgroundModeEnabled
);
790 menu
->SetCommandIdEnabled(IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND
,
793 menu
->AddItemWithStringId(IDC_EXIT
, IDS_EXIT
);
795 context_menu_
= menu
.get();
796 status_icon_
->SetContextMenu(menu
.Pass());
799 void BackgroundModeManager::RemoveStatusTrayIcon() {
801 status_tray_
->RemoveStatusIcon(status_icon_
);
803 context_menu_
= NULL
;
806 BackgroundModeManager::BackgroundModeData
*
807 BackgroundModeManager::GetBackgroundModeData(Profile
* const profile
) const {
808 DCHECK(background_mode_data_
.find(profile
) != background_mode_data_
.end());
809 return background_mode_data_
.find(profile
)->second
.get();
812 BackgroundModeManager::BackgroundModeInfoMap::iterator
813 BackgroundModeManager::GetBackgroundModeIterator(
814 const base::string16
& profile_name
) {
815 BackgroundModeInfoMap::iterator profile_it
=
816 background_mode_data_
.end();
817 for (BackgroundModeInfoMap::iterator it
=
818 background_mode_data_
.begin();
819 it
!= background_mode_data_
.end();
821 if (it
->second
->name() == profile_name
) {
828 bool BackgroundModeManager::IsBackgroundModePrefEnabled() const {
829 PrefService
* service
= g_browser_process
->local_state();
831 return service
->GetBoolean(prefs::kBackgroundModeEnabled
);