summaryrefslogtreecommitdiffstats
path: root/chrome/browser/resource_dispatcher_host.h
blob: ab03fcf208d8ad8fdeaf2ed3a12abfcad1a40f9a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
// Copyright 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//    * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//    * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// This is the browser side of the resource dispatcher, it receives requests
// from the RenderProcessHosts, and dispatches them to URLRequests. It then
// fowards the messages from the URLRequests back to the correct process for
// handling.
//
// See http://wiki.corp.google.com/twiki/bin/view/Main/ChromeMultiProcessResourceLoading

#ifndef CHROME_BROWSER_RESOURCE_DISPATCHER_HOST_H__
#define CHROME_BROWSER_RESOURCE_DISPATCHER_HOST_H__

#include <map>
#include <string>

#include "base/logging.h"
#include "base/observer_list.h"
#include "base/ref_counted.h"
#include "base/shared_memory.h"
#include "base/task.h"
#include "base/timer.h"
#include "chrome/common/filter_policy.h"
#include "chrome/common/ipc_message.h"
#include "net/url_request/url_request.h"
#include "webkit/glue/resource_type.h"

class DownloadFileManager;
class SaveFileManager;
class MessageLoop;
class PluginService;
class SafeBrowsingService;
class TabContents;
class URLRequestContext;
class LoginHandler;
struct ViewHostMsg_Resource_Request;
struct ViewMsg_Resource_ResponseHead;

class ResourceDispatcherHost : public URLRequest::Delegate {
 public:
  // Simple wrapper that refcounts ViewMsg_Resource_ResponseHead.
  struct Response;

  // The resource dispatcher host uses this interface to push load events to the
  // renderer, allowing for differences in the types of IPC messages generated.
  // See the implementations of this interface defined below.
  class EventHandler : public base::RefCounted<EventHandler> {
   public:
    virtual ~EventHandler() {}

    // Called as upload progress is made.
    virtual bool OnUploadProgress(int request_id,
                                  uint64 position,
                                  uint64 size) {
      return true;
    }

    // The request was redirected to a new URL.
    virtual bool OnRequestRedirected(int request_id, const GURL& url) = 0;

    // Response headers and meta data are available.
    virtual bool OnResponseStarted(int request_id, Response* response) = 0;

    // Data will be read for the response.  Upon success, this method places the
    // size and address of the buffer where the data is to be written in its
    // out-params.  This call will be followed by either OnReadCompleted or
    // OnResponseCompleted, at which point the buffer may be recycled.
    virtual bool OnWillRead(int request_id,
                            char** buf,
                            int* buf_size,
                            int min_size) = 0;

    // Data (*bytes_read bytes) was written into the buffer provided by
    // OnWillRead.
    virtual bool OnReadCompleted(int request_id, int* bytes_read) = 0;

    // The response is complete.  The final response status is given.
    // Returns false if the handler is deferring the call to a later time.
    virtual bool OnResponseCompleted(int request_id,
                                     const URLRequestStatus& status) = 0;
  };

  // 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 renderer 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).
  typedef IPC::Message::Sender Receiver;

  // Forward declaration of CrossSiteEventHandler, so that it can be referenced
  // in ExtraRequestInfo.
  class CrossSiteEventHandler;

  // Holds the data we would like to associate with each request
  class ExtraRequestInfo : public URLRequest::UserData {
   friend ResourceDispatcherHost;
   public:
    ExtraRequestInfo(EventHandler* handler,
                     int request_id,
                     int render_process_host_id,
                     int render_view_id,
                     bool mixed_content,
                     ResourceType::Type resource_type,
                     uint64 upload_size)
        : event_handler(handler),
          cross_site_handler(NULL),
          login_handler(NULL),
          request_id(request_id),
          render_process_host_id(render_process_host_id),
          render_view_id(render_view_id),
          is_download(false),
          pause_count(0),
          mixed_content(mixed_content),
          resource_type(resource_type),
          filter_policy(FilterPolicy::DONT_FILTER),
          last_load_state(net::LOAD_STATE_IDLE),
          pending_data_count(0),
          upload_size(upload_size),
          last_upload_position(0),
          waiting_for_upload_progress_ack(false),
          is_paused(false),
          has_started_reading(false),
          paused_read_bytes(0) {
    }

    // Top-level EventHandler servicing this request.
    scoped_refptr<EventHandler> event_handler;

    // CrossSiteEventHandler for this request, if it is a cross-site request.
    // (NULL otherwise.)  This handler is part of the chain of EventHandlers
    // pointed to by event_handler.
    CrossSiteEventHandler* cross_site_handler;

    LoginHandler* login_handler;

    int request_id;

    int render_process_host_id;

    int render_view_id;

    int pending_data_count;

    // Downloads allowed only as a top level request.
    bool allow_download;

    // Whether this is a download.
    bool is_download;

    // The number of clients that have called pause on this request.
    int pause_count;

    // Whether this request is served over HTTP and the main page was served
    // over HTTPS.
    bool mixed_content;

    ResourceType::Type resource_type;

    // Whether the content for this request should be filtered (on the renderer
    // side) to make it more secure: images are stamped, frame content is
    // replaced with an error message and all other resources are entirely
    // filtered out.
    FilterPolicy::Type filter_policy;

    net::LoadState last_load_state;

    uint64 upload_size;

    uint64 last_upload_position;

    TimeTicks last_upload_ticks;

    bool waiting_for_upload_progress_ack;

   private:
    // Request is temporarily not handling network data. Should be used only
    // by the ResourceDispatcherHost, not the event handlers.
    bool is_paused;

    // Whether this request has started reading any bytes from the response
    // yet.  Will be true after the first (unpaused) call to Read.
    bool has_started_reading;

    // How many bytes have been read while this request has been paused.
    int paused_read_bytes;
  };

  class Observer {
   public:
    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;
  };

  // Uniquely identifies a URLRequest.
  struct GlobalRequestID {
    GlobalRequestID() : render_process_host_id(-1), request_id(-1) {
    }
    GlobalRequestID(int render_process_host_id, int request_id)
        : render_process_host_id(render_process_host_id),
          request_id(request_id) {
    }

    int render_process_host_id;
    int request_id;

    bool operator<(const GlobalRequestID& other) const {
      if (render_process_host_id == other.render_process_host_id)
        return request_id < other.request_id;
      return render_process_host_id < other.render_process_host_id;
    }
  };

  explicit ResourceDispatcherHost(MessageLoop* io_loop);
  ~ResourceDispatcherHost();

  void Initialize();

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

  // Begins a resource request with the given params on behalf of the specified
  // render view.  Responses will be dispatched through the given receiver. The
  // RenderProcessHost ID is used to lookup TabContents from routing_id's.
  // request_context is the cookie/cache context to be used for this request.
  //
  // If sync_result is non-null, then a SyncLoad reply will be generated, else
  // a normal asynchronous set of response messages will be generated.
  //
  void BeginRequest(Receiver* receiver,
                    HANDLE render_process_handle,
                    int render_process_host_id,
                    int render_view_id,
                    int request_id,
                    const ViewHostMsg_Resource_Request& request,
                    URLRequestContext* request_context,
                    IPC::Message* sync_result);

  // Initiate a download from the browser process (as opposed to a resource
  // request from the renderer).
  void BeginDownload(const GURL& url,
                     const GURL& referrer,
                     int render_process_host_id,
                     int render_view_id,
                     URLRequestContext* request_context);

  // Initiate a save file from the browser process (as opposed to a resource
  // request from the renderer).
  void BeginSaveFile(const GURL& url,
                     const GURL& referrer,
                     int render_process_host_id,
                     int render_view_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 render_process_host_id,
                     int request_id,
                     bool from_renderer);

  // 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 OnDataReceivedACK(int render_process_host_id, int request_id);

  // Resets the waiting_for_upload_progress_ack flag.
  void OnUploadProgressACK(int render_process_host_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 render_process_host_id, int request_id);

  // Pauses or resumes network activity for a particular request.
  void PauseRequest(int render_process_host_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());
  }

  DownloadFileManager* download_file_manager() const {
    return download_file_manager_;
  }

  SaveFileManager* save_file_manager() const {
    return save_file_manager_;
  }

  SafeBrowsingService* safe_browsing_service() const {
    return safe_browsing_;
  }

  MessageLoop* ui_loop() const { return ui_loop_; }

  // Called when the onunload handler for a cross-site request has finished.
  void OnClosePageACK(int render_process_host_id, int request_id);

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

  // Force cancels any pending requests for the given render view.  This method
  // acts like CancelRequestsForProcess when render_view_id is -1.
  void CancelRequestsForRenderView(int render_process_host_id,
                                   int render_view_id);

  // URLRequest::Delegate
  virtual void OnReceivedRedirect(URLRequest* request,
                                  const GURL& new_url);
  virtual void OnAuthRequired(URLRequest* request,
                              net::AuthChallengeInfo* auth_info);
  virtual void OnSSLCertificateError(URLRequest* request,
                                     int cert_error,
                                     net::X509Certificate* cert);
  virtual void OnResponseStarted(URLRequest* request);
  virtual void OnReadCompleted(URLRequest* request, int bytes_read);
  void OnResponseCompleted(URLRequest* request);

  // Helper function 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 ExtraRequestInfo* ExtraInfoForRequest(URLRequest* request) {
    ExtraRequestInfo* r = static_cast<ExtraRequestInfo*>(request->user_data());
    DLOG_IF(WARNING, !r) << "Request doesn't seem to have our data";
    return r;
  }

  static const ExtraRequestInfo* ExtraInfoForRequest(const URLRequest* request) {
    const ExtraRequestInfo* r =
        static_cast<const ExtraRequestInfo*>(request->user_data());
    DLOG_IF(WARNING, !r) << "Request doesn't seem to have our data";
    return r;
  }

  // Add 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);

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

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

 private:
  class AsyncEventHandler;
  class SyncEventHandler;
  class CrossSiteNotifyTabTask;
  class DownloadEventHandler;
  class BufferedEventHandler;
  class SaveFileEventHandler;
  class ShutdownTask;

  friend class ShutdownTask;

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

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

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

  // 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 *, 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, bool mixed_content);

  // 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;

  // A test to determining whether a given request should be forwarded to the
  // download thread.
  bool ShouldDownload(const std::string& mime_type,
                      const std::string& content_disposition);

  void RemovePendingRequest(int render_process_host_id, int request_id);
  void RemovePendingRequest(PendingRequestList::iterator& iter);

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

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

  // Notify our observers that a request has been redirected.
  void NofityReceivedRedirect(URLRequest* request,
                              int render_process_host_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 render_process_host_id,
                              int tab_contents_id,
                              const GURL& url,
                              ResourceType::Type resource_type,
                              EventHandler* handler);

  void UpdateLoadStates();

  void MaybeUpdateUploadProgress(ExtraRequestInfo *info, URLRequest *request);

  PendingRequestList pending_requests_;

  // We cache the UI message loop so we can create new UI-related objects on it.
  MessageLoop* ui_loop_;

  // We cache the IO loop to ensure that GetURLRequest is only called from the
  // IO thread.
  MessageLoop* io_loop_;

  // A timer that periodically calls UpdateLoadStates while pending_requests_
  // is not empty.
  RepeatingTimer update_load_states_timer_;

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

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

  scoped_refptr<SafeBrowsingService> safe_browsing_;

  // Request ID for non-renderer initiated requests. request_ids generated by
  // the renderer process 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_;

  PluginService* plugin_service_;

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

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

  DISALLOW_EVIL_CONSTRUCTORS(ResourceDispatcherHost);
};

#endif  // CHROME_BROWSER_RESOURCE_DISPATCHER_HOST_H__