• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SchemeIsInSchemes(const std::string & scheme,const std::vector<std::string> & schemes)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.
CheckURLIsValid(const GURL & url)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.
URLToRequestPath(const GURL & url,std::string * path)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 
set_mime_type(const std::string & mime_type)127   void set_mime_type(const std::string& mime_type) {
128     mime_type_ = mime_type;
129   }
130 
set_allow_caching(bool allow_caching)131   void set_allow_caching(bool allow_caching) {
132     allow_caching_ = allow_caching;
133   }
134 
set_add_content_security_policy(bool add_content_security_policy)135   void set_add_content_security_policy(bool add_content_security_policy) {
136     add_content_security_policy_ = add_content_security_policy;
137   }
138 
set_content_security_policy_object_source(const std::string & data)139   void set_content_security_policy_object_source(
140       const std::string& data) {
141     content_security_policy_object_source_ = data;
142   }
143 
set_content_security_policy_frame_source(const std::string & data)144   void set_content_security_policy_frame_source(
145       const std::string& data) {
146     content_security_policy_frame_source_ = data;
147   }
148 
set_deny_xframe_options(bool deny_xframe_options)149   void set_deny_xframe_options(bool deny_xframe_options) {
150     deny_xframe_options_ = deny_xframe_options;
151   }
152 
set_send_content_type_header(bool send_content_type_header)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.
is_incognito() const158   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 
URLRequestChromeJob(net::URLRequest * request,net::NetworkDelegate * network_delegate,URLDataManagerBackend * backend,bool is_incognito)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 
~URLRequestChromeJob()237 URLRequestChromeJob::~URLRequestChromeJob() {
238   CHECK(!backend_->HasPendingJob(this));
239 }
240 
Start()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 
Kill()256 void URLRequestChromeJob::Kill() {
257   backend_->RemoveRequest(this);
258 }
259 
GetMimeType(std::string * mime_type) const260 bool URLRequestChromeJob::GetMimeType(std::string* mime_type) const {
261   *mime_type = mime_type_;
262   return !mime_type_.empty();
263 }
264 
GetResponseCode() const265 int URLRequestChromeJob::GetResponseCode() const {
266   return net::HTTP_OK;
267 }
268 
GetResponseInfo(net::HttpResponseInfo * info)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 
MimeTypeAvailable(const std::string & mime_type)300 void URLRequestChromeJob::MimeTypeAvailable(const std::string& mime_type) {
301   set_mime_type(mime_type);
302   NotifyHeadersComplete();
303 }
304 
DataAvailable(base::RefCountedMemory * bytes)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 
ReadRawData(net::IOBuffer * buf,int buf_size,int * bytes_read)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 
CompleteRead(net::IOBuffer * buf,int buf_size,int * bytes_read)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 
CheckStoragePartitionMatches(int render_process_id,const GURL & url,const base::WeakPtr<URLRequestChromeJob> & job)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 
StartAsync(bool allowed)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.
GetMimeTypeOnUI(URLDataSourceImpl * source,const std::string & path,const base::WeakPtr<URLRequestChromeJob> & job)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.
ChromeProtocolHandler(ResourceContext * resource_context,bool is_incognito,AppCacheServiceImpl * appcache_service,ChromeBlobStorageContext * blob_storage_context)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) {}
~ChromeProtocolHandler()437   virtual ~ChromeProtocolHandler() {}
438 
MaybeCreateJob(net::URLRequest * request,net::NetworkDelegate * network_delegate) const439   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 
IsSafeRedirectTarget(const GURL & location) const482   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 
URLDataManagerBackend()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 
~URLDataManagerBackend()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*
CreateProtocolHandler(content::ResourceContext * resource_context,bool is_incognito,AppCacheServiceImpl * appcache_service,ChromeBlobStorageContext * blob_storage_context)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 
AddDataSource(URLDataSourceImpl * source)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 
HasPendingJob(URLRequestChromeJob * job) const541 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 
StartRequest(const net::URLRequest * request,URLRequestChromeJob * job)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 
GetDataSourceFromURL(const GURL & url)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 
CallStartRequest(scoped_refptr<URLDataSourceImpl> source,const std::string & path,int render_process_id,int render_frame_id,int request_id)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 
RemoveRequest(URLRequestChromeJob * job)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 
DataAvailable(RequestID request_id,base::RefCountedMemory * bytes)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 
DevToolsJobFactory(content::ResourceContext * resource_context,bool is_incognito)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 
~DevToolsJobFactory()723 DevToolsJobFactory::~DevToolsJobFactory() {}
724 
725 net::URLRequestJob*
MaybeCreateJob(net::URLRequest * request,net::NetworkDelegate * network_delegate) const726 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*
CreateDevToolsProtocolHandler(content::ResourceContext * resource_context,bool is_incognito)736 CreateDevToolsProtocolHandler(content::ResourceContext* resource_context,
737                               bool is_incognito) {
738   return new DevToolsJobFactory(resource_context, is_incognito);
739 }
740 
741 }  // namespace content
742