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