// 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_PROCESS_MANAGER_H_
#define EXTENSIONS_BROWSER_PROCESS_MANAGER_H_

#include <map>
#include <set>
#include <string>

#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/time/time.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "extensions/common/view_type.h"

class GURL;

namespace content {
class BrowserContext;
class DevToolsAgentHost;
class RenderViewHost;
class RenderFrameHost;
class SiteInstance;
};

namespace extensions {

class Extension;
class ExtensionHost;
class ProcessManagerObserver;

// Manages dynamic state of running Chromium extensions. There is one instance
// of this class per Profile. OTR Profiles have a separate instance that keeps
// track of split-mode extensions only.
class ProcessManager : public content::NotificationObserver {
 public:
  typedef std::set<extensions::ExtensionHost*> ExtensionHostSet;
  typedef ExtensionHostSet::const_iterator const_iterator;

  static ProcessManager* Create(content::BrowserContext* context);
  virtual ~ProcessManager();

  const ExtensionHostSet& background_hosts() const {
    return background_hosts_;
  }

  typedef std::set<content::RenderViewHost*> ViewSet;
  const ViewSet GetAllViews() const;

  // The typical observer interface.
  void AddObserver(ProcessManagerObserver* observer);
  void RemoveObserver(ProcessManagerObserver* observer);

  // Creates a new UI-less extension instance.  Like CreateViewHost, but not
  // displayed anywhere.  Returns false if no background host can be created,
  // for example for hosted apps and extensions that aren't enabled in
  // Incognito.
  virtual bool CreateBackgroundHost(const Extension* extension,
                                    const GURL& url);

  // Gets the ExtensionHost for the background page for an extension, or NULL if
  // the extension isn't running or doesn't have a background page.
  ExtensionHost* GetBackgroundHostForExtension(const std::string& extension_id);

  // Returns the SiteInstance that the given URL belongs to.
  // TODO(aa): This only returns correct results for extensions and packaged
  // apps, not hosted apps.
  virtual content::SiteInstance* GetSiteInstanceForURL(const GURL& url);

  // Unregisters a RenderViewHost as hosting any extension.
  void UnregisterRenderViewHost(content::RenderViewHost* render_view_host);

  // Returns all RenderViewHosts that are registered for the specified
  // extension.
  std::set<content::RenderViewHost*> GetRenderViewHostsForExtension(
      const std::string& extension_id);

  // Returns the extension associated with the specified RenderViewHost, or
  // NULL.
  const Extension* GetExtensionForRenderViewHost(
      content::RenderViewHost* render_view_host);

  // Returns true if the (lazy) background host for the given extension has
  // already been sent the unload event and is shutting down.
  bool IsBackgroundHostClosing(const std::string& extension_id);

  // Getter and setter for the lazy background page's keepalive count. This is
  // the count of how many outstanding "things" are keeping the page alive.
  // When this reaches 0, we will begin the process of shutting down the page.
  // "Things" include pending events, resource loads, and API calls.
  int GetLazyKeepaliveCount(const Extension* extension);
  void IncrementLazyKeepaliveCount(const Extension* extension);
  void DecrementLazyKeepaliveCount(const Extension* extension);

  void IncrementLazyKeepaliveCountForView(
      content::RenderViewHost* render_view_host);

  // Keeps a background page alive. Unlike IncrementLazyKeepaliveCount, these
  // impulses will only keep the page alive for a limited amount of time unless
  // called regularly.
  void KeepaliveImpulse(const Extension* extension);

  // Handles a response to the ShouldSuspend message, used for lazy background
  // pages.
  void OnShouldSuspendAck(const std::string& extension_id, int sequence_id);

  // Same as above, for the Suspend message.
  void OnSuspendAck(const std::string& extension_id);

  // Tracks network requests for a given RenderFrameHost, used to know
  // when network activity is idle for lazy background pages.
  void OnNetworkRequestStarted(content::RenderFrameHost* render_frame_host);
  void OnNetworkRequestDone(content::RenderFrameHost* render_frame_host);

  // Prevents |extension|'s background page from being closed and sends the
  // onSuspendCanceled() event to it.
  void CancelSuspend(const Extension* extension);

  // Ensures background hosts are loaded for a new browser window.
  void OnBrowserWindowReady();

  // Gets the BrowserContext associated with site_instance_ and all other
  // related SiteInstances.
  content::BrowserContext* GetBrowserContext() const;

  // Sets callbacks for testing keepalive impulse behavior.
  typedef base::Callback<void(const std::string& extension_id)>
      ImpulseCallbackForTesting;
  void SetKeepaliveImpulseCallbackForTesting(
      const ImpulseCallbackForTesting& callback);
  void SetKeepaliveImpulseDecrementCallbackForTesting(
      const ImpulseCallbackForTesting& callback);

  // Creates an incognito-context instance for tests. Tests for non-incognito
  // contexts can just use Create() above.
  static ProcessManager* CreateIncognitoForTesting(
      content::BrowserContext* incognito_context,
      content::BrowserContext* original_context,
      ProcessManager* original_manager);

 protected:
  // If |context| is incognito pass the master context as |original_context|.
  // Otherwise pass the same context for both.
  ProcessManager(content::BrowserContext* context,
                 content::BrowserContext* original_context);

  // Called on browser shutdown to close our extension hosts.
  void CloseBackgroundHosts();

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

  // Load all background pages once the profile data is ready and the pages
  // should be loaded.
  void CreateBackgroundHostsForProfileStartup();

  content::NotificationRegistrar registrar_;

  // The set of ExtensionHosts running viewless background extensions.
  ExtensionHostSet background_hosts_;

  // A SiteInstance related to the SiteInstance for all extensions in
  // this profile.  We create it in such a way that a new
  // browsing instance is created.  This controls process grouping.
  scoped_refptr<content::SiteInstance> site_instance_;

 private:
  friend class ProcessManagerTest;

  // Extra information we keep for each extension's background page.
  struct BackgroundPageData;
  typedef std::string ExtensionId;
  typedef std::map<ExtensionId, BackgroundPageData> BackgroundPageDataMap;
  typedef std::map<content::RenderViewHost*,
      extensions::ViewType> ExtensionRenderViews;

  // Called just after |host| is created so it can be registered in our lists.
  void OnBackgroundHostCreated(ExtensionHost* host);

  // Close the given |host| iff it's a background page.
  void CloseBackgroundHost(ExtensionHost* host);

  // Internal implementation of DecrementLazyKeepaliveCount with an
  // |extension_id| known to have a lazy background page.
  void DecrementLazyKeepaliveCount(const std::string& extension_id);

  // Checks if keepalive impulses have occured, and adjusts keep alive count.
  void OnKeepaliveImpulseCheck();

  // These are called when the extension transitions between idle and active.
  // They control the process of closing the background page when idle.
  void OnLazyBackgroundPageIdle(const std::string& extension_id,
                                int sequence_id);
  void OnLazyBackgroundPageActive(const std::string& extension_id);
  void CloseLazyBackgroundPageNow(const std::string& extension_id,
                                  int sequence_id);

  // Potentially registers a RenderViewHost, if it is associated with an
  // extension. Does nothing if this is not an extension renderer.
  // Returns true, if render_view_host was registered (it is associated
  // with an extension).
  bool RegisterRenderViewHost(content::RenderViewHost* render_view_host);

  // Unregister RenderViewHosts and clear background page data for an extension
  // which has been unloaded.
  void UnregisterExtension(const std::string& extension_id);

  // Clears background page data for this extension.
  void ClearBackgroundPageData(const std::string& extension_id);

  // Returns true if loading background pages should be deferred.
  bool DeferLoadingBackgroundHosts() const;

  void OnDevToolsStateChanged(content::DevToolsAgentHost*, bool attached);

  // Contains all active extension-related RenderViewHost instances for all
  // extensions. We also keep a cache of the host's view type, because that
  // information is not accessible at registration/deregistration time.
  ExtensionRenderViews all_extension_views_;

  BackgroundPageDataMap background_page_data_;

  // The time to delay between an extension becoming idle and
  // sending a ShouldSuspend message; read from command-line switch.
  base::TimeDelta event_page_idle_time_;

  // The time to delay between sending a ShouldSuspend message and
  // sending a Suspend message; read from command-line switch.
  base::TimeDelta event_page_suspending_time_;

  // True if we have created the startup set of background hosts.
  bool startup_background_hosts_created_;

  base::Callback<void(content::DevToolsAgentHost*, bool)> devtools_callback_;

  ImpulseCallbackForTesting keepalive_impulse_callback_for_testing_;
  ImpulseCallbackForTesting keepalive_impulse_decrement_callback_for_testing_;

  ObserverList<ProcessManagerObserver> observer_list_;

  base::WeakPtrFactory<ProcessManager> weak_ptr_factory_;

  DISALLOW_COPY_AND_ASSIGN(ProcessManager);
};

}  // namespace extensions

#endif  // EXTENSIONS_BROWSER_PROCESS_MANAGER_H_