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