blob: f33aaf902027668413fb381db93cad723e9f607c [file] [log] [blame]
// Copyright 2015 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/loader/loader.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/threading/thread_task_runner_handle.h"
namespace cobalt {
namespace loader {
//////////////////////////////////////////////////////////////////
// Loader::FetcherHandlerToDecoderAdapter
//////////////////////////////////////////////////////////////////
// This class is responsible for passing chunks of data from fetcher to
// decoder and notifying fetching is done or aborted on error.
class Loader::FetcherHandlerToDecoderAdapter : public Fetcher::Handler {
public:
FetcherHandlerToDecoderAdapter(Decoder* decoder,
OnCompleteFunction load_complete_callback)
: decoder_(decoder), load_complete_callback_(load_complete_callback) {}
// From Fetcher::Handler.
LoadResponseType OnResponseStarted(
Fetcher* fetcher,
const scoped_refptr<net::HttpResponseHeaders>& headers) override {
if (headers) {
return decoder_->OnResponseStarted(fetcher, headers);
} else {
return kLoadResponseContinue;
}
}
void OnReceived(Fetcher* fetcher, const char* data, size_t size) override {
decoder_->DecodeChunk(data, size);
}
void OnReceivedPassed(Fetcher* fetcher,
std::unique_ptr<std::string> data) override {
decoder_->DecodeChunkPassed(std::move(data));
}
void OnDone(Fetcher* fetcher) override {
DCHECK(fetcher);
decoder_->SetLastURLOrigin(fetcher->last_url_origin());
decoder_->Finish();
}
void OnError(Fetcher* fetcher, const std::string& error) override {
load_complete_callback_.Run(error);
}
private:
Decoder* decoder_;
OnCompleteFunction load_complete_callback_;
};
//////////////////////////////////////////////////////////////////
// Loader
//////////////////////////////////////////////////////////////////
Loader::Loader(const FetcherCreator& fetcher_creator,
const DecoderCreator& decoder_creator,
const OnCompleteFunction& on_load_complete,
const OnDestructionFunction& on_destruction, bool is_suspended)
: fetcher_creator_(fetcher_creator),
decoder_creator_(decoder_creator),
on_load_complete_(on_load_complete),
on_destruction_(on_destruction),
is_suspended_(is_suspended) {
DCHECK(!fetcher_creator_.is_null());
DCHECK(!decoder_creator_.is_null());
DCHECK(!on_load_complete_.is_null());
load_timing_info_ = net::LoadTimingInfo();
std::unique_ptr<Decoder> decoder = decoder_creator_.Run(
base::Bind(&Loader::LoadComplete, base::Unretained(this)));
// We are doing this bizarre check because decoder_ used to be a scoped_ptr
// and scoped_ptr checks equality of held raw pointer and only delete rhs if
// not equal.
if (decoder.get() == decoder_.get()) {
decoder_.release();
}
decoder_ = std::move(decoder);
if (!is_suspended_) {
Start();
}
}
Loader::~Loader() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!on_destruction_.is_null()) {
on_destruction_.Run(this);
}
fetcher_creator_error_closure_.Cancel();
}
void Loader::Suspend() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!is_suspended_);
is_suspended_ = true;
bool suspendable = decoder_->Suspend();
if (fetcher_) {
fetcher_.reset();
}
fetcher_handler_to_decoder_adaptor_.reset();
fetcher_creator_error_closure_.Cancel();
if (!suspendable) {
LoadComplete(std::string("Aborted."));
}
}
void Loader::Resume(render_tree::ResourceProvider* resource_provider) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(is_suspended_);
is_suspended_ = false;
decoder_->Resume(resource_provider);
if (!is_load_complete_) Start();
}
bool Loader::DidFailFromTransientError() const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return fetcher_ && fetcher_->did_fail_from_transient_error();
}
void Loader::LoadComplete(const base::Optional<std::string>& error) {
is_load_complete_ = true;
on_load_complete_.Run(error);
}
void Loader::Start() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!is_suspended_);
fetcher_handler_to_decoder_adaptor_.reset(new FetcherHandlerToDecoderAdapter(
decoder_.get(),
base::Bind(&Loader::LoadComplete, base::Unretained(this))));
fetcher_ = fetcher_creator_.Run(fetcher_handler_to_decoder_adaptor_.get());
if (fetcher_) {
fetcher_->SetLoadTimingInfoCallback(
base::Bind(&Loader::set_load_timing_info, base::Unretained(this)));
}
// Post the error callback on the current message loop in case the loader is
// destroyed in the callback.
if (!fetcher_) {
fetcher_creator_error_closure_.Reset(
base::Bind(base::Bind(&Loader::LoadComplete, base::Unretained(this)),
std::string("Fetcher was not created.")));
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, fetcher_creator_error_closure_.callback());
}
}
void Loader::set_load_timing_info(const net::LoadTimingInfo& timing_info) {
load_timing_info_ = timing_info;
}
net::LoadTimingInfo Loader::get_load_timing_info() { return load_timing_info_; }
} // namespace loader
} // namespace cobalt