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
NewSimpleDataSource(MessageLoop * render_loop,WebKit::WebFrame * frame)22 static WebDataSource* NewSimpleDataSource(MessageLoop* render_loop,
23 WebKit::WebFrame* frame) {
24 return new SimpleDataSource(render_loop, frame);
25 }
26
27 // static
CreateFactory(MessageLoop * render_loop,WebKit::WebFrame * frame,WebDataSourceBuildObserverHack * build_observer)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
SimpleDataSource(MessageLoop * render_loop,WebKit::WebFrame * frame)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
~SimpleDataSource()48 SimpleDataSource::~SimpleDataSource() {
49 base::AutoLock auto_lock(lock_);
50 DCHECK(state_ == UNINITIALIZED || state_ == STOPPED);
51 }
52
set_host(media::FilterHost * host)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
Stop(media::FilterCallback * callback)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
Initialize(const std::string & url,media::PipelineStatusCallback * callback)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
CancelInitialize()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
media_format()113 const media::MediaFormat& SimpleDataSource::media_format() {
114 return media_format_;
115 }
116
Read(int64 position,size_t size,uint8 * data,ReadCallback * read_callback)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
GetSize(int64 * size_out)133 bool SimpleDataSource::GetSize(int64* size_out) {
134 *size_out = size_;
135 return true;
136 }
137
IsStreaming()138 bool SimpleDataSource::IsStreaming() {
139 return false;
140 }
141
SetPreload(media::Preload preload)142 void SimpleDataSource::SetPreload(media::Preload preload) {}
143
SetURLLoaderForTest(WebKit::WebURLLoader * mock_loader)144 void SimpleDataSource::SetURLLoaderForTest(WebKit::WebURLLoader* mock_loader) {
145 url_loader_.reset(mock_loader);
146 keep_test_loader_ = true;
147 }
148
willSendRequest(WebKit::WebURLLoader * loader,WebKit::WebURLRequest & newRequest,const WebKit::WebURLResponse & redirectResponse)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
didSendData(WebKit::WebURLLoader * loader,unsigned long long bytesSent,unsigned long long totalBytesToBeSent)163 void SimpleDataSource::didSendData(
164 WebKit::WebURLLoader* loader,
165 unsigned long long bytesSent,
166 unsigned long long totalBytesToBeSent) {
167 NOTIMPLEMENTED();
168 }
169
didReceiveResponse(WebKit::WebURLLoader * loader,const WebKit::WebURLResponse & response)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
didDownloadData(WebKit::WebURLLoader * loader,int dataLength)177 void SimpleDataSource::didDownloadData(
178 WebKit::WebURLLoader* loader,
179 int dataLength) {
180 NOTIMPLEMENTED();
181 }
182
didReceiveData(WebKit::WebURLLoader * loader,const char * data,int data_length,int encoded_data_length)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
didReceiveCachedMetadata(WebKit::WebURLLoader * loader,const char * data,int dataLength)192 void SimpleDataSource::didReceiveCachedMetadata(
193 WebKit::WebURLLoader* loader,
194 const char* data,
195 int dataLength) {
196 NOTIMPLEMENTED();
197 }
198
didFinishLoading(WebKit::WebURLLoader * loader,double finishTime)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
didFail(WebKit::WebURLLoader * loader,const WebKit::WebURLError & error)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
HasSingleOrigin()255 bool SimpleDataSource::HasSingleOrigin() {
256 DCHECK(MessageLoop::current() == render_loop_);
257 return single_origin_;
258 }
259
Abort()260 void SimpleDataSource::Abort() {
261 DCHECK(MessageLoop::current() == render_loop_);
262 frame_ = NULL;
263 }
264
SetURL(const GURL & url)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
StartTask()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
CancelTask()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
DoneInitialization_Locked(bool success)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
UpdateHostState()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