blob: 7d2cd4bb50c8b173f0c6e6b23b3307c522b7ace0 [file] [log] [blame]
// Copyright 2016 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 "third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.h"
#include <stdint.h>
#include <memory>
#include <string>
#include <utility>
#include "base/files/file_util.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "third_party/blink/public/platform/file_path_conversion.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_url_error.h"
#include "third_party/blink/public/platform/web_url_request.h"
#include "third_party/blink/public/platform/web_url_response.h"
#include "third_party/blink/public/web/web_navigation_params.h"
#include "third_party/blink/renderer/platform/exported/wrapped_resource_response.h"
#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
#include "third_party/blink/renderer/platform/loader/static_data_navigation_body_loader.h"
#include "third_party/blink/renderer/platform/network/network_utils.h"
#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
#include "third_party/blink/renderer/platform/testing/weburl_loader_mock.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
namespace blink {
// static
WebURLLoaderMockFactory* WebURLLoaderMockFactory::GetSingletonInstance() {
DEFINE_STATIC_LOCAL(WebURLLoaderMockFactoryImpl, s_singleton, (nullptr));
return &s_singleton;
}
WebURLLoaderMockFactoryImpl::WebURLLoaderMockFactoryImpl(
TestingPlatformSupport* platform)
: platform_(platform) {}
WebURLLoaderMockFactoryImpl::~WebURLLoaderMockFactoryImpl() = default;
std::unique_ptr<WebURLLoader> WebURLLoaderMockFactoryImpl::CreateURLLoader() {
return std::make_unique<WebURLLoaderMock>(this);
}
void WebURLLoaderMockFactoryImpl::RegisterURL(const WebURL& url,
const WebURLResponse& response,
const WebString& file_path) {
ResponseInfo response_info;
response_info.response = response;
if (!file_path.IsNull() && !file_path.IsEmpty()) {
response_info.file_path = blink::WebStringToFilePath(file_path);
DCHECK(base::PathExists(response_info.file_path))
<< response_info.file_path.MaybeAsASCII() << " does not exist.";
}
DCHECK(url_to_response_info_.find(url) == url_to_response_info_.end());
url_to_response_info_.Set(url, response_info);
}
void WebURLLoaderMockFactoryImpl::RegisterErrorURL(
const WebURL& url,
const WebURLResponse& response,
const WebURLError& error) {
DCHECK(url_to_response_info_.find(url) == url_to_response_info_.end());
RegisterURL(url, response, WebString());
url_to_error_info_.Set(url, error);
}
void WebURLLoaderMockFactoryImpl::UnregisterURL(const blink::WebURL& url) {
URLToResponseMap::iterator iter = url_to_response_info_.find(url);
DCHECK(iter != url_to_response_info_.end());
url_to_response_info_.erase(iter);
URLToErrorMap::iterator error_iter = url_to_error_info_.find(url);
if (error_iter != url_to_error_info_.end())
url_to_error_info_.erase(error_iter);
}
void WebURLLoaderMockFactoryImpl::RegisterURLProtocol(
const WebString& protocol,
const WebURLResponse& response,
const WebString& file_path) {
DCHECK(protocol.ContainsOnlyASCII());
ResponseInfo response_info;
response_info.response = response;
if (!file_path.IsNull() && !file_path.IsEmpty()) {
response_info.file_path = blink::WebStringToFilePath(file_path);
DCHECK(base::PathExists(response_info.file_path))
<< response_info.file_path.MaybeAsASCII() << " does not exist.";
}
DCHECK(protocol_to_response_info_.find(protocol) ==
protocol_to_response_info_.end());
protocol_to_response_info_.Set(protocol, response_info);
}
void WebURLLoaderMockFactoryImpl::UnregisterURLProtocol(
const WebString& protocol) {
ProtocolToResponseMap::iterator iter =
protocol_to_response_info_.find(protocol);
DCHECK(iter != protocol_to_response_info_.end());
protocol_to_response_info_.erase(iter);
}
void WebURLLoaderMockFactoryImpl::UnregisterAllURLsAndClearMemoryCache() {
url_to_response_info_.clear();
url_to_error_info_.clear();
protocol_to_response_info_.clear();
if (IsMainThread())
GetMemoryCache()->EvictResources();
}
void WebURLLoaderMockFactoryImpl::ServeAsynchronousRequests() {
// Serving a request might trigger more requests, so we cannot iterate on
// pending_loaders_ as it might get modified.
while (!pending_loaders_.IsEmpty()) {
LoaderToRequestMap::iterator iter = pending_loaders_.begin();
base::WeakPtr<WebURLLoaderMock> loader(iter->key->GetWeakPtr());
std::unique_ptr<network::ResourceRequest> request = std::move(iter->value);
pending_loaders_.erase(loader.get());
WebURLResponse response;
base::Optional<WebURLError> error;
WebData data;
LoadRequest(WebURL(KURL(request->url)), &response, &error, &data);
// Follow any redirects while the loader is still active.
while (response.HttpStatusCode() >= 300 &&
response.HttpStatusCode() < 400) {
WebURL new_url = loader->ServeRedirect(
WebString::FromLatin1(request->method), response);
RunUntilIdle();
if (!loader || loader->is_cancelled() || loader->is_deferred())
break;
LoadRequest(new_url, &response, &error, &data);
}
// Serve the request if the loader is still active.
if (loader && !loader->is_cancelled() && !loader->is_deferred()) {
loader->ServeAsynchronousRequest(delegate_, response, data, error);
RunUntilIdle();
}
}
RunUntilIdle();
}
void WebURLLoaderMockFactoryImpl::FillNavigationParamsResponse(
WebNavigationParams* params) {
KURL kurl = params->url;
if (kurl.ProtocolIsData()) {
ResourceResponse response;
scoped_refptr<SharedBuffer> buffer;
int result;
std::tie(result, response, buffer) =
network_utils::ParseDataURL(kurl, params->http_method);
DCHECK(buffer);
DCHECK_EQ(net::OK, result);
params->response = WrappedResourceResponse(response);
auto body_loader = std::make_unique<StaticDataNavigationBodyLoader>();
body_loader->Write(*buffer);
body_loader->Finish();
params->body_loader = std::move(body_loader);
return;
}
if (delegate_ && delegate_->FillNavigationParamsResponse(params))
return;
base::Optional<WebURLError> error;
WebData data;
size_t redirects = 0;
LoadRequest(params->url, &params->response, &error, &data);
DCHECK(!error);
while (params->response.HttpStatusCode() >= 300 &&
params->response.HttpStatusCode() < 400) {
WebURL new_url(KURL(params->response.HttpHeaderField("Location")));
++redirects;
params->redirects.reserve(redirects);
params->redirects.resize(redirects);
params->redirects[redirects - 1].redirect_response = params->response;
params->redirects[redirects - 1].new_url = new_url;
params->redirects[redirects - 1].new_http_method = "GET";
LoadRequest(new_url, &params->response, &error, &data);
DCHECK(!error);
}
auto body_loader = std::make_unique<StaticDataNavigationBodyLoader>();
if (!data.IsNull()) {
scoped_refptr<SharedBuffer> buffer = data;
body_loader->Write(*buffer);
body_loader->Finish();
}
params->body_loader = std::move(body_loader);
}
bool WebURLLoaderMockFactoryImpl::IsMockedURL(const blink::WebURL& url) {
base::Optional<WebURLError> error;
ResponseInfo response_info;
return LookupURL(url, &error, &response_info);
}
void WebURLLoaderMockFactoryImpl::CancelLoad(WebURLLoaderMock* loader) {
pending_loaders_.erase(loader);
}
void WebURLLoaderMockFactoryImpl::LoadSynchronously(
std::unique_ptr<network::ResourceRequest> request,
WebURLResponse* response,
base::Optional<WebURLError>* error,
WebData* data,
int64_t* encoded_data_length) {
LoadRequest(WebURL(KURL(request->url)), response, error, data);
*encoded_data_length = data->size();
}
void WebURLLoaderMockFactoryImpl::LoadAsynchronouly(
std::unique_ptr<network::ResourceRequest> request,
WebURLLoaderMock* loader) {
DCHECK(!pending_loaders_.Contains(loader));
pending_loaders_.Set(loader, std::move(request));
}
void WebURLLoaderMockFactoryImpl::RunUntilIdle() {
if (platform_) {
platform_->RunUntilIdle();
} else {
base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed).RunUntilIdle();
}
}
void WebURLLoaderMockFactoryImpl::LoadRequest(
const WebURL& url,
WebURLResponse* response,
base::Optional<WebURLError>* error,
WebData* data) {
ResponseInfo response_info;
if (!LookupURL(url, error, &response_info)) {
// Non mocked URLs should not have been passed to the default URLLoader.
NOTREACHED();
return;
}
if (!*error && !ReadFile(response_info.file_path, data)) {
NOTREACHED();
return;
}
*response = response_info.response;
}
bool WebURLLoaderMockFactoryImpl::LookupURL(const WebURL& url,
base::Optional<WebURLError>* error,
ResponseInfo* response_info) {
URLToErrorMap::const_iterator error_iter = url_to_error_info_.find(url);
if (error_iter != url_to_error_info_.end())
*error = error_iter->value;
URLToResponseMap::const_iterator iter = url_to_response_info_.find(url);
if (iter != url_to_response_info_.end()) {
*response_info = iter->value;
return true;
}
for (const auto& key_value_pair : protocol_to_response_info_) {
String protocol = key_value_pair.key;
if (url.ProtocolIs(protocol.Ascii().c_str())) {
*response_info = key_value_pair.value;
return true;
}
}
return false;
}
// static
bool WebURLLoaderMockFactoryImpl::ReadFile(const base::FilePath& file_path,
WebData* data) {
// If the path is empty then we return an empty file so tests can simulate
// requests without needing to actually load files.
if (file_path.empty())
return true;
std::string buffer;
if (!base::ReadFileToString(file_path, &buffer))
return false;
data->Assign(buffer.data(), buffer.size());
return true;
}
} // namespace blink