blob: bb5eeba4593f040301504e443611b5e68342a528 [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 "content/child/url_loader_client_impl.h"
#include "base/callback.h"
#include "base/single_thread_task_runner.h"
#include "content/child/resource_dispatcher.h"
#include "content/child/url_response_body_consumer.h"
#include "content/common/resource_messages.h"
#include "mojo/public/cpp/bindings/associated_group.h"
#include "net/url_request/redirect_info.h"
namespace content {
URLLoaderClientImpl::URLLoaderClientImpl(
int request_id,
ResourceDispatcher* resource_dispatcher,
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: binding_(this),
request_id_(request_id),
resource_dispatcher_(resource_dispatcher),
task_runner_(std::move(task_runner)),
weak_factory_(this) {}
URLLoaderClientImpl::~URLLoaderClientImpl() {
if (body_consumer_)
body_consumer_->Cancel();
}
void URLLoaderClientImpl::Bind(
mojom::URLLoaderClientAssociatedPtrInfo* client_ptr_info,
mojo::AssociatedGroup* associated_group) {
binding_.Bind(client_ptr_info, associated_group);
}
void URLLoaderClientImpl::SetDefersLoading() {
is_deferred_ = true;
if (body_consumer_)
body_consumer_->SetDefersLoading();
}
void URLLoaderClientImpl::UnsetDefersLoading() {
is_deferred_ = false;
}
void URLLoaderClientImpl::FlushDeferredMessages() {
DCHECK(!is_deferred_);
std::vector<IPC::Message> messages;
messages.swap(deferred_messages_);
bool has_completion_message = false;
base::WeakPtr<URLLoaderClientImpl> weak_this = weak_factory_.GetWeakPtr();
// First, dispatch all messages excluding the followings:
// - response body (dispatched by |body_consumer_|)
// - transfer size change (dispatched later)
// - completion (dispatched by |body_consumer_| or dispatched later)
for (size_t index = 0; index < messages.size(); ++index) {
if (messages[index].type() == ResourceMsg_RequestComplete::ID) {
// The completion message arrives at the end of the message queue.
DCHECK(!has_completion_message);
DCHECK_EQ(index, messages.size() - 1);
has_completion_message = true;
break;
}
Dispatch(messages[index]);
if (!weak_this)
return;
if (is_deferred_) {
deferred_messages_.insert(deferred_messages_.begin(),
messages.begin() + index + 1, messages.end());
return;
}
}
// Dispatch the transfer size update.
if (accumulated_transfer_size_diff_during_deferred_ > 0) {
auto transfer_size_diff = accumulated_transfer_size_diff_during_deferred_;
accumulated_transfer_size_diff_during_deferred_ = 0;
resource_dispatcher_->OnTransferSizeUpdated(request_id_,
transfer_size_diff);
if (!weak_this)
return;
if (is_deferred_) {
if (has_completion_message) {
DCHECK_GT(messages.size(), 0u);
DCHECK_EQ(messages.back().type(),
static_cast<uint32_t>(ResourceMsg_RequestComplete::ID));
deferred_messages_.emplace_back(std::move(messages.back()));
}
return;
}
}
if (body_consumer_) {
// When we have |body_consumer_|, the completion message is dispatched by
// it, not by this object.
DCHECK(!has_completion_message);
// Dispatch the response body.
body_consumer_->UnsetDefersLoading();
return;
}
// Dispatch the completion message.
if (has_completion_message) {
DCHECK_GT(messages.size(), 0u);
DCHECK_EQ(messages.back().type(),
static_cast<uint32_t>(ResourceMsg_RequestComplete::ID));
Dispatch(messages.back());
}
}
void URLLoaderClientImpl::OnReceiveResponse(
const ResourceResponseHead& response_head,
mojom::DownloadedTempFileAssociatedPtrInfo downloaded_file) {
has_received_response_ = true;
downloaded_file_.Bind(std::move(downloaded_file));
Dispatch(ResourceMsg_ReceivedResponse(request_id_, response_head));
}
void URLLoaderClientImpl::OnReceiveRedirect(
const net::RedirectInfo& redirect_info,
const ResourceResponseHead& response_head) {
DCHECK(!has_received_response_);
DCHECK(!body_consumer_);
Dispatch(
ResourceMsg_ReceivedRedirect(request_id_, redirect_info, response_head));
}
void URLLoaderClientImpl::OnDataDownloaded(int64_t data_len,
int64_t encoded_data_len) {
Dispatch(ResourceMsg_DataDownloaded(request_id_, data_len, encoded_data_len));
}
void URLLoaderClientImpl::OnReceiveCachedMetadata(
const std::vector<uint8_t>& data) {
const char* data_ptr = reinterpret_cast<const char*>(data.data());
Dispatch(ResourceMsg_ReceivedCachedMetadata(
request_id_, std::vector<char>(data_ptr, data_ptr + data.size())));
}
void URLLoaderClientImpl::OnTransferSizeUpdated(int32_t transfer_size_diff) {
if (is_deferred_) {
accumulated_transfer_size_diff_during_deferred_ += transfer_size_diff;
} else {
resource_dispatcher_->OnTransferSizeUpdated(request_id_,
transfer_size_diff);
}
}
void URLLoaderClientImpl::OnStartLoadingResponseBody(
mojo::ScopedDataPipeConsumerHandle body) {
DCHECK(!body_consumer_);
DCHECK(has_received_response_);
body_consumer_ = new URLResponseBodyConsumer(
request_id_, resource_dispatcher_, std::move(body), task_runner_);
if (is_deferred_)
body_consumer_->SetDefersLoading();
}
void URLLoaderClientImpl::OnComplete(
const ResourceRequestCompletionStatus& status) {
if (!body_consumer_) {
Dispatch(ResourceMsg_RequestComplete(request_id_, status));
return;
}
body_consumer_->OnComplete(status);
}
void URLLoaderClientImpl::Dispatch(const IPC::Message& message) {
if (is_deferred_) {
deferred_messages_.push_back(message);
} else if (deferred_messages_.size() > 0) {
deferred_messages_.push_back(message);
FlushDeferredMessages();
} else {
resource_dispatcher_->DispatchMessage(message);
}
}
void URLLoaderClientImpl::OnUploadProgress(int64_t current_position,
int64_t total_size,
const base::Closure& ack_callback) {
Dispatch(
ResourceMsg_UploadProgress(request_id_, current_position, total_size));
ack_callback.Run();
}
} // namespace content