// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "sky/services/platform/weburlloader_impl.h" #include "base/bind.h" #include "base/logging.h" #include "base/strings/string_util.h" #include "base/thread_task_runner_handle.h" #include "mojo/common/common_type_converters.h" #include "mojo/services/network/public/interfaces/network_service.mojom.h" #include "sky/engine/public/platform/WebURLError.h" #include "sky/engine/public/platform/WebURLLoadTiming.h" #include "sky/engine/public/platform/WebURLLoaderClient.h" #include "sky/engine/public/platform/WebURLResponse.h" #include "sky/services/platform/net_constants.h" #include "sky/services/platform/url_request_types.h" namespace sky { namespace { static blink::WebURLResponse::HTTPVersion StatusLineToHTTPVersion( const mojo::String& status_line) { if (status_line.is_null()) return blink::WebURLResponse::HTTP_0_9; if (StartsWithASCII(status_line, "HTTP/1.0", true)) return blink::WebURLResponse::HTTP_1_0; if (StartsWithASCII(status_line, "HTTP/1.1", true)) return blink::WebURLResponse::HTTP_1_1; return blink::WebURLResponse::Unknown; } blink::WebURLResponse ToWebURLResponse(const mojo::URLResponsePtr& url_response) { blink::WebURLResponse result; result.initialize(); result.setURL(GURL(url_response->url)); result.setMIMEType(blink::WebString::fromUTF8(url_response->mime_type)); result.setTextEncodingName(blink::WebString::fromUTF8(url_response->charset)); result.setHTTPVersion(StatusLineToHTTPVersion(url_response->status_line)); result.setHTTPStatusCode(url_response->status_code); // TODO(darin): Initialize timing properly. blink::WebURLLoadTiming timing; timing.initialize(); result.setLoadTiming(timing); for (size_t i = 0; i < url_response->headers.size(); ++i) { const auto& header = url_response->headers[i]; result.setHTTPHeaderField(blink::WebString::fromUTF8(header->name), blink::WebString::fromUTF8(header->value)); } return result; } } // namespace WebURLLoaderImpl::WebURLLoaderImpl(mojo::NetworkService* network_service) : client_(NULL), weak_factory_(this) { network_service->CreateURLLoader(GetProxy(&url_loader_)); } WebURLLoaderImpl::~WebURLLoaderImpl() { } void WebURLLoaderImpl::loadAsynchronously(const blink::WebURLRequest& request, blink::WebURLLoaderClient* client) { client_ = client; url_ = request.url(); mojo::URLRequestPtr url_request = mojo::URLRequest::From(request); url_request->auto_follow_redirects = false; url_loader_->Start(url_request.Pass(), base::Bind(&WebURLLoaderImpl::OnReceivedResponse, weak_factory_.GetWeakPtr())); } void WebURLLoaderImpl::cancel() { url_loader_.reset(); response_body_stream_.reset(); mojo::URLResponsePtr failed_response(mojo::URLResponse::New()); failed_response->url = mojo::String::From(url_); failed_response->error = mojo::NetworkError::New(); failed_response->error->code = kNetErrorAborted; base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&WebURLLoaderImpl::OnReceivedResponse, weak_factory_.GetWeakPtr(), base::Passed(&failed_response))); } void WebURLLoaderImpl::OnReceivedResponse(mojo::URLResponsePtr url_response) { url_ = GURL(url_response->url); if (url_response->error) { OnReceivedError(url_response.Pass()); } else if (url_response->redirect_url) { OnReceivedRedirect(url_response.Pass()); } else { base::WeakPtr self(weak_factory_.GetWeakPtr()); client_->didReceiveResponse(this, ToWebURLResponse(url_response)); // We may have been deleted during didReceiveResponse. if (!self) return; // Start streaming data response_body_stream_ = url_response->body.Pass(); ReadMore(); } } void WebURLLoaderImpl::OnReceivedError(mojo::URLResponsePtr url_response) { blink::WebURLError web_error; web_error.domain = blink::WebString::fromUTF8(kNetErrorDomain); web_error.reason = url_response->error->code; web_error.unreachableURL = GURL(url_response->url); web_error.staleCopyInCache = false; web_error.isCancellation = url_response->error->code == kNetErrorAborted ? true : false; client_->didFail(this, web_error); } void WebURLLoaderImpl::OnReceivedRedirect(mojo::URLResponsePtr url_response) { blink::WebURLRequest new_request; new_request.initialize(); new_request.setURL(GURL(url_response->redirect_url)); new_request.setHTTPMethod( blink::WebString::fromUTF8(url_response->redirect_method)); client_->willSendRequest(this, new_request, ToWebURLResponse(url_response)); // TODO(darin): Check if new_request was rejected. url_loader_->FollowRedirect( base::Bind(&WebURLLoaderImpl::OnReceivedResponse, weak_factory_.GetWeakPtr())); } void WebURLLoaderImpl::ReadMore() { const void* buf; uint32_t buf_size; MojoResult rv = mojo::BeginReadDataRaw(response_body_stream_.get(), &buf, &buf_size, MOJO_READ_DATA_FLAG_NONE); if (rv == MOJO_RESULT_OK) { client_->didReceiveData(this, static_cast(buf), buf_size, -1); EndReadDataRaw(response_body_stream_.get(), buf_size); WaitToReadMore(); } else if (rv == MOJO_RESULT_SHOULD_WAIT) { WaitToReadMore(); } else if (rv == MOJO_RESULT_FAILED_PRECONDITION) { // We reached end-of-file. double finish_time = base::Time::Now().ToDoubleT(); client_->didFinishLoading( this, finish_time, blink::WebURLLoaderClient::kUnknownEncodedDataLength); } else { // TODO(darin): Oops! } } void WebURLLoaderImpl::WaitToReadMore() { handle_watcher_.Start( response_body_stream_.get(), MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, base::Bind(&WebURLLoaderImpl::OnResponseBodyStreamReady, weak_factory_.GetWeakPtr())); } void WebURLLoaderImpl::OnResponseBodyStreamReady(MojoResult result) { ReadMore(); } } // namespace sky