Home | History | Annotate | Download | only in media
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "webkit/glue/media/simple_data_source.h"
      6 
      7 #include "base/message_loop.h"
      8 #include "base/process_util.h"
      9 #include "media/base/filter_host.h"
     10 #include "net/base/data_url.h"
     11 #include "net/base/load_flags.h"
     12 #include "net/url_request/url_request_status.h"
     13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebKit.h"
     14 #include "third_party/WebKit/Source/WebKit/chromium/public/WebKitClient.h"
     15 #include "webkit/glue/media/web_data_source_factory.h"
     16 #include "webkit/glue/webkit_glue.h"
     17 
     18 namespace webkit_glue {
     19 
     20 static const char kDataScheme[] = "data";
     21 
     22 static WebDataSource* NewSimpleDataSource(MessageLoop* render_loop,
     23                                           WebKit::WebFrame* frame) {
     24   return new SimpleDataSource(render_loop, frame);
     25 }
     26 
     27 // static
     28 media::DataSourceFactory* SimpleDataSource::CreateFactory(
     29     MessageLoop* render_loop,
     30     WebKit::WebFrame* frame,
     31     WebDataSourceBuildObserverHack* build_observer) {
     32   return new WebDataSourceFactory(render_loop, frame, &NewSimpleDataSource,
     33                                   build_observer);
     34 }
     35 
     36 SimpleDataSource::SimpleDataSource(
     37     MessageLoop* render_loop,
     38     WebKit::WebFrame* frame)
     39     : render_loop_(render_loop),
     40       frame_(frame),
     41       size_(-1),
     42       single_origin_(true),
     43       state_(UNINITIALIZED),
     44       keep_test_loader_(false) {
     45   DCHECK(render_loop);
     46 }
     47 
     48 SimpleDataSource::~SimpleDataSource() {
     49   base::AutoLock auto_lock(lock_);
     50   DCHECK(state_ == UNINITIALIZED || state_ == STOPPED);
     51 }
     52 
     53 void SimpleDataSource::set_host(media::FilterHost* host) {
     54   DataSource::set_host(host);
     55 
     56   base::AutoLock auto_lock(lock_);
     57   if (state_ == INITIALIZED) {
     58     UpdateHostState();
     59   }
     60 }
     61 
     62 void SimpleDataSource::Stop(media::FilterCallback* callback) {
     63   base::AutoLock auto_lock(lock_);
     64   state_ = STOPPED;
     65   if (callback) {
     66     callback->Run();
     67     delete callback;
     68   }
     69 
     70   // Post a task to the render thread to cancel loading the resource.
     71   render_loop_->PostTask(FROM_HERE,
     72       NewRunnableMethod(this, &SimpleDataSource::CancelTask));
     73 }
     74 
     75 void SimpleDataSource::Initialize(
     76     const std::string& url,
     77     media::PipelineStatusCallback* callback) {
     78   // Reference to prevent destruction while inside the |initialize_callback_|
     79   // call. This is a temporary fix to prevent crashes caused by holding the
     80   // lock and running the destructor.
     81   scoped_refptr<SimpleDataSource> destruction_guard(this);
     82   {
     83     base::AutoLock auto_lock(lock_);
     84     DCHECK_EQ(state_, UNINITIALIZED);
     85     DCHECK(callback);
     86     state_ = INITIALIZING;
     87     initialize_callback_.reset(callback);
     88 
     89     // Validate the URL.
     90     SetURL(GURL(url));
     91     if (!url_.is_valid() || !IsProtocolSupportedForMedia(url_)) {
     92       DoneInitialization_Locked(false);
     93       return;
     94     }
     95 
     96     // Post a task to the render thread to start loading the resource.
     97     render_loop_->PostTask(FROM_HERE,
     98         NewRunnableMethod(this, &SimpleDataSource::StartTask));
     99   }
    100 }
    101 
    102 void SimpleDataSource::CancelInitialize() {
    103   base::AutoLock auto_lock(lock_);
    104   DCHECK(initialize_callback_.get());
    105   state_ = STOPPED;
    106   initialize_callback_.reset();
    107 
    108   // Post a task to the render thread to cancel loading the resource.
    109   render_loop_->PostTask(FROM_HERE,
    110       NewRunnableMethod(this, &SimpleDataSource::CancelTask));
    111 }
    112 
    113 const media::MediaFormat& SimpleDataSource::media_format() {
    114   return media_format_;
    115 }
    116 
    117 void SimpleDataSource::Read(int64 position,
    118                             size_t size,
    119                             uint8* data,
    120                             ReadCallback* read_callback) {
    121   DCHECK_GE(size_, 0);
    122   if (position >= size_) {
    123     read_callback->RunWithParams(Tuple1<size_t>(0));
    124     delete read_callback;
    125   } else {
    126     size_t copied = std::min(size, static_cast<size_t>(size_ - position));
    127     memcpy(data, data_.c_str() + position, copied);
    128     read_callback->RunWithParams(Tuple1<size_t>(copied));
    129     delete read_callback;
    130   }
    131 }
    132 
    133 bool SimpleDataSource::GetSize(int64* size_out) {
    134   *size_out = size_;
    135   return true;
    136 }
    137 
    138 bool SimpleDataSource::IsStreaming() {
    139   return false;
    140 }
    141 
    142 void SimpleDataSource::SetPreload(media::Preload preload) {}
    143 
    144 void SimpleDataSource::SetURLLoaderForTest(WebKit::WebURLLoader* mock_loader) {
    145   url_loader_.reset(mock_loader);
    146   keep_test_loader_ = true;
    147 }
    148 
    149 void SimpleDataSource::willSendRequest(
    150     WebKit::WebURLLoader* loader,
    151     WebKit::WebURLRequest& newRequest,
    152     const WebKit::WebURLResponse& redirectResponse) {
    153   DCHECK(MessageLoop::current() == render_loop_);
    154   base::AutoLock auto_lock(lock_);
    155 
    156   // Only allow |single_origin_| if we haven't seen a different origin yet.
    157   if (single_origin_)
    158     single_origin_ = url_.GetOrigin() == GURL(newRequest.url()).GetOrigin();
    159 
    160   url_ = newRequest.url();
    161 }
    162 
    163 void SimpleDataSource::didSendData(
    164     WebKit::WebURLLoader* loader,
    165     unsigned long long bytesSent,
    166     unsigned long long totalBytesToBeSent) {
    167   NOTIMPLEMENTED();
    168 }
    169 
    170 void SimpleDataSource::didReceiveResponse(
    171     WebKit::WebURLLoader* loader,
    172     const WebKit::WebURLResponse& response) {
    173   DCHECK(MessageLoop::current() == render_loop_);
    174   size_ = response.expectedContentLength();
    175 }
    176 
    177 void SimpleDataSource::didDownloadData(
    178     WebKit::WebURLLoader* loader,
    179     int dataLength) {
    180   NOTIMPLEMENTED();
    181 }
    182 
    183 void SimpleDataSource::didReceiveData(
    184     WebKit::WebURLLoader* loader,
    185     const char* data,
    186     int data_length,
    187     int encoded_data_length) {
    188   DCHECK(MessageLoop::current() == render_loop_);
    189   data_.append(data, data_length);
    190 }
    191 
    192 void SimpleDataSource::didReceiveCachedMetadata(
    193     WebKit::WebURLLoader* loader,
    194     const char* data,
    195     int dataLength) {
    196   NOTIMPLEMENTED();
    197 }
    198 
    199 void SimpleDataSource::didFinishLoading(
    200     WebKit::WebURLLoader* loader,
    201     double finishTime) {
    202   DCHECK(MessageLoop::current() == render_loop_);
    203   // Reference to prevent destruction while inside the |initialize_callback_|
    204   // call. This is a temporary fix to prevent crashes caused by holding the
    205   // lock and running the destructor.
    206   scoped_refptr<SimpleDataSource> destruction_guard(this);
    207   {
    208     base::AutoLock auto_lock(lock_);
    209     // It's possible this gets called after Stop(), in which case |host_| is no
    210     // longer valid.
    211     if (state_ == STOPPED)
    212       return;
    213 
    214     // Otherwise we should be initializing and have created a WebURLLoader.
    215     DCHECK_EQ(state_, INITIALIZING);
    216 
    217     // If we don't get a content length or the request has failed, report it
    218     // as a network error.
    219     if (size_ == -1)
    220       size_ = data_.length();
    221     DCHECK(static_cast<size_t>(size_) == data_.length());
    222 
    223     DoneInitialization_Locked(true);
    224   }
    225 }
    226 
    227 void SimpleDataSource::didFail(
    228     WebKit::WebURLLoader* loader,
    229     const WebKit::WebURLError& error) {
    230   DCHECK(MessageLoop::current() == render_loop_);
    231   // Reference to prevent destruction while inside the |initialize_callback_|
    232   // call. This is a temporary fix to prevent crashes caused by holding the
    233   // lock and running the destructor.
    234   scoped_refptr<SimpleDataSource> destruction_guard(this);
    235   {
    236     base::AutoLock auto_lock(lock_);
    237     // It's possible this gets called after Stop(), in which case |host_| is no
    238     // longer valid.
    239     if (state_ == STOPPED)
    240       return;
    241 
    242     // Otherwise we should be initializing and have created a WebURLLoader.
    243     DCHECK_EQ(state_, INITIALIZING);
    244 
    245     // If we don't get a content length or the request has failed, report it
    246     // as a network error.
    247     if (size_ == -1)
    248       size_ = data_.length();
    249     DCHECK(static_cast<size_t>(size_) == data_.length());
    250 
    251     DoneInitialization_Locked(false);
    252   }
    253 }
    254 
    255 bool SimpleDataSource::HasSingleOrigin() {
    256   DCHECK(MessageLoop::current() == render_loop_);
    257   return single_origin_;
    258 }
    259 
    260 void SimpleDataSource::Abort() {
    261   DCHECK(MessageLoop::current() == render_loop_);
    262   frame_ = NULL;
    263 }
    264 
    265 void SimpleDataSource::SetURL(const GURL& url) {
    266   url_ = url;
    267   media_format_.Clear();
    268   media_format_.SetAsString(media::MediaFormat::kURL, url.spec());
    269 }
    270 
    271 void SimpleDataSource::StartTask() {
    272   DCHECK(MessageLoop::current() == render_loop_);
    273   // Reference to prevent destruction while inside the |initialize_callback_|
    274   // call. This is a temporary fix to prevent crashes caused by holding the
    275   // lock and running the destructor.
    276   scoped_refptr<SimpleDataSource> destruction_guard(this);
    277   {
    278     base::AutoLock auto_lock(lock_);
    279 
    280     // We may have stopped.
    281     if (state_ == STOPPED)
    282       return;
    283 
    284     CHECK(frame_);
    285 
    286     DCHECK_EQ(state_, INITIALIZING);
    287 
    288     if (url_.SchemeIs(kDataScheme)) {
    289       // If this using data protocol, we just need to decode it.
    290       std::string mime_type, charset;
    291       bool success = net::DataURL::Parse(url_, &mime_type, &charset, &data_);
    292 
    293       // Don't care about the mime-type just proceed if decoding was successful.
    294       size_ = data_.length();
    295       DoneInitialization_Locked(success);
    296     } else {
    297       // Prepare the request.
    298       WebKit::WebURLRequest request(url_);
    299       request.setTargetType(WebKit::WebURLRequest::TargetIsMedia);
    300 
    301       frame_->setReferrerForRequest(request, WebKit::WebURL());
    302 
    303       // This flag is for unittests as we don't want to reset |url_loader|
    304       if (!keep_test_loader_)
    305         url_loader_.reset(frame_->createAssociatedURLLoader());
    306 
    307       // Start the resource loading.
    308       url_loader_->loadAsynchronously(request, this);
    309     }
    310   }
    311 }
    312 
    313 void SimpleDataSource::CancelTask() {
    314   DCHECK(MessageLoop::current() == render_loop_);
    315   base::AutoLock auto_lock(lock_);
    316   DCHECK_EQ(state_, STOPPED);
    317 
    318   // Cancel any pending requests.
    319   if (url_loader_.get()) {
    320     url_loader_->cancel();
    321     url_loader_.reset();
    322   }
    323 }
    324 
    325 void SimpleDataSource::DoneInitialization_Locked(bool success) {
    326   lock_.AssertAcquired();
    327   media::PipelineStatus status = media::PIPELINE_ERROR_NETWORK;
    328   if (success) {
    329     state_ = INITIALIZED;
    330 
    331     UpdateHostState();
    332     status = media::PIPELINE_OK;
    333   } else {
    334     state_ = UNINITIALIZED;
    335     url_loader_.reset();
    336   }
    337 
    338   scoped_ptr<media::PipelineStatusCallback> initialize_callback(
    339       initialize_callback_.release());
    340   initialize_callback->Run(status);
    341 }
    342 
    343 void SimpleDataSource::UpdateHostState() {
    344   if (host()) {
    345     host()->SetTotalBytes(size_);
    346     host()->SetBufferedBytes(size_);
    347     // If scheme is file or data, say we are loaded.
    348     host()->SetLoaded(url_.SchemeIsFile() || url_.SchemeIs(kDataScheme));
    349   }
    350 }
    351 
    352 }  // namespace webkit_glue
    353