Home | History | Annotate | Download | only in webui
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/browser/webui/url_data_manager_backend.h"
      6 
      7 #include <set>
      8 
      9 #include "base/basictypes.h"
     10 #include "base/bind.h"
     11 #include "base/command_line.h"
     12 #include "base/compiler_specific.h"
     13 #include "base/debug/trace_event.h"
     14 #include "base/lazy_instance.h"
     15 #include "base/memory/ref_counted.h"
     16 #include "base/memory/ref_counted_memory.h"
     17 #include "base/memory/weak_ptr.h"
     18 #include "base/message_loop/message_loop.h"
     19 #include "base/strings/string_util.h"
     20 #include "content/browser/fileapi/chrome_blob_storage_context.h"
     21 #include "content/browser/histogram_internals_request_job.h"
     22 #include "content/browser/net/view_blob_internals_job_factory.h"
     23 #include "content/browser/net/view_http_cache_job_factory.h"
     24 #include "content/browser/resource_context_impl.h"
     25 #include "content/browser/tcmalloc_internals_request_job.h"
     26 #include "content/browser/webui/shared_resources_data_source.h"
     27 #include "content/browser/webui/url_data_source_impl.h"
     28 #include "content/public/browser/browser_thread.h"
     29 #include "content/public/browser/content_browser_client.h"
     30 #include "content/public/browser/render_process_host.h"
     31 #include "content/public/browser/resource_request_info.h"
     32 #include "content/public/common/url_constants.h"
     33 #include "net/base/io_buffer.h"
     34 #include "net/base/net_errors.h"
     35 #include "net/http/http_response_headers.h"
     36 #include "net/http/http_status_code.h"
     37 #include "net/url_request/url_request.h"
     38 #include "net/url_request/url_request_context.h"
     39 #include "net/url_request/url_request_job.h"
     40 #include "net/url_request/url_request_job_factory.h"
     41 #include "url/url_util.h"
     42 #include "webkit/browser/appcache/view_appcache_internals_job.h"
     43 
     44 using appcache::AppCacheService;
     45 
     46 namespace content {
     47 
     48 namespace {
     49 
     50 // TODO(tsepez) remove unsafe-eval when bidichecker_packaged.js fixed.
     51 const char kChromeURLContentSecurityPolicyHeaderBase[] =
     52     "Content-Security-Policy: script-src chrome://resources "
     53     "'self' 'unsafe-eval'; ";
     54 
     55 const char kChromeURLXFrameOptionsHeader[] = "X-Frame-Options: DENY";
     56 
     57 bool SchemeIsInSchemes(const std::string& scheme,
     58                        const std::vector<std::string>& schemes) {
     59   return std::find(schemes.begin(), schemes.end(), scheme) != schemes.end();
     60 }
     61 
     62 // Parse a URL into the components used to resolve its request. |source_name|
     63 // is the hostname and |path| is the remaining portion of the URL.
     64 void URLToRequest(const GURL& url, std::string* source_name,
     65                   std::string* path) {
     66   std::vector<std::string> additional_schemes;
     67   DCHECK(url.SchemeIs(chrome::kChromeDevToolsScheme) ||
     68          url.SchemeIs(chrome::kChromeUIScheme) ||
     69          (GetContentClient()->browser()->GetAdditionalWebUISchemes(
     70              &additional_schemes),
     71           SchemeIsInSchemes(url.scheme(), additional_schemes)));
     72 
     73   if (!url.is_valid()) {
     74     NOTREACHED();
     75     return;
     76   }
     77 
     78   // Our input looks like: chrome://source_name/extra_bits?foo .
     79   // So the url's "host" is our source, and everything after the host is
     80   // the path.
     81   source_name->assign(url.host());
     82 
     83   const std::string& spec = url.possibly_invalid_spec();
     84   const url_parse::Parsed& parsed = url.parsed_for_possibly_invalid_spec();
     85   // + 1 to skip the slash at the beginning of the path.
     86   int offset = parsed.CountCharactersBefore(url_parse::Parsed::PATH, false) + 1;
     87 
     88   if (offset < static_cast<int>(spec.size()))
     89     path->assign(spec.substr(offset));
     90 }
     91 
     92 }  // namespace
     93 
     94 // URLRequestChromeJob is a net::URLRequestJob that manages running
     95 // chrome-internal resource requests asynchronously.
     96 // It hands off URL requests to ChromeURLDataManager, which asynchronously
     97 // calls back once the data is available.
     98 class URLRequestChromeJob : public net::URLRequestJob,
     99                             public base::SupportsWeakPtr<URLRequestChromeJob> {
    100  public:
    101   // |is_incognito| set when job is generated from an incognito profile.
    102   URLRequestChromeJob(net::URLRequest* request,
    103                       net::NetworkDelegate* network_delegate,
    104                       URLDataManagerBackend* backend,
    105                       bool is_incognito);
    106 
    107   // net::URLRequestJob implementation.
    108   virtual void Start() OVERRIDE;
    109   virtual void Kill() OVERRIDE;
    110   virtual bool ReadRawData(net::IOBuffer* buf,
    111                            int buf_size,
    112                            int* bytes_read) OVERRIDE;
    113   virtual bool GetMimeType(std::string* mime_type) const OVERRIDE;
    114   virtual int GetResponseCode() const OVERRIDE;
    115   virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE;
    116 
    117   // Used to notify that the requested data's |mime_type| is ready.
    118   void MimeTypeAvailable(const std::string& mime_type);
    119 
    120   // Called by ChromeURLDataManager to notify us that the data blob is ready
    121   // for us.
    122   void DataAvailable(base::RefCountedMemory* bytes);
    123 
    124   void set_mime_type(const std::string& mime_type) {
    125     mime_type_ = mime_type;
    126   }
    127 
    128   void set_allow_caching(bool allow_caching) {
    129     allow_caching_ = allow_caching;
    130   }
    131 
    132   void set_add_content_security_policy(bool add_content_security_policy) {
    133     add_content_security_policy_ = add_content_security_policy;
    134   }
    135 
    136   void set_content_security_policy_object_source(
    137       const std::string& data) {
    138     content_security_policy_object_source_ = data;
    139   }
    140 
    141   void set_content_security_policy_frame_source(
    142       const std::string& data) {
    143     content_security_policy_frame_source_ = data;
    144   }
    145 
    146   void set_deny_xframe_options(bool deny_xframe_options) {
    147     deny_xframe_options_ = deny_xframe_options;
    148   }
    149 
    150   // Returns true when job was generated from an incognito profile.
    151   bool is_incognito() const {
    152     return is_incognito_;
    153   }
    154 
    155  private:
    156   virtual ~URLRequestChromeJob();
    157 
    158   // Helper for Start(), to let us start asynchronously.
    159   // (This pattern is shared by most net::URLRequestJob implementations.)
    160   void StartAsync();
    161 
    162   // Do the actual copy from data_ (the data we're serving) into |buf|.
    163   // Separate from ReadRawData so we can handle async I/O.
    164   void CompleteRead(net::IOBuffer* buf, int buf_size, int* bytes_read);
    165 
    166   // The actual data we're serving.  NULL until it's been fetched.
    167   scoped_refptr<base::RefCountedMemory> data_;
    168   // The current offset into the data that we're handing off to our
    169   // callers via the Read interfaces.
    170   int data_offset_;
    171 
    172   // For async reads, we keep around a pointer to the buffer that
    173   // we're reading into.
    174   scoped_refptr<net::IOBuffer> pending_buf_;
    175   int pending_buf_size_;
    176   std::string mime_type_;
    177 
    178   // If true, set a header in the response to prevent it from being cached.
    179   bool allow_caching_;
    180 
    181   // If true, set the Content Security Policy (CSP) header.
    182   bool add_content_security_policy_;
    183 
    184   // These are used with the CSP.
    185   std::string content_security_policy_object_source_;
    186   std::string content_security_policy_frame_source_;
    187 
    188   // If true, sets  the "X-Frame-Options: DENY" header.
    189   bool deny_xframe_options_;
    190 
    191   // True when job is generated from an incognito profile.
    192   const bool is_incognito_;
    193 
    194   // The backend is owned by ChromeURLRequestContext and always outlives us.
    195   URLDataManagerBackend* backend_;
    196 
    197   base::WeakPtrFactory<URLRequestChromeJob> weak_factory_;
    198 
    199   DISALLOW_COPY_AND_ASSIGN(URLRequestChromeJob);
    200 };
    201 
    202 URLRequestChromeJob::URLRequestChromeJob(net::URLRequest* request,
    203                                          net::NetworkDelegate* network_delegate,
    204                                          URLDataManagerBackend* backend,
    205                                          bool is_incognito)
    206     : net::URLRequestJob(request, network_delegate),
    207       data_offset_(0),
    208       pending_buf_size_(0),
    209       allow_caching_(true),
    210       add_content_security_policy_(true),
    211       content_security_policy_object_source_("object-src 'none';"),
    212       content_security_policy_frame_source_("frame-src 'none';"),
    213       deny_xframe_options_(true),
    214       is_incognito_(is_incognito),
    215       backend_(backend),
    216       weak_factory_(this) {
    217   DCHECK(backend);
    218 }
    219 
    220 URLRequestChromeJob::~URLRequestChromeJob() {
    221   CHECK(!backend_->HasPendingJob(this));
    222 }
    223 
    224 void URLRequestChromeJob::Start() {
    225   // Start reading asynchronously so that all error reporting and data
    226   // callbacks happen as they would for network requests.
    227   base::MessageLoop::current()->PostTask(
    228       FROM_HERE,
    229       base::Bind(&URLRequestChromeJob::StartAsync, weak_factory_.GetWeakPtr()));
    230 
    231   TRACE_EVENT_ASYNC_BEGIN1("browser", "DataManager:Request", this, "URL",
    232       request_->url().possibly_invalid_spec());
    233 }
    234 
    235 void URLRequestChromeJob::Kill() {
    236   backend_->RemoveRequest(this);
    237 }
    238 
    239 bool URLRequestChromeJob::GetMimeType(std::string* mime_type) const {
    240   *mime_type = mime_type_;
    241   return !mime_type_.empty();
    242 }
    243 
    244 int URLRequestChromeJob::GetResponseCode() const {
    245   return net::HTTP_OK;
    246 }
    247 
    248 void URLRequestChromeJob::GetResponseInfo(net::HttpResponseInfo* info) {
    249   DCHECK(!info->headers.get());
    250   // Set the headers so that requests serviced by ChromeURLDataManager return a
    251   // status code of 200. Without this they return a 0, which makes the status
    252   // indistiguishable from other error types. Instant relies on getting a 200.
    253   info->headers = new net::HttpResponseHeaders("HTTP/1.1 200 OK");
    254 
    255   // Determine the least-privileged content security policy header, if any,
    256   // that is compatible with a given WebUI URL, and append it to the existing
    257   // response headers.
    258   if (add_content_security_policy_) {
    259     std::string base = kChromeURLContentSecurityPolicyHeaderBase;
    260     base.append(content_security_policy_object_source_);
    261     base.append(content_security_policy_frame_source_);
    262     info->headers->AddHeader(base);
    263   }
    264 
    265   if (deny_xframe_options_)
    266     info->headers->AddHeader(kChromeURLXFrameOptionsHeader);
    267 
    268   if (!allow_caching_)
    269     info->headers->AddHeader("Cache-Control: no-cache");
    270 }
    271 
    272 void URLRequestChromeJob::MimeTypeAvailable(const std::string& mime_type) {
    273   set_mime_type(mime_type);
    274   NotifyHeadersComplete();
    275 }
    276 
    277 void URLRequestChromeJob::DataAvailable(base::RefCountedMemory* bytes) {
    278   TRACE_EVENT_ASYNC_END0("browser", "DataManager:Request", this);
    279   if (bytes) {
    280     // The request completed, and we have all the data.
    281     // Clear any IO pending status.
    282     SetStatus(net::URLRequestStatus());
    283 
    284     data_ = bytes;
    285     int bytes_read;
    286     if (pending_buf_.get()) {
    287       CHECK(pending_buf_->data());
    288       CompleteRead(pending_buf_.get(), pending_buf_size_, &bytes_read);
    289       pending_buf_ = NULL;
    290       NotifyReadComplete(bytes_read);
    291     }
    292   } else {
    293     // The request failed.
    294     NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
    295                                      net::ERR_FAILED));
    296   }
    297 }
    298 
    299 bool URLRequestChromeJob::ReadRawData(net::IOBuffer* buf, int buf_size,
    300                                       int* bytes_read) {
    301   if (!data_.get()) {
    302     SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
    303     DCHECK(!pending_buf_.get());
    304     CHECK(buf->data());
    305     pending_buf_ = buf;
    306     pending_buf_size_ = buf_size;
    307     return false;  // Tell the caller we're still waiting for data.
    308   }
    309 
    310   // Otherwise, the data is available.
    311   CompleteRead(buf, buf_size, bytes_read);
    312   return true;
    313 }
    314 
    315 void URLRequestChromeJob::CompleteRead(net::IOBuffer* buf, int buf_size,
    316                                        int* bytes_read) {
    317   int remaining = static_cast<int>(data_->size()) - data_offset_;
    318   if (buf_size > remaining)
    319     buf_size = remaining;
    320   if (buf_size > 0) {
    321     memcpy(buf->data(), data_->front() + data_offset_, buf_size);
    322     data_offset_ += buf_size;
    323   }
    324   *bytes_read = buf_size;
    325 }
    326 
    327 void URLRequestChromeJob::StartAsync() {
    328   if (!request_)
    329     return;
    330 
    331   if (!backend_->StartRequest(request_, this)) {
    332     NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
    333                                            net::ERR_INVALID_URL));
    334   }
    335 }
    336 
    337 namespace {
    338 
    339 // Gets mime type for data that is available from |source| by |path|.
    340 // After that, notifies |job| that mime type is available. This method
    341 // should be called on the UI thread, but notification is performed on
    342 // the IO thread.
    343 void GetMimeTypeOnUI(URLDataSourceImpl* source,
    344                      const std::string& path,
    345                      const base::WeakPtr<URLRequestChromeJob>& job) {
    346   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    347   std::string mime_type = source->source()->GetMimeType(path);
    348   BrowserThread::PostTask(
    349       BrowserThread::IO, FROM_HERE,
    350       base::Bind(&URLRequestChromeJob::MimeTypeAvailable, job, mime_type));
    351 }
    352 
    353 }  // namespace
    354 
    355 namespace {
    356 
    357 class ChromeProtocolHandler
    358     : public net::URLRequestJobFactory::ProtocolHandler {
    359  public:
    360   // |is_incognito| should be set for incognito profiles.
    361   ChromeProtocolHandler(ResourceContext* resource_context,
    362                         bool is_incognito,
    363                         AppCacheService* appcache_service,
    364                         ChromeBlobStorageContext* blob_storage_context)
    365       : resource_context_(resource_context),
    366         is_incognito_(is_incognito),
    367         appcache_service_(appcache_service),
    368         blob_storage_context_(blob_storage_context) {}
    369   virtual ~ChromeProtocolHandler() {}
    370 
    371   virtual net::URLRequestJob* MaybeCreateJob(
    372       net::URLRequest* request,
    373       net::NetworkDelegate* network_delegate) const OVERRIDE {
    374     DCHECK(request);
    375 
    376     // Check for chrome://view-http-cache/*, which uses its own job type.
    377     if (ViewHttpCacheJobFactory::IsSupportedURL(request->url()))
    378       return ViewHttpCacheJobFactory::CreateJobForRequest(request,
    379                                                           network_delegate);
    380 
    381     // Next check for chrome://appcache-internals/, which uses its own job type.
    382     if (request->url().SchemeIs(chrome::kChromeUIScheme) &&
    383         request->url().host() == kChromeUIAppCacheInternalsHost) {
    384       return appcache::ViewAppCacheInternalsJobFactory::CreateJobForRequest(
    385           request, network_delegate, appcache_service_);
    386     }
    387 
    388     // Next check for chrome://blob-internals/, which uses its own job type.
    389     if (ViewBlobInternalsJobFactory::IsSupportedURL(request->url())) {
    390       return ViewBlobInternalsJobFactory::CreateJobForRequest(
    391           request, network_delegate, blob_storage_context_->controller());
    392     }
    393 
    394 #if defined(USE_TCMALLOC)
    395     // Next check for chrome://tcmalloc/, which uses its own job type.
    396     if (request->url().SchemeIs(chrome::kChromeUIScheme) &&
    397         request->url().host() == kChromeUITcmallocHost) {
    398       return new TcmallocInternalsRequestJob(request, network_delegate);
    399     }
    400 #endif
    401 
    402     // Next check for chrome://histograms/, which uses its own job type.
    403     if (request->url().SchemeIs(chrome::kChromeUIScheme) &&
    404         request->url().host() == kChromeUIHistogramHost) {
    405       return new HistogramInternalsRequestJob(request, network_delegate);
    406     }
    407 
    408     // Fall back to using a custom handler
    409     return new URLRequestChromeJob(
    410         request, network_delegate,
    411         GetURLDataManagerForResourceContext(resource_context_), is_incognito_);
    412   }
    413 
    414   virtual bool IsSafeRedirectTarget(const GURL& location) const OVERRIDE {
    415     return false;
    416   }
    417 
    418  private:
    419   // These members are owned by ProfileIOData, which owns this ProtocolHandler.
    420   content::ResourceContext* const resource_context_;
    421 
    422   // True when generated from an incognito profile.
    423   const bool is_incognito_;
    424   AppCacheService* appcache_service_;
    425   ChromeBlobStorageContext* blob_storage_context_;
    426 
    427   DISALLOW_COPY_AND_ASSIGN(ChromeProtocolHandler);
    428 };
    429 
    430 }  // namespace
    431 
    432 URLDataManagerBackend::URLDataManagerBackend()
    433     : next_request_id_(0) {
    434   URLDataSource* shared_source = new SharedResourcesDataSource();
    435   URLDataSourceImpl* source_impl =
    436       new URLDataSourceImpl(shared_source->GetSource(), shared_source);
    437   AddDataSource(source_impl);
    438 }
    439 
    440 URLDataManagerBackend::~URLDataManagerBackend() {
    441   for (DataSourceMap::iterator i = data_sources_.begin();
    442        i != data_sources_.end(); ++i) {
    443     i->second->backend_ = NULL;
    444   }
    445   data_sources_.clear();
    446 }
    447 
    448 // static
    449 net::URLRequestJobFactory::ProtocolHandler*
    450 URLDataManagerBackend::CreateProtocolHandler(
    451     content::ResourceContext* resource_context,
    452     bool is_incognito,
    453     AppCacheService* appcache_service,
    454     ChromeBlobStorageContext* blob_storage_context) {
    455   DCHECK(resource_context);
    456   return new ChromeProtocolHandler(
    457       resource_context, is_incognito, appcache_service, blob_storage_context);
    458 }
    459 
    460 void URLDataManagerBackend::AddDataSource(
    461     URLDataSourceImpl* source) {
    462   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    463   DataSourceMap::iterator i = data_sources_.find(source->source_name());
    464   if (i != data_sources_.end()) {
    465     if (!source->source()->ShouldReplaceExistingSource())
    466       return;
    467     i->second->backend_ = NULL;
    468   }
    469   data_sources_[source->source_name()] = source;
    470   source->backend_ = this;
    471 }
    472 
    473 bool URLDataManagerBackend::HasPendingJob(
    474     URLRequestChromeJob* job) const {
    475   for (PendingRequestMap::const_iterator i = pending_requests_.begin();
    476        i != pending_requests_.end(); ++i) {
    477     if (i->second == job)
    478       return true;
    479   }
    480   return false;
    481 }
    482 
    483 bool URLDataManagerBackend::StartRequest(const net::URLRequest* request,
    484                                          URLRequestChromeJob* job) {
    485   // Parse the URL into a request for a source and path.
    486   std::string source_name;
    487   std::string path;
    488   URLToRequest(request->url(), &source_name, &path);
    489 
    490   // Look up the data source for the request.
    491   DataSourceMap::iterator i = data_sources_.find(source_name);
    492   if (i == data_sources_.end())
    493     return false;
    494 
    495   URLDataSourceImpl* source = i->second.get();
    496 
    497   if (!source->source()->ShouldServiceRequest(request))
    498     return false;
    499   source->source()->WillServiceRequest(request, &path);
    500 
    501   // Save this request so we know where to send the data.
    502   RequestID request_id = next_request_id_++;
    503   pending_requests_.insert(std::make_pair(request_id, job));
    504 
    505   job->set_allow_caching(source->source()->AllowCaching());
    506   job->set_add_content_security_policy(
    507       source->source()->ShouldAddContentSecurityPolicy());
    508   job->set_content_security_policy_object_source(
    509       source->source()->GetContentSecurityPolicyObjectSrc());
    510   job->set_content_security_policy_frame_source(
    511       source->source()->GetContentSecurityPolicyFrameSrc());
    512   job->set_deny_xframe_options(
    513       source->source()->ShouldDenyXFrameOptions());
    514 
    515   // Look up additional request info to pass down.
    516   int render_process_id = -1;
    517   int render_view_id = -1;
    518   ResourceRequestInfo::GetRenderViewForRequest(request,
    519                                                &render_process_id,
    520                                                &render_view_id);
    521 
    522   // Forward along the request to the data source.
    523   base::MessageLoop* target_message_loop =
    524       source->source()->MessageLoopForRequestPath(path);
    525   if (!target_message_loop) {
    526     job->MimeTypeAvailable(source->source()->GetMimeType(path));
    527     // Eliminate potentially dangling pointer to avoid future use.
    528     job = NULL;
    529 
    530     // The DataSource is agnostic to which thread StartDataRequest is called
    531     // on for this path.  Call directly into it from this thread, the IO
    532     // thread.
    533     source->source()->StartDataRequest(
    534         path, render_process_id, render_view_id,
    535         base::Bind(&URLDataSourceImpl::SendResponse, source, request_id));
    536   } else {
    537     // URLRequestChromeJob should receive mime type before data. This
    538     // is guaranteed because request for mime type is placed in the
    539     // message loop before request for data. And correspondingly their
    540     // replies are put on the IO thread in the same order.
    541     target_message_loop->PostTask(
    542         FROM_HERE,
    543         base::Bind(&GetMimeTypeOnUI,
    544                    scoped_refptr<URLDataSourceImpl>(source),
    545                    path, job->AsWeakPtr()));
    546 
    547     // The DataSource wants StartDataRequest to be called on a specific thread,
    548     // usually the UI thread, for this path.
    549     target_message_loop->PostTask(
    550         FROM_HERE,
    551         base::Bind(&URLDataManagerBackend::CallStartRequest,
    552                    make_scoped_refptr(source), path, render_process_id,
    553                    render_view_id, request_id));
    554   }
    555   return true;
    556 }
    557 
    558 void URLDataManagerBackend::CallStartRequest(
    559     scoped_refptr<URLDataSourceImpl> source,
    560     const std::string& path,
    561     int render_process_id,
    562     int render_view_id,
    563     int request_id) {
    564   if (BrowserThread::CurrentlyOn(BrowserThread::UI) &&
    565       render_process_id != -1 &&
    566       !RenderProcessHost::FromID(render_process_id)) {
    567     // Make the request fail if its initiating renderer is no longer valid.
    568     // This can happen when the IO thread posts this task just before the
    569     // renderer shuts down.
    570     source->SendResponse(request_id, NULL);
    571     return;
    572   }
    573   source->source()->StartDataRequest(
    574       path,
    575       render_process_id,
    576       render_view_id,
    577       base::Bind(&URLDataSourceImpl::SendResponse, source, request_id));
    578 }
    579 
    580 void URLDataManagerBackend::RemoveRequest(URLRequestChromeJob* job) {
    581   // Remove the request from our list of pending requests.
    582   // If/when the source sends the data that was requested, the data will just
    583   // be thrown away.
    584   for (PendingRequestMap::iterator i = pending_requests_.begin();
    585        i != pending_requests_.end(); ++i) {
    586     if (i->second == job) {
    587       pending_requests_.erase(i);
    588       return;
    589     }
    590   }
    591 }
    592 
    593 void URLDataManagerBackend::DataAvailable(RequestID request_id,
    594                                           base::RefCountedMemory* bytes) {
    595   // Forward this data on to the pending net::URLRequest, if it exists.
    596   PendingRequestMap::iterator i = pending_requests_.find(request_id);
    597   if (i != pending_requests_.end()) {
    598     URLRequestChromeJob* job(i->second);
    599     pending_requests_.erase(i);
    600     job->DataAvailable(bytes);
    601   }
    602 }
    603 
    604 namespace {
    605 
    606 class DevToolsJobFactory
    607     : public net::URLRequestJobFactory::ProtocolHandler {
    608  public:
    609   // |is_incognito| should be set for incognito profiles.
    610   DevToolsJobFactory(content::ResourceContext* resource_context,
    611                      bool is_incognito);
    612   virtual ~DevToolsJobFactory();
    613 
    614   virtual net::URLRequestJob* MaybeCreateJob(
    615       net::URLRequest* request,
    616       net::NetworkDelegate* network_delegate) const OVERRIDE;
    617 
    618  private:
    619   // |resource_context_| and |network_delegate_| are owned by ProfileIOData,
    620   // which owns this ProtocolHandler.
    621   content::ResourceContext* const resource_context_;
    622 
    623   // True when generated from an incognito profile.
    624   const bool is_incognito_;
    625 
    626   DISALLOW_COPY_AND_ASSIGN(DevToolsJobFactory);
    627 };
    628 
    629 DevToolsJobFactory::DevToolsJobFactory(
    630     content::ResourceContext* resource_context,
    631     bool is_incognito)
    632     : resource_context_(resource_context),
    633       is_incognito_(is_incognito) {
    634   DCHECK(resource_context_);
    635 }
    636 
    637 DevToolsJobFactory::~DevToolsJobFactory() {}
    638 
    639 net::URLRequestJob*
    640 DevToolsJobFactory::MaybeCreateJob(
    641     net::URLRequest* request, net::NetworkDelegate* network_delegate) const {
    642   return new URLRequestChromeJob(
    643       request, network_delegate,
    644       GetURLDataManagerForResourceContext(resource_context_), is_incognito_);
    645 }
    646 
    647 }  // namespace
    648 
    649 net::URLRequestJobFactory::ProtocolHandler*
    650 CreateDevToolsProtocolHandler(content::ResourceContext* resource_context,
    651                               bool is_incognito) {
    652   return new DevToolsJobFactory(resource_context, is_incognito);
    653 }
    654 
    655 }  // namespace content
    656