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#ifndef CHROME_BROWSER_BACKGROUND_BACKGROUND_MODE_MANAGER_H_
6#define CHROME_BROWSER_BACKGROUND_BACKGROUND_MODE_MANAGER_H_
7
8#include <map>
9
10#include "base/gtest_prod_util.h"
11#include "base/memory/scoped_vector.h"
12#include "base/prefs/pref_change_registrar.h"
13#include "chrome/browser/background/background_application_list_model.h"
14#include "chrome/browser/profiles/profile_info_cache_observer.h"
15#include "chrome/browser/status_icons/status_icon.h"
16#include "chrome/browser/status_icons/status_icon_menu_model.h"
17#include "chrome/browser/ui/browser_list_observer.h"
18#include "components/keyed_service/core/keyed_service.h"
19#include "content/public/browser/notification_observer.h"
20#include "content/public/browser/notification_registrar.h"
21#include "extensions/common/extension.h"
22
23class Browser;
24class PrefRegistrySimple;
25class Profile;
26class ProfileInfoCache;
27class StatusIcon;
28class StatusTray;
29
30namespace base {
31class CommandLine;
32}
33
34typedef std::vector<int> CommandIdExtensionVector;
35
36// BackgroundModeManager is responsible for switching Chrome into and out of
37// "background mode" and for providing UI for the user to exit Chrome when there
38// are no open browser windows.
39//
40// Chrome enters background mode whenever there is an application with the
41// "background" permission installed. This class monitors the set of
42// installed/loaded extensions to ensure that Chrome enters/exits background
43// mode at the appropriate time.
44//
45// When Chrome is in background mode, it will continue running even after the
46// last browser window is closed, until the user explicitly exits the app.
47// Additionally, when in background mode, Chrome will launch on OS login with
48// no open windows to allow apps with the "background" permission to run in the
49// background.
50class BackgroundModeManager
51    : public content::NotificationObserver,
52      public chrome::BrowserListObserver,
53      public BackgroundApplicationListModel::Observer,
54      public ProfileInfoCacheObserver,
55      public StatusIconMenuModel::Delegate {
56 public:
57  BackgroundModeManager(base::CommandLine* command_line,
58                        ProfileInfoCache* profile_cache);
59  virtual ~BackgroundModeManager();
60
61  static void RegisterPrefs(PrefRegistrySimple* registry);
62
63  virtual void RegisterProfile(Profile* profile);
64
65  static void LaunchBackgroundApplication(Profile* profile,
66      const extensions::Extension* extension);
67
68  // Returns true if background mode is active.
69  virtual bool IsBackgroundModeActive();
70
71  // Suspends background mode until either ResumeBackgroundMode is called or
72  // Chrome is restarted. This has the same effect as ending background mode
73  // for the current browser session.
74  virtual void SuspendBackgroundMode();
75
76  // Resumes background mode. This ends a suspension of background mode, but
77  // will not start it if it is not enabled.
78  virtual void ResumeBackgroundMode();
79
80  // For testing purposes.
81  int NumberOfBackgroundModeData();
82
83 private:
84  friend class AppBackgroundPageApiTest;
85  friend class BackgroundModeManagerTest;
86  friend class BackgroundModeManagerWithExtensionsTest;
87  friend class TestBackgroundModeManager;
88  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerTest,
89                           BackgroundAppLoadUnload);
90  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerTest,
91                           BackgroundLaunchOnStartup);
92  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerTest,
93                           BackgroundAppInstallUninstallWhileDisabled);
94  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerTest,
95                           BackgroundModeDisabledPreventsKeepAliveOnStartup);
96  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerTest,
97                           DisableBackgroundModeUnderTestFlag);
98  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerTest,
99                           EnableAfterBackgroundAppInstall);
100  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerTest,
101                           MultiProfile);
102  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerTest,
103                           ProfileInfoCacheStorage);
104  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerTest,
105                           ProfileInfoCacheObserver);
106  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerTest,
107                           DeleteBackgroundProfile);
108  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerWithExtensionsTest,
109                           BackgroundMenuGeneration);
110  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerWithExtensionsTest,
111                           BackgroundMenuGenerationMultipleProfile);
112  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerWithExtensionsTest,
113                           BalloonDisplay);
114  FRIEND_TEST_ALL_PREFIXES(BackgroundAppBrowserTest,
115                           ReloadBackgroundApp);
116
117  class BackgroundModeData : public StatusIconMenuModel::Delegate {
118   public:
119    explicit BackgroundModeData(
120        Profile* profile,
121        CommandIdExtensionVector* command_id_extension_vector);
122    virtual ~BackgroundModeData();
123
124    // The cached list of BackgroundApplications.
125    scoped_ptr<BackgroundApplicationListModel> applications_;
126
127    // Overrides from StatusIconMenuModel::Delegate implementation.
128    virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE;
129
130    // Returns a browser window, or creates one if none are open. Used by
131    // operations (like displaying the preferences dialog) that require a
132    // Browser window.
133    Browser* GetBrowserWindow();
134
135    // Returns the number of background apps for this profile.
136    int GetBackgroundAppCount() const;
137
138    // Builds the profile specific parts of the menu. The menu passed in may
139    // be a submenu in the case of multi-profiles or the main menu in the case
140    // of the single profile case. If containing_menu is valid, we will add
141    // menu as a submenu to it.
142    void BuildProfileMenu(StatusIconMenuModel* menu,
143                          StatusIconMenuModel* containing_menu);
144
145    // Set the name associated with this background mode data for displaying in
146    // the status tray.
147    void SetName(const base::string16& new_profile_name);
148
149    // The name associated with this background mode data. This should match
150    // the name in the ProfileInfoCache for this profile.
151    base::string16 name();
152
153    // Used for sorting BackgroundModeData*s.
154    static bool BackgroundModeDataCompare(const BackgroundModeData* bmd1,
155                                          const BackgroundModeData* bmd2);
156
157    // Returns the set of new background apps (apps that have been loaded since
158    // the last call to GetNewBackgroundApps()).
159    std::set<const extensions::Extension*> GetNewBackgroundApps();
160
161   private:
162    // Name associated with this profile which is used to label its submenu.
163    base::string16 name_;
164
165    // The profile associated with this background app data.
166    Profile* profile_;
167
168    // Weak ref vector owned by BackgroundModeManager where the
169    // indices correspond to Command IDs and values correspond to
170    // extension indices. A value of -1 indicates no extension is associated
171    // with the index.
172    CommandIdExtensionVector* command_id_extension_vector_;
173
174    // The list of notified extensions for this profile. We track this to ensure
175    // that we never notify the user about the same extension twice in a single
176    // browsing session - this is done because the extension subsystem is not
177    // good about tracking changes to the background permission around
178    // extension reloads, and will sometimes report spurious permission changes.
179    std::set<extensions::ExtensionId> current_extensions_;
180  };
181
182  // Ideally we would want our BackgroundModeData to be scoped_ptrs,
183  // but since maps copy their entries, we can't used scoped_ptrs.
184  // Similarly, we can't just have a map of BackgroundModeData objects,
185  // since BackgroundModeData contains a scoped_ptr which once again
186  // can't be copied. So rather than using BackgroundModeData* which
187  // we'd have to remember to delete, we use the ref-counted linked_ptr
188  // which is similar to a shared_ptr.
189  typedef linked_ptr<BackgroundModeData> BackgroundModeInfo;
190
191  typedef std::map<Profile*, BackgroundModeInfo> BackgroundModeInfoMap;
192
193  // content::NotificationObserver implementation.
194  virtual void Observe(int type,
195                       const content::NotificationSource& source,
196                       const content::NotificationDetails& details) OVERRIDE;
197
198  // Called when the kBackgroundModeEnabled preference changes.
199  void OnBackgroundModeEnabledPrefChanged();
200
201  // BackgroundApplicationListModel::Observer implementation.
202  virtual void OnApplicationDataChanged(const extensions::Extension* extension,
203                                        Profile* profile) OVERRIDE;
204  virtual void OnApplicationListChanged(Profile* profile) OVERRIDE;
205
206  // Overrides from ProfileInfoCacheObserver
207  virtual void OnProfileAdded(const base::FilePath& profile_path) OVERRIDE;
208  virtual void OnProfileWillBeRemoved(
209      const base::FilePath& profile_path) OVERRIDE;
210  virtual void OnProfileNameChanged(
211      const base::FilePath& profile_path,
212      const base::string16& old_profile_name) OVERRIDE;
213
214  // Overrides from StatusIconMenuModel::Delegate implementation.
215  virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE;
216
217  // chrome::BrowserListObserver implementation.
218  virtual void OnBrowserAdded(Browser* browser) OVERRIDE;
219
220  // Invoked when an extension is installed so we can ensure that
221  // launch-on-startup is enabled if appropriate. |extension| can be NULL when
222  // called from unit tests.
223  void OnBackgroundAppInstalled(
224      const extensions::Extension* extension);
225
226  // Walk the list of profiles and see if an extension or app is being
227  // currently upgraded or reloaded by any profile.  If so, update the
228  // output variables appropriately.
229  void CheckReloadStatus(
230      const extensions::Extension* extension,
231      bool* is_being_reloaded);
232
233  // Called to make sure that our launch-on-startup mode is properly set.
234  // (virtual so we can override for tests).
235  virtual void EnableLaunchOnStartup(bool should_launch);
236
237  // Invoked when a background app is installed so we can display a
238  // platform-specific notification to the user.
239  virtual void DisplayAppInstalledNotification(
240      const extensions::Extension* extension);
241
242  // Invoked to put Chrome in KeepAlive mode - chrome runs in the background
243  // and has a status bar icon.
244  void StartBackgroundMode();
245
246  // Invoked to take Chrome out of KeepAlive mode - chrome stops running in
247  // the background and removes its status bar icon.
248  void EndBackgroundMode();
249
250  // Enables keep alive and the status tray icon if and only if background mode
251  // is active and not suspended.
252  virtual void UpdateKeepAliveAndTrayIcon();
253
254  // If --no-startup-window is passed, BackgroundModeManager will manually keep
255  // chrome running while waiting for apps to load. This is called when we no
256  // longer need to do this (either because the user has chosen to exit chrome
257  // manually, or all apps have been loaded).
258  void DecrementKeepAliveCountForStartup();
259
260  // Return an appropriate name for a Preferences menu entry.  Preferences is
261  // sometimes called Options or Settings.
262  base::string16 GetPreferencesMenuLabel();
263
264  // Create a status tray icon to allow the user to shutdown Chrome when running
265  // in background mode. Virtual to enable testing.
266  virtual void CreateStatusTrayIcon();
267
268  // Removes the status tray icon because we are exiting background mode.
269  // Virtual to enable testing.
270  virtual void RemoveStatusTrayIcon();
271
272  // Create a context menu, or replace/update an existing context menu, for the
273  // status tray icon which, among other things, allows the user to shutdown
274  // Chrome when running in background mode. All profiles are listed under
275  // the one context menu.
276  virtual void UpdateStatusTrayIconContextMenu();
277
278  // Returns the BackgroundModeData associated with this profile. If it does
279  // not exist, returns NULL.
280  BackgroundModeManager::BackgroundModeData* GetBackgroundModeData(
281      Profile* const profile) const;
282
283  // Returns the iterator associated with a particular profile name.
284  // This should not be used to iterate over the background mode data. It is
285  // used to efficiently delete an item from the background mode data map.
286  BackgroundModeInfoMap::iterator GetBackgroundModeIterator(
287      const base::string16& profile_name);
288
289  // Returns true if the "Let chrome run in the background" pref is checked.
290  // (virtual to allow overriding in tests).
291  virtual bool IsBackgroundModePrefEnabled() const;
292
293  // Turns off background mode if it's currently enabled.
294  void DisableBackgroundMode();
295
296  // Turns on background mode if it's currently disabled.
297  void EnableBackgroundMode();
298
299  // Returns the number of background apps in the system (virtual to allow
300  // overriding in unit tests).
301  virtual int GetBackgroundAppCount() const;
302
303  // Returns the number of background apps for a profile.
304  virtual int GetBackgroundAppCountForProfile(Profile* const profile) const;
305
306  // Returns true if we should be in background mode.
307  bool ShouldBeInBackgroundMode() const;
308
309  // Finds the BackgroundModeData associated with the last active profile,
310  // if the profile isn't locked. Returns NULL otherwise.
311  BackgroundModeManager::BackgroundModeData*
312      GetBackgroundModeDataForLastProfile() const;
313
314  // Reference to the profile info cache. It is used to update the background
315  // app status of profiles when they open/close background apps.
316  ProfileInfoCache* profile_cache_;
317
318  // Registrars for managing our change observers.
319  content::NotificationRegistrar registrar_;
320  PrefChangeRegistrar pref_registrar_;
321
322  // The profile-keyed data for this background mode manager. Keyed on profile.
323  BackgroundModeInfoMap background_mode_data_;
324
325  // Contains the dynamic Command IDs for the entire background menu.
326  CommandIdExtensionVector command_id_extension_vector_;
327
328  // Maintains submenu lifetime for the multiple profile context menu.
329  ScopedVector<StatusIconMenuModel> submenus;
330
331  // Reference to our status tray. If null, the platform doesn't support status
332  // icons.
333  StatusTray* status_tray_;
334
335  // Reference to our status icon (if any) - owned by the StatusTray.
336  StatusIcon* status_icon_;
337
338  // Reference to our status icon's context menu (if any) - owned by the
339  // status_icon_.
340  StatusIconMenuModel* context_menu_;
341
342  // Set to true when we are running in background mode. Allows us to track our
343  // current background state so we can take the appropriate action when the
344  // user disables/enables background mode via preferences.
345  bool in_background_mode_;
346
347  // Set when we are keeping chrome running during the startup process - this
348  // is required when running with the --no-startup-window flag, as otherwise
349  // chrome would immediately exit due to having no open windows.
350  bool keep_alive_for_startup_;
351
352  // Set to true when Chrome is running with the --keep-alive-for-test flag
353  // (used for testing background mode without having to install a background
354  // app).
355  bool keep_alive_for_test_;
356
357  // Set to true when background mode is suspended.
358  bool background_mode_suspended_;
359
360  // Set to true when background mode is keeping Chrome alive.
361  bool keeping_alive_;
362
363  DISALLOW_COPY_AND_ASSIGN(BackgroundModeManager);
364};
365
366#endif  // CHROME_BROWSER_BACKGROUND_BACKGROUND_MODE_MANAGER_H_
367