// 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_EXTENSION_REGISTRY_H_
#define EXTENSIONS_BROWSER_EXTENSION_REGISTRY_H_

#include <string>

#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/observer_list.h"
#include "components/keyed_service/core/keyed_service.h"
#include "extensions/browser/uninstall_reason.h"
#include "extensions/common/extension_set.h"

#if !defined(ENABLE_EXTENSIONS)
#error "Extensions must be enabled"
#endif

namespace content {
class BrowserContext;
}

namespace extensions {
class Extension;
class ExtensionRegistryObserver;

// ExtensionRegistry holds sets of the installed extensions for a given
// BrowserContext. An incognito browser context and its master browser context
// share a single registry.
class ExtensionRegistry : public KeyedService {
 public:
  // Flags to pass to GetExtensionById() to select which sets to look in.
  enum IncludeFlag {
    NONE = 0,
    ENABLED = 1 << 0,
    DISABLED = 1 << 1,
    TERMINATED = 1 << 2,
    BLACKLISTED = 1 << 3,
    BLOCKED = 1 << 4,
    EVERYTHING = (1 << 5) - 1,
  };

  explicit ExtensionRegistry(content::BrowserContext* browser_context);
  ~ExtensionRegistry() override;

  // Returns the instance for the given |browser_context|.
  static ExtensionRegistry* Get(content::BrowserContext* browser_context);

  content::BrowserContext* browser_context() const { return browser_context_; }

  // NOTE: These sets are *eventually* mutually exclusive, but an extension can
  // appear in two sets for short periods of time.
  const ExtensionSet& enabled_extensions() const {
    return enabled_extensions_;
  }
  const ExtensionSet& disabled_extensions() const {
    return disabled_extensions_;
  }
  const ExtensionSet& terminated_extensions() const {
    return terminated_extensions_;
  }
  const ExtensionSet& blacklisted_extensions() const {
    return blacklisted_extensions_;
  }
  const ExtensionSet& blocked_extensions() const { return blocked_extensions_; }
  const ExtensionSet& ready_extensions() const { return ready_extensions_; }

  // Returns the set of all installed extensions, regardless of state (enabled,
  // disabled, etc). Equivalent to GenerateInstalledExtensionSet(EVERYTHING).
  std::unique_ptr<ExtensionSet> GenerateInstalledExtensionsSet() const;

  // Returns a set of all extensions in the subsets specified by |include_mask|.
  //  * enabled_extensions()     --> ExtensionRegistry::ENABLED
  //  * disabled_extensions()    --> ExtensionRegistry::DISABLED
  //  * terminated_extensions()  --> ExtensionRegistry::TERMINATED
  //  * blacklisted_extensions() --> ExtensionRegistry::BLACKLISTED
  //  * blocked_extensions()     --> ExtensionRegistry::BLOCKED
  std::unique_ptr<ExtensionSet> GenerateInstalledExtensionsSet(
      int include_mask) const;

  // The usual observer interface.
  void AddObserver(ExtensionRegistryObserver* observer);
  void RemoveObserver(ExtensionRegistryObserver* observer);

  // Invokes the observer method OnExtensionLoaded(). The extension must be
  // enabled at the time of the call.
  void TriggerOnLoaded(const Extension* extension);

  // Invokes the observer method OnExtensionReady(). This always follows
  // an OnLoaded event, but is not called until it's safe to create the
  // extension's child process.
  void TriggerOnReady(const Extension* extension);

  // Invokes the observer method OnExtensionUnloaded(). The extension must not
  // be enabled at the time of the call.
  void TriggerOnUnloaded(const Extension* extension,
                         UnloadedExtensionInfo::Reason reason);

  // If this is a fresh install then |is_update| is false and there must not be
  // any installed extension with |extension|'s ID. If this is an update then
  // |is_update| is true and must be an installed extension with |extension|'s
  // ID, and |old_name| must be non-empty.
  void TriggerOnWillBeInstalled(const Extension* extension,
                                bool is_update,
                                const std::string& old_name);

  // Invokes the observer method OnExtensionInstalled(). The extension must be
  // contained in one of the registry's extension sets.
  void TriggerOnInstalled(const Extension* extension,
                          bool is_update);

  // Invokes the observer method OnExtensionUninstalled(). The extension must
  // not be any installed extension with |extension|'s ID.
  void TriggerOnUninstalled(const Extension* extension, UninstallReason reason);

  // Find an extension by ID using |include_mask| to pick the sets to search:
  //  * enabled_extensions()     --> ExtensionRegistry::ENABLED
  //  * disabled_extensions()    --> ExtensionRegistry::DISABLED
  //  * terminated_extensions()  --> ExtensionRegistry::TERMINATED
  //  * blacklisted_extensions() --> ExtensionRegistry::BLACKLISTED
  //  * blocked_extensions()     --> ExtensionRegistry::BLOCKED
  // Returns NULL if the extension is not found in the selected sets.
  const Extension* GetExtensionById(const std::string& id,
                                    int include_mask) const;

  // Looks up an extension by ID, regardless of whether it's enabled,
  // disabled, blacklisted, or terminated.
  const Extension* GetInstalledExtension(const std::string& id) const;

  // Adds the specified extension to the enabled set. The registry becomes an
  // owner. Any previous extension with the same ID is removed.
  // Returns true if there is no previous extension.
  // NOTE: You probably want to use ExtensionService instead of calling this
  // method directly.
  bool AddEnabled(const scoped_refptr<const Extension>& extension);

  // Removes the specified extension from the enabled set.
  // Returns true if the set contained the specified extension.
  // NOTE: You probably want to use ExtensionService instead of calling this
  // method directly.
  bool RemoveEnabled(const std::string& id);

  // As above, but for the disabled set.
  bool AddDisabled(const scoped_refptr<const Extension>& extension);
  bool RemoveDisabled(const std::string& id);

  // As above, but for the terminated set.
  bool AddTerminated(const scoped_refptr<const Extension>& extension);
  bool RemoveTerminated(const std::string& id);

  // As above, but for the blacklisted set.
  bool AddBlacklisted(const scoped_refptr<const Extension>& extension);
  bool RemoveBlacklisted(const std::string& id);

  // As above, but for the blocked set.
  bool AddBlocked(const scoped_refptr<const Extension>& extension);
  bool RemoveBlocked(const std::string& id);

  // As above, but for the ready set.
  bool AddReady(const scoped_refptr<const Extension>& extension);
  bool RemoveReady(const std::string& id);

  // Removes all extensions from all sets.
  void ClearAll();

  // KeyedService implementation:
  void Shutdown() override;

 private:
  // Extensions that are installed, enabled and not terminated.
  ExtensionSet enabled_extensions_;

  // Extensions that are installed and disabled.
  ExtensionSet disabled_extensions_;

  // Extensions that are installed and terminated.
  ExtensionSet terminated_extensions_;

  // Extensions that are installed and blacklisted. Generally these shouldn't be
  // considered as installed by the extension platform: we only keep them around
  // so that if extensions are blacklisted by mistake they can easily be
  // un-blacklisted.
  ExtensionSet blacklisted_extensions_;

  // Extensions that are installed and blocked. Will never be loaded.
  ExtensionSet blocked_extensions_;

  // Extensions that are ready for execution. This set is a non-exclusive
  // subset of |enabled_extensions_|.
  ExtensionSet ready_extensions_;

  base::ObserverList<ExtensionRegistryObserver> observers_;

  content::BrowserContext* const browser_context_;

  DISALLOW_COPY_AND_ASSIGN(ExtensionRegistry);
};

}  // namespace extensions

#endif  // EXTENSIONS_BROWSER_EXTENSION_REGISTRY_H_
