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