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