Home | History | Annotate | Download | only in glue
      1 // Copyright (c) 2011 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 // An implementation of WebURLLoader in terms of ResourceLoaderBridge.
      6 
      7 #include "webkit/glue/weburlloader_impl.h"
      8 
      9 #include "base/file_path.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/message_loop.h"
     12 #include "base/process_util.h"
     13 #include "base/string_util.h"
     14 #include "base/time.h"
     15 #include "net/base/data_url.h"
     16 #include "net/base/load_flags.h"
     17 #include "net/base/mime_util.h"
     18 #include "net/base/net_errors.h"
     19 #include "net/base/net_util.h"
     20 #include "net/http/http_response_headers.h"
     21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebHTTPHeaderVisitor.h"
     22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebHTTPLoadInfo.h"
     23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityPolicy.h"
     24 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURL.h"
     25 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLError.h"
     26 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLLoadTiming.h"
     27 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLLoaderClient.h"
     28 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLRequest.h"
     29 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLResponse.h"
     30 #include "webkit/glue/ftp_directory_listing_response_delegate.h"
     31 #include "webkit/glue/multipart_response_delegate.h"
     32 #include "webkit/glue/resource_loader_bridge.h"
     33 #include "webkit/glue/site_isolation_metrics.h"
     34 #include "webkit/glue/webkit_glue.h"
     35 
     36 using base::Time;
     37 using base::TimeDelta;
     38 using WebKit::WebData;
     39 using WebKit::WebHTTPBody;
     40 using WebKit::WebHTTPHeaderVisitor;
     41 using WebKit::WebHTTPLoadInfo;
     42 using WebKit::WebSecurityPolicy;
     43 using WebKit::WebString;
     44 using WebKit::WebURL;
     45 using WebKit::WebURLError;
     46 using WebKit::WebURLLoadTiming;
     47 using WebKit::WebURLLoader;
     48 using WebKit::WebURLLoaderClient;
     49 using WebKit::WebURLRequest;
     50 using WebKit::WebURLResponse;
     51 
     52 namespace webkit_glue {
     53 
     54 // Utilities ------------------------------------------------------------------
     55 
     56 namespace {
     57 
     58 class HeaderFlattener : public WebHTTPHeaderVisitor {
     59  public:
     60   explicit HeaderFlattener(int load_flags)
     61       : load_flags_(load_flags),
     62         has_accept_header_(false) {
     63   }
     64 
     65   virtual void visitHeader(const WebString& name, const WebString& value) {
     66     // TODO(darin): is UTF-8 really correct here?  It is if the strings are
     67     // already ASCII (i.e., if they are already escaped properly).
     68     const std::string& name_utf8 = name.utf8();
     69     const std::string& value_utf8 = value.utf8();
     70 
     71     // Skip over referrer headers found in the header map because we already
     72     // pulled it out as a separate parameter.
     73     if (LowerCaseEqualsASCII(name_utf8, "referer"))
     74       return;
     75 
     76     // Skip over "Cache-Control: max-age=0" header if the corresponding
     77     // load flag is already specified. FrameLoader sets both the flag and
     78     // the extra header -- the extra header is redundant since our network
     79     // implementation will add the necessary headers based on load flags.
     80     // See http://code.google.com/p/chromium/issues/detail?id=3434.
     81     if ((load_flags_ & net::LOAD_VALIDATE_CACHE) &&
     82         LowerCaseEqualsASCII(name_utf8, "cache-control") &&
     83         LowerCaseEqualsASCII(value_utf8, "max-age=0"))
     84       return;
     85 
     86     if (LowerCaseEqualsASCII(name_utf8, "accept"))
     87       has_accept_header_ = true;
     88 
     89     if (!buffer_.empty())
     90       buffer_.append("\r\n");
     91     buffer_.append(name_utf8 + ": " + value_utf8);
     92   }
     93 
     94   const std::string& GetBuffer() {
     95     // In some cases, WebKit doesn't add an Accept header, but not having the
     96     // header confuses some web servers.  See bug 808613.
     97     if (!has_accept_header_) {
     98       if (!buffer_.empty())
     99         buffer_.append("\r\n");
    100       buffer_.append("Accept: */*");
    101       has_accept_header_ = true;
    102     }
    103     return buffer_;
    104   }
    105 
    106  private:
    107   int load_flags_;
    108   std::string buffer_;
    109   bool has_accept_header_;
    110 };
    111 
    112 ResourceType::Type FromTargetType(WebURLRequest::TargetType type) {
    113   switch (type) {
    114     case WebURLRequest::TargetIsMainFrame:
    115       return ResourceType::MAIN_FRAME;
    116     case WebURLRequest::TargetIsSubframe:
    117       return ResourceType::SUB_FRAME;
    118     case WebURLRequest::TargetIsSubresource:
    119       return ResourceType::SUB_RESOURCE;
    120     case WebURLRequest::TargetIsStyleSheet:
    121       return ResourceType::STYLESHEET;
    122     case WebURLRequest::TargetIsScript:
    123       return ResourceType::SCRIPT;
    124     case WebURLRequest::TargetIsFontResource:
    125       return ResourceType::FONT_RESOURCE;
    126     case WebURLRequest::TargetIsImage:
    127       return ResourceType::IMAGE;
    128     case WebURLRequest::TargetIsObject:
    129       return ResourceType::OBJECT;
    130     case WebURLRequest::TargetIsMedia:
    131       return ResourceType::MEDIA;
    132     case WebURLRequest::TargetIsWorker:
    133       return ResourceType::WORKER;
    134     case WebURLRequest::TargetIsSharedWorker:
    135       return ResourceType::SHARED_WORKER;
    136     case WebURLRequest::TargetIsPrefetch:
    137       return ResourceType::PREFETCH;
    138     case WebURLRequest::TargetIsFavicon:
    139       return ResourceType::FAVICON;
    140     default:
    141       NOTREACHED();
    142       return ResourceType::SUB_RESOURCE;
    143   }
    144 }
    145 
    146 // Extracts the information from a data: url.
    147 bool GetInfoFromDataURL(const GURL& url,
    148                         ResourceResponseInfo* info,
    149                         std::string* data,
    150                         net::URLRequestStatus* status) {
    151   std::string mime_type;
    152   std::string charset;
    153   if (net::DataURL::Parse(url, &mime_type, &charset, data)) {
    154     *status = net::URLRequestStatus(net::URLRequestStatus::SUCCESS, 0);
    155     info->request_time = Time::Now();
    156     info->response_time = Time::Now();
    157     info->headers = NULL;
    158     info->mime_type.swap(mime_type);
    159     info->charset.swap(charset);
    160     info->security_info.clear();
    161     info->content_length = -1;
    162     info->encoded_data_length = 0;
    163     info->load_timing.base_time = Time::Now();
    164 
    165     return true;
    166   }
    167 
    168   *status = net::URLRequestStatus(net::URLRequestStatus::FAILED,
    169                                   net::ERR_INVALID_URL);
    170   return false;
    171 }
    172 
    173 typedef ResourceDevToolsInfo::HeadersVector HeadersVector;
    174 
    175 void PopulateURLResponse(
    176     const GURL& url,
    177     const ResourceResponseInfo& info,
    178     WebURLResponse* response) {
    179   response->setURL(url);
    180   response->setResponseTime(info.response_time.ToDoubleT());
    181   response->setMIMEType(WebString::fromUTF8(info.mime_type));
    182   response->setTextEncodingName(WebString::fromUTF8(info.charset));
    183   response->setExpectedContentLength(info.content_length);
    184   response->setSecurityInfo(info.security_info);
    185   response->setAppCacheID(info.appcache_id);
    186   response->setAppCacheManifestURL(info.appcache_manifest_url);
    187   response->setWasCached(!info.load_timing.base_time.is_null() &&
    188       info.response_time < info.load_timing.base_time);
    189   response->setWasFetchedViaSPDY(info.was_fetched_via_spdy);
    190   response->setWasNpnNegotiated(info.was_npn_negotiated);
    191   response->setWasAlternateProtocolAvailable(
    192       info.was_alternate_protocol_available);
    193   response->setWasFetchedViaProxy(info.was_fetched_via_proxy);
    194   response->setRemoteIPAddress(
    195       WebString::fromUTF8(info.socket_address.host()));
    196   response->setRemotePort(info.socket_address.port());
    197   response->setConnectionID(info.connection_id);
    198   response->setConnectionReused(info.connection_reused);
    199   response->setDownloadFilePath(FilePathToWebString(info.download_file_path));
    200 
    201   const ResourceLoadTimingInfo& timing_info = info.load_timing;
    202   if (!timing_info.base_time.is_null()) {
    203     WebURLLoadTiming timing;
    204     timing.initialize();
    205     timing.setRequestTime(timing_info.base_time.ToDoubleT());
    206     timing.setProxyStart(timing_info.proxy_start);
    207     timing.setProxyEnd(timing_info.proxy_end);
    208     timing.setDNSStart(timing_info.dns_start);
    209     timing.setDNSEnd(timing_info.dns_end);
    210     timing.setConnectStart(timing_info.connect_start);
    211     timing.setConnectEnd(timing_info.connect_end);
    212     timing.setSSLStart(timing_info.ssl_start);
    213     timing.setSSLEnd(timing_info.ssl_end);
    214     timing.setSendStart(timing_info.send_start);
    215     timing.setSendEnd(timing_info.send_end);
    216     timing.setReceiveHeadersEnd(timing_info.receive_headers_end);
    217     response->setLoadTiming(timing);
    218   }
    219 
    220   if (info.devtools_info.get()) {
    221     WebHTTPLoadInfo load_info;
    222 
    223     load_info.setHTTPStatusCode(info.devtools_info->http_status_code);
    224     load_info.setHTTPStatusText(WebString::fromUTF8(
    225         info.devtools_info->http_status_text));
    226     load_info.setEncodedDataLength(info.encoded_data_length);
    227 
    228     const HeadersVector& request_headers = info.devtools_info->request_headers;
    229     for (HeadersVector::const_iterator it = request_headers.begin();
    230          it != request_headers.end(); ++it) {
    231       load_info.addRequestHeader(WebString::fromUTF8(it->first),
    232           WebString::fromUTF8(it->second));
    233     }
    234     const HeadersVector& response_headers =
    235         info.devtools_info->response_headers;
    236     for (HeadersVector::const_iterator it = response_headers.begin();
    237          it != response_headers.end(); ++it) {
    238       load_info.addResponseHeader(WebString::fromUTF8(it->first),
    239           WebString::fromUTF8(it->second));
    240     }
    241     response->setHTTPLoadInfo(load_info);
    242   }
    243 
    244   const net::HttpResponseHeaders* headers = info.headers;
    245   if (!headers)
    246     return;
    247 
    248   response->setHTTPStatusCode(headers->response_code());
    249   response->setHTTPStatusText(WebString::fromUTF8(headers->GetStatusText()));
    250 
    251   // TODO(darin): We should leverage HttpResponseHeaders for this, and this
    252   // should be using the same code as ResourceDispatcherHost.
    253   // TODO(jungshik): Figure out the actual value of the referrer charset and
    254   // pass it to GetSuggestedFilename.
    255   std::string value;
    256   if (headers->EnumerateHeader(NULL, "content-disposition", &value)) {
    257     response->setSuggestedFileName(
    258         net::GetSuggestedFilename(url, value, "", string16()));
    259   }
    260 
    261   Time time_val;
    262   if (headers->GetLastModifiedValue(&time_val))
    263     response->setLastModifiedDate(time_val.ToDoubleT());
    264 
    265   // Build up the header map.
    266   void* iter = NULL;
    267   std::string name;
    268   while (headers->EnumerateHeaderLines(&iter, &name, &value)) {
    269     response->addHTTPHeaderField(WebString::fromUTF8(name),
    270                                  WebString::fromUTF8(value));
    271   }
    272 }
    273 
    274 }  // namespace
    275 
    276 // WebURLLoaderImpl::Context --------------------------------------------------
    277 
    278 // This inner class exists since the WebURLLoader may be deleted while inside a
    279 // call to WebURLLoaderClient.  The bridge requires its Peer to stay alive
    280 // until it receives OnCompletedRequest.
    281 class WebURLLoaderImpl::Context : public base::RefCounted<Context>,
    282                                   public ResourceLoaderBridge::Peer {
    283  public:
    284   explicit Context(WebURLLoaderImpl* loader);
    285 
    286   WebURLLoaderClient* client() const { return client_; }
    287   void set_client(WebURLLoaderClient* client) { client_ = client; }
    288 
    289   void Cancel();
    290   void SetDefersLoading(bool value);
    291   void Start(
    292       const WebURLRequest& request,
    293       ResourceLoaderBridge::SyncLoadResponse* sync_load_response);
    294 
    295   // ResourceLoaderBridge::Peer methods:
    296   virtual void OnUploadProgress(uint64 position, uint64 size);
    297   virtual bool OnReceivedRedirect(
    298       const GURL& new_url,
    299       const ResourceResponseInfo& info,
    300       bool* has_new_first_party_for_cookies,
    301       GURL* new_first_party_for_cookies);
    302   virtual void OnReceivedResponse(const ResourceResponseInfo& info);
    303   virtual void OnDownloadedData(int len);
    304   virtual void OnReceivedData(const char* data,
    305                               int data_length,
    306                               int encoded_data_length);
    307   virtual void OnReceivedCachedMetadata(const char* data, int len);
    308   virtual void OnCompletedRequest(const net::URLRequestStatus& status,
    309                                   const std::string& security_info,
    310                                   const base::Time& completion_time);
    311 
    312  private:
    313   friend class base::RefCounted<Context>;
    314   ~Context() {}
    315 
    316   // We can optimize the handling of data URLs in most cases.
    317   bool CanHandleDataURL(const GURL& url) const;
    318   void HandleDataURL();
    319 
    320   WebURLLoaderImpl* loader_;
    321   WebURLRequest request_;
    322   WebURLLoaderClient* client_;
    323   scoped_ptr<ResourceLoaderBridge> bridge_;
    324   scoped_ptr<FtpDirectoryListingResponseDelegate> ftp_listing_delegate_;
    325   scoped_ptr<MultipartResponseDelegate> multipart_delegate_;
    326   scoped_ptr<ResourceLoaderBridge> completed_bridge_;
    327 
    328   // TODO(japhet): Storing this is a temporary hack for site isolation logging.
    329   WebURL response_url_;
    330 };
    331 
    332 WebURLLoaderImpl::Context::Context(WebURLLoaderImpl* loader)
    333     : loader_(loader),
    334       client_(NULL) {
    335 }
    336 
    337 void WebURLLoaderImpl::Context::Cancel() {
    338   // The bridge will still send OnCompletedRequest, which will Release() us, so
    339   // we don't do that here.
    340   if (bridge_.get())
    341     bridge_->Cancel();
    342 
    343   // Ensure that we do not notify the multipart delegate anymore as it has
    344   // its own pointer to the client.
    345   if (multipart_delegate_.get())
    346     multipart_delegate_->Cancel();
    347 
    348   // Do not make any further calls to the client.
    349   client_ = NULL;
    350   loader_ = NULL;
    351 }
    352 
    353 void WebURLLoaderImpl::Context::SetDefersLoading(bool value) {
    354   if (bridge_.get())
    355     bridge_->SetDefersLoading(value);
    356 }
    357 
    358 void WebURLLoaderImpl::Context::Start(
    359     const WebURLRequest& request,
    360     ResourceLoaderBridge::SyncLoadResponse* sync_load_response) {
    361   DCHECK(!bridge_.get());
    362 
    363   request_ = request;  // Save the request.
    364 
    365   GURL url = request.url();
    366   if (url.SchemeIs("data") && CanHandleDataURL(url)) {
    367     if (sync_load_response) {
    368       // This is a sync load. Do the work now.
    369       sync_load_response->url = url;
    370       std::string data;
    371       GetInfoFromDataURL(sync_load_response->url, sync_load_response,
    372                          &sync_load_response->data,
    373                          &sync_load_response->status);
    374     } else {
    375       AddRef();  // Balanced in OnCompletedRequest
    376       MessageLoop::current()->PostTask(FROM_HERE,
    377           NewRunnableMethod(this, &Context::HandleDataURL));
    378     }
    379     return;
    380   }
    381 
    382   GURL referrer_url(
    383       request.httpHeaderField(WebString::fromUTF8("Referer")).utf8());
    384   const std::string& method = request.httpMethod().utf8();
    385 
    386   int load_flags = net::LOAD_NORMAL;
    387   switch (request.cachePolicy()) {
    388     case WebURLRequest::ReloadIgnoringCacheData:
    389       // Required by LayoutTests/http/tests/misc/refresh-headers.php
    390       load_flags |= net::LOAD_VALIDATE_CACHE;
    391       break;
    392     case WebURLRequest::ReturnCacheDataElseLoad:
    393       load_flags |= net::LOAD_PREFERRING_CACHE;
    394       break;
    395     case WebURLRequest::ReturnCacheDataDontLoad:
    396       load_flags |= net::LOAD_ONLY_FROM_CACHE;
    397       break;
    398     case WebURLRequest::UseProtocolCachePolicy:
    399       break;
    400   }
    401 
    402   if (request.reportUploadProgress())
    403     load_flags |= net::LOAD_ENABLE_UPLOAD_PROGRESS;
    404   if (request.reportLoadTiming())
    405     load_flags |= net::LOAD_ENABLE_LOAD_TIMING;
    406   if (request.reportRawHeaders())
    407     load_flags |= net::LOAD_REPORT_RAW_HEADERS;
    408 
    409   if (!request.allowCookies() || !request.allowStoredCredentials()) {
    410     load_flags |= net::LOAD_DO_NOT_SAVE_COOKIES;
    411     load_flags |= net::LOAD_DO_NOT_SEND_COOKIES;
    412   }
    413 
    414   if (!request.allowStoredCredentials())
    415     load_flags |= net::LOAD_DO_NOT_SEND_AUTH_DATA;
    416 
    417   HeaderFlattener flattener(load_flags);
    418   request.visitHTTPHeaderFields(&flattener);
    419 
    420   // TODO(abarth): These are wrong!  I need to figure out how to get the right
    421   //               strings here.  See: http://crbug.com/8706
    422   std::string frame_origin = request.firstPartyForCookies().spec();
    423   std::string main_frame_origin = request.firstPartyForCookies().spec();
    424 
    425   // TODO(brettw) this should take parameter encoding into account when
    426   // creating the GURLs.
    427 
    428   ResourceLoaderBridge::RequestInfo request_info;
    429   request_info.method = method;
    430   request_info.url = url;
    431   request_info.first_party_for_cookies = request.firstPartyForCookies();
    432   request_info.referrer = referrer_url;
    433   request_info.frame_origin = frame_origin;
    434   request_info.main_frame_origin = main_frame_origin;
    435   request_info.headers = flattener.GetBuffer();
    436   request_info.load_flags = load_flags;
    437   // requestor_pid only needs to be non-zero if the request originates outside
    438   // the render process, so we can use requestorProcessID even for requests
    439   // from in-process plugins.
    440   request_info.requestor_pid = request.requestorProcessID();
    441   request_info.request_type = FromTargetType(request.targetType());
    442   request_info.appcache_host_id = request.appCacheHostID();
    443   request_info.routing_id = request.requestorID();
    444   request_info.download_to_file = request.downloadToFile();
    445   request_info.has_user_gesture = request.hasUserGesture();
    446   bridge_.reset(ResourceLoaderBridge::Create(request_info));
    447 
    448   if (!request.httpBody().isNull()) {
    449     // GET and HEAD requests shouldn't have http bodies.
    450     DCHECK(method != "GET" && method != "HEAD");
    451     const WebHTTPBody& httpBody = request.httpBody();
    452     size_t i = 0;
    453     WebHTTPBody::Element element;
    454     while (httpBody.elementAt(i++, element)) {
    455       switch (element.type) {
    456         case WebHTTPBody::Element::TypeData:
    457           if (!element.data.isEmpty()) {
    458             // WebKit sometimes gives up empty data to append. These aren't
    459             // necessary so we just optimize those out here.
    460             bridge_->AppendDataToUpload(
    461                 element.data.data(), static_cast<int>(element.data.size()));
    462           }
    463           break;
    464         case WebHTTPBody::Element::TypeFile:
    465           if (element.fileLength == -1) {
    466             bridge_->AppendFileToUpload(
    467                 WebStringToFilePath(element.filePath));
    468           } else {
    469             bridge_->AppendFileRangeToUpload(
    470                 WebStringToFilePath(element.filePath),
    471                 static_cast<uint64>(element.fileStart),
    472                 static_cast<uint64>(element.fileLength),
    473                 base::Time::FromDoubleT(element.modificationTime));
    474           }
    475           break;
    476         case WebHTTPBody::Element::TypeBlob:
    477           bridge_->AppendBlobToUpload(GURL(element.blobURL));
    478           break;
    479         default:
    480           NOTREACHED();
    481       }
    482     }
    483     bridge_->SetUploadIdentifier(request.httpBody().identifier());
    484   }
    485 
    486   if (sync_load_response) {
    487     bridge_->SyncLoad(sync_load_response);
    488     return;
    489   }
    490 
    491   if (bridge_->Start(this)) {
    492     AddRef();  // Balanced in OnCompletedRequest
    493   } else {
    494     bridge_.reset();
    495   }
    496 }
    497 
    498 void WebURLLoaderImpl::Context::OnUploadProgress(uint64 position, uint64 size) {
    499   if (client_)
    500     client_->didSendData(loader_, position, size);
    501 }
    502 
    503 bool WebURLLoaderImpl::Context::OnReceivedRedirect(
    504     const GURL& new_url,
    505     const ResourceResponseInfo& info,
    506     bool* has_new_first_party_for_cookies,
    507     GURL* new_first_party_for_cookies) {
    508   if (!client_)
    509     return false;
    510 
    511   WebURLResponse response;
    512   response.initialize();
    513   PopulateURLResponse(request_.url(), info, &response);
    514 
    515   // TODO(darin): We lack sufficient information to construct the actual
    516   // request that resulted from the redirect.
    517   WebURLRequest new_request(new_url);
    518   new_request.setFirstPartyForCookies(request_.firstPartyForCookies());
    519   new_request.setDownloadToFile(request_.downloadToFile());
    520 
    521   WebString referrer_string = WebString::fromUTF8("Referer");
    522   WebString referrer = request_.httpHeaderField(referrer_string);
    523   if (!WebSecurityPolicy::shouldHideReferrer(new_url, referrer))
    524     new_request.setHTTPHeaderField(referrer_string, referrer);
    525 
    526   if (response.httpStatusCode() == 307)
    527     new_request.setHTTPMethod(request_.httpMethod());
    528 
    529   client_->willSendRequest(loader_, new_request, response);
    530   request_ = new_request;
    531   *has_new_first_party_for_cookies = true;
    532   *new_first_party_for_cookies = request_.firstPartyForCookies();
    533 
    534   // Only follow the redirect if WebKit left the URL unmodified.
    535   if (new_url == GURL(new_request.url()))
    536     return true;
    537 
    538   // We assume that WebKit only changes the URL to suppress a redirect, and we
    539   // assume that it does so by setting it to be invalid.
    540   DCHECK(!new_request.url().isValid());
    541   return false;
    542 }
    543 
    544 void WebURLLoaderImpl::Context::OnReceivedResponse(
    545     const ResourceResponseInfo& info) {
    546   if (!client_)
    547     return;
    548 
    549   WebURLResponse response;
    550   response.initialize();
    551   PopulateURLResponse(request_.url(), info, &response);
    552 
    553   bool show_raw_listing = (GURL(request_.url()).query() == "raw");
    554 
    555   if (info.mime_type == "text/vnd.chromium.ftp-dir") {
    556     if (show_raw_listing) {
    557       // Set the MIME type to plain text to prevent any active content.
    558       response.setMIMEType("text/plain");
    559     } else {
    560       // We're going to produce a parsed listing in HTML.
    561       response.setMIMEType("text/html");
    562     }
    563   }
    564 
    565   client_->didReceiveResponse(loader_, response);
    566 
    567   // We may have been cancelled after didReceiveResponse, which would leave us
    568   // without a client and therefore without much need to do further handling.
    569   if (!client_)
    570     return;
    571 
    572   DCHECK(!ftp_listing_delegate_.get());
    573   DCHECK(!multipart_delegate_.get());
    574   if (info.headers && info.mime_type == "multipart/x-mixed-replace") {
    575     std::string content_type;
    576     info.headers->EnumerateHeader(NULL, "content-type", &content_type);
    577 
    578     std::string boundary = net::GetHeaderParamValue(
    579         content_type, "boundary", net::QuoteRule::REMOVE_OUTER_QUOTES);
    580     TrimString(boundary, " \"", &boundary);
    581 
    582     // If there's no boundary, just handle the request normally.  In the gecko
    583     // code, nsMultiMixedConv::OnStartRequest throws an exception.
    584     if (!boundary.empty()) {
    585       multipart_delegate_.reset(
    586           new MultipartResponseDelegate(client_, loader_, response, boundary));
    587     }
    588   } else if (info.mime_type == "text/vnd.chromium.ftp-dir" &&
    589              !show_raw_listing) {
    590     ftp_listing_delegate_.reset(
    591         new FtpDirectoryListingResponseDelegate(client_, loader_, response));
    592   }
    593 
    594   response_url_ = response.url();
    595 }
    596 
    597 void WebURLLoaderImpl::Context::OnDownloadedData(int len) {
    598   if (client_)
    599     client_->didDownloadData(loader_, len);
    600 }
    601 
    602 void WebURLLoaderImpl::Context::OnReceivedData(const char* data,
    603                                                int data_length,
    604                                                int encoded_data_length) {
    605   if (!client_)
    606     return;
    607 
    608   // Temporary logging, see site_isolation_metrics.h/cc.
    609   SiteIsolationMetrics::SniffCrossOriginHTML(response_url_, data, data_length);
    610 
    611   if (ftp_listing_delegate_.get()) {
    612     // The FTP listing delegate will make the appropriate calls to
    613     // client_->didReceiveData and client_->didReceiveResponse.
    614     ftp_listing_delegate_->OnReceivedData(data, data_length);
    615   } else if (multipart_delegate_.get()) {
    616     // The multipart delegate will make the appropriate calls to
    617     // client_->didReceiveData and client_->didReceiveResponse.
    618     multipart_delegate_->OnReceivedData(data, data_length, encoded_data_length);
    619   } else {
    620     client_->didReceiveData(loader_, data, data_length, encoded_data_length);
    621   }
    622 }
    623 
    624 void WebURLLoaderImpl::Context::OnReceivedCachedMetadata(
    625     const char* data, int len) {
    626   if (client_)
    627     client_->didReceiveCachedMetadata(loader_, data, len);
    628 }
    629 
    630 void WebURLLoaderImpl::Context::OnCompletedRequest(
    631     const net::URLRequestStatus& status,
    632     const std::string& security_info,
    633     const base::Time& completion_time) {
    634   if (ftp_listing_delegate_.get()) {
    635     ftp_listing_delegate_->OnCompletedRequest();
    636     ftp_listing_delegate_.reset(NULL);
    637   } else if (multipart_delegate_.get()) {
    638     multipart_delegate_->OnCompletedRequest();
    639     multipart_delegate_.reset(NULL);
    640   }
    641 
    642   // Prevent any further IPC to the browser now that we're complete, but
    643   // don't delete it to keep any downloaded temp files alive.
    644   DCHECK(!completed_bridge_.get());
    645   completed_bridge_.swap(bridge_);
    646 
    647   if (client_) {
    648     if (status.status() != net::URLRequestStatus::SUCCESS) {
    649       int error_code;
    650       if (status.status() == net::URLRequestStatus::HANDLED_EXTERNALLY) {
    651         // By marking this request as aborted we insure that we don't navigate
    652         // to an error page.
    653         error_code = net::ERR_ABORTED;
    654       } else {
    655         error_code = status.os_error();
    656       }
    657       WebURLError error;
    658       error.domain = WebString::fromUTF8(net::kErrorDomain);
    659       error.reason = error_code;
    660       error.unreachableURL = request_.url();
    661       client_->didFail(loader_, error);
    662     } else {
    663       client_->didFinishLoading(loader_, completion_time.ToDoubleT());
    664     }
    665   }
    666 
    667   // Temporary logging, see site_isolation_metrics.h/cc
    668   SiteIsolationMetrics::RemoveCompletedResponse(response_url_);
    669 
    670   // We are done with the bridge now, and so we need to release the reference
    671   // to ourselves that we took on behalf of the bridge.  This may cause our
    672   // destruction.
    673   Release();
    674 }
    675 
    676 bool WebURLLoaderImpl::Context::CanHandleDataURL(const GURL& url) const {
    677   DCHECK(url.SchemeIs("data"));
    678 
    679   // Optimize for the case where we can handle a data URL locally.  We must
    680   // skip this for data URLs targetted at frames since those could trigger a
    681   // download.
    682   //
    683   // NOTE: We special case MIME types we can render both for performance
    684   // reasons as well as to support unit tests, which do not have an underlying
    685   // ResourceLoaderBridge implementation.
    686 
    687   if (request_.targetType() != WebURLRequest::TargetIsMainFrame &&
    688       request_.targetType() != WebURLRequest::TargetIsSubframe)
    689     return true;
    690 
    691   std::string mime_type, unused_charset;
    692   if (net::DataURL::Parse(url, &mime_type, &unused_charset, NULL) &&
    693       net::IsSupportedMimeType(mime_type))
    694     return true;
    695 
    696   return false;
    697 }
    698 
    699 void WebURLLoaderImpl::Context::HandleDataURL() {
    700   ResourceResponseInfo info;
    701   net::URLRequestStatus status;
    702   std::string data;
    703 
    704   if (GetInfoFromDataURL(request_.url(), &info, &data, &status)) {
    705     OnReceivedResponse(info);
    706     if (!data.empty())
    707       OnReceivedData(data.data(), data.size(), 0);
    708   }
    709 
    710   OnCompletedRequest(status, info.security_info, base::Time::Now());
    711 }
    712 
    713 // WebURLLoaderImpl -----------------------------------------------------------
    714 
    715 WebURLLoaderImpl::WebURLLoaderImpl()
    716     : ALLOW_THIS_IN_INITIALIZER_LIST(context_(new Context(this))) {
    717 }
    718 
    719 WebURLLoaderImpl::~WebURLLoaderImpl() {
    720   cancel();
    721 }
    722 
    723 void WebURLLoaderImpl::loadSynchronously(const WebURLRequest& request,
    724                                          WebURLResponse& response,
    725                                          WebURLError& error,
    726                                          WebData& data) {
    727   ResourceLoaderBridge::SyncLoadResponse sync_load_response;
    728   context_->Start(request, &sync_load_response);
    729 
    730   const GURL& final_url = sync_load_response.url;
    731 
    732   // TODO(tc): For file loads, we may want to include a more descriptive
    733   // status code or status text.
    734   const net::URLRequestStatus::Status& status =
    735       sync_load_response.status.status();
    736   if (status != net::URLRequestStatus::SUCCESS &&
    737       status != net::URLRequestStatus::HANDLED_EXTERNALLY) {
    738     response.setURL(final_url);
    739     error.domain = WebString::fromUTF8(net::kErrorDomain);
    740     error.reason = sync_load_response.status.os_error();
    741     error.unreachableURL = final_url;
    742     return;
    743   }
    744 
    745   PopulateURLResponse(final_url, sync_load_response, &response);
    746 
    747   data.assign(sync_load_response.data.data(),
    748               sync_load_response.data.size());
    749 }
    750 
    751 void WebURLLoaderImpl::loadAsynchronously(const WebURLRequest& request,
    752                                           WebURLLoaderClient* client) {
    753   DCHECK(!context_->client());
    754 
    755   context_->set_client(client);
    756   context_->Start(request, NULL);
    757 }
    758 
    759 void WebURLLoaderImpl::cancel() {
    760   context_->Cancel();
    761 }
    762 
    763 void WebURLLoaderImpl::setDefersLoading(bool value) {
    764   context_->SetDefersLoading(value);
    765 }
    766 
    767 }  // namespace webkit_glue
    768