Upstream version 11.39.250.0
[platform/framework/web/crosswalk.git] / src / content / child / web_url_loader_impl.cc
index 138963b..696d8e6 100644 (file)
 #include "base/message_loop/message_loop.h"
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
-#include "content/child/child_thread.h"
 #include "content/child/ftp_directory_listing_response_delegate.h"
+#include "content/child/multipart_response_delegate.h"
 #include "content/child/request_extra_data.h"
 #include "content/child/request_info.h"
 #include "content/child/resource_dispatcher.h"
+#include "content/child/resource_loader_bridge.h"
 #include "content/child/sync_load_response.h"
+#include "content/child/web_url_request_util.h"
+#include "content/child/weburlresponse_extradata_impl.h"
 #include "content/common/resource_request_body.h"
 #include "content/public/child/request_peer.h"
 #include "net/base/data_url.h"
@@ -27,7 +30,8 @@
 #include "net/base/net_errors.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_util.h"
-#include "net/url_request/url_request.h"
+#include "net/url_request/redirect_info.h"
+#include "net/url_request/url_request_data_job.h"
 #include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h"
 #include "third_party/WebKit/public/platform/WebHTTPLoadInfo.h"
 #include "third_party/WebKit/public/platform/WebURL.h"
@@ -37,9 +41,6 @@
 #include "third_party/WebKit/public/platform/WebURLRequest.h"
 #include "third_party/WebKit/public/platform/WebURLResponse.h"
 #include "third_party/WebKit/public/web/WebSecurityPolicy.h"
-#include "webkit/child/multipart_response_delegate.h"
-#include "webkit/child/resource_loader_bridge.h"
-#include "webkit/child/weburlresponse_extradata_impl.h"
 
 using base::Time;
 using base::TimeTicks;
@@ -57,9 +58,6 @@ using blink::WebURLLoader;
 using blink::WebURLLoaderClient;
 using blink::WebURLRequest;
 using blink::WebURLResponse;
-using webkit_glue::MultipartResponseDelegate;
-using webkit_glue::ResourceLoaderBridge;
-using webkit_glue::WebURLResponseExtraDataImpl;
 
 namespace content {
 
@@ -73,7 +71,7 @@ const char kThrottledErrorDescription[] =
 
 class HeaderFlattener : public WebHTTPHeaderVisitor {
  public:
-  explicit HeaderFlattener() : has_accept_header_(false) {}
+  HeaderFlattener() : has_accept_header_(false) {}
 
   virtual void visitHeader(const WebString& name, const WebString& value) {
     // Headers are latin1.
@@ -110,35 +108,6 @@ class HeaderFlattener : public WebHTTPHeaderVisitor {
   bool has_accept_header_;
 };
 
-// Extracts the information from a data: url.
-bool GetInfoFromDataURL(const GURL& url,
-                        ResourceResponseInfo* info,
-                        std::string* data,
-                        int* error_code) {
-  std::string mime_type;
-  std::string charset;
-  if (net::DataURL::Parse(url, &mime_type, &charset, data)) {
-    *error_code = net::OK;
-    // Assure same time for all time fields of data: URLs.
-    Time now = Time::Now();
-    info->load_timing.request_start = TimeTicks::Now();
-    info->load_timing.request_start_time = now;
-    info->request_time = now;
-    info->response_time = now;
-    info->headers = NULL;
-    info->mime_type.swap(mime_type);
-    info->charset.swap(charset);
-    info->security_info.clear();
-    info->content_length = data->length();
-    info->encoded_data_length = 0;
-
-    return true;
-  }
-
-  *error_code = net::ERR_INVALID_URL;
-  return false;
-}
-
 typedef ResourceDevToolsInfo::HeadersVector HeadersVector;
 
 // Converts timing data from |load_timing| to the format used by WebKit.
@@ -199,17 +168,48 @@ net::RequestPriority ConvertWebKitPriorityToNetPriority(
   }
 }
 
+// Extracts info from a data scheme URL into |info| and |data|. Returns net::OK
+// if successful. Returns a net error code otherwise. Exported only for testing.
+int GetInfoFromDataURL(const GURL& url,
+                       ResourceResponseInfo* info,
+                       std::string* data) {
+  // Assure same time for all time fields of data: URLs.
+  Time now = Time::Now();
+  info->load_timing.request_start = TimeTicks::Now();
+  info->load_timing.request_start_time = now;
+  info->request_time = now;
+  info->response_time = now;
+
+  std::string mime_type;
+  std::string charset;
+  scoped_refptr<net::HttpResponseHeaders> headers(
+      new net::HttpResponseHeaders(std::string()));
+  int result = net::URLRequestDataJob::BuildResponse(
+      url, &mime_type, &charset, data, headers.get());
+  if (result != net::OK)
+    return result;
+
+  info->headers = headers;
+  info->mime_type.swap(mime_type);
+  info->charset.swap(charset);
+  info->security_info.clear();
+  info->content_length = data->length();
+  info->encoded_data_length = 0;
+
+  return net::OK;
+}
+
 }  // namespace
 
 // WebURLLoaderImpl::Context --------------------------------------------------
 
 // This inner class exists since the WebURLLoader may be deleted while inside a
-// call to WebURLLoaderClient.  The bridge requires its Peer to stay alive
-// until it receives OnCompletedRequest.
+// call to WebURLLoaderClient.  Refcounting is to keep the context from being
+// deleted if it may have work to do after calling into the client.
 class WebURLLoaderImpl::Context : public base::RefCounted<Context>,
                                   public RequestPeer {
  public:
-  explicit Context(WebURLLoaderImpl* loader);
+  Context(WebURLLoaderImpl* loader, ResourceDispatcher* resource_dispatcher);
 
   WebURLLoaderClient* client() const { return client_; }
   void set_client(WebURLLoaderClient* client) { client_ = client; }
@@ -225,8 +225,7 @@ class WebURLLoaderImpl::Context : public base::RefCounted<Context>,
 
   // RequestPeer methods:
   virtual void OnUploadProgress(uint64 position, uint64 size) OVERRIDE;
-  virtual bool OnReceivedRedirect(const GURL& new_url,
-                                  const GURL& new_first_party_for_cookies,
+  virtual bool OnReceivedRedirect(const net::RedirectInfo& redirect_info,
                                   const ResourceResponseInfo& info) OVERRIDE;
   virtual void OnReceivedResponse(const ResourceResponseInfo& info) OVERRIDE;
   virtual void OnDownloadedData(int len, int encoded_data_length) OVERRIDE;
@@ -247,12 +246,13 @@ class WebURLLoaderImpl::Context : public base::RefCounted<Context>,
   virtual ~Context() {}
 
   // We can optimize the handling of data URLs in most cases.
-  bool CanHandleDataURL(const GURL& url) const;
+  bool CanHandleDataURLRequestLocally() const;
   void HandleDataURL();
 
   WebURLLoaderImpl* loader_;
   WebURLRequest request_;
   WebURLLoaderClient* client_;
+  ResourceDispatcher* resource_dispatcher_;
   WebReferrerPolicy referrer_policy_;
   scoped_ptr<ResourceLoaderBridge> bridge_;
   scoped_ptr<FtpDirectoryListingResponseDelegate> ftp_listing_delegate_;
@@ -260,17 +260,19 @@ class WebURLLoaderImpl::Context : public base::RefCounted<Context>,
   scoped_ptr<ResourceLoaderBridge> completed_bridge_;
 };
 
-WebURLLoaderImpl::Context::Context(WebURLLoaderImpl* loader)
+WebURLLoaderImpl::Context::Context(WebURLLoaderImpl* loader,
+                                   ResourceDispatcher* resource_dispatcher)
     : loader_(loader),
       client_(NULL),
+      resource_dispatcher_(resource_dispatcher),
       referrer_policy_(blink::WebReferrerPolicyDefault) {
 }
 
 void WebURLLoaderImpl::Context::Cancel() {
-  // The bridge will still send OnCompletedRequest, which will Release() us, so
-  // we don't do that here.
-  if (bridge_)
+  if (bridge_) {
     bridge_->Cancel();
+    bridge_.reset();
+  }
 
   // Ensure that we do not notify the multipart delegate anymore as it has
   // its own pointer to the client.
@@ -312,16 +314,14 @@ void WebURLLoaderImpl::Context::Start(const WebURLRequest& request,
   request_ = request;  // Save the request.
 
   GURL url = request.url();
-  if (url.SchemeIs("data") && CanHandleDataURL(url)) {
+  if (CanHandleDataURLRequestLocally()) {
     if (sync_load_response) {
       // This is a sync load. Do the work now.
       sync_load_response->url = url;
-      std::string data;
-      GetInfoFromDataURL(sync_load_response->url, sync_load_response,
-                         &sync_load_response->data,
-                         &sync_load_response->error_code);
+      sync_load_response->error_code =
+          GetInfoFromDataURL(sync_load_response->url, sync_load_response,
+                             &sync_load_response->data);
     } else {
-      AddRef();  // Balanced in OnCompletedRequest
       base::MessageLoop::current()->PostTask(
           FROM_HERE, base::Bind(&Context::HandleDataURL, this));
     }
@@ -332,7 +332,7 @@ void WebURLLoaderImpl::Context::Start(const WebURLRequest& request,
       request.httpHeaderField(WebString::fromUTF8("Referer")).latin1());
   const std::string& method = request.httpMethod().latin1();
 
-  int load_flags = net::LOAD_NORMAL | net::LOAD_ENABLE_LOAD_TIMING;
+  int load_flags = net::LOAD_NORMAL;
   switch (request.cachePolicy()) {
     case WebURLRequest::ReloadIgnoringCacheData:
       // Required by LayoutTests/http/tests/misc/refresh-headers.php
@@ -366,7 +366,7 @@ void WebURLLoaderImpl::Context::Start(const WebURLRequest& request,
   if (!request.allowStoredCredentials())
     load_flags |= net::LOAD_DO_NOT_SEND_AUTH_DATA;
 
-  if (request.targetType() == WebURLRequest::TargetIsXHR &&
+  if (request.requestContext() == WebURLRequest::RequestContextXMLHttpRequest &&
       (url.has_username() || url.has_password())) {
     load_flags |= net::LOAD_DO_NOT_PROMPT_FOR_LOGIN;
   }
@@ -384,23 +384,23 @@ void WebURLLoaderImpl::Context::Start(const WebURLRequest& request,
   request_info.referrer = referrer_url;
   request_info.headers = flattener.GetBuffer();
   request_info.load_flags = load_flags;
+  request_info.enable_load_timing = true;
   // requestor_pid only needs to be non-zero if the request originates outside
   // the render process, so we can use requestorProcessID even for requests
   // from in-process plugins.
   request_info.requestor_pid = request.requestorProcessID();
-  request_info.request_type =
-      ResourceType::FromTargetType(request.targetType());
+  request_info.request_type = WebURLRequestToResourceType(request);
   request_info.priority =
       ConvertWebKitPriorityToNetPriority(request.priority());
   request_info.appcache_host_id = request.appCacheHostID();
   request_info.routing_id = request.requestorID();
   request_info.download_to_file = request.downloadToFile();
   request_info.has_user_gesture = request.hasUserGesture();
+  request_info.skip_service_worker = request.skipServiceWorker();
   request_info.extra_data = request.extraData();
   referrer_policy_ = request.referrerPolicy();
   request_info.referrer_policy = request.referrerPolicy();
-  bridge_.reset(ChildThread::current()->resource_dispatcher()->CreateBridge(
-      request_info));
+  bridge_.reset(resource_dispatcher_->CreateBridge(request_info));
 
   if (!request.httpBody().isNull()) {
     // GET and HEAD requests shouldn't have http bodies.
@@ -458,11 +458,11 @@ void WebURLLoaderImpl::Context::Start(const WebURLRequest& request,
     return;
   }
 
-  if (bridge_->Start(this)) {
-    AddRef();  // Balanced in OnCompletedRequest
-  } else {
+  // TODO(mmenke):  This case probably never happens, anyways.  Probably should
+  // not handle this case at all.  If it's worth handling, this code currently
+  // results in the request just hanging, which should be fixed.
+  if (!bridge_->Start(this))
     bridge_.reset();
-  }
 }
 
 void WebURLLoaderImpl::Context::OnUploadProgress(uint64 position, uint64 size) {
@@ -471,8 +471,7 @@ void WebURLLoaderImpl::Context::OnUploadProgress(uint64 position, uint64 size) {
 }
 
 bool WebURLLoaderImpl::Context::OnReceivedRedirect(
-    const GURL& new_url,
-    const GURL& new_first_party_for_cookies,
+    const net::RedirectInfo& redirect_info,
     const ResourceResponseInfo& info) {
   if (!client_)
     return false;
@@ -483,34 +482,33 @@ bool WebURLLoaderImpl::Context::OnReceivedRedirect(
 
   // TODO(darin): We lack sufficient information to construct the actual
   // request that resulted from the redirect.
-  WebURLRequest new_request(new_url);
-  new_request.setFirstPartyForCookies(new_first_party_for_cookies);
+  WebURLRequest new_request(redirect_info.new_url);
+  new_request.setFirstPartyForCookies(
+      redirect_info.new_first_party_for_cookies);
   new_request.setDownloadToFile(request_.downloadToFile());
+  new_request.setRequestContext(request_.requestContext());
+  new_request.setFrameType(request_.frameType());
 
-  WebString referrer_string = WebString::fromUTF8("Referer");
-  WebString referrer = WebSecurityPolicy::generateReferrerHeader(
-      referrer_policy_,
-      new_url,
-      request_.httpHeaderField(referrer_string));
-  if (!referrer.isEmpty())
-    new_request.setHTTPReferrer(referrer, referrer_policy_);
-
-  std::string method = request_.httpMethod().utf8();
-  std::string new_method = net::URLRequest::ComputeMethodForRedirect(
-      method, response.httpStatusCode());
-  new_request.setHTTPMethod(WebString::fromUTF8(new_method));
-  if (new_method == method)
+  new_request.setHTTPReferrer(WebString::fromUTF8(redirect_info.new_referrer),
+                              referrer_policy_);
+
+  std::string old_method = request_.httpMethod().utf8();
+  new_request.setHTTPMethod(WebString::fromUTF8(redirect_info.new_method));
+  if (redirect_info.new_method == old_method)
     new_request.setHTTPBody(request_.httpBody());
 
+  // Protect from deletion during call to willSendRequest.
+  scoped_refptr<Context> protect(this);
+
   client_->willSendRequest(loader_, new_request, response);
   request_ = new_request;
 
   // Only follow the redirect if WebKit left the URL unmodified.
-  if (new_url == GURL(new_request.url())) {
+  if (redirect_info.new_url == GURL(new_request.url())) {
     // First-party cookie logic moved from DocumentLoader in Blink to
-    // CrossSiteResourceHandler in the browser. Assert that Blink didn't try to
-    // change it to something else.
-    DCHECK_EQ(new_first_party_for_cookies.spec(),
+    // net::URLRequest in the browser. Assert that Blink didn't try to change it
+    // to something else.
+    DCHECK_EQ(redirect_info.new_first_party_for_cookies.spec(),
               request_.firstPartyForCookies().string().utf8());
     return true;
   }
@@ -528,6 +526,12 @@ void WebURLLoaderImpl::Context::OnReceivedResponse(
 
   WebURLResponse response;
   response.initialize();
+  // Updates the request url if the response was fetched by a ServiceWorker,
+  // and it was not generated inside the ServiceWorker.
+  if (info.was_fetched_via_service_worker &&
+      !info.original_url_via_service_worker.is_empty()) {
+    request_.setURL(info.original_url_via_service_worker);
+  }
   PopulateURLResponse(request_.url(), info, &response);
 
   bool show_raw_listing = (GURL(request_.url()).query() == "raw");
@@ -542,6 +546,9 @@ void WebURLLoaderImpl::Context::OnReceivedResponse(
     }
   }
 
+  // Prevent |this| from being destroyed if the client destroys the loader,
+  // ether in didReceiveResponse, or when the multipart/ftp delegate calls into
+  // it.
   scoped_refptr<Context> protect(this);
   client_->didReceiveResponse(loader_, response);
 
@@ -591,11 +598,17 @@ void WebURLLoaderImpl::Context::OnReceivedData(const char* data,
 
   if (ftp_listing_delegate_) {
     // The FTP listing delegate will make the appropriate calls to
-    // client_->didReceiveData and client_->didReceiveResponse.
+    // client_->didReceiveData and client_->didReceiveResponse.  Since the
+    // delegate may want to do work after sending data to the delegate, keep
+    // |this| and the delegate alive until it's finished handling the data.
+    scoped_refptr<Context> protect(this);
     ftp_listing_delegate_->OnReceivedData(data, data_length);
   } else if (multipart_delegate_) {
     // The multipart delegate will make the appropriate calls to
-    // client_->didReceiveData and client_->didReceiveResponse.
+    // client_->didReceiveData and client_->didReceiveResponse.  Since the
+    // delegate may want to do work after sending data to the delegate, keep
+    // |this| and the delegate alive until it's finished handling the data.
+    scoped_refptr<Context> protect(this);
     multipart_delegate_->OnReceivedData(data, data_length, encoded_data_length);
   } else {
     client_->didReceiveData(loader_, data, data_length, encoded_data_length);
@@ -615,6 +628,12 @@ void WebURLLoaderImpl::Context::OnCompletedRequest(
     const std::string& security_info,
     const base::TimeTicks& completion_time,
     int64 total_transfer_size) {
+  // The WebURLLoaderImpl may be deleted in any of the calls to the client or
+  // the delegates below (As they also may call in to the client).  Keep |this|
+  // alive in that case, to avoid a crash.  If that happens, the request will be
+  // cancelled and |client_| will be set to NULL.
+  scoped_refptr<Context> protect(this);
+
   if (ftp_listing_delegate_) {
     ftp_listing_delegate_->OnCompletedRequest();
     ftp_listing_delegate_.reset(NULL);
@@ -639,15 +658,17 @@ void WebURLLoaderImpl::Context::OnCompletedRequest(
           total_transfer_size);
     }
   }
-
-  // We are done with the bridge now, and so we need to release the reference
-  // to ourselves that we took on behalf of the bridge.  This may cause our
-  // destruction.
-  Release();
 }
 
-bool WebURLLoaderImpl::Context::CanHandleDataURL(const GURL& url) const {
-  DCHECK(url.SchemeIs("data"));
+bool WebURLLoaderImpl::Context::CanHandleDataURLRequestLocally() const {
+  GURL url = request_.url();
+  if (!url.SchemeIs(url::kDataScheme))
+    return false;
+
+  // The fast paths for data URL, Start() and HandleDataURL(), don't support
+  // the downloadToFile option.
+  if (request_.downloadToFile())
+    return false;
 
   // Optimize for the case where we can handle a data URL locally.  We must
   // skip this for data URLs targetted at frames since those could trigger a
@@ -660,16 +681,16 @@ bool WebURLLoaderImpl::Context::CanHandleDataURL(const GURL& url) const {
 #if defined(OS_ANDROID)
   // For compatibility reasons on Android we need to expose top-level data://
   // to the browser.
-  if (request_.targetType() == WebURLRequest::TargetIsMainFrame)
+  if (request_.frameType() == WebURLRequest::FrameTypeTopLevel)
     return false;
 #endif
 
-  if (request_.targetType() != WebURLRequest::TargetIsMainFrame &&
-      request_.targetType() != WebURLRequest::TargetIsSubframe)
+  if (request_.frameType() != WebURLRequest::FrameTypeTopLevel &&
+      request_.frameType() != WebURLRequest::FrameTypeNested)
     return true;
 
   std::string mime_type, unused_charset;
-  if (net::DataURL::Parse(url, &mime_type, &unused_charset, NULL) &&
+  if (net::DataURL::Parse(request_.url(), &mime_type, &unused_charset, NULL) &&
       net::IsSupportedMimeType(mime_type))
     return true;
 
@@ -678,10 +699,11 @@ bool WebURLLoaderImpl::Context::CanHandleDataURL(const GURL& url) const {
 
 void WebURLLoaderImpl::Context::HandleDataURL() {
   ResourceResponseInfo info;
-  int error_code;
   std::string data;
 
-  if (GetInfoFromDataURL(request_.url(), &info, &data, &error_code)) {
+  int error_code = GetInfoFromDataURL(request_.url(), &info, &data);
+
+  if (error_code == net::OK) {
     OnReceivedResponse(info);
     if (!data.empty())
       OnReceivedData(data.data(), data.size(), 0);
@@ -693,8 +715,8 @@ void WebURLLoaderImpl::Context::HandleDataURL() {
 
 // WebURLLoaderImpl -----------------------------------------------------------
 
-WebURLLoaderImpl::WebURLLoaderImpl()
-    : context_(new Context(this)) {
+WebURLLoaderImpl::WebURLLoaderImpl(ResourceDispatcher* resource_dispatcher)
+    : context_(new Context(this, resource_dispatcher)) {
 }
 
 WebURLLoaderImpl::~WebURLLoaderImpl() {
@@ -740,6 +762,7 @@ void WebURLLoaderImpl::PopulateURLResponse(const GURL& url,
   response->setConnectionID(info.load_timing.socket_log_id);
   response->setConnectionReused(info.load_timing.socket_reused);
   response->setDownloadFilePath(info.download_file_path.AsUTF16Unsafe());
+  response->setWasFetchedViaServiceWorker(info.was_fetched_via_service_worker);
   WebURLResponseExtraDataImpl* extra_data =
       new WebURLResponseExtraDataImpl(info.npn_negotiated_protocol);
   response->setExtraData(extra_data);
@@ -756,6 +779,13 @@ void WebURLLoaderImpl::PopulateURLResponse(const GURL& url,
   if (!info.load_timing.receive_headers_end.is_null()) {
     WebURLLoadTiming timing;
     PopulateURLLoadTiming(info.load_timing, &timing);
+    const TimeTicks kNullTicks;
+    timing.setServiceWorkerFetchStart(
+        (info.service_worker_fetch_start - kNullTicks).InSecondsF());
+    timing.setServiceWorkerFetchReady(
+        (info.service_worker_fetch_ready - kNullTicks).InSecondsF());
+    timing.setServiceWorkerFetchEnd(
+        (info.service_worker_fetch_end - kNullTicks).InSecondsF());
     response->setLoadTiming(timing);
   }