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