// Copyright (c) 2010 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 "base/message_loop.h" #include "base/process_util.h" #include "media/base/filter_host.h" #include "net/base/load_flags.h" #include "net/base/data_url.h" #include "net/http/http_response_headers.h" #include "net/url_request/url_request_status.h" #include "webkit/glue/media/simple_data_source.h" #include "webkit/glue/resource_loader_bridge.h" #include "webkit/glue/webkit_glue.h" namespace { const char kHttpScheme[] = "http"; const char kHttpsScheme[] = "https"; const char kDataScheme[] = "data"; // A helper method that accepts only HTTP, HTTPS and FILE protocol. bool IsDataProtocol(const GURL& url) { return url.SchemeIs(kDataScheme); } } // namespace namespace webkit_glue { SimpleDataSource::SimpleDataSource( MessageLoop* render_loop, webkit_glue::MediaResourceLoaderBridgeFactory* bridge_factory) : render_loop_(render_loop), bridge_factory_(bridge_factory), size_(-1), single_origin_(true), state_(UNINITIALIZED) { DCHECK(render_loop); } SimpleDataSource::~SimpleDataSource() { AutoLock auto_lock(lock_); DCHECK(state_ == UNINITIALIZED || state_ == STOPPED); } void SimpleDataSource::Stop(media::FilterCallback* callback) { 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::FilterCallback* callback) { 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_)) { host()->SetError(media::PIPELINE_ERROR_NETWORK); initialize_callback_->Run(); initialize_callback_.reset(); return; } // Post a task to the render thread to start loading the resource. render_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, &SimpleDataSource::StartTask)); } 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; } bool SimpleDataSource::OnReceivedRedirect( const GURL& new_url, const webkit_glue::ResourceResponseInfo& info, bool* has_new_first_party_for_cookies, GURL* new_first_party_for_cookies) { DCHECK(MessageLoop::current() == render_loop_); single_origin_ = url_.GetOrigin() == new_url.GetOrigin(); // TODO(wtc): should we return a new first party for cookies URL? *has_new_first_party_for_cookies = false; return true; } void SimpleDataSource::OnReceivedResponse( const webkit_glue::ResourceResponseInfo& info, bool content_filtered) { DCHECK(MessageLoop::current() == render_loop_); size_ = info.content_length; } void SimpleDataSource::OnReceivedData(const char* data, int len) { DCHECK(MessageLoop::current() == render_loop_); data_.append(data, len); } void SimpleDataSource::OnCompletedRequest(const URLRequestStatus& status, const std::string& security_info, const base::Time& completion_time) { DCHECK(MessageLoop::current() == render_loop_); 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 bridge. DCHECK_EQ(state_, INITIALIZING); DCHECK(bridge_.get()); bridge_.reset(); // 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(status.is_success()); } bool SimpleDataSource::HasSingleOrigin() { DCHECK(MessageLoop::current() == render_loop_); return single_origin_; } void SimpleDataSource::Abort() { DCHECK(MessageLoop::current() == render_loop_); NOTIMPLEMENTED(); } void SimpleDataSource::SetURL(const GURL& url) { url_ = url; media_format_.Clear(); media_format_.SetAsString(media::MediaFormat::kMimeType, media::mime_type::kApplicationOctetStream); media_format_.SetAsString(media::MediaFormat::kURL, url.spec()); } void SimpleDataSource::StartTask() { DCHECK(MessageLoop::current() == render_loop_); AutoLock auto_lock(lock_); // We may have stopped. if (state_ == STOPPED) return; DCHECK_EQ(state_, INITIALIZING); if (IsDataProtocol(url_)) { // 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 { // Create our bridge and start loading the resource. bridge_.reset(bridge_factory_->CreateBridge( url_, net::LOAD_BYPASS_CACHE, -1, -1)); bridge_->Start(this); } } void SimpleDataSource::CancelTask() { DCHECK(MessageLoop::current() == render_loop_); AutoLock auto_lock(lock_); DCHECK_EQ(state_, STOPPED); // Cancel any pending requests. if (bridge_.get()) { bridge_->Cancel(); bridge_.reset(); } } void SimpleDataSource::DoneInitialization_Locked(bool success) { lock_.AssertAcquired(); if (success) { state_ = INITIALIZED; host()->SetTotalBytes(size_); host()->SetBufferedBytes(size_); // If scheme is file or data, say we are loaded. host()->SetLoaded(url_.SchemeIsFile() || IsDataProtocol(url_)); } else { host()->SetError(media::PIPELINE_ERROR_NETWORK); } initialize_callback_->Run(); initialize_callback_.reset(); } } // namespace webkit_glue