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