blob: 1a82d44aade7d4c2648d9f8a370cbee97fc4163c [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/loader/reconnectable_url_loader_factory.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/run_loop.h"
#include "content/browser/loader/url_loader_factory_utils.h"
#include "services/network/public/cpp/cross_thread_pending_shared_url_loader_factory.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace content {
ReconnectableURLLoaderFactory::ReconnectableURLLoaderFactory(
CreateCallback create_url_loader_factory_callback)
: create_url_loader_factory_callback_(
std::move(create_url_loader_factory_callback)) {}
ReconnectableURLLoaderFactory::~ReconnectableURLLoaderFactory() = default;
void ReconnectableURLLoaderFactory::CreateLoaderAndStart(
mojo::PendingReceiver<network::mojom::URLLoader> receiver,
int32_t request_id,
uint32_t options,
const network::ResourceRequest& url_request,
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
if (network::mojom::URLLoaderFactory* factory = GetURLLoaderFactory()) {
factory->CreateLoaderAndStart(std::move(receiver), request_id, options,
url_request, std::move(client),
traffic_annotation);
}
}
void ReconnectableURLLoaderFactory::Clone(
mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) {
if (network::mojom::URLLoaderFactory* factory = GetURLLoaderFactory()) {
factory->Clone(std::move(receiver));
}
}
std::unique_ptr<network::PendingSharedURLLoaderFactory>
ReconnectableURLLoaderFactory::Clone() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return std::make_unique<network::CrossThreadPendingSharedURLLoaderFactory>(
this);
}
network::mojom::URLLoaderFactory*
ReconnectableURLLoaderFactory::GetURLLoaderFactory() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Create the URLLoaderFactory as needed, but make sure not to reuse a
// previously created one if the test override has changed.
if (url_loader_factory_ && url_loader_factory_.is_connected() &&
is_test_url_loader_factory_ ==
!!url_loader_factory::GetTestingInterceptor()) {
return url_loader_factory_.get();
}
is_test_url_loader_factory_ = !!url_loader_factory::GetTestingInterceptor();
url_loader_factory_.reset();
mojo::PendingRemote<network::mojom::URLLoaderFactory> url_loader_factory;
create_url_loader_factory_callback_.Run(&url_loader_factory);
if (!url_loader_factory) {
return nullptr;
}
url_loader_factory_.Bind(std::move(url_loader_factory));
return url_loader_factory_.get();
}
void ReconnectableURLLoaderFactory::Reset() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
url_loader_factory_.reset();
}
void ReconnectableURLLoaderFactory::FlushForTesting() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (url_loader_factory_) {
url_loader_factory_.FlushForTesting(); // IN-TEST
}
}
// -----------------------------------------------------------------------------
class ReconnectableURLLoaderFactoryForIOThread::
PendingURLLoaderFactoryForIOThread final
: public network::PendingSharedURLLoaderFactory {
public:
explicit PendingURLLoaderFactoryForIOThread(
scoped_refptr<ReconnectableURLLoaderFactoryForIOThread> factory_getter)
: factory_getter_(std::move(factory_getter)) {
CHECK(factory_getter_);
}
PendingURLLoaderFactoryForIOThread(
const PendingURLLoaderFactoryForIOThread&) = delete;
PendingURLLoaderFactoryForIOThread& operator=(
const PendingURLLoaderFactoryForIOThread&) = delete;
~PendingURLLoaderFactoryForIOThread() override = default;
protected:
// PendingSharedURLLoaderFactory implementation.
scoped_refptr<network::SharedURLLoaderFactory> CreateFactory() override;
const scoped_refptr<ReconnectableURLLoaderFactoryForIOThread> factory_getter_;
};
class ReconnectableURLLoaderFactoryForIOThread::URLLoaderFactoryForIOThread
final : public network::SharedURLLoaderFactory {
public:
explicit URLLoaderFactoryForIOThread(
scoped_refptr<ReconnectableURLLoaderFactoryForIOThread> factory_getter)
: factory_getter_(std::move(factory_getter)) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
CHECK(factory_getter_);
}
URLLoaderFactoryForIOThread(const URLLoaderFactoryForIOThread&) = delete;
URLLoaderFactoryForIOThread& operator=(const URLLoaderFactoryForIOThread&) =
delete;
// mojom::URLLoaderFactory implementation:
void CreateLoaderAndStart(
mojo::PendingReceiver<network::mojom::URLLoader> receiver,
int32_t request_id,
uint32_t options,
const network::ResourceRequest& url_request,
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
override {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
factory_getter_->GetURLLoaderFactory()->CreateLoaderAndStart(
std::move(receiver), request_id, options, url_request,
std::move(client), traffic_annotation);
}
void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)
override {
factory_getter_->GetURLLoaderFactory()->Clone(std::move(receiver));
}
// SharedURLLoaderFactory implementation:
std::unique_ptr<network::PendingSharedURLLoaderFactory> Clone() override {
NOTREACHED() << "This isn't supported. If you need a SharedURLLoaderFactory"
" on the UI thread, get it from StoragePartition.";
}
private:
friend class base::RefCounted<URLLoaderFactoryForIOThread>;
~URLLoaderFactoryForIOThread() override = default;
const scoped_refptr<ReconnectableURLLoaderFactoryForIOThread> factory_getter_;
};
scoped_refptr<network::SharedURLLoaderFactory>
ReconnectableURLLoaderFactoryForIOThread::PendingURLLoaderFactoryForIOThread::
CreateFactory() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
return base::MakeRefCounted<URLLoaderFactoryForIOThread>(
std::move(factory_getter_));
}
// -----------------------------------------------------------------------------
ReconnectableURLLoaderFactoryForIOThread::
ReconnectableURLLoaderFactoryForIOThread(
CreateCallback create_url_loader_factory_callback)
: create_url_loader_factory_callback_(
std::move(create_url_loader_factory_callback)) {}
void ReconnectableURLLoaderFactoryForIOThread::Initialize() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Create a mojo::PendingRemote<URLLoaderFactory> synchronously and push it to
// the IO thread. If the pipe errors out later due to a network service crash,
// the pipe is created on the IO thread, and the request send back to the UI
// thread.
// TODO(mmenke): Is one less thread hop on startup worth the extra complexity
// of two different pipe creation paths?
mojo::PendingRemote<network::mojom::URLLoaderFactory> network_factory;
HandleNetworkFactoryRequestOnUIThread(
network_factory.InitWithNewPipeAndPassReceiver());
GetIOThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(
&ReconnectableURLLoaderFactoryForIOThread::InitializeOnIOThread, this,
std::move(network_factory)));
}
std::unique_ptr<network::PendingSharedURLLoaderFactory>
ReconnectableURLLoaderFactoryForIOThread::CloneForIOThread() {
return std::make_unique<PendingURLLoaderFactoryForIOThread>(
base::WrapRefCounted(this));
}
network::mojom::URLLoaderFactory*
ReconnectableURLLoaderFactoryForIOThread::GetURLLoaderFactory() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (url_loader_factory_ && url_loader_factory_.is_connected() &&
is_test_url_loader_factory_ ==
url_loader_factory::HasInterceptorOnIOThreadForTesting()) {
return url_loader_factory_.get();
}
is_test_url_loader_factory_ =
url_loader_factory::HasInterceptorOnIOThreadForTesting();
mojo::Remote<network::mojom::URLLoaderFactory> network_factory;
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&ReconnectableURLLoaderFactoryForIOThread::
HandleNetworkFactoryRequestOnUIThread,
this, network_factory.BindNewPipeAndPassReceiver()));
ReinitializeOnIOThread(std::move(network_factory));
return url_loader_factory_.get();
}
void ReconnectableURLLoaderFactoryForIOThread::Reset() {
Initialize();
}
void ReconnectableURLLoaderFactoryForIOThread::FlushForTesting() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
base::RunLoop run_loop;
GetIOThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(
&ReconnectableURLLoaderFactoryForIOThread::FlushOnIOThreadForTesting,
this, run_loop.QuitClosure()));
run_loop.Run();
}
void ReconnectableURLLoaderFactoryForIOThread::FlushOnIOThreadForTesting(
base::OnceClosure callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (url_loader_factory_) {
url_loader_factory_.FlushAsyncForTesting(std::move(callback)); // IN-TEST
}
}
ReconnectableURLLoaderFactoryForIOThread::
~ReconnectableURLLoaderFactoryForIOThread() = default;
void ReconnectableURLLoaderFactoryForIOThread::InitializeOnIOThread(
mojo::PendingRemote<network::mojom::URLLoaderFactory> network_factory) {
ReinitializeOnIOThread(mojo::Remote<network::mojom::URLLoaderFactory>(
std::move(network_factory)));
}
void ReconnectableURLLoaderFactoryForIOThread::ReinitializeOnIOThread(
mojo::Remote<network::mojom::URLLoaderFactory> network_factory) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(network_factory.is_bound());
// Set a disconnect handler so that connection errors on the pipes are
// noticed, but the class doesn't actually do anything when the error is
// observed - instead, a new pipe is created in GetURLLoaderFactory() as
// needed. This is to avoid incrementing the reference count of |this| in the
// callback, as that could result in increasing the reference count from 0 to
// 1 while there's a pending task to delete |this|. See
// https://crbug.com/870942 for more details.
network_factory.set_disconnect_handler(base::DoNothing());
url_loader_factory_ = std::move(network_factory);
}
void ReconnectableURLLoaderFactoryForIOThread::
HandleNetworkFactoryRequestOnUIThread(
mojo::PendingReceiver<network::mojom::URLLoaderFactory>
network_factory_receiver) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
mojo::PendingRemote<network::mojom::URLLoaderFactory> factory_remote;
create_url_loader_factory_callback_.Run(&factory_remote);
if (!factory_remote) {
// The underlying URLLoaderFactory has went away while
// `ReconnectableURLLoaderFactoryForIOThread` is still held by consumers.
return;
}
CHECK(mojo::FusePipes(std::move(network_factory_receiver),
std::move(factory_remote)));
}
// -----------------------------------------------------------------------------
ReconnectableURLLoaderFactoryForIOThreadWrapper::
ReconnectableURLLoaderFactoryForIOThreadWrapper(
CreateCallback create_url_loader_factory_callback)
: factory_(base::MakeRefCounted<ReconnectableURLLoaderFactory>(
create_url_loader_factory_callback)),
factory_for_io_thread_(
base::MakeRefCounted<ReconnectableURLLoaderFactoryForIOThread>(
create_url_loader_factory_callback)) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
ReconnectableURLLoaderFactoryForIOThreadWrapper::
~ReconnectableURLLoaderFactoryForIOThreadWrapper() = default;
} // namespace content