// Copyright 2013 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 EXTENSIONS_BROWSER_PENDING_EXTENSION_MANAGER_H_
#define EXTENSIONS_BROWSER_PENDING_EXTENSION_MANAGER_H_

#include <list>
#include <string>

#include "extensions/browser/pending_extension_info.h"
#include "extensions/common/manifest.h"

class ExtensionServiceInterface;
class GURL;

namespace base {
class Version;
}

FORWARD_DECLARE_TEST(ExtensionServiceTest,
                     UpdatePendingExtensionAlreadyInstalled);

namespace extensions {
class Extension;
class PendingExtensionManager;

class ExtensionUpdaterTest;
void SetupPendingExtensionManagerForTest(
    int count, const GURL& update_url,
    PendingExtensionManager* pending_extension_manager);

// Class PendingExtensionManager manages the set of extensions which are
// being installed or updated. In general, installation and updates take
// time, because they involve downloading, unpacking, and installing.
// This class allows us to avoid race cases where multiple sources install
// the same extension.
// The extensions service creates an instance of this class, and manages
// its lifetime. This class should only be used from the UI thread.
class PendingExtensionManager {
 public:
  // |service| is a reference to the ExtensionService whose pending
  // extensions we are managing. The service creates an instance of
  // this class on construction, and destroys it on destruction.
  // The service remains valid over the entire lifetime of this class.
  explicit PendingExtensionManager(const ExtensionServiceInterface& service);
  ~PendingExtensionManager();

  // TODO(skerner): Many of these methods can be private once code in
  // ExtensionService is moved into methods of this class.

  // Remove extension with id |id| from the set of pending extensions. Returns
  // true if such an extension was found and removed, false otherwise.
  bool Remove(const std::string& id);

  // Get the  information for a pending extension.  Returns a pointer to the
  // pending extension with id |id|, or NULL if there is no such extension.
  const PendingExtensionInfo* GetById(const std::string& id) const;

  // Is |id| in the set of pending extensions?
  bool IsIdPending(const std::string& id) const;

  // Returns true if there are any extensions pending.
  bool HasPendingExtensions() const;

  // Whether there is pending extension install from sync.
  bool HasPendingExtensionFromSync() const;

  // Adds an extension in a pending state; the extension with the
  // given info will be installed on the next auto-update cycle.
  // Return true if the extension was added.  Will return false
  // if the extension is pending from another source which overrides
  // sync installs (such as a policy extension) or if the extension
  // is already installed.
  //
  // TODO(akalin): Replace |install_silently| with a list of
  // pre-enabled permissions.
  bool AddFromSync(
      const std::string& id,
      const GURL& update_url,
      PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install,
      bool install_silently);

  // Adds an extension that was depended on by another extension.
  bool AddFromExtensionImport(
      const std::string& id,
      const GURL& update_url,
      PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install);

  // Given an extension id and an update URL, schedule the extension
  // to be fetched, installed, and activated.
  bool AddFromExternalUpdateUrl(const std::string& id,
                                const GURL& update_url,
                                Manifest::Location location,
                                int creation_flags,
                                bool mark_acknowledged);

  // Add a pending extension record for an external CRX file.
  // Return true if the CRX should be installed, false if an existing
  // pending record overrides it.
  bool AddFromExternalFile(
      const std::string& id,
      Manifest::Location location,
      const base::Version& version,
      int creation_flags,
      bool mark_acknowledged);

  // Get the list of pending IDs that should be installed from an update URL.
  // Pending extensions that will be installed from local files will not be
  // included in the set.
  void GetPendingIdsForUpdateCheck(
      std::list<std::string>* out_ids_for_update_check) const;

 private:
  typedef std::list<PendingExtensionInfo> PendingExtensionList;

  // Assumes an extension with id |id| is not already installed.
  // Return true if the extension was added.
  bool AddExtensionImpl(
      const std::string& id,
      const GURL& update_url,
      const base::Version& version,
      PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install,
      bool is_from_sync,
      bool install_silently,
      Manifest::Location install_source,
      int creation_flags,
      bool mark_acknowledged);

  // Add a pending extension record directly.  Used for unit tests that need
  // to set an inital state. Use friendship to allow the tests to call this
  // method.
  void AddForTesting(const PendingExtensionInfo& pending_extension_info);

  // Reference to the extension service whose pending extensions this class is
  // managing.  Because this class is a member of |service_|, it is created
  // and destroyed with |service_|. We only use methods from the interface
  // ExtensionServiceInterface.
  const ExtensionServiceInterface& service_;

  PendingExtensionList pending_extension_list_;

  FRIEND_TEST_ALL_PREFIXES(::ExtensionServiceTest,
                           UpdatePendingExtensionAlreadyInstalled);
  friend class ExtensionUpdaterTest;
  friend void SetupPendingExtensionManagerForTest(
      int count, const GURL& update_url,
      PendingExtensionManager* pending_extension_manager);

  DISALLOW_COPY_AND_ASSIGN(PendingExtensionManager);
};

}  // namespace extensions

#endif  // EXTENSIONS_BROWSER_PENDING_EXTENSION_MANAGER_H_