// Copyright (c) 2010 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.

// This is the browser side of the resource dispatcher, it receives requests
// from the child process (i.e. [Renderer, Plugin, Worker]ProcessHost), and
// dispatches them to URLRequests. It then fowards the messages from the
// URLRequests back to the correct process for handling.
//
// See http://dev.chromium.org/developers/design-documents/multi-process-resource-loading

#ifndef CHROME_BROWSER_RENDERER_HOST_RESOURCE_DISPATCHER_HOST_H_
#define CHROME_BROWSER_RENDERER_HOST_RESOURCE_DISPATCHER_HOST_H_

#include <map>
#include <string>
#include <vector>

#include "base/basictypes.h"
#include "base/gtest_prod_util.h"
#include "base/logging.h"
#include "base/observer_list.h"
#include "base/process.h"
#include "base/timer.h"
#include "chrome/common/child_process_info.h"
#include "chrome/browser/renderer_host/resource_queue.h"
#include "ipc/ipc_message.h"
#include "net/url_request/url_request.h"
#include "webkit/glue/resource_type.h"

class CrossSiteResourceHandler;
class DownloadFileManager;
class DownloadRequestLimiter;
class LoginHandler;
class PluginService;
class ResourceDispatcherHostRequestInfo;
class ResourceHandler;
class SafeBrowsingService;
class SaveFileManager;
class SocketStreamDispatcherHost;
class SSLClientAuthHandler;
class UserScriptListener;
class URLRequestContext;
class WebKitThread;
struct DownloadSaveInfo;
struct GlobalRequestID;
struct ViewHostMsg_Resource_Request;
struct ViewMsg_ClosePage_Params;

class ResourceDispatcherHost : public URLRequest::Delegate {
 public:
  // Implemented by the client of ResourceDispatcherHost to receive messages in
  // response to a resource load.  The messages are intended to be forwarded to
  // the ResourceDispatcher in the child process via an IPC channel that the
  // client manages.
  //
  // NOTE: This class unfortunately cannot be named 'Delegate' because that
  // conflicts with the name of ResourceDispatcherHost's base class.
  //
  // If the receiver is unable to send a given message (i.e., if Send returns
  // false), then the ResourceDispatcherHost assumes the receiver has failed,
  // and the given request will be dropped. (This happens, for example, when a
  // renderer crashes and the channel dies).
  class Receiver : public IPC::Message::Sender,
                   public ChildProcessInfo {
   public:
    // Returns the URLRequestContext for the given request.
    // If NULL is returned, the default context for the profile is used.
    virtual URLRequestContext* GetRequestContext(
        uint32 request_id,
        const ViewHostMsg_Resource_Request& request_data) = 0;

   protected:
    explicit Receiver(ChildProcessInfo::ProcessType type, int child_id)
        : ChildProcessInfo(type, child_id) {}
    virtual ~Receiver() {}
  };

  class Observer {
   public:
    virtual ~Observer() {}
    virtual void OnRequestStarted(ResourceDispatcherHost* resource_dispatcher,
                                  URLRequest* request) = 0;
    virtual void OnResponseCompleted(
        ResourceDispatcherHost* resource_dispatcher,
        URLRequest* request) = 0;
    virtual void OnReceivedRedirect(ResourceDispatcherHost* resource_dispatcher,
                                    URLRequest* request,
                                    const GURL& new_url) = 0;
  };

  ResourceDispatcherHost();
  ~ResourceDispatcherHost();

  void Initialize();

  // Puts the resource dispatcher host in an inactive state (unable to begin
  // new requests).  Cancels all pending requests.
  void Shutdown();

  // Returns true if the message was a resource message that was processed.
  // If it was, message_was_ok will be false iff the message was corrupt.
  bool OnMessageReceived(const IPC::Message& message,
                         Receiver* receiver,
                         bool* message_was_ok);

  // Initiates a download from the browser process (as opposed to a resource
  // request from the renderer or another child process).
  void BeginDownload(const GURL& url,
                     const GURL& referrer,
                     const DownloadSaveInfo& save_info,
                     bool prompt_for_save_location,
                     int process_unique_id,
                     int route_id,
                     URLRequestContext* request_context);

  // Initiates a save file from the browser process (as opposed to a resource
  // request from the renderer or another child process).
  void BeginSaveFile(const GURL& url,
                     const GURL& referrer,
                     int process_unique_id,
                     int route_id,
                     URLRequestContext* request_context);

  // Cancels the given request if it still exists. We ignore cancels from the
  // renderer in the event of a download.
  void CancelRequest(int process_unique_id,
                     int request_id,
                     bool from_renderer);

  // Follows a deferred redirect for the given request.
  // new_first_party_for_cookies, if non-empty, is the new cookie policy URL
  // for the redirected URL.  If the cookie policy URL needs changing, pass
  // true as has_new_first_party_for_cookies and the new cookie policy URL as
  // new_first_party_for_cookies.  Otherwise, pass false as
  // has_new_first_party_for_cookies, and new_first_party_for_cookies will not
  // be used.
  void FollowDeferredRedirect(int process_unique_id,
                              int request_id,
                              bool has_new_first_party_for_cookies,
                              const GURL& new_first_party_for_cookies);

  // Starts a request that was deferred during ResourceHandler::OnWillStart().
  void StartDeferredRequest(int process_unique_id, int request_id);

  // Returns true if it's ok to send the data. If there are already too many
  // data messages pending, it pauses the request and returns false. In this
  // case the caller should not send the data.
  bool WillSendData(int process_unique_id, int request_id);

  // Pauses or resumes network activity for a particular request.
  void PauseRequest(int process_unique_id, int request_id, bool pause);

  // Returns the number of pending requests. This is designed for the unittests
  int pending_requests() const {
    return static_cast<int>(pending_requests_.size());
  }

  // Intended for unit-tests only. Returns the memory cost of all the
  // outstanding requests (pending and blocked) for |process_unique_id|.
  int GetOutstandingRequestsMemoryCost(int process_unique_id) const;

  // Intended for unit-tests only. Overrides the outstanding requests bound.
  void set_max_outstanding_requests_cost_per_process(int limit) {
    max_outstanding_requests_cost_per_process_ = limit;
  }

  // The average private bytes increase of the browser for each new pending
  // request. Experimentally obtained.
  static const int kAvgBytesPerOutstandingRequest = 4400;

  DownloadFileManager* download_file_manager() const {
    return download_file_manager_;
  }

  DownloadRequestLimiter* download_request_limiter() const {
    return download_request_limiter_.get();
  }

  SaveFileManager* save_file_manager() const {
    return save_file_manager_;
  }

  SafeBrowsingService* safe_browsing_service() const {
    return safe_browsing_;
  }

  WebKitThread* webkit_thread() const {
    return webkit_thread_.get();
  }

  // Called when the onunload handler for a cross-site request has finished.
  void OnClosePageACK(const ViewMsg_ClosePage_Params& params);

  // Force cancels any pending requests for the given process.
  void CancelRequestsForProcess(int process_unique_id);

  // Force cancels any pending requests for the given route id.  This method
  // acts like CancelRequestsForProcess when route_id is -1.
  void CancelRequestsForRoute(int process_unique_id, int route_id);

  // URLRequest::Delegate
  virtual void OnReceivedRedirect(URLRequest* request,
                                  const GURL& new_url,
                                  bool* defer_redirect);
  virtual void OnAuthRequired(URLRequest* request,
                              net::AuthChallengeInfo* auth_info);
  virtual void OnCertificateRequested(
      URLRequest* request,
      net::SSLCertRequestInfo* cert_request_info);
  virtual void OnSSLCertificateError(URLRequest* request,
                                     int cert_error,
                                     net::X509Certificate* cert);
  virtual void OnSetCookie(URLRequest* request,
                           const std::string& cookie_line,
                           bool blocked_by_policy);
  virtual void OnResponseStarted(URLRequest* request);
  virtual void OnReadCompleted(URLRequest* request, int bytes_read);
  void OnResponseCompleted(URLRequest* request);

  // Helper functions to get our extra data out of a request. The given request
  // must have been one we created so that it has the proper extra data pointer.
  static ResourceDispatcherHostRequestInfo* InfoForRequest(URLRequest* request);
  static const ResourceDispatcherHostRequestInfo* InfoForRequest(
      const URLRequest* request);

  // Extracts the render view/process host's identifiers from the given request
  // and places them in the given out params (both required). If there are no
  // such IDs associated with the request (such as non-page-related requests),
  // this function will return false and both out params will be -1.
  static bool RenderViewForRequest(const URLRequest* request,
                                   int* render_process_host_id,
                                   int* render_view_host_id);

  // Adds an observer.  The observer will be called on the IO thread.  To
  // observe resource events on the UI thread, subscribe to the
  // NOTIFY_RESOURCE_* notifications of the notification service.
  void AddObserver(Observer* obs);

  // Removes an observer.
  void RemoveObserver(Observer* obs);

  // Retrieves a URLRequest.  Must be called from the IO thread.
  URLRequest* GetURLRequest(const GlobalRequestID& request_id) const;

  // Notifies our observers that a request has been cancelled.
  void NotifyResponseCompleted(URLRequest* request, int process_unique_id);

  void RemovePendingRequest(int process_unique_id, int request_id);

  // Causes all new requests for the route identified by
  // |process_unique_id| and |route_id| to be blocked (not being
  // started) until ResumeBlockedRequestsForRoute or
  // CancelBlockedRequestsForRoute is called.
  void BlockRequestsForRoute(int process_unique_id, int route_id);

  // Resumes any blocked request for the specified route id.
  void ResumeBlockedRequestsForRoute(int process_unique_id, int route_id);

  // Cancels any blocked request for the specified route id.
  void CancelBlockedRequestsForRoute(int process_unique_id, int route_id);

  // Decrements the pending_data_count for the request and resumes
  // the request if it was paused due to too many pending data
  // messages sent.
  void DataReceivedACK(int process_unique_id, int request_id);

  // Needed for the sync IPC message dispatcher macros.
  bool Send(IPC::Message* message) {
    delete message;
    return false;
  }

 private:
  FRIEND_TEST_ALL_PREFIXES(ResourceDispatcherHostTest,
                           TestBlockedRequestsProcessDies);
  FRIEND_TEST_ALL_PREFIXES(ResourceDispatcherHostTest,
                           IncrementOutstandingRequestsMemoryCost);
  FRIEND_TEST_ALL_PREFIXES(ResourceDispatcherHostTest,
                           CalculateApproximateMemoryCost);
  FRIEND_TEST_ALL_PREFIXES(ApplyExtensionLocalizationFilterTest, WrongScheme);
  FRIEND_TEST_ALL_PREFIXES(ApplyExtensionLocalizationFilterTest, GoodScheme);
  FRIEND_TEST_ALL_PREFIXES(ApplyExtensionLocalizationFilterTest,
                           GoodSchemeWrongResourceType);

  class ShutdownTask;

  friend class ShutdownTask;

  // Associates the given info with the given request. The info will then be
  // owned by the request.
  void SetRequestInfo(URLRequest* request,
                      ResourceDispatcherHostRequestInfo* info);

  // A shutdown helper that runs on the IO thread.
  void OnShutdown();

  // Returns true if the request is paused.
  bool PauseRequestIfNeeded(ResourceDispatcherHostRequestInfo* info);

  // Resumes the given request by calling OnResponseStarted or OnReadCompleted.
  void ResumeRequest(const GlobalRequestID& request_id);

  // Internal function to start reading for the first time.
  void StartReading(URLRequest* request);

  // Reads data from the response using our internal buffer as async IO.
  // Returns true if data is available immediately, false otherwise.  If the
  // return value is false, we will receive a OnReadComplete() callback later.
  bool Read(URLRequest* request, int* bytes_read);

  // Internal function to finish an async IO which has completed.  Returns
  // true if there is more data to read (e.g. we haven't read EOF yet and
  // no errors have occurred).
  bool CompleteRead(URLRequest*, int* bytes_read);

  // Internal function to finish handling the ResponseStarted message.  Returns
  // true on success.
  bool CompleteResponseStarted(URLRequest* request);

  // Helper function for regular and download requests.
  void BeginRequestInternal(URLRequest* request);

  // Helper function that cancels |request|.
  void CancelRequestInternal(URLRequest* request, bool from_renderer);

  // Helper function that inserts |request| into the resource queue.
  void InsertIntoResourceQueue(
      URLRequest* request,
      const ResourceDispatcherHostRequestInfo& request_info);

  // Updates the "cost" of outstanding requests for |process_unique_id|.
  // The "cost" approximates how many bytes are consumed by all the in-memory
  // data structures supporting this request (URLRequest object,
  // HttpNetworkTransaction, etc...).
  // The value of |cost| is added to the running total, and the resulting
  // sum is returned.
  int IncrementOutstandingRequestsMemoryCost(int cost,
                                             int process_unique_id);

  // Estimate how much heap space |request| will consume to run.
  static int CalculateApproximateMemoryCost(URLRequest* request);

  // The list of all requests that we have pending. This list is not really
  // optimized, and assumes that we have relatively few requests pending at once
  // since some operations require brute-force searching of the list.
  //
  // It may be enhanced in the future to provide some kind of prioritization
  // mechanism. We should also consider a hashtable or binary tree if it turns
  // out we have a lot of things here.
  typedef std::map<GlobalRequestID, URLRequest*> PendingRequestList;

  // Deletes the pending request identified by the iterator passed in.
  // This function will invalidate the iterator passed in. Callers should
  // not rely on this iterator being valid on return.
  void RemovePendingRequest(const PendingRequestList::iterator& iter);

  // Notify our observers that we started receiving a response for a request.
  void NotifyResponseStarted(URLRequest* request, int process_unique_id);

  // Notify our observers that a request has been redirected.
  void NotifyReceivedRedirect(URLRequest* request,
                              int process_unique_id,
                              const GURL& new_url);

  // Tries to handle the url with an external protocol. If the request is
  // handled, the function returns true. False otherwise.
  bool HandleExternalProtocol(int request_id,
                              int process_unique_id,
                              int route_id,
                              const GURL& url,
                              ResourceType::Type resource_type,
                              ResourceHandler* handler);

  // Checks all pending requests and updates the load states and upload
  // progress if necessary.
  void UpdateLoadStates();

  // Checks the upload state and sends an update if one is necessary.
  bool MaybeUpdateUploadProgress(ResourceDispatcherHostRequestInfo *info,
                                 URLRequest *request);

  // Resumes or cancels (if |cancel_requests| is true) any blocked requests.
  void ProcessBlockedRequestsForRoute(int process_unique_id,
                                      int route_id,
                                      bool cancel_requests);

  void OnRequestResource(const IPC::Message& msg,
                         int request_id,
                         const ViewHostMsg_Resource_Request& request_data);
  void OnSyncLoad(int request_id,
                  const ViewHostMsg_Resource_Request& request_data,
                  IPC::Message* sync_result);
  void BeginRequest(int request_id,
                    const ViewHostMsg_Resource_Request& request_data,
                    IPC::Message* sync_result,  // only valid for sync
                    int route_id);  // only valid for async
  void OnDataReceivedACK(int request_id);
  void OnUploadProgressACK(int request_id);
  void OnCancelRequest(int request_id);
  void OnFollowRedirect(int request_id,
                        bool has_new_first_party_for_cookies,
                        const GURL& new_first_party_for_cookies);

  ResourceHandler* CreateSafeBrowsingResourceHandler(
      ResourceHandler* handler, int child_id, int route_id,
      ResourceType::Type resource_type);

  // Creates ResourceDispatcherHostRequestInfo for a browser-initiated request
  // (a download or a page save). |download| should be true if the request
  // is a file download.
  ResourceDispatcherHostRequestInfo* CreateRequestInfoForBrowserRequest(
      ResourceHandler* handler, int child_id, int route_id, bool download);

  // Returns true if |request| is in |pending_requests_|.
  bool IsValidRequest(URLRequest* request);

  // Returns true if the message passed in is a resource related message.
  static bool IsResourceDispatcherHostMessage(const IPC::Message&);

  // Sets replace_extension_localization_templates on all text/css requests that
  // have "chrome-extension://" scheme.
  static void ApplyExtensionLocalizationFilter(
      const GURL& url,
      const ResourceType::Type& resource_type,
      ResourceDispatcherHostRequestInfo* request_info);

  // Determine request priority based on how critical this resource typically
  // is to user-perceived page load performance.
  static net::RequestPriority DetermineRequestPriority(ResourceType::Type type);

  PendingRequestList pending_requests_;

  // A timer that periodically calls UpdateLoadStates while pending_requests_
  // is not empty.
  base::RepeatingTimer<ResourceDispatcherHost> update_load_states_timer_;

  // Handles the resource requests from the moment we want to start them.
  ResourceQueue resource_queue_;

  // We own the download file writing thread and manager
  scoped_refptr<DownloadFileManager> download_file_manager_;

  // Determines whether a download is allowed.
  scoped_refptr<DownloadRequestLimiter> download_request_limiter_;

  // We own the save file manager.
  scoped_refptr<SaveFileManager> save_file_manager_;

  scoped_refptr<UserScriptListener> user_script_listener_;

  scoped_refptr<SafeBrowsingService> safe_browsing_;

  scoped_ptr<SocketStreamDispatcherHost> socket_stream_dispatcher_host_;

  // We own the WebKit thread and see to its destruction.
  scoped_ptr<WebKitThread> webkit_thread_;

  // Request ID for browser initiated requests. request_ids generated by
  // child processes are counted up from 0, while browser created requests
  // start at -2 and go down from there. (We need to start at -2 because -1 is
  // used as a special value all over the resource_dispatcher_host for
  // uninitialized variables.) This way, we no longer have the unlikely (but
  // observed in the real world!) event where we have two requests with the same
  // request_id_.
  int request_id_;

  // List of objects observing resource dispatching.
  ObserverList<Observer> observer_list_;

  // For running tasks.
  ScopedRunnableMethodFactory<ResourceDispatcherHost> method_runner_;

  // True if the resource dispatcher host has been shut down.
  bool is_shutdown_;

  typedef std::vector<URLRequest*> BlockedRequestsList;
  typedef std::pair<int, int> ProcessRouteIDs;
  typedef std::map<ProcessRouteIDs, BlockedRequestsList*> BlockedRequestMap;
  BlockedRequestMap blocked_requests_map_;

  // Maps the process_unique_ids to the approximate number of bytes
  // being used to service its resource requests. No entry implies 0 cost.
  typedef std::map<int, int> OutstandingRequestsMemoryCostMap;
  OutstandingRequestsMemoryCostMap outstanding_requests_memory_cost_map_;

  // |max_outstanding_requests_cost_per_process_| is the upper bound on how
  // many outstanding requests can be issued per child process host.
  // The constraint is expressed in terms of bytes (where the cost of
  // individual requests is given by CalculateApproximateMemoryCost).
  // The total number of outstanding requests is roughly:
  //   (max_outstanding_requests_cost_per_process_ /
  //       kAvgBytesPerOutstandingRequest)
  int max_outstanding_requests_cost_per_process_;

  // Used during IPC message dispatching so that the handlers can get a pointer
  // to the source of the message.
  Receiver* receiver_;

  DISALLOW_COPY_AND_ASSIGN(ResourceDispatcherHost);
};

#endif  // CHROME_BROWSER_RENDERER_HOST_RESOURCE_DISPATCHER_HOST_H_