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
18namespace webkit_glue {
19
20static const char kDataScheme[] = "data";
21
22static WebDataSource* NewSimpleDataSource(MessageLoop* render_loop,
23                                          WebKit::WebFrame* frame) {
24  return new SimpleDataSource(render_loop, frame);
25}
26
27// static
28media::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
36SimpleDataSource::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
48SimpleDataSource::~SimpleDataSource() {
49  base::AutoLock auto_lock(lock_);
50  DCHECK(state_ == UNINITIALIZED || state_ == STOPPED);
51}
52
53void 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
62void 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
75void 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
102void 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
113const media::MediaFormat& SimpleDataSource::media_format() {
114  return media_format_;
115}
116
117void 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
133bool SimpleDataSource::GetSize(int64* size_out) {
134  *size_out = size_;
135  return true;
136}
137
138bool SimpleDataSource::IsStreaming() {
139  return false;
140}
141
142void SimpleDataSource::SetPreload(media::Preload preload) {}
143
144void SimpleDataSource::SetURLLoaderForTest(WebKit::WebURLLoader* mock_loader) {
145  url_loader_.reset(mock_loader);
146  keep_test_loader_ = true;
147}
148
149void 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
163void SimpleDataSource::didSendData(
164    WebKit::WebURLLoader* loader,
165    unsigned long long bytesSent,
166    unsigned long long totalBytesToBeSent) {
167  NOTIMPLEMENTED();
168}
169
170void SimpleDataSource::didReceiveResponse(
171    WebKit::WebURLLoader* loader,
172    const WebKit::WebURLResponse& response) {
173  DCHECK(MessageLoop::current() == render_loop_);
174  size_ = response.expectedContentLength();
175}
176
177void SimpleDataSource::didDownloadData(
178    WebKit::WebURLLoader* loader,
179    int dataLength) {
180  NOTIMPLEMENTED();
181}
182
183void 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
192void SimpleDataSource::didReceiveCachedMetadata(
193    WebKit::WebURLLoader* loader,
194    const char* data,
195    int dataLength) {
196  NOTIMPLEMENTED();
197}
198
199void 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
227void 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
255bool SimpleDataSource::HasSingleOrigin() {
256  DCHECK(MessageLoop::current() == render_loop_);
257  return single_origin_;
258}
259
260void SimpleDataSource::Abort() {
261  DCHECK(MessageLoop::current() == render_loop_);
262  frame_ = NULL;
263}
264
265void SimpleDataSource::SetURL(const GURL& url) {
266  url_ = url;
267  media_format_.Clear();
268  media_format_.SetAsString(media::MediaFormat::kURL, url.spec());
269}
270
271void 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
313void 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
325void 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
343void 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