Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2010 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/buffered_resource_loader.h" | |
| 6 | |
| 7 #include "base/format_macros.h" | |
| 8 #include "base/string_util.h" | |
| 9 #include "net/base/net_errors.h" | |
| 10 #include "net/http/http_util.h" | |
| 11 #include "third_party/WebKit/WebKit/chromium/public/WebKit.h" | |
| 12 #include "third_party/WebKit/WebKit/chromium/public/WebKitClient.h" | |
| 13 #include "third_party/WebKit/WebKit/chromium/public/WebString.h" | |
| 14 #include "third_party/WebKit/WebKit/chromium/public/WebURLError.h" | |
| 15 #include "webkit/glue/multipart_response_delegate.h" | |
| 16 #include "webkit/glue/webkit_glue.h" | |
| 17 | |
| 18 using WebKit::WebFrame; | |
| 19 using WebKit::WebString; | |
| 20 using WebKit::WebURLError; | |
| 21 using WebKit::WebURLLoader; | |
| 22 using WebKit::WebURLRequest; | |
| 23 using WebKit::WebURLResponse; | |
| 24 using webkit_glue::MultipartResponseDelegate; | |
| 25 | |
| 26 namespace webkit_glue { | |
| 27 | |
| 28 BufferedResourceLoader::BufferedResourceLoader( | |
| 29 const GURL& url, | |
| 30 int64 first_byte_position, | |
| 31 int64 last_byte_position) | |
| 32 : buffer_(new media::SeekableBuffer(net::kBackwardCapcity, net::kForwardCapa city)), | |
|
scherkus (not reviewing)
2010/12/14 18:48:19
>80 chars
annacc
2010/12/14 21:10:17
Done.
| |
| 33 deferred_(false), | |
| 34 defer_allowed_(true), | |
| 35 completed_(false), | |
| 36 range_requested_(false), | |
| 37 partial_response_(false), | |
| 38 url_(url), | |
| 39 first_byte_position_(first_byte_position), | |
| 40 last_byte_position_(last_byte_position), | |
| 41 start_callback_(NULL), | |
| 42 offset_(0), | |
| 43 content_length_(net::kPositionNotSpecified), | |
| 44 instance_size_(net::kPositionNotSpecified), | |
| 45 read_callback_(NULL), | |
| 46 read_position_(0), | |
| 47 read_size_(0), | |
| 48 read_buffer_(NULL), | |
| 49 first_offset_(0), | |
| 50 last_offset_(0), | |
| 51 keep_test_loader_(false) { | |
| 52 } | |
| 53 | |
| 54 BufferedResourceLoader::~BufferedResourceLoader() { | |
| 55 if (!completed_ && url_loader_.get()) | |
| 56 url_loader_->cancel(); | |
| 57 } | |
| 58 | |
| 59 void BufferedResourceLoader::Start(net::CompletionCallback* start_callback, | |
| 60 NetworkEventCallback* event_callback, | |
| 61 WebFrame* frame) { | |
| 62 // Make sure we have not started. | |
| 63 DCHECK(!start_callback_.get()); | |
| 64 DCHECK(!event_callback_.get()); | |
| 65 DCHECK(start_callback); | |
| 66 DCHECK(event_callback); | |
| 67 CHECK(frame); | |
| 68 | |
| 69 start_callback_.reset(start_callback); | |
| 70 event_callback_.reset(event_callback); | |
| 71 | |
| 72 if (first_byte_position_ != net::kPositionNotSpecified) { | |
| 73 range_requested_ = true; | |
| 74 // TODO(hclam): server may not support range request so |offset_| may not | |
| 75 // equal to |first_byte_position_|. | |
| 76 offset_ = first_byte_position_; | |
| 77 } | |
| 78 | |
| 79 // Increment the reference count right before we start the request. This | |
| 80 // reference will be release when this request has ended. | |
| 81 AddRef(); | |
| 82 | |
| 83 // Prepare the request. | |
| 84 WebURLRequest request(url_); | |
| 85 request.setHTTPHeaderField(WebString::fromUTF8("Range"), | |
| 86 WebString::fromUTF8(GenerateHeaders( | |
| 87 first_byte_position_, | |
| 88 last_byte_position_))); | |
| 89 frame->setReferrerForRequest(request, WebKit::WebURL()); | |
| 90 | |
| 91 // This flag is for unittests as we don't want to reset |url_loader| | |
| 92 if (!keep_test_loader_) | |
| 93 url_loader_.reset(frame->createAssociatedURLLoader()); | |
| 94 | |
| 95 // Start the resource loading. | |
| 96 url_loader_->loadAsynchronously(request, this); | |
| 97 } | |
| 98 | |
| 99 void BufferedResourceLoader::Stop() { | |
| 100 // Reset callbacks. | |
| 101 start_callback_.reset(); | |
| 102 event_callback_.reset(); | |
| 103 read_callback_.reset(); | |
| 104 | |
| 105 // Use the internal buffer to signal that we have been stopped. | |
| 106 // TODO(hclam): Not so pretty to do this. | |
| 107 if (!buffer_.get()) | |
| 108 return; | |
| 109 | |
| 110 // Destroy internal buffer. | |
| 111 buffer_.reset(); | |
| 112 | |
| 113 if (url_loader_.get()) { | |
| 114 if (deferred_) | |
| 115 url_loader_->setDefersLoading(false); | |
| 116 deferred_ = false; | |
| 117 | |
| 118 if (!completed_) { | |
| 119 url_loader_->cancel(); | |
| 120 completed_ = true; | |
| 121 } | |
| 122 } | |
| 123 } | |
| 124 | |
| 125 void BufferedResourceLoader::Read(int64 position, | |
| 126 int read_size, | |
| 127 uint8* buffer, | |
| 128 net::CompletionCallback* read_callback) { | |
| 129 DCHECK(!read_callback_.get()); | |
| 130 DCHECK(buffer_.get()); | |
| 131 DCHECK(read_callback); | |
| 132 DCHECK(buffer); | |
| 133 | |
| 134 // Save the parameter of reading. | |
| 135 read_callback_.reset(read_callback); | |
| 136 read_position_ = position; | |
| 137 read_size_ = read_size; | |
| 138 read_buffer_ = buffer; | |
| 139 | |
| 140 // If read position is beyond the instance size, we cannot read there. | |
| 141 if (instance_size_ != net::kPositionNotSpecified && | |
| 142 instance_size_ <= read_position_) { | |
| 143 DoneRead(0); | |
| 144 return; | |
| 145 } | |
| 146 | |
| 147 // Make sure |offset_| and |read_position_| does not differ by a large | |
| 148 // amount. | |
| 149 if (read_position_ > offset_ + kint32max || | |
| 150 read_position_ < offset_ + kint32min) { | |
| 151 DoneRead(net::ERR_CACHE_MISS); | |
| 152 return; | |
| 153 } | |
| 154 | |
| 155 // Prepare the parameters. | |
| 156 first_offset_ = static_cast<int>(read_position_ - offset_); | |
| 157 last_offset_ = first_offset_ + read_size_; | |
| 158 | |
| 159 // If we can serve the request now, do the actual read. | |
| 160 if (CanFulfillRead()) { | |
| 161 ReadInternal(); | |
| 162 DisableDeferIfNeeded(); | |
| 163 return; | |
| 164 } | |
| 165 | |
| 166 // If we expected the read request to be fulfilled later, returns | |
| 167 // immediately and let more data to flow in. | |
| 168 if (WillFulfillRead()) | |
| 169 return; | |
| 170 | |
| 171 // Make a callback to report failure. | |
| 172 DoneRead(net::ERR_CACHE_MISS); | |
| 173 } | |
| 174 | |
| 175 int64 BufferedResourceLoader::GetBufferedFirstBytePosition() { | |
| 176 if (buffer_.get()) | |
| 177 return offset_ - static_cast<int>(buffer_->backward_bytes()); | |
| 178 return net::kPositionNotSpecified; | |
| 179 } | |
| 180 | |
| 181 int64 BufferedResourceLoader::GetBufferedLastBytePosition() { | |
| 182 if (buffer_.get()) | |
| 183 return offset_ + static_cast<int>(buffer_->forward_bytes()) - 1; | |
| 184 return net::kPositionNotSpecified; | |
| 185 } | |
| 186 | |
| 187 void BufferedResourceLoader::SetAllowDefer(bool is_allowed) { | |
| 188 defer_allowed_ = is_allowed; | |
| 189 DisableDeferIfNeeded(); | |
| 190 } | |
| 191 | |
| 192 void BufferedResourceLoader::SetURLLoaderForTest(WebURLLoader* mock_loader) { | |
| 193 url_loader_.reset(mock_loader); | |
| 194 keep_test_loader_ = true; | |
| 195 } | |
| 196 | |
| 197 // WebKit::WebURLLoaderClient implementations. | |
| 198 void BufferedResourceLoader::willSendRequest( | |
| 199 WebURLLoader* loader, | |
| 200 WebURLRequest& newRequest, | |
| 201 const WebURLResponse& redirectResponse) { | |
| 202 | |
| 203 // The load may have been stopped and |start_callback| is destroyed. | |
| 204 // In this case we shouldn't do anything. | |
| 205 if (!start_callback_.get()) { | |
| 206 // Set the url in the request to an invalid value (empty url). | |
| 207 newRequest.setURL(WebKit::WebURL()); | |
| 208 return; | |
| 209 } | |
| 210 | |
| 211 if (!IsProtocolSupportedForMedia(newRequest.url())) { | |
| 212 // Set the url in the request to an invalid value (empty url). | |
| 213 newRequest.setURL(WebKit::WebURL()); | |
| 214 DoneStart(net::ERR_ADDRESS_INVALID); | |
| 215 Stop(); | |
| 216 return; | |
| 217 } | |
| 218 | |
| 219 url_ = newRequest.url(); | |
| 220 } | |
| 221 | |
| 222 void BufferedResourceLoader::didSendData( | |
| 223 WebURLLoader* loader, | |
| 224 unsigned long long bytes_sent, | |
| 225 unsigned long long total_bytes_to_be_sent) { | |
| 226 NOTIMPLEMENTED(); | |
| 227 } | |
| 228 | |
| 229 void BufferedResourceLoader::didReceiveResponse( | |
| 230 WebURLLoader* loader, | |
| 231 const WebURLResponse& response) { | |
| 232 | |
| 233 // The loader may have been stopped and |start_callback| is destroyed. | |
| 234 // In this case we shouldn't do anything. | |
| 235 if (!start_callback_.get()) | |
| 236 return; | |
| 237 | |
| 238 // We make a strong assumption that when we reach here we have either | |
| 239 // received a response from HTTP/HTTPS protocol or the request was | |
| 240 // successful (in particular range request). So we only verify the partial | |
| 241 // response for HTTP and HTTPS protocol. | |
| 242 if (net::HttpUtil::IsHttpProtocol(url_)) { | |
| 243 int error = net::OK; | |
| 244 | |
| 245 if (response.httpStatusCode() == net::kHttpPartialContent) | |
| 246 partial_response_ = true; | |
| 247 | |
| 248 if (range_requested_ && partial_response_) { | |
| 249 // If we have verified the partial response and it is correct, we will | |
| 250 // return net::OK. | |
| 251 if (!VerifyPartialResponse(response)) | |
| 252 error = net::ERR_INVALID_RESPONSE; | |
| 253 } else if (response.httpStatusCode() != net::kHttpOK) { | |
| 254 // We didn't request a range but server didn't reply with "200 OK". | |
| 255 error = net::ERR_FAILED; | |
| 256 } | |
| 257 | |
| 258 if (error != net::OK) { | |
| 259 DoneStart(error); | |
| 260 Stop(); | |
| 261 return; | |
| 262 } | |
| 263 } else { | |
| 264 // For any protocol other than HTTP and HTTPS, assume range request is | |
| 265 // always fulfilled. | |
| 266 partial_response_ = range_requested_; | |
| 267 } | |
| 268 | |
| 269 // Expected content length can be -1, in that case |content_length_| is | |
| 270 // not specified and this is a streaming response. | |
| 271 content_length_ = response.expectedContentLength(); | |
| 272 | |
| 273 // If we have not requested a range, then the size of the instance is equal | |
| 274 // to the content length. | |
| 275 if (!partial_response_) | |
| 276 instance_size_ = content_length_; | |
| 277 | |
| 278 // Calls with a successful response. | |
| 279 DoneStart(net::OK); | |
| 280 } | |
| 281 | |
| 282 void BufferedResourceLoader::didReceiveData( | |
| 283 WebURLLoader* loader, | |
| 284 const char* data, | |
| 285 int data_length) { | |
| 286 DCHECK(!completed_); | |
| 287 DCHECK_GT(data_length, 0); | |
| 288 | |
| 289 // If this loader has been stopped, |buffer_| would be destroyed. | |
| 290 // In this case we shouldn't do anything. | |
| 291 if (!buffer_.get()) | |
| 292 return; | |
| 293 | |
| 294 // Writes more data to |buffer_|. | |
| 295 buffer_->Append(reinterpret_cast<const uint8*>(data), data_length); | |
| 296 | |
| 297 // If there is an active read request, try to fulfill the request. | |
| 298 if (HasPendingRead() && CanFulfillRead()) { | |
| 299 ReadInternal(); | |
| 300 } else if (!defer_allowed_) { | |
| 301 // If we're not allowed to defer, slide the buffer window forward instead | |
| 302 // of deferring. | |
| 303 if (buffer_->forward_bytes() > buffer_->forward_capacity()) { | |
| 304 size_t excess = buffer_->forward_bytes() - buffer_->forward_capacity(); | |
| 305 bool success = buffer_->Seek(excess); | |
| 306 DCHECK(success); | |
| 307 offset_ += first_offset_ + excess; | |
| 308 } | |
| 309 } | |
| 310 | |
| 311 // At last see if the buffer is full and we need to defer the downloading. | |
| 312 EnableDeferIfNeeded(); | |
| 313 | |
| 314 // Notify that we have received some data. | |
| 315 NotifyNetworkEvent(); | |
| 316 } | |
| 317 | |
| 318 void BufferedResourceLoader::didDownloadData( | |
| 319 WebKit::WebURLLoader* loader, | |
| 320 int dataLength) { | |
| 321 NOTIMPLEMENTED(); | |
| 322 } | |
| 323 | |
| 324 void BufferedResourceLoader::didReceiveCachedMetadata( | |
| 325 WebURLLoader* loader, | |
| 326 const char* data, | |
| 327 int data_length) { | |
| 328 NOTIMPLEMENTED(); | |
| 329 } | |
| 330 | |
| 331 void BufferedResourceLoader::didFinishLoading( | |
| 332 WebURLLoader* loader, | |
| 333 double finishTime) { | |
| 334 DCHECK(!completed_); | |
| 335 completed_ = true; | |
| 336 | |
| 337 // If there is a start callback, calls it. | |
| 338 if (start_callback_.get()) { | |
| 339 DoneStart(net::OK); | |
| 340 } | |
| 341 | |
| 342 // If there is a pending read but the request has ended, returns with what | |
| 343 // we have. | |
| 344 if (HasPendingRead()) { | |
| 345 // Make sure we have a valid buffer before we satisfy a read request. | |
| 346 DCHECK(buffer_.get()); | |
| 347 | |
| 348 // Try to fulfill with what is in the buffer. | |
| 349 if (CanFulfillRead()) | |
| 350 ReadInternal(); | |
| 351 else | |
| 352 DoneRead(net::ERR_CACHE_MISS); | |
| 353 } | |
| 354 | |
| 355 // There must not be any outstanding read request. | |
| 356 DCHECK(!HasPendingRead()); | |
| 357 | |
| 358 // Notify that network response is completed. | |
| 359 NotifyNetworkEvent(); | |
| 360 | |
| 361 url_loader_.reset(); | |
| 362 Release(); | |
| 363 } | |
| 364 | |
| 365 void BufferedResourceLoader::didFail( | |
| 366 WebURLLoader* loader, | |
| 367 const WebURLError& error) { | |
| 368 DCHECK(!completed_); | |
| 369 completed_ = true; | |
| 370 | |
| 371 // If there is a start callback, calls it. | |
| 372 if (start_callback_.get()) { | |
| 373 DoneStart(error.reason); | |
| 374 } | |
| 375 | |
| 376 // If there is a pending read but the request failed, return with the | |
| 377 // reason for the error. | |
| 378 if (HasPendingRead()) { | |
| 379 DoneRead(error.reason); | |
| 380 } | |
| 381 | |
| 382 // Notify that network response is completed. | |
| 383 NotifyNetworkEvent(); | |
| 384 | |
| 385 url_loader_.reset(); | |
| 386 Release(); | |
| 387 } | |
| 388 | |
| 389 void BufferedResourceLoader::EnableDeferIfNeeded() { | |
| 390 if (!defer_allowed_) | |
| 391 return; | |
| 392 | |
| 393 if (!deferred_ && | |
| 394 buffer_->forward_bytes() >= buffer_->forward_capacity()) { | |
| 395 deferred_ = true; | |
| 396 | |
| 397 if (url_loader_.get()) | |
| 398 url_loader_->setDefersLoading(true); | |
| 399 | |
| 400 NotifyNetworkEvent(); | |
| 401 } | |
| 402 } | |
| 403 | |
| 404 void BufferedResourceLoader::DisableDeferIfNeeded() { | |
| 405 if (deferred_ && | |
| 406 (!defer_allowed_ || | |
| 407 buffer_->forward_bytes() < buffer_->forward_capacity() / 2)) { | |
| 408 deferred_ = false; | |
| 409 | |
| 410 if (url_loader_.get()) | |
| 411 url_loader_->setDefersLoading(false); | |
| 412 | |
| 413 NotifyNetworkEvent(); | |
| 414 } | |
| 415 } | |
| 416 | |
| 417 bool BufferedResourceLoader::CanFulfillRead() { | |
| 418 // If we are reading too far in the backward direction. | |
| 419 if (first_offset_ < 0 && | |
| 420 first_offset_ + static_cast<int>(buffer_->backward_bytes()) < 0) | |
| 421 return false; | |
| 422 | |
| 423 // If the start offset is too far ahead. | |
| 424 if (first_offset_ >= static_cast<int>(buffer_->forward_bytes())) | |
| 425 return false; | |
| 426 | |
| 427 // At the point, we verified that first byte requested is within the buffer. | |
| 428 // If the request has completed, then just returns with what we have now. | |
| 429 if (completed_) | |
| 430 return true; | |
| 431 | |
| 432 // If the resource request is still active, make sure the whole requested | |
| 433 // range is covered. | |
| 434 if (last_offset_ > static_cast<int>(buffer_->forward_bytes())) | |
| 435 return false; | |
| 436 | |
| 437 return true; | |
| 438 } | |
| 439 | |
| 440 bool BufferedResourceLoader::WillFulfillRead() { | |
| 441 // Reading too far in the backward direction. | |
| 442 if (first_offset_ < 0 && | |
| 443 first_offset_ + static_cast<int>(buffer_->backward_bytes()) < 0) | |
| 444 return false; | |
| 445 | |
| 446 // Try to read too far ahead. | |
| 447 if (last_offset_ > net::kForwardWaitThreshold) | |
| 448 return false; | |
| 449 | |
| 450 // The resource request has completed, there's no way we can fulfill the | |
| 451 // read request. | |
| 452 if (completed_) | |
| 453 return false; | |
| 454 | |
| 455 return true; | |
| 456 } | |
| 457 | |
| 458 void BufferedResourceLoader::ReadInternal() { | |
| 459 // Seek to the first byte requested. | |
| 460 bool ret = buffer_->Seek(first_offset_); | |
| 461 DCHECK(ret); | |
| 462 | |
| 463 // Then do the read. | |
| 464 int read = static_cast<int>(buffer_->Read(read_buffer_, read_size_)); | |
| 465 offset_ += first_offset_ + read; | |
| 466 | |
| 467 // And report with what we have read. | |
| 468 DoneRead(read); | |
| 469 } | |
| 470 | |
| 471 bool BufferedResourceLoader::VerifyPartialResponse( | |
| 472 const WebURLResponse& response) { | |
| 473 int first_byte_position, last_byte_position, instance_size; | |
| 474 | |
| 475 if (!MultipartResponseDelegate::ReadContentRanges(response, | |
| 476 &first_byte_position, | |
| 477 &last_byte_position, | |
| 478 &instance_size)) { | |
| 479 return false; | |
| 480 } | |
| 481 | |
| 482 if (instance_size != net::kPositionNotSpecified) { | |
| 483 instance_size_ = instance_size; | |
| 484 } | |
| 485 | |
| 486 if (first_byte_position_ != net::kPositionNotSpecified && | |
| 487 first_byte_position_ != first_byte_position) { | |
| 488 return false; | |
| 489 } | |
| 490 | |
| 491 // TODO(hclam): I should also check |last_byte_position|, but since | |
| 492 // we will never make such a request that it is ok to leave it unimplemented. | |
| 493 return true; | |
| 494 } | |
| 495 | |
| 496 std::string BufferedResourceLoader::GenerateHeaders( | |
| 497 int64 first_byte_position, | |
| 498 int64 last_byte_position) { | |
| 499 // Construct the value for the range header. | |
| 500 std::string header; | |
| 501 if (first_byte_position > net::kPositionNotSpecified && | |
| 502 last_byte_position > net::kPositionNotSpecified) { | |
| 503 if (first_byte_position <= last_byte_position) { | |
| 504 header = base::StringPrintf("bytes=%" PRId64 "-%" PRId64, | |
| 505 first_byte_position, | |
| 506 last_byte_position); | |
| 507 } | |
| 508 } else if (first_byte_position > net::kPositionNotSpecified) { | |
| 509 header = base::StringPrintf("bytes=%" PRId64 "-", | |
| 510 first_byte_position); | |
| 511 } else if (last_byte_position > net::kPositionNotSpecified) { | |
| 512 NOTIMPLEMENTED() << "Suffix range not implemented"; | |
| 513 } | |
| 514 return header; | |
| 515 } | |
| 516 | |
| 517 void BufferedResourceLoader::DoneRead(int error) { | |
| 518 read_callback_->RunWithParams(Tuple1<int>(error)); | |
| 519 read_callback_.reset(); | |
| 520 read_position_ = 0; | |
| 521 read_size_ = 0; | |
| 522 read_buffer_ = NULL; | |
| 523 first_offset_ = 0; | |
| 524 last_offset_ = 0; | |
| 525 } | |
| 526 | |
| 527 void BufferedResourceLoader::DoneStart(int error) { | |
| 528 start_callback_->RunWithParams(Tuple1<int>(error)); | |
| 529 start_callback_.reset(); | |
| 530 } | |
| 531 | |
| 532 void BufferedResourceLoader::NotifyNetworkEvent() { | |
| 533 if (event_callback_.get()) | |
| 534 event_callback_->Run(); | |
| 535 } | |
| 536 | |
| 537 } // namespace webkit_glue | |
| OLD | NEW |