C++程序  |  346行  |  14.09 KB

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_BACKGROUND_BACKGROUND_MODE_MANAGER_H_
#define CHROME_BROWSER_BACKGROUND_BACKGROUND_MODE_MANAGER_H_

#include <map>

#include "base/gtest_prod_util.h"
#include "base/memory/scoped_vector.h"
#include "base/prefs/pref_change_registrar.h"
#include "chrome/browser/background/background_application_list_model.h"
#include "chrome/browser/profiles/profile_info_cache_observer.h"
#include "chrome/browser/status_icons/status_icon.h"
#include "chrome/browser/status_icons/status_icon_menu_model.h"
#include "chrome/browser/ui/browser_list_observer.h"
#include "components/browser_context_keyed_service/browser_context_keyed_service.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"

class Browser;
class CommandLine;
class PrefRegistrySimple;
class Profile;
class ProfileInfoCache;
class StatusIcon;
class StatusTray;

namespace extensions {
class Extension;
}

typedef std::vector<int> CommandIdExtensionVector;

// BackgroundModeManager is responsible for switching Chrome into and out of
// "background mode" and for providing UI for the user to exit Chrome when there
// are no open browser windows.
//
// Chrome enters background mode whenever there is an application with the
// "background" permission installed. This class monitors the set of
// installed/loaded extensions to ensure that Chrome enters/exits background
// mode at the appropriate time.
//
// When Chrome is in background mode, it will continue running even after the
// last browser window is closed, until the user explicitly exits the app.
// Additionally, when in background mode, Chrome will launch on OS login with
// no open windows to allow apps with the "background" permission to run in the
// background.
class BackgroundModeManager
    : public content::NotificationObserver,
      public chrome::BrowserListObserver,
      public BackgroundApplicationListModel::Observer,
      public ProfileInfoCacheObserver,
      public StatusIconMenuModel::Delegate {
 public:
  BackgroundModeManager(CommandLine* command_line,
                        ProfileInfoCache* profile_cache);
  virtual ~BackgroundModeManager();

  static void RegisterPrefs(PrefRegistrySimple* registry);

  virtual void RegisterProfile(Profile* profile);

  static void LaunchBackgroundApplication(Profile* profile,
      const extensions::Extension* extension);

  // Returns true if background mode is active.
  virtual bool IsBackgroundModeActive();

  // Suspends background mode until either ResumeBackgroundMode is called or
  // Chrome is restarted. This has the same effect as ending background mode
  // for the current browser session.
  virtual void SuspendBackgroundMode();

  // Resumes background mode. This ends a suspension of background mode, but
  // will not start it if it is not enabled.
  virtual void ResumeBackgroundMode();

  // For testing purposes.
  int NumberOfBackgroundModeData();

 private:
  friend class AppBackgroundPageApiTest;
  friend class BackgroundModeManagerTest;
  friend class TestBackgroundModeManager;
  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerTest,
                           BackgroundAppLoadUnload);
  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerTest,
                           BackgroundLaunchOnStartup);
  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerTest,
                           BackgroundAppInstallUninstallWhileDisabled);
  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerTest,
                           BackgroundModeDisabledPreventsKeepAliveOnStartup);
  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerTest,
                           DisableBackgroundModeUnderTestFlag);
  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerTest,
                           EnableAfterBackgroundAppInstall);
  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerTest,
                           MultiProfile);
  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerTest,
                           ProfileInfoCacheStorage);
  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerTest,
                           ProfileInfoCacheObserver);
  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerTest,
                           BackgroundMenuGeneration);
  FRIEND_TEST_ALL_PREFIXES(BackgroundModeManagerTest,
                           BackgroundMenuGenerationMultipleProfile);
  FRIEND_TEST_ALL_PREFIXES(BackgroundAppBrowserTest,
                           ReloadBackgroundApp);

  class BackgroundModeData : public StatusIconMenuModel::Delegate {
   public:
    explicit BackgroundModeData(
        Profile* profile,
        CommandIdExtensionVector* command_id_extension_vector);
    virtual ~BackgroundModeData();

    // The cached list of BackgroundApplications.
    scoped_ptr<BackgroundApplicationListModel> applications_;

    // Overrides from StatusIconMenuModel::Delegate implementation.
    virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE;

    // Returns a browser window, or creates one if none are open. Used by
    // operations (like displaying the preferences dialog) that require a
    // Browser window.
    Browser* GetBrowserWindow();

    // Returns the number of background apps for this profile.
    int GetBackgroundAppCount() const;

    // Builds the profile specific parts of the menu. The menu passed in may
    // be a submenu in the case of multi-profiles or the main menu in the case
    // of the single profile case. If containing_menu is valid, we will add
    // menu as a submenu to it.
    void BuildProfileMenu(StatusIconMenuModel* menu,
                          StatusIconMenuModel* containing_menu);

    // Set the name associated with this background mode data for displaying in
    // the status tray.
    void SetName(const base::string16& new_profile_name);

    // The name associated with this background mode data. This should match
    // the name in the ProfileInfoCache for this profile.
    base::string16 name();

    // Used for sorting BackgroundModeData*s.
    static bool BackgroundModeDataCompare(const BackgroundModeData* bmd1,
                                          const BackgroundModeData* bmd2);

   private:
    // Name associated with this profile which is used to label its submenu.
    base::string16 name_;

    // The profile associated with this background app data.
    Profile* profile_;

    // Weak ref vector owned by BackgroundModeManager where the
    // indices correspond to Command IDs and values correspond to
    // extension indices. A value of -1 indicates no extension is associated
    // with the index.
    CommandIdExtensionVector* command_id_extension_vector_;
  };

  // Ideally we would want our BackgroundModeData to be scoped_ptrs,
  // but since maps copy their entries, we can't used scoped_ptrs.
  // Similarly, we can't just have a map of BackgroundModeData objects,
  // since BackgroundModeData contains a scoped_ptr which once again
  // can't be copied. So rather than using BackgroundModeData* which
  // we'd have to remember to delete, we use the ref-counted linked_ptr
  // which is similar to a shared_ptr.
  typedef linked_ptr<BackgroundModeData> BackgroundModeInfo;

  typedef std::map<Profile*, BackgroundModeInfo> BackgroundModeInfoMap;

  // content::NotificationObserver implementation.
  virtual void Observe(int type,
                       const content::NotificationSource& source,
                       const content::NotificationDetails& details) OVERRIDE;

  // Called when the kBackgroundModeEnabled preference changes.
  void OnBackgroundModeEnabledPrefChanged();

  // BackgroundApplicationListModel::Observer implementation.
  virtual void OnApplicationDataChanged(const extensions::Extension* extension,
                                        Profile* profile) OVERRIDE;
  virtual void OnApplicationListChanged(Profile* profile) OVERRIDE;

  // Overrides from ProfileInfoCacheObserver
  virtual void OnProfileAdded(const base::FilePath& profile_path) OVERRIDE;
  virtual void OnProfileWillBeRemoved(
      const base::FilePath& profile_path) OVERRIDE;
  virtual void OnProfileNameChanged(
      const base::FilePath& profile_path,
      const base::string16& old_profile_name) OVERRIDE;

  // Overrides from StatusIconMenuModel::Delegate implementation.
  virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE;

  // chrome::BrowserListObserver implementation.
  virtual void OnBrowserAdded(Browser* browser) OVERRIDE;

  // Invoked when an extension is installed so we can ensure that
  // launch-on-startup is enabled if appropriate. |extension| can be NULL when
  // called from unit tests.
  void OnBackgroundAppInstalled(
      const extensions::Extension* extension);

  // Walk the list of profiles and see if an extension or app is being
  // currently upgraded or reloaded by any profile.  If so, update the
  // output variables appropriately.
  void CheckReloadStatus(
      const extensions::Extension* extension,
      bool* is_being_reloaded);

  // Called to make sure that our launch-on-startup mode is properly set.
  // (virtual so we can override for tests).
  virtual void EnableLaunchOnStartup(bool should_launch);

  // Invoked when a background app is installed so we can display a
  // platform-specific notification to the user.
  virtual void DisplayAppInstalledNotification(
      const extensions::Extension* extension);

  // Invoked to put Chrome in KeepAlive mode - chrome runs in the background
  // and has a status bar icon.
  void StartBackgroundMode();

  // Invoked to take Chrome out of KeepAlive mode - chrome stops running in
  // the background and removes its status bar icon.
  void EndBackgroundMode();

  // Enables keep alive and the status tray icon if and only if background mode
  // is active and not suspended.
  virtual void UpdateKeepAliveAndTrayIcon();

  // If --no-startup-window is passed, BackgroundModeManager will manually keep
  // chrome running while waiting for apps to load. This is called when we no
  // longer need to do this (either because the user has chosen to exit chrome
  // manually, or all apps have been loaded).
  void EndKeepAliveForStartup();

  // Return an appropriate name for a Preferences menu entry.  Preferences is
  // sometimes called Options or Settings.
  base::string16 GetPreferencesMenuLabel();

  // Create a status tray icon to allow the user to shutdown Chrome when running
  // in background mode. Virtual to enable testing.
  virtual void CreateStatusTrayIcon();

  // Removes the status tray icon because we are exiting background mode.
  // Virtual to enable testing.
  virtual void RemoveStatusTrayIcon();

  // Create a context menu, or replace/update an existing context menu, for the
  // status tray icon which, among other things, allows the user to shutdown
  // Chrome when running in background mode. All profiles are listed under
  // the one context menu.
  virtual void UpdateStatusTrayIconContextMenu();

  // Returns the BackgroundModeData associated with this profile. If it does
  // not exist, returns NULL.
  BackgroundModeManager::BackgroundModeData* GetBackgroundModeData(
      Profile* const profile) const;

  // Returns the iterator associated with a particular profile name.
  // This should not be used to iterate over the background mode data. It is
  // used to efficiently delete an item from the background mode data map.
  BackgroundModeInfoMap::iterator GetBackgroundModeIterator(
      const base::string16& profile_name);

  // Returns true if the "Let chrome run in the background" pref is checked.
  // (virtual to allow overriding in tests).
  virtual bool IsBackgroundModePrefEnabled() const;

  // Turns off background mode if it's currently enabled.
  void DisableBackgroundMode();

  // Turns on background mode if it's currently disabled.
  void EnableBackgroundMode();

  // Returns the number of background apps in the system (virtual to allow
  // overriding in unit tests).
  virtual int GetBackgroundAppCount() const;

  // Returns the number of background apps for a profile.
  virtual int GetBackgroundAppCountForProfile(Profile* const profile) const;

  // Returns true if we should be in background mode.
  bool ShouldBeInBackgroundMode() const;

  // Reference to the profile info cache. It is used to update the background
  // app status of profiles when they open/close background apps.
  ProfileInfoCache* profile_cache_;

  // Registrars for managing our change observers.
  content::NotificationRegistrar registrar_;
  PrefChangeRegistrar pref_registrar_;

  // The profile-keyed data for this background mode manager. Keyed on profile.
  BackgroundModeInfoMap background_mode_data_;

  // Contains the dynamic Command IDs for the entire background menu.
  CommandIdExtensionVector command_id_extension_vector_;

  // Maintains submenu lifetime for the multiple profile context menu.
  ScopedVector<StatusIconMenuModel> submenus;

  // Reference to our status tray. If null, the platform doesn't support status
  // icons.
  StatusTray* status_tray_;

  // Reference to our status icon (if any) - owned by the StatusTray.
  StatusIcon* status_icon_;

  // Reference to our status icon's context menu (if any) - owned by the
  // status_icon_.
  StatusIconMenuModel* context_menu_;

  // Set to true when we are running in background mode. Allows us to track our
  // current background state so we can take the appropriate action when the
  // user disables/enables background mode via preferences.
  bool in_background_mode_;

  // Set when we are keeping chrome running during the startup process - this
  // is required when running with the --no-startup-window flag, as otherwise
  // chrome would immediately exit due to having no open windows.
  bool keep_alive_for_startup_;

  // Set to true when Chrome is running with the --keep-alive-for-test flag
  // (used for testing background mode without having to install a background
  // app).
  bool keep_alive_for_test_;

  // Set to true when background mode is suspended.
  bool background_mode_suspended_;

  // Set to true when background mode is keeping Chrome alive.
  bool keeping_alive_;

  DISALLOW_COPY_AND_ASSIGN(BackgroundModeManager);
};

#endif  // CHROME_BROWSER_BACKGROUND_BACKGROUND_MODE_MANAGER_H_