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.
4 
5 #include "chrome/browser/background/background_mode_manager.h"
6 
7 #include <stddef.h>
8 #include <algorithm>
9 #include <string>
10 #include <utility>
11 #include <vector>
12 
13 #include "base/base_paths.h"
14 #include "base/bind.h"
15 #include "base/callback.h"
16 #include "base/callback_helpers.h"
17 #include "base/command_line.h"
18 #include "base/location.h"
19 #include "base/logging.h"
20 #include "base/metrics/histogram_macros.h"
21 #include "base/metrics/user_metrics.h"
22 #include "base/one_shot_event.h"
23 #include "base/single_thread_task_runner.h"
24 #include "base/stl_util.h"
25 #include "base/strings/utf_string_conversions.h"
26 #include "base/threading/thread_task_runner_handle.h"
27 #include "build/build_config.h"
28 #include "build/chromeos_buildflags.h"
29 #include "chrome/app/chrome_command_ids.h"
30 #include "chrome/browser/apps/app_service/app_service_proxy.h"
31 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
32 #include "chrome/browser/apps/app_service/browser_app_launcher.h"
33 #include "chrome/browser/background/background_application_list_model.h"
34 #include "chrome/browser/background/background_mode_optimizer.h"
35 #include "chrome/browser/browser_process.h"
36 #include "chrome/browser/chrome_notification_types.h"
37 #include "chrome/browser/extensions/extension_service.h"
38 #include "chrome/browser/lifetime/application_lifetime.h"
39 #include "chrome/browser/lifetime/browser_shutdown.h"
40 #include "chrome/browser/policy/profile_policy_connector.h"
41 #include "chrome/browser/profiles/profile.h"
42 #include "chrome/browser/profiles/profile_attributes_entry.h"
43 #include "chrome/browser/profiles/profile_manager.h"
44 #include "chrome/browser/status_icons/status_icon.h"
45 #include "chrome/browser/status_icons/status_tray.h"
46 #include "chrome/browser/ui/browser.h"
47 #include "chrome/browser/ui/browser_commands.h"
48 #include "chrome/browser/ui/browser_dialogs.h"
49 #include "chrome/browser/ui/browser_finder.h"
50 #include "chrome/browser/ui/browser_list.h"
51 #include "chrome/browser/ui/chrome_pages.h"
52 #include "chrome/browser/ui/extensions/app_launch_params.h"
53 #include "chrome/browser/ui/user_manager.h"
54 #include "chrome/common/chrome_constants.h"
55 #include "chrome/common/chrome_switches.h"
56 #include "chrome/common/extensions/extension_constants.h"
57 #include "chrome/common/pref_names.h"
58 #include "chrome/grit/chrome_unscaled_resources.h"
59 #include "chrome/grit/chromium_strings.h"
60 #include "chrome/grit/generated_resources.h"
61 #include "components/keep_alive_registry/keep_alive_registry.h"
62 #include "components/keep_alive_registry/keep_alive_types.h"
63 #include "components/prefs/pref_registry_simple.h"
64 #include "components/prefs/pref_service.h"
65 #include "components/startup_metric_utils/browser/startup_metric_utils.h"
66 #include "content/public/browser/notification_service.h"
67 #include "extensions/browser/extension_system.h"
68 #include "extensions/common/constants.h"
69 #include "extensions/common/extension.h"
70 #include "extensions/common/manifest_handlers/options_page_info.h"
71 #include "extensions/common/permissions/permission_set.h"
72 #include "ui/base/l10n/l10n_util.h"
73 #include "ui/base/models/image_model.h"
74 #include "ui/base/resource/resource_bundle.h"
75 #include "ui/gfx/image/image_family.h"
76 
77 #if defined(OS_WIN)
78 #include "chrome/browser/win/app_icon.h"
79 #endif
80 
81 using base::UserMetricsAction;
82 using extensions::Extension;
83 
84 namespace {
85 
86 // Enum for recording menu item clicks in UMA.
87 // NOTE: Do not renumber these as that would confuse interpretation of
88 // previously logged data. When making changes, also update histograms.xml.
89 enum MenuItem {
90   MENU_ITEM_ABOUT = 0,
91   MENU_ITEM_TASK_MANAGER = 1,
92   MENU_ITEM_BACKGROUND_CLIENT = 2,
93   MENU_ITEM_KEEP_RUNNING = 3,
94   MENU_ITEM_EXIT = 4,
95   MENU_ITEM_NUM_STATES
96 };
97 
RecordMenuItemClick(MenuItem item)98 void RecordMenuItemClick(MenuItem item) {
99   UMA_HISTOGRAM_ENUMERATION("BackgroundMode.MenuItemClick", item,
100                             MENU_ITEM_NUM_STATES);
101 }
102 }  // namespace
103 
104 // static
105 bool BackgroundModeManager::should_restart_in_background_ = false;
106 
BackgroundModeData(BackgroundModeManager * manager,Profile * profile,CommandIdHandlerVector * command_id_handler_vector)107 BackgroundModeManager::BackgroundModeData::BackgroundModeData(
108     BackgroundModeManager* manager,
109     Profile* profile,
110     CommandIdHandlerVector* command_id_handler_vector)
111     : manager_(manager),
112       applications_(std::make_unique<BackgroundApplicationListModel>(profile)),
113       profile_(profile),
114       command_id_handler_vector_(command_id_handler_vector) {
115   profile_observer_.Add(profile_);
116 }
117 
118 BackgroundModeManager::BackgroundModeData::~BackgroundModeData() = default;
119 
SetTracker(extensions::ForceInstalledTracker * tracker)120 void BackgroundModeManager::BackgroundModeData::SetTracker(
121     extensions::ForceInstalledTracker* tracker) {
122   force_installed_tracker_observer_.Add(tracker);
123 }
124 
OnProfileWillBeDestroyed(Profile * profile)125 void BackgroundModeManager::BackgroundModeData::OnProfileWillBeDestroyed(
126     Profile* profile) {
127   DCHECK_EQ(profile_, profile);
128   profile_observer_.RemoveAll();
129   force_installed_tracker_observer_.RemoveAll();
130 }
131 
132 ///////////////////////////////////////////////////////////////////////////////
133 //  BackgroundModeManager::BackgroundModeData, StatusIconMenuModel overrides
ExecuteCommand(int command_id,int event_flags)134 void BackgroundModeManager::BackgroundModeData::ExecuteCommand(
135     int command_id,
136     int event_flags) {
137   switch (command_id) {
138     case IDC_MinimumLabelValue:
139       // Do nothing. This is just a label.
140       break;
141     default:
142       DCHECK(!command_id_handler_vector_->at(command_id).is_null());
143       RecordMenuItemClick(MENU_ITEM_BACKGROUND_CLIENT);
144       command_id_handler_vector_->at(command_id).Run();
145       break;
146   }
147 }
148 
149 void BackgroundModeManager::BackgroundModeData::
OnForceInstalledExtensionsReady()150     OnForceInstalledExtensionsReady() {
151   manager_->ReleaseForceInstalledExtensionsKeepAlive();
152 }
153 
GetBrowserWindow()154 Browser* BackgroundModeManager::BackgroundModeData::GetBrowserWindow() {
155   return BackgroundModeManager::GetBrowserWindowForProfile(profile_);
156 }
157 
HasPersistentBackgroundClient() const158 bool BackgroundModeManager::BackgroundModeData::HasPersistentBackgroundClient()
159     const {
160   return applications_->HasPersistentBackgroundApps();
161 }
162 
HasAnyBackgroundClient() const163 bool BackgroundModeManager::BackgroundModeData::HasAnyBackgroundClient() const {
164   return applications_->size() > 0;
165 }
166 
BuildProfileMenu(StatusIconMenuModel * menu,StatusIconMenuModel * containing_menu)167 void BackgroundModeManager::BackgroundModeData::BuildProfileMenu(
168     StatusIconMenuModel* menu,
169     StatusIconMenuModel* containing_menu) {
170   if (HasAnyBackgroundClient()) {
171     // Add a menu item for each application (extension).
172     for (const auto& application : *applications_) {
173       gfx::ImageSkia icon = applications_->GetIcon(application.get());
174       const std::string& name = application->name();
175       int command_id = command_id_handler_vector_->size();
176       // Check that the command ID is within the dynamic range.
177       DCHECK_LT(command_id, IDC_MinimumLabelValue);
178       command_id_handler_vector_->push_back(base::BindRepeating(
179           &BackgroundModeManager::LaunchBackgroundApplication, profile_,
180           base::RetainedRef(application)));
181       menu->AddItem(command_id, base::UTF8ToUTF16(name));
182       if (!icon.isNull())
183         menu->SetIcon(menu->GetItemCount() - 1,
184                       ui::ImageModel::FromImageSkia(icon));
185 
186       // Component extensions with background that do not have an options page
187       // will cause this menu item to go to the extensions page with an
188       // absent component extension.
189       //
190       // Ideally, we would remove this item, but this conflicts with the user
191       // model where this menu shows the extensions with background.
192       //
193       // The compromise is to disable the item, avoiding the non-actionable
194       // navigate to the extensions page and preserving the user model.
195       if (application->location() == extensions::Manifest::COMPONENT) {
196         GURL options_page =
197             extensions::OptionsPageInfo::GetOptionsPage(application.get());
198         if (!options_page.is_valid())
199           menu->SetCommandIdEnabled(command_id, false);
200       }
201     }
202 
203   } else {
204     // When there are no background clients, we want to display just a label
205     // stating that none are running.
206     menu->AddItemWithStringId(IDC_MinimumLabelValue,
207                               IDS_BACKGROUND_APP_NOT_INSTALLED);
208     menu->SetCommandIdEnabled(IDC_MinimumLabelValue, false);
209   }
210   if (containing_menu) {
211     int menu_command_id = command_id_handler_vector_->size();
212     // Check that the command ID is within the dynamic range.
213     DCHECK_LT(menu_command_id, IDC_MinimumLabelValue);
214     command_id_handler_vector_->push_back(base::DoNothing());
215     containing_menu->AddSubMenu(menu_command_id, name_, menu);
216   }
217 }
218 
SetName(const base::string16 & new_profile_name)219 void BackgroundModeManager::BackgroundModeData::SetName(
220     const base::string16& new_profile_name) {
221   name_ = new_profile_name;
222 }
223 
name()224 base::string16 BackgroundModeManager::BackgroundModeData::name() {
225   return name_;
226 }
227 
228 std::set<const extensions::Extension*>
GetNewBackgroundApps()229 BackgroundModeManager::BackgroundModeData::GetNewBackgroundApps() {
230   std::set<const extensions::Extension*> new_apps;
231 
232   // Copy all current extensions into our list of |current_extensions_|.
233   for (const auto& application : *applications_) {
234     const extensions::ExtensionId& id = application->id();
235     if (current_extensions_.count(id) == 0) {
236       // Not found in our set yet - add it and maybe return as a previously
237       // unseen extension.
238       current_extensions_.insert(id);
239       // If this application has been newly loaded after the initial startup and
240       // this is a persistent background app, notify the user.
241       if (applications_->startup_done() &&
242           BackgroundApplicationListModel::IsPersistentBackgroundApp(
243               *application, profile_)) {
244         new_apps.insert(application.get());
245       }
246     }
247   }
248   return new_apps;
249 }
250 
251 // static
BackgroundModeDataCompare(const BackgroundModeData * bmd1,const BackgroundModeData * bmd2)252 bool BackgroundModeManager::BackgroundModeData::BackgroundModeDataCompare(
253     const BackgroundModeData* bmd1,
254     const BackgroundModeData* bmd2) {
255   return bmd1->name_ < bmd2->name_;
256 }
257 
258 ///////////////////////////////////////////////////////////////////////////////
259 //  BackgroundModeManager, public
BackgroundModeManager(const base::CommandLine & command_line,ProfileAttributesStorage * profile_storage)260 BackgroundModeManager::BackgroundModeManager(
261     const base::CommandLine& command_line,
262     ProfileAttributesStorage* profile_storage)
263     : profile_storage_(profile_storage), task_runner_(CreateTaskRunner()) {
264   // We should never start up if there is no browser process or if we are
265   // currently quitting.
266   CHECK(g_browser_process);
267   CHECK(!browser_shutdown::IsTryingToQuit());
268 
269   // Add self as an observer for the ProfileAttributesStorage so we know when
270   // profiles are deleted and their names change.
271   profile_storage_->AddObserver(this);
272 
273   UMA_HISTOGRAM_BOOLEAN("BackgroundMode.OnStartup.AutoLaunchState",
274                         command_line.HasSwitch(switches::kNoStartupWindow));
275   UMA_HISTOGRAM_BOOLEAN("BackgroundMode.OnStartup.IsBackgroundModePrefEnabled",
276                         IsBackgroundModePrefEnabled());
277 
278   // Listen for the background mode preference changing.
279   if (g_browser_process->local_state()) {  // Skip for unit tests
280     pref_registrar_.Init(g_browser_process->local_state());
281     pref_registrar_.Add(
282         prefs::kBackgroundModeEnabled,
283         base::BindRepeating(
284             &BackgroundModeManager::OnBackgroundModeEnabledPrefChanged,
285             base::Unretained(this)));
286   }
287 
288   // Keep the browser alive until extensions are done loading - this is needed
289   // by the --no-startup-window flag. We want to stay alive until we load
290   // extensions, at which point we should either run in background mode (if
291   // there are background apps) or exit if there are none.
292   if (command_line.HasSwitch(switches::kNoStartupWindow)) {
293     keep_alive_for_startup_.reset(
294         new ScopedKeepAlive(KeepAliveOrigin::BACKGROUND_MODE_MANAGER_STARTUP,
295                             KeepAliveRestartOption::DISABLED));
296     // Wait for force-installed extensions to install, as well.
297     keep_alive_for_force_installed_extensions_.reset(new ScopedKeepAlive(
298         KeepAliveOrigin::BACKGROUND_MODE_MANAGER_FORCE_INSTALLED_EXTENSIONS,
299         KeepAliveRestartOption::DISABLED));
300   } else {
301     // Otherwise, start with background mode suspended in case we're launching
302     // in a mode that doesn't open a browser window. It will be resumed when the
303     // first browser window is opened.
304     SuspendBackgroundMode();
305     optimizer_ = BackgroundModeOptimizer::Create();
306   }
307 
308   // If the -keep-alive-for-test flag is passed, then always keep chrome running
309   // in the background until the user explicitly terminates it.
310   if (command_line.HasSwitch(switches::kKeepAliveForTest))
311     keep_alive_for_test_ = true;
312 
313   if (ShouldBeInBackgroundMode())
314     StartBackgroundMode();
315 
316   // Listen for the application shutting down so we can release our KeepAlive.
317   registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
318                  content::NotificationService::AllSources());
319   BrowserList::AddObserver(this);
320 }
321 
~BackgroundModeManager()322 BackgroundModeManager::~BackgroundModeManager() {
323   // Remove ourselves from the application observer list (only needed by unit
324   // tests since APP_TERMINATING is what does this in a real running system).
325   for (const auto& it : background_mode_data_)
326     it.second->applications()->RemoveObserver(this);
327   BrowserList::RemoveObserver(this);
328 
329   // We're going away, so exit background mode (does nothing if we aren't in
330   // background mode currently). This is primarily needed for unit tests,
331   // because in an actual running system we'd get an APP_TERMINATING
332   // notification before being destroyed.
333   EndBackgroundMode();
334 }
335 
336 // static
RegisterPrefs(PrefRegistrySimple * registry)337 void BackgroundModeManager::RegisterPrefs(PrefRegistrySimple* registry) {
338 #if defined(OS_MAC)
339   registry->RegisterBooleanPref(prefs::kUserRemovedLoginItem, false);
340   registry->RegisterBooleanPref(prefs::kChromeCreatedLoginItem, false);
341   registry->RegisterBooleanPref(prefs::kMigratedLoginItemPref, false);
342 #endif
343   registry->RegisterBooleanPref(prefs::kBackgroundModeEnabled, true);
344 }
345 
RegisterProfile(Profile * profile)346 void BackgroundModeManager::RegisterProfile(Profile* profile) {
347   // We don't want to register multiple times for one profile.
348   DCHECK(!base::Contains(background_mode_data_, profile));
349   auto bmd = std::make_unique<BackgroundModeData>(this, profile,
350                                                   &command_id_handler_vector_);
351   BackgroundModeData* bmd_ptr = bmd.get();
352   background_mode_data_[profile] = std::move(bmd);
353 
354   // Initially set the name for this background mode data.
355   base::string16 name = l10n_util::GetStringUTF16(IDS_PROFILES_DEFAULT_NAME);
356   ProfileAttributesEntry* entry;
357   if (profile_storage_->GetProfileAttributesWithPath(
358       profile->GetPath(), &entry)) {
359     name = entry->GetName();
360   }
361   bmd_ptr->SetName(name);
362 
363   // Check for the presence of background apps after all extensions have been
364   // loaded, to handle the case where an extension has been manually removed
365   // while Chrome was not running.
366   extensions::ExtensionSystem::Get(profile)->ready().Post(
367       FROM_HERE, base::BindOnce(&BackgroundModeManager::OnExtensionsReady,
368                                 weak_factory_.GetWeakPtr(), profile));
369 
370   bmd_ptr->applications()->AddObserver(this);
371 
372   // If we're adding a new profile and running in multi-profile mode, this new
373   // profile should be added to the status icon if one currently exists.
374   if (in_background_mode_ && status_icon_)
375     UpdateStatusTrayIconContextMenu();
376 }
377 
378 // static
LaunchBackgroundApplication(Profile * profile,const Extension * extension)379 void BackgroundModeManager::LaunchBackgroundApplication(
380     Profile* profile,
381     const Extension* extension) {
382   apps::AppServiceProxyFactory::GetForProfile(profile)
383       ->BrowserAppLauncher()
384       ->LaunchAppWithParams(CreateAppLaunchParamsUserContainer(
385           profile, extension, WindowOpenDisposition::NEW_FOREGROUND_TAB,
386           apps::mojom::AppLaunchSource::kSourceBackground));
387 }
388 
389 // static
GetBrowserWindowForProfile(Profile * profile)390 Browser* BackgroundModeManager::GetBrowserWindowForProfile(Profile* profile) {
391   Browser* browser = chrome::FindLastActiveWithProfile(profile);
392   return browser ? browser : chrome::OpenEmptyWindow(profile);
393 }
394 
IsBackgroundModeActive()395 bool BackgroundModeManager::IsBackgroundModeActive() {
396   return in_background_mode_;
397 }
398 
IsBackgroundWithoutWindows() const399 bool BackgroundModeManager::IsBackgroundWithoutWindows() const {
400   return KeepAliveRegistry::GetInstance()->WouldRestartWithout({
401       // Transient startup related KeepAlives, not related to any UI.
402       KeepAliveOrigin::SESSION_RESTORE,
403       KeepAliveOrigin::BACKGROUND_MODE_MANAGER_STARTUP,
404 
405       KeepAliveOrigin::BACKGROUND_SYNC,
406 
407       // Notification KeepAlives are not dependent on the Chrome UI being
408       // loaded, and can be registered when we were in pure background mode.
409       // They just block it to avoid issues. Ignore them when determining if we
410       // are in that mode.
411       KeepAliveOrigin::NOTIFICATION,
412       KeepAliveOrigin::PENDING_NOTIFICATION_CLICK_EVENT,
413       KeepAliveOrigin::IN_FLIGHT_PUSH_MESSAGE,
414   });
415 }
416 
NumberOfBackgroundModeData()417 size_t BackgroundModeManager::NumberOfBackgroundModeData() {
418   return background_mode_data_.size();
419 }
420 
421 ///////////////////////////////////////////////////////////////////////////////
422 //  BackgroundModeManager, content::NotificationObserver overrides
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)423 void BackgroundModeManager::Observe(
424     int type,
425     const content::NotificationSource& source,
426     const content::NotificationDetails& details) {
427   DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type);
428 
429   // Make sure we aren't still keeping the app alive (only happens if we
430   // don't receive an EXTENSIONS_READY notification for some reason).
431   ReleaseStartupKeepAlive();
432   // Performing an explicit shutdown, so exit background mode (does nothing
433   // if we aren't in background mode currently).
434   EndBackgroundMode();
435   // Shutting down, so don't listen for any more notifications so we don't
436   // try to re-enter/exit background mode again.
437   registrar_.RemoveAll();
438   for (const auto& it : background_mode_data_)
439     it.second->applications()->RemoveObserver(this);
440 }
441 
OnExtensionsReady(Profile * profile)442 void BackgroundModeManager::OnExtensionsReady(Profile* profile) {
443   BackgroundModeManager::BackgroundModeData* bmd =
444       GetBackgroundModeData(profile);
445   if (bmd) {
446     UMA_HISTOGRAM_COUNTS_100("BackgroundMode.BackgroundApplicationsCount",
447                              bmd->applications()->size());
448   }
449   // Extensions are loaded, so we don't need to manually keep the browser
450   // process alive any more when running in no-startup-window mode.
451   ReleaseStartupKeepAlive();
452 
453   auto* extension_service =
454       extensions::ExtensionSystem::Get(profile)->extension_service();
455   auto* tracker = extension_service->force_installed_tracker();
456   if (tracker->IsReady() || !bmd)
457     ReleaseForceInstalledExtensionsKeepAlive();
458   else
459     bmd->SetTracker(tracker);
460 }
461 
OnBackgroundModeEnabledPrefChanged()462 void BackgroundModeManager::OnBackgroundModeEnabledPrefChanged() {
463   bool enabled = IsBackgroundModePrefEnabled();
464   UMA_HISTOGRAM_BOOLEAN("BackgroundMode.BackgroundModeEnabledPrefChanged",
465                         enabled);
466   UpdateEnableLaunchOnStartup();
467   if (enabled)
468     EnableBackgroundMode();
469   else
470     DisableBackgroundMode();
471 }
472 
473 ///////////////////////////////////////////////////////////////////////////////
474 //  BackgroundModeManager, BackgroundApplicationListModel::Observer overrides
OnApplicationDataChanged()475 void BackgroundModeManager::OnApplicationDataChanged() {
476   UpdateStatusTrayIconContextMenu();
477 }
478 
OnApplicationListChanged(const Profile * profile)479 void BackgroundModeManager::OnApplicationListChanged(const Profile* profile) {
480   if (!IsBackgroundModePrefEnabled())
481     return;
482 
483   BackgroundModeManager::BackgroundModeData* bmd =
484       GetBackgroundModeData(profile);
485   if (!bmd)
486     return;
487 
488   // Get the new apps (if any) and process them.
489   std::set<const extensions::Extension*> new_apps = bmd->GetNewBackgroundApps();
490   std::vector<base::string16> new_names;
491   for (auto* app : new_apps)
492     new_names.push_back(base::UTF8ToUTF16(app->name()));
493   OnClientsChanged(profile, new_names);
494 }
495 
496 ///////////////////////////////////////////////////////////////////////////////
497 //  BackgroundModeManager, ProfileAttributesStorage::Observer overrides
OnProfileAdded(const base::FilePath & profile_path)498 void BackgroundModeManager::OnProfileAdded(const base::FilePath& profile_path) {
499   ProfileAttributesEntry* entry;
500   bool success =
501       profile_storage_->GetProfileAttributesWithPath(profile_path, &entry);
502   DCHECK(success);
503   base::string16 profile_name = entry->GetName();
504   // At this point, the profile should be registered with the background mode
505   // manager, but when it's actually added to the ProfileAttributesStorage is
506   // when its name is set so we need up to update that with the
507   // background_mode_data.
508   for (const auto& it : background_mode_data_) {
509     if (it.first->GetPath() == profile_path) {
510       it.second->SetName(profile_name);
511       UpdateStatusTrayIconContextMenu();
512       return;
513     }
514   }
515 }
516 
OnProfileWillBeRemoved(const base::FilePath & profile_path)517 void BackgroundModeManager::OnProfileWillBeRemoved(
518     const base::FilePath& profile_path) {
519   ProfileAttributesEntry* entry;
520   bool success =
521       profile_storage_->GetProfileAttributesWithPath(profile_path, &entry);
522   DCHECK(success);
523   base::string16 profile_name = entry->GetName();
524   // Remove the profile from our map of profiles.
525   auto it = GetBackgroundModeIterator(profile_name);
526   // If a profile isn't running a background app, it may not be in the map.
527   if (it != background_mode_data_.end()) {
528     it->second->applications()->RemoveObserver(this);
529     background_mode_data_.erase(it);
530     // If there are no background mode profiles any longer, then turn off
531     // background mode.
532     UpdateEnableLaunchOnStartup();
533     if (!ShouldBeInBackgroundMode()) {
534       EndBackgroundMode();
535     }
536     UpdateStatusTrayIconContextMenu();
537   }
538 }
539 
OnProfileNameChanged(const base::FilePath & profile_path,const base::string16 & old_profile_name)540 void BackgroundModeManager::OnProfileNameChanged(
541     const base::FilePath& profile_path,
542     const base::string16& old_profile_name) {
543   ProfileAttributesEntry* entry;
544   bool success =
545       profile_storage_->GetProfileAttributesWithPath(profile_path, &entry);
546   DCHECK(success);
547   base::string16 new_profile_name = entry->GetName();
548   BackgroundModeInfoMap::const_iterator it =
549       GetBackgroundModeIterator(old_profile_name);
550   // We check that the returned iterator is valid due to unittests, but really
551   // this should only be called on profiles already known by the background
552   // mode manager.
553   if (it != background_mode_data_.end()) {
554     it->second->SetName(new_profile_name);
555     UpdateStatusTrayIconContextMenu();
556   }
557 }
558 
559 BackgroundModeManager::BackgroundModeData*
GetBackgroundModeDataForLastProfile() const560 BackgroundModeManager::GetBackgroundModeDataForLastProfile() const {
561   Profile* most_recent_profile = g_browser_process->profile_manager()->
562       GetLastUsedProfileAllowedByPolicy();
563   auto profile_background_data =
564       background_mode_data_.find(most_recent_profile);
565 
566   if (profile_background_data == background_mode_data_.end())
567     return nullptr;
568 
569   // Do not permit a locked profile to be used to open a browser.
570   ProfileAttributesEntry* entry;
571   bool success = profile_storage_->GetProfileAttributesWithPath(
572       profile_background_data->first->GetPath(), &entry);
573   DCHECK(success);
574   if (entry->IsSigninRequired())
575     return nullptr;
576 
577   return profile_background_data->second.get();
578 }
579 
580 ///////////////////////////////////////////////////////////////////////////////
581 //  BackgroundModeManager::BackgroundModeData, StatusIconMenuModel overrides
ExecuteCommand(int command_id,int event_flags)582 void BackgroundModeManager::ExecuteCommand(int command_id, int event_flags) {
583   BackgroundModeData* bmd = GetBackgroundModeDataForLastProfile();
584   switch (command_id) {
585     case IDC_ABOUT:
586       RecordMenuItemClick(MENU_ITEM_ABOUT);
587       if (bmd) {
588         chrome::ShowAboutChrome(bmd->GetBrowserWindow());
589       } else {
590         UserManager::Show(base::FilePath(),
591                           profiles::USER_MANAGER_SELECT_PROFILE_ABOUT_CHROME);
592       }
593       break;
594     case IDC_TASK_MANAGER:
595     RecordMenuItemClick(MENU_ITEM_TASK_MANAGER);
596       if (bmd) {
597         chrome::OpenTaskManager(bmd->GetBrowserWindow());
598       } else {
599         UserManager::Show(base::FilePath(),
600                           profiles::USER_MANAGER_SELECT_PROFILE_TASK_MANAGER);
601       }
602       break;
603     case IDC_EXIT:
604       RecordMenuItemClick(MENU_ITEM_EXIT);
605       base::RecordAction(UserMetricsAction("Exit"));
606       chrome::CloseAllBrowsers();
607       break;
608     case IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND: {
609       // Background mode must already be enabled (as otherwise this menu would
610       // not be visible).
611       DCHECK(IsBackgroundModePrefEnabled());
612       DCHECK(KeepAliveRegistry::GetInstance()->IsKeepingAlive());
613 
614       RecordMenuItemClick(MENU_ITEM_KEEP_RUNNING);
615 
616       // Set the background mode pref to "disabled" - the resulting notification
617       // will result in a call to DisableBackgroundMode().
618       PrefService* service = g_browser_process->local_state();
619       DCHECK(service);
620       service->SetBoolean(prefs::kBackgroundModeEnabled, false);
621       break;
622     }
623     default:
624       if (bmd) {
625         bmd->ExecuteCommand(command_id, event_flags);
626       } else {
627         UserManager::Show(base::FilePath(),
628                           profiles::USER_MANAGER_SELECT_PROFILE_NO_ACTION);
629       }
630       break;
631   }
632 }
633 
634 ///////////////////////////////////////////////////////////////////////////////
635 //  BackgroundModeManager, private
ReleaseStartupKeepAliveCallback()636 void BackgroundModeManager::ReleaseStartupKeepAliveCallback() {
637   keep_alive_for_startup_.reset();
638   optimizer_ = BackgroundModeOptimizer::Create();
639 }
640 
ReleaseStartupKeepAlive()641 void BackgroundModeManager::ReleaseStartupKeepAlive() {
642   if (keep_alive_for_startup_) {
643     // We call this via the message queue to make sure we don't try to end
644     // keep-alive (which can shutdown Chrome) before the message loop has
645     // started. This object reference is safe because it's going to be kept
646     // alive by the browser process until after the callback is called.
647     base::ThreadTaskRunnerHandle::Get()->PostTask(
648         FROM_HERE,
649         base::BindOnce(&BackgroundModeManager::ReleaseStartupKeepAliveCallback,
650                        base::Unretained(this)));
651   }
652 }
653 
ReleaseForceInstalledExtensionsKeepAlive()654 void BackgroundModeManager::ReleaseForceInstalledExtensionsKeepAlive() {
655   if (keep_alive_for_force_installed_extensions_) {
656     // We call this via the message queue to make sure we don't try to end
657     // keep-alive (which can shutdown Chrome) before the message loop has
658     // started. This object reference is safe because it's going to be kept
659     // alive by the browser process until after the callback is called.
660     base::ThreadTaskRunnerHandle::Get()->PostTask(
661         FROM_HERE, base::BindOnce(
662                        [](std::unique_ptr<ScopedKeepAlive> keep_alive) {
663                          // Cleans up unique_ptr when it goes out of scope.
664                        },
665                        std::move(keep_alive_for_force_installed_extensions_)));
666   }
667 }
668 
StartBackgroundMode()669 void BackgroundModeManager::StartBackgroundMode() {
670   DCHECK(ShouldBeInBackgroundMode());
671   // Don't bother putting ourselves in background mode if we're already there
672   // or if background mode is disabled.
673   if (in_background_mode_)
674     return;
675 
676   startup_metric_utils::SetBackgroundModeEnabled();
677 
678   // Mark ourselves as running in background mode.
679   in_background_mode_ = true;
680 
681   UpdateKeepAliveAndTrayIcon();
682 }
683 
EndBackgroundMode()684 void BackgroundModeManager::EndBackgroundMode() {
685   if (!in_background_mode_)
686     return;
687   in_background_mode_ = false;
688 
689   UpdateKeepAliveAndTrayIcon();
690 }
691 
EnableBackgroundMode()692 void BackgroundModeManager::EnableBackgroundMode() {
693   DCHECK(IsBackgroundModePrefEnabled());
694   // If background mode should be enabled, but isn't, turn it on.
695   if (!in_background_mode_ && ShouldBeInBackgroundMode()) {
696     StartBackgroundMode();
697 
698     UpdateEnableLaunchOnStartup();
699   }
700 }
701 
DisableBackgroundMode()702 void BackgroundModeManager::DisableBackgroundMode() {
703   DCHECK(!IsBackgroundModePrefEnabled());
704   // If background mode is currently enabled, turn it off.
705   if (in_background_mode_) {
706     EndBackgroundMode();
707   }
708 }
709 
SuspendBackgroundMode()710 void BackgroundModeManager::SuspendBackgroundMode() {
711   background_mode_suspended_ = true;
712   UpdateKeepAliveAndTrayIcon();
713 }
714 
ResumeBackgroundMode()715 void BackgroundModeManager::ResumeBackgroundMode() {
716   background_mode_suspended_ = false;
717   UpdateKeepAliveAndTrayIcon();
718 }
719 
UpdateKeepAliveAndTrayIcon()720 void BackgroundModeManager::UpdateKeepAliveAndTrayIcon() {
721   if (in_background_mode_ && !background_mode_suspended_) {
722     if (!keep_alive_) {
723       keep_alive_ = std::make_unique<ScopedKeepAlive>(
724           KeepAliveOrigin::BACKGROUND_MODE_MANAGER,
725           KeepAliveRestartOption::ENABLED);
726     }
727     CreateStatusTrayIcon();
728     return;
729   }
730 
731   RemoveStatusTrayIcon();
732   keep_alive_.reset();
733 }
734 
OnBrowserAdded(Browser * browser)735 void BackgroundModeManager::OnBrowserAdded(Browser* browser) {
736   ResumeBackgroundMode();
737 }
738 
OnClientsChanged(const Profile * profile,const std::vector<base::string16> & new_client_names)739 void BackgroundModeManager::OnClientsChanged(
740     const Profile* profile,
741     const std::vector<base::string16>& new_client_names) {
742   DCHECK(IsBackgroundModePrefEnabled());
743 
744   // Update the ProfileAttributesStorage with the fact whether background
745   // clients are running for this profile.
746   ProfileAttributesEntry* entry;
747   if (profile_storage_->
748       GetProfileAttributesWithPath(profile->GetPath(), &entry)) {
749     entry->SetBackgroundStatus(
750         HasPersistentBackgroundClientForProfile(profile));
751   }
752 
753   UpdateEnableLaunchOnStartup();
754   if (!ShouldBeInBackgroundMode()) {
755     // We've uninstalled our last background client, make sure we exit
756     // background mode and no longer launch on startup.
757     EndBackgroundMode();
758   } else {
759     // We have at least one background client - make sure we're in background
760     // mode.
761     if (!in_background_mode_) {
762       // We're entering background mode - make sure we have launch-on-startup
763       // enabled. On Mac, the platform-specific code tracks whether the user
764       // has deleted a login item in the past, and if so, no login item will
765       // be created (to avoid overriding the specific user action).
766       StartBackgroundMode();
767     }
768 
769     // List of clients changed so update the UI.
770     UpdateStatusTrayIconContextMenu();
771 
772     // Notify the user about any new clients.
773     for (const auto& name : new_client_names)
774       OnBackgroundClientInstalled(name);
775   }
776 }
777 
HasPersistentBackgroundClient() const778 bool BackgroundModeManager::HasPersistentBackgroundClient() const {
779   for (const auto& it : background_mode_data_) {
780     if (it.second->HasPersistentBackgroundClient())
781       return true;
782   }
783   return false;
784 }
785 
HasAnyBackgroundClient() const786 bool BackgroundModeManager::HasAnyBackgroundClient() const {
787   for (const auto& it : background_mode_data_) {
788     if (it.second->HasAnyBackgroundClient())
789       return true;
790   }
791   return false;
792 }
793 
HasPersistentBackgroundClientForProfile(const Profile * profile) const794 bool BackgroundModeManager::HasPersistentBackgroundClientForProfile(
795     const Profile* profile) const {
796   BackgroundModeManager::BackgroundModeData* bmd =
797       GetBackgroundModeData(profile);
798   return bmd && bmd->HasPersistentBackgroundClient();
799 }
800 
ShouldBeInBackgroundMode() const801 bool BackgroundModeManager::ShouldBeInBackgroundMode() const {
802   return IsBackgroundModePrefEnabled() &&
803          (HasAnyBackgroundClient() || keep_alive_for_test_);
804 }
805 
OnBackgroundClientInstalled(const base::string16 & name)806 void BackgroundModeManager::OnBackgroundClientInstalled(
807     const base::string16& name) {
808   // Background mode is disabled - don't do anything.
809   if (!IsBackgroundModePrefEnabled())
810     return;
811 
812   // Ensure we have a tray icon (needed so we can display the app-installed
813   // notification below).
814   EnableBackgroundMode();
815   ResumeBackgroundMode();
816 
817   ++client_installed_notifications_;
818   // Notify the user that a background client has been installed.
819   DisplayClientInstalledNotification(name);
820 }
821 
UpdateEnableLaunchOnStartup()822 void BackgroundModeManager::UpdateEnableLaunchOnStartup() {
823   bool new_launch_on_startup =
824       ShouldBeInBackgroundMode() && HasPersistentBackgroundClient();
825   if (launch_on_startup_enabled_ &&
826       new_launch_on_startup == *launch_on_startup_enabled_) {
827     return;
828   }
829   launch_on_startup_enabled_.emplace(new_launch_on_startup);
830   EnableLaunchOnStartup(*launch_on_startup_enabled_);
831 }
832 
833 // Gets the image for the status tray icon, at the correct size for the current
834 // platform and display settings.
GetStatusTrayIcon()835 gfx::ImageSkia GetStatusTrayIcon() {
836 #if defined(OS_WIN)
837   // On Windows, use GetSmallAppIconSize to get the correct image size. The
838   // user's "text size" setting in Windows determines how large the system tray
839   // icon should be.
840   gfx::Size size = GetSmallAppIconSize();
841 
842   // This loads all of the icon images, which is a bit wasteful because we're
843   // going to pick one and throw the rest away, but that is the price of using
844   // the ImageFamily abstraction. Note: We could just use the LoadImage function
845   // from the Windows API, but that does a *terrible* job scaling images.
846   // Therefore, we fetch the images and do our own high-quality scaling.
847   std::unique_ptr<gfx::ImageFamily> family = GetAppIconImageFamily();
848   DCHECK(family);
849   if (!family)
850     return gfx::ImageSkia();
851 
852   return family->CreateExact(size).AsImageSkia();
853 #elif defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD)
854   return *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
855       IDR_PRODUCT_LOGO_128);
856 #elif defined(OS_MAC)
857   return *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
858       IDR_STATUS_TRAY_ICON);
859 #else
860   NOTREACHED();
861   return gfx::ImageSkia();
862 #endif
863 }
864 
CreateStatusTrayIcon()865 void BackgroundModeManager::CreateStatusTrayIcon() {
866   // Only need status icons on windows/linux. ChromeOS doesn't allow exiting
867   // Chrome and Mac can use the dock icon instead.
868 
869   // Since there are multiple profiles which share the status tray, we now
870   // use the browser process to keep track of it.
871 #if !defined(OS_MAC) && !defined(OS_CHROMEOS) && !BUILDFLAG(IS_LACROS)
872   if (!status_tray_)
873     status_tray_ = g_browser_process->status_tray();
874 #endif
875 
876   // If the platform doesn't support status icons, or we've already created
877   // our status icon, just return.
878   if (!status_tray_ || status_icon_)
879     return;
880 
881   status_icon_ = status_tray_->CreateStatusIcon(
882       StatusTray::BACKGROUND_MODE_ICON, GetStatusTrayIcon(),
883       l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
884   if (!status_icon_)
885     return;
886   UpdateStatusTrayIconContextMenu();
887 }
888 
UpdateStatusTrayIconContextMenu()889 void BackgroundModeManager::UpdateStatusTrayIconContextMenu() {
890   // Ensure we have a tray icon if appropriate.
891   UpdateKeepAliveAndTrayIcon();
892 
893   // If we don't have a status icon or one could not be created succesfully,
894   // then no need to continue the update.
895   if (!status_icon_)
896     return;
897 
898   // We should only get here if we have a profile loaded, or if we're running
899   // in test mode.
900   if (background_mode_data_.empty()) {
901     DCHECK(keep_alive_for_test_);
902     return;
903   }
904 
905   command_id_handler_vector_.clear();
906   submenus.clear();
907 
908   std::unique_ptr<StatusIconMenuModel> menu(new StatusIconMenuModel(this));
909   menu->AddItem(IDC_ABOUT, l10n_util::GetStringUTF16(IDS_ABOUT));
910   menu->AddItemWithStringId(IDC_TASK_MANAGER, IDS_TASK_MANAGER);
911   menu->AddSeparator(ui::NORMAL_SEPARATOR);
912 
913   // If there are multiple profiles they each get a submenu.
914   if (profile_storage_->GetNumberOfProfiles() > 1) {
915     std::vector<BackgroundModeData*> bmd_vector;
916     for (const auto& it : background_mode_data_)
917       bmd_vector.push_back(it.second.get());
918     std::sort(bmd_vector.begin(), bmd_vector.end(),
919               &BackgroundModeData::BackgroundModeDataCompare);
920     int profiles_using_background_mode = 0;
921     for (auto* bmd : bmd_vector) {
922       // We should only display the profile in the status icon if it has at
923       // least one background app.
924       if (bmd->HasAnyBackgroundClient()) {
925         // The submenu constructor caller owns the lifetime of the submenu.
926         // The containing menu does not handle the lifetime.
927         submenus.push_back(std::make_unique<StatusIconMenuModel>(bmd));
928         bmd->BuildProfileMenu(submenus.back().get(), menu.get());
929         profiles_using_background_mode++;
930       }
931     }
932     // We should only be displaying the status tray icon if there is at least
933     // one profile using background mode. If |keep_alive_for_test_| is set,
934     // there may not be any profiles and that is okay.
935     DCHECK(profiles_using_background_mode > 0 || keep_alive_for_test_);
936   } else {
937     // We should only have one profile in the ProfileAttributesStorage if we are
938     // not using multi-profiles. If |keep_alive_for_test_| is set, then we may
939     // not have any profiles in the ProfileAttributesStorage.
940     DCHECK(profile_storage_->GetNumberOfProfiles() == size_t(1) ||
941            keep_alive_for_test_);
942     background_mode_data_.begin()->second->BuildProfileMenu(menu.get(),
943                                                             nullptr);
944   }
945 
946   menu->AddSeparator(ui::NORMAL_SEPARATOR);
947   menu->AddCheckItemWithStringId(
948       IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND,
949       IDS_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND);
950   menu->SetCommandIdChecked(IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND,
951                             true);
952 
953   PrefService* service = g_browser_process->local_state();
954   DCHECK(service);
955   bool enabled =
956       service->IsUserModifiablePreference(prefs::kBackgroundModeEnabled);
957   menu->SetCommandIdEnabled(IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND,
958                             enabled);
959 
960   menu->AddItemWithStringId(IDC_EXIT, IDS_EXIT);
961 
962   context_menu_ = menu.get();
963   status_icon_->SetContextMenu(std::move(menu));
964 }
965 
RemoveStatusTrayIcon()966 void BackgroundModeManager::RemoveStatusTrayIcon() {
967   if (status_icon_)
968     status_tray_->RemoveStatusIcon(status_icon_);
969   status_icon_ = nullptr;
970   context_menu_ = nullptr;
971 }
972 
973 BackgroundModeManager::BackgroundModeData*
GetBackgroundModeData(const Profile * profile) const974 BackgroundModeManager::GetBackgroundModeData(const Profile* profile) const {
975   // Profiles are shut down and destroyed asynchronously after
976   // OnProfileWillBeRemoved is called, so we may have dropped anything
977   // associated with the profile already.
978   auto it = background_mode_data_.find(profile);
979   return it != background_mode_data_.end() ? it->second.get() : nullptr;
980 }
981 
982 BackgroundModeManager::BackgroundModeInfoMap::iterator
GetBackgroundModeIterator(const base::string16 & profile_name)983 BackgroundModeManager::GetBackgroundModeIterator(
984     const base::string16& profile_name) {
985   auto profile_it = background_mode_data_.end();
986   for (auto it = background_mode_data_.begin();
987        it != background_mode_data_.end(); ++it) {
988     if (it->second->name() == profile_name) {
989       profile_it = it;
990     }
991   }
992   return profile_it;
993 }
994 
IsBackgroundModePrefEnabled() const995 bool BackgroundModeManager::IsBackgroundModePrefEnabled() const {
996   PrefService* service = g_browser_process->local_state();
997   DCHECK(service);
998   return service->GetBoolean(prefs::kBackgroundModeEnabled);
999 }
1000