// 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 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(0)); delete read_callback; } else { size_t copied = std::min(size, static_cast(size_ - position)); memcpy(data, data_.c_str() + position, copied); read_callback->RunWithParams(Tuple1(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 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_) == 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 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_) == 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 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 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