// Copyright (c) 2011 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 "webkit/glue/media/simple_data_source.h"
#include "base/message_loop.h"
#include "base/process_util.h"
#include "media/base/filter_host.h"
#include "net/base/data_url.h"
#include "net/base/load_flags.h"
#include "net/url_request/url_request_status.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebKit.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebKitClient.h"
#include "webkit/glue/media/web_data_source_factory.h"
#include "webkit/glue/webkit_glue.h"
namespace webkit_glue {
static const char kDataScheme[] = "data";
static WebDataSource* NewSimpleDataSource(MessageLoop* render_loop,
WebKit::WebFrame* frame) {
return new SimpleDataSource(render_loop, frame);
}
// static
media::DataSourceFactory* SimpleDataSource::CreateFactory(
MessageLoop* render_loop,
WebKit::WebFrame* frame,
WebDataSourceBuildObserverHack* build_observer) {
return new WebDataSourceFactory(render_loop, frame, &NewSimpleDataSource,
build_observer);
}
SimpleDataSource::SimpleDataSource(
MessageLoop* render_loop,
WebKit::WebFrame* frame)
: render_loop_(render_loop),
frame_(frame),
size_(-1),
single_origin_(true),
state_(UNINITIALIZED),
keep_test_loader_(false) {
DCHECK(render_loop);
}
SimpleDataSource::~SimpleDataSource() {
base::AutoLock auto_lock(lock_);
DCHECK(state_ == UNINITIALIZED || state_ == STOPPED);
}
void SimpleDataSource::set_host(media::FilterHost* host) {
DataSource::set_host(host);
base::AutoLock auto_lock(lock_);
if (state_ == INITIALIZED) {
UpdateHostState();
}
}
void SimpleDataSource::Stop(media::FilterCallback* callback) {
base::AutoLock auto_lock(lock_);
state_ = STOPPED;
if (callback) {
callback->Run();
delete callback;
}
// Post a task to the render thread to cancel loading the resource.
render_loop_->PostTask(FROM_HERE,
NewRunnableMethod(this, &SimpleDataSource::CancelTask));
}
void SimpleDataSource::Initialize(
const std::string& url,
media::PipelineStatusCallback* callback) {
// Reference to prevent destruction while inside the |initialize_callback_|
// call. This is a temporary fix to prevent crashes caused by holding the
// lock and running the destructor.
scoped_refptr<SimpleDataSource> destruction_guard(this);
{
base::AutoLock auto_lock(lock_);
DCHECK_EQ(state_, UNINITIALIZED);
DCHECK(callback);
state_ = INITIALIZING;
initialize_callback_.reset(callback);
// Validate the URL.
SetURL(GURL(url));
if (!url_.is_valid() || !IsProtocolSupportedForMedia(url_)) {
DoneInitialization_Locked(false);
return;
}
// Post a task to the render thread to start loading the resource.
render_loop_->PostTask(FROM_HERE,
NewRunnableMethod(this, &SimpleDataSource::StartTask));
}
}
void SimpleDataSource::CancelInitialize() {
base::AutoLock auto_lock(lock_);
DCHECK(initialize_callback_.get());
state_ = STOPPED;
initialize_callback_.reset();
// Post a task to the render thread to cancel loading the resource.
render_loop_->PostTask(FROM_HERE,
NewRunnableMethod(this, &SimpleDataSource::CancelTask));
}
const media::MediaFormat& SimpleDataSource::media_format() {
return media_format_;
}
void SimpleDataSource::Read(int64 position,
size_t size,
uint8* data,
ReadCallback* read_callback) {
DCHECK_GE(size_, 0);
if (position >= size_) {
read_callback->RunWithParams(Tuple1<size_t>(0));
delete read_callback;
} else {
size_t copied = std::min(size, static_cast<size_t>(size_ - position));
memcpy(data, data_.c_str() + position, copied);
read_callback->RunWithParams(Tuple1<size_t>(copied));
delete read_callback;
}
}
bool SimpleDataSource::GetSize(int64* size_out) {
*size_out = size_;
return true;
}
bool SimpleDataSource::IsStreaming() {
return false;
}
void SimpleDataSource::SetPreload(media::Preload preload) {}
void SimpleDataSource::SetURLLoaderForTest(WebKit::WebURLLoader* mock_loader) {
url_loader_.reset(mock_loader);
keep_test_loader_ = true;
}
void SimpleDataSource::willSendRequest(
WebKit::WebURLLoader* loader,
WebKit::WebURLRequest& newRequest,
const WebKit::WebURLResponse& redirectResponse) {
DCHECK(MessageLoop::current() == render_loop_);
base::AutoLock auto_lock(lock_);
// Only allow |single_origin_| if we haven't seen a different origin yet.
if (single_origin_)
single_origin_ = url_.GetOrigin() == GURL(newRequest.url()).GetOrigin();
url_ = newRequest.url();
}
void SimpleDataSource::didSendData(
WebKit::WebURLLoader* loader,
unsigned long long bytesSent,
unsigned long long totalBytesToBeSent) {
NOTIMPLEMENTED();
}
void SimpleDataSource::didReceiveResponse(
WebKit::WebURLLoader* loader,
const WebKit::WebURLResponse& response) {
DCHECK(MessageLoop::current() == render_loop_);
size_ = response.expectedContentLength();
}
void SimpleDataSource::didDownloadData(
WebKit::WebURLLoader* loader,
int dataLength) {
NOTIMPLEMENTED();
}
void SimpleDataSource::didReceiveData(
WebKit::WebURLLoader* loader,
const char* data,
int data_length,
int encoded_data_length) {
DCHECK(MessageLoop::current() == render_loop_);
data_.append(data, data_length);
}
void SimpleDataSource::didReceiveCachedMetadata(
WebKit::WebURLLoader* loader,
const char* data,
int dataLength) {
NOTIMPLEMENTED();
}
void SimpleDataSource::didFinishLoading(
WebKit::WebURLLoader* loader,
double finishTime) {
DCHECK(MessageLoop::current() == render_loop_);
// Reference to prevent destruction while inside the |initialize_callback_|
// call. This is a temporary fix to prevent crashes caused by holding the
// lock and running the destructor.
scoped_refptr<SimpleDataSource> destruction_guard(this);
{
base::AutoLock auto_lock(lock_);
// It's possible this gets called after Stop(), in which case |host_| is no
// longer valid.
if (state_ == STOPPED)
return;
// Otherwise we should be initializing and have created a WebURLLoader.
DCHECK_EQ(state_, INITIALIZING);
// If we don't get a content length or the request has failed, report it
// as a network error.
if (size_ == -1)
size_ = data_.length();
DCHECK(static_cast<size_t>(size_) == data_.length());
DoneInitialization_Locked(true);
}
}
void SimpleDataSource::didFail(
WebKit::WebURLLoader* loader,
const WebKit::WebURLError& error) {
DCHECK(MessageLoop::current() == render_loop_);
// Reference to prevent destruction while inside the |initialize_callback_|
// call. This is a temporary fix to prevent crashes caused by holding the
// lock and running the destructor.
scoped_refptr<SimpleDataSource> destruction_guard(this);
{
base::AutoLock auto_lock(lock_);
// It's possible this gets called after Stop(), in which case |host_| is no
// longer valid.
if (state_ == STOPPED)
return;
// Otherwise we should be initializing and have created a WebURLLoader.
DCHECK_EQ(state_, INITIALIZING);
// If we don't get a content length or the request has failed, report it
// as a network error.
if (size_ == -1)
size_ = data_.length();
DCHECK(static_cast<size_t>(size_) == data_.length());
DoneInitialization_Locked(false);
}
}
bool SimpleDataSource::HasSingleOrigin() {
DCHECK(MessageLoop::current() == render_loop_);
return single_origin_;
}
void SimpleDataSource::Abort() {
DCHECK(MessageLoop::current() == render_loop_);
frame_ = NULL;
}
void SimpleDataSource::SetURL(const GURL& url) {
url_ = url;
media_format_.Clear();
media_format_.SetAsString(media::MediaFormat::kURL, url.spec());
}
void SimpleDataSource::StartTask() {
DCHECK(MessageLoop::current() == render_loop_);
// Reference to prevent destruction while inside the |initialize_callback_|
// call. This is a temporary fix to prevent crashes caused by holding the
// lock and running the destructor.
scoped_refptr<SimpleDataSource> destruction_guard(this);
{
base::AutoLock auto_lock(lock_);
// We may have stopped.
if (state_ == STOPPED)
return;
CHECK(frame_);
DCHECK_EQ(state_, INITIALIZING);
if (url_.SchemeIs(kDataScheme)) {
// If this using data protocol, we just need to decode it.
std::string mime_type, charset;
bool success = net::DataURL::Parse(url_, &mime_type, &charset, &data_);
// Don't care about the mime-type just proceed if decoding was successful.
size_ = data_.length();
DoneInitialization_Locked(success);
} else {
// Prepare the request.
WebKit::WebURLRequest request(url_);
request.setTargetType(WebKit::WebURLRequest::TargetIsMedia);
frame_->setReferrerForRequest(request, WebKit::WebURL());
// This flag is for unittests as we don't want to reset |url_loader|
if (!keep_test_loader_)
url_loader_.reset(frame_->createAssociatedURLLoader());
// Start the resource loading.
url_loader_->loadAsynchronously(request, this);
}
}
}
void SimpleDataSource::CancelTask() {
DCHECK(MessageLoop::current() == render_loop_);
base::AutoLock auto_lock(lock_);
DCHECK_EQ(state_, STOPPED);
// Cancel any pending requests.
if (url_loader_.get()) {
url_loader_->cancel();
url_loader_.reset();
}
}
void SimpleDataSource::DoneInitialization_Locked(bool success) {
lock_.AssertAcquired();
media::PipelineStatus status = media::PIPELINE_ERROR_NETWORK;
if (success) {
state_ = INITIALIZED;
UpdateHostState();
status = media::PIPELINE_OK;
} else {
state_ = UNINITIALIZED;
url_loader_.reset();
}
scoped_ptr<media::PipelineStatusCallback> initialize_callback(
initialize_callback_.release());
initialize_callback->Run(status);
}
void SimpleDataSource::UpdateHostState() {
if (host()) {
host()->SetTotalBytes(size_);
host()->SetBufferedBytes(size_);
// If scheme is file or data, say we are loaded.
host()->SetLoaded(url_.SchemeIsFile() || url_.SchemeIs(kDataScheme));
}
}
} // namespace webkit_glue