// Copyright 2014 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_API_RUNTIME_RUNTIME_API_H_
#define EXTENSIONS_BROWSER_API_RUNTIME_RUNTIME_API_H_

#include <string>

#include "base/scoped_observer.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "extensions/browser/api/runtime/runtime_api_delegate.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "extensions/browser/extension_function.h"
#include "extensions/browser/extension_registry_observer.h"
#include "extensions/browser/process_manager_observer.h"
#include "extensions/browser/update_observer.h"
#include "extensions/common/api/runtime.h"

namespace base {
class Version;
}

namespace content {
class BrowserContext;
}

namespace extensions {

namespace core_api {
namespace runtime {
struct PlatformInfo;
}
}

class Extension;
class ExtensionHost;
class ExtensionRegistry;

// Runtime API dispatches onStartup, onInstalled, and similar events to
// extensions. There is one instance shared between a browser context and
// its related incognito instance.
class RuntimeAPI : public BrowserContextKeyedAPI,
                   public content::NotificationObserver,
                   public ExtensionRegistryObserver,
                   public UpdateObserver,
                   public ProcessManagerObserver {
 public:
  static BrowserContextKeyedAPIFactory<RuntimeAPI>* GetFactoryInstance();

  explicit RuntimeAPI(content::BrowserContext* context);
  virtual ~RuntimeAPI();

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

  void ReloadExtension(const std::string& extension_id);
  bool CheckForUpdates(const std::string& extension_id,
                       const RuntimeAPIDelegate::UpdateCheckCallback& callback);
  void OpenURL(const GURL& uninstall_url);
  bool GetPlatformInfo(core_api::runtime::PlatformInfo* info);
  bool RestartDevice(std::string* error_message);

 private:
  friend class BrowserContextKeyedAPIFactory<RuntimeAPI>;

  // ExtensionRegistryObserver implementation.
  virtual void OnExtensionLoaded(content::BrowserContext* browser_context,
                                 const Extension* extension) OVERRIDE;
  virtual void OnExtensionWillBeInstalled(
      content::BrowserContext* browser_context,
      const Extension* extension,
      bool is_update,
      bool from_ephemeral,
      const std::string& old_name) OVERRIDE;
  virtual void OnExtensionUninstalled(content::BrowserContext* browser_context,
                                      const Extension* extension,
                                      UninstallReason reason) OVERRIDE;

  // BrowserContextKeyedAPI implementation:
  static const char* service_name() { return "RuntimeAPI"; }
  static const bool kServiceRedirectedInIncognito = true;
  static const bool kServiceIsNULLWhileTesting = true;
  virtual void Shutdown() OVERRIDE;

  // extensions::UpdateObserver overrides:
  virtual void OnAppUpdateAvailable(const Extension* extension) OVERRIDE;
  virtual void OnChromeUpdateAvailable() OVERRIDE;

  // ProcessManagerObserver implementation:
  virtual void OnBackgroundHostStartup(const Extension* extension) OVERRIDE;

  scoped_ptr<RuntimeAPIDelegate> delegate_;

  content::BrowserContext* browser_context_;

  // True if we should dispatch the chrome.runtime.onInstalled event with
  // reason "chrome_update" upon loading each extension.
  bool dispatch_chrome_updated_event_;

  content::NotificationRegistrar registrar_;

  // Listen to extension notifications.
  ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
      extension_registry_observer_;

  DISALLOW_COPY_AND_ASSIGN(RuntimeAPI);
};

class RuntimeEventRouter {
 public:
  // Dispatches the onStartup event to all currently-loaded extensions.
  static void DispatchOnStartupEvent(content::BrowserContext* context,
                                     const std::string& extension_id);

  // Dispatches the onInstalled event to the given extension.
  static void DispatchOnInstalledEvent(content::BrowserContext* context,
                                       const std::string& extension_id,
                                       const base::Version& old_version,
                                       bool chrome_updated);

  // Dispatches the onUpdateAvailable event to the given extension.
  static void DispatchOnUpdateAvailableEvent(
      content::BrowserContext* context,
      const std::string& extension_id,
      const base::DictionaryValue* manifest);

  // Dispatches the onBrowserUpdateAvailable event to all extensions.
  static void DispatchOnBrowserUpdateAvailableEvent(
      content::BrowserContext* context);

  // Dispatches the onRestartRequired event to the given app.
  static void DispatchOnRestartRequiredEvent(
      content::BrowserContext* context,
      const std::string& app_id,
      core_api::runtime::OnRestartRequired::Reason reason);

  // Does any work needed at extension uninstall (e.g. load uninstall url).
  static void OnExtensionUninstalled(content::BrowserContext* context,
                                     const std::string& extension_id,
                                     UninstallReason reason);
};

class RuntimeGetBackgroundPageFunction : public UIThreadExtensionFunction {
 public:
  DECLARE_EXTENSION_FUNCTION("runtime.getBackgroundPage",
                             RUNTIME_GETBACKGROUNDPAGE)

 protected:
  virtual ~RuntimeGetBackgroundPageFunction() {}
  virtual ResponseAction Run() OVERRIDE;

 private:
  void OnPageLoaded(ExtensionHost*);
};

class RuntimeSetUninstallURLFunction : public UIThreadExtensionFunction {
 public:
  DECLARE_EXTENSION_FUNCTION("runtime.setUninstallURL", RUNTIME_SETUNINSTALLURL)

 protected:
  virtual ~RuntimeSetUninstallURLFunction() {}
  virtual ResponseAction Run() OVERRIDE;
};

class RuntimeReloadFunction : public UIThreadExtensionFunction {
 public:
  DECLARE_EXTENSION_FUNCTION("runtime.reload", RUNTIME_RELOAD)

 protected:
  virtual ~RuntimeReloadFunction() {}
  virtual ResponseAction Run() OVERRIDE;
};

class RuntimeRequestUpdateCheckFunction : public UIThreadExtensionFunction {
 public:
  DECLARE_EXTENSION_FUNCTION("runtime.requestUpdateCheck",
                             RUNTIME_REQUESTUPDATECHECK)

 protected:
  virtual ~RuntimeRequestUpdateCheckFunction() {}
  virtual ResponseAction Run() OVERRIDE;

 private:
  void CheckComplete(const RuntimeAPIDelegate::UpdateCheckResult& result);
};

class RuntimeRestartFunction : public UIThreadExtensionFunction {
 public:
  DECLARE_EXTENSION_FUNCTION("runtime.restart", RUNTIME_RESTART)

 protected:
  virtual ~RuntimeRestartFunction() {}
  virtual ResponseAction Run() OVERRIDE;
};

class RuntimeGetPlatformInfoFunction : public UIThreadExtensionFunction {
 public:
  DECLARE_EXTENSION_FUNCTION("runtime.getPlatformInfo",
                             RUNTIME_GETPLATFORMINFO);

 protected:
  virtual ~RuntimeGetPlatformInfoFunction() {}
  virtual ResponseAction Run() OVERRIDE;
};

class RuntimeGetPackageDirectoryEntryFunction
    : public UIThreadExtensionFunction {
 public:
  DECLARE_EXTENSION_FUNCTION("runtime.getPackageDirectoryEntry",
                             RUNTIME_GETPACKAGEDIRECTORYENTRY)

 protected:
  virtual ~RuntimeGetPackageDirectoryEntryFunction() {}
  virtual ResponseAction Run() OVERRIDE;
};

}  // namespace extensions

#endif  // EXTENSIONS_BROWSER_API_RUNTIME_RUNTIME_API_H_