// Copyright (c) 2009 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_WORKER_HOST_WORKER_PROCESS_HOST_H_
#define CHROME_BROWSER_WORKER_HOST_WORKER_PROCESS_HOST_H_

#include <list>

#include "base/basictypes.h"
#include "base/task.h"
#include "chrome/browser/worker_host/worker_document_set.h"
#include "chrome/common/child_process_host.h"
#include "googleurl/src/gurl.h"
#include "ipc/ipc_channel.h"

struct ViewHostMsg_CreateWorker_Params;

class WorkerProcessHost : public ChildProcessHost {
 public:

  // Contains information about each worker instance, needed to forward messages
  // between the renderer and worker processes.
  class WorkerInstance {
   public:
    WorkerInstance(const GURL& url,
                   bool shared,
                   bool off_the_record,
                   const string16& name,
                   int worker_route_id);

    // Unique identifier for a worker client.
    typedef std::pair<IPC::Message::Sender*, int> SenderInfo;

    // APIs to manage the sender list for a given instance.
    void AddSender(IPC::Message::Sender* sender, int sender_route_id);
    void RemoveSender(IPC::Message::Sender* sender, int sender_route_id);
    void RemoveSenders(IPC::Message::Sender* sender);
    bool HasSender(IPC::Message::Sender* sender, int sender_route_id) const;
    bool RendererIsParent(int renderer_id, int render_view_route_id) const;
    int NumSenders() const { return senders_.size(); }
    // Returns the single sender (must only be one).
    SenderInfo GetSender() const;

    typedef std::list<SenderInfo> SenderList;
    const SenderList& senders() const { return senders_; }

    // Checks if this WorkerInstance matches the passed url/name params
    // (per the comparison algorithm in the WebWorkers spec). This API only
    // applies to shared workers.
    bool Matches(
        const GURL& url, const string16& name, bool off_the_record) const;

    // Shares the passed instance's WorkerDocumentSet with this instance. This
    // instance's current WorkerDocumentSet is dereferenced (and freed if this
    // is the only reference) as a result.
    void ShareDocumentSet(const WorkerInstance& instance) {
      worker_document_set_ = instance.worker_document_set_;
    };

    // Accessors
    bool shared() const { return shared_; }
    bool off_the_record() const { return off_the_record_; }
    bool closed() const { return closed_; }
    void set_closed(bool closed) { closed_ = closed; }
    const GURL& url() const { return url_; }
    const string16 name() const { return name_; }
    int worker_route_id() const { return worker_route_id_; }
    WorkerDocumentSet* worker_document_set() const {
      return worker_document_set_;
    }

   private:
    // Set of all senders (clients) associated with this worker.
    GURL url_;
    bool shared_;
    bool off_the_record_;
    bool closed_;
    string16 name_;
    int worker_route_id_;
    SenderList senders_;
    scoped_refptr<WorkerDocumentSet> worker_document_set_;
  };

  explicit WorkerProcessHost(ResourceDispatcherHost* resource_dispatcher_host);
  ~WorkerProcessHost();

  // Starts the process.  Returns true iff it succeeded.
  bool Init();

  // Creates a worker object in the process.
  void CreateWorker(const WorkerInstance& instance);

  // Returns true iff the given message from a renderer process was forwarded to
  // the worker.
  bool FilterMessage(const IPC::Message& message, IPC::Message::Sender* sender);

  void SenderShutdown(IPC::Message::Sender* sender);

  // Shuts down any shared workers that are no longer referenced by active
  // documents.
  void DocumentDetached(IPC::Message::Sender* sender,
                        unsigned long long document_id);

 protected:
  friend class WorkerService;

  typedef std::list<WorkerInstance> Instances;
  const Instances& instances() const { return instances_; }
  Instances& mutable_instances() { return instances_; }

 private:
  // ResourceDispatcherHost::Receiver implementation:
  virtual URLRequestContext* GetRequestContext(
      uint32 request_id,
      const ViewHostMsg_Resource_Request& request_data);

  // Called when a message arrives from the worker process.
  void OnMessageReceived(const IPC::Message& message);

  // Called when the app invokes close() from within worker context.
  void OnWorkerContextClosed(int worker_route_id);

  // Called if a worker tries to connect to a shared worker.
  void OnLookupSharedWorker(const GURL& url,
                            const string16& name,
                            unsigned long long document_id,
                            int render_view_route_id,
                            int* route_id,
                            bool* url_error);

  // Given a Sender, returns the callback that generates a new routing id.
  static CallbackWithReturnValue<int>::Type* GetNextRouteIdCallback(
      IPC::Message::Sender* sender);

  // Relays a message to the given endpoint.  Takes care of parsing the message
  // if it contains a message port and sending it a valid route id.
  static void RelayMessage(const IPC::Message& message,
                           IPC::Message::Sender* sender,
                           int route_id,
                           CallbackWithReturnValue<int>::Type* next_route_id);

  virtual bool CanShutdown() { return instances_.empty(); }

  // Updates the title shown in the task manager.
  void UpdateTitle();

  void OnCreateWorker(const ViewHostMsg_CreateWorker_Params& params,
                      int* route_id);
  void OnCancelCreateDedicatedWorker(int route_id);
  void OnForwardToWorker(const IPC::Message& message);

  Instances instances_;

  // A callback to create a routing id for the associated worker process.
  scoped_ptr<CallbackWithReturnValue<int>::Type> next_route_id_callback_;

  DISALLOW_COPY_AND_ASSIGN(WorkerProcessHost);
};

#endif  // CHROME_BROWSER_WORKER_HOST_WORKER_PROCESS_HOST_H_