Update V8 to version 4.5.107.
[chromium-blink-merge.git] / media / blink / buffered_resource_loader.cc
blob81b76d03edc3d124ec34b9bf9b747ee2a6fceb9a
1 // Copyright 2013 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.
5 #include "media/blink/buffered_resource_loader.h"
7 #include "base/bits.h"
8 #include "base/callback_helpers.h"
9 #include "base/metrics/histogram.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "media/base/media_log.h"
13 #include "media/blink/cache_util.h"
14 #include "net/http/http_byte_range.h"
15 #include "net/http/http_request_headers.h"
16 #include "third_party/WebKit/public/platform/WebString.h"
17 #include "third_party/WebKit/public/platform/WebURLError.h"
18 #include "third_party/WebKit/public/platform/WebURLResponse.h"
19 #include "third_party/WebKit/public/web/WebKit.h"
20 #include "third_party/WebKit/public/web/WebURLLoaderOptions.h"
22 using blink::WebFrame;
23 using blink::WebString;
24 using blink::WebURLError;
25 using blink::WebURLLoader;
26 using blink::WebURLLoaderOptions;
27 using blink::WebURLRequest;
28 using blink::WebURLResponse;
30 namespace media {
32 static const int kHttpOK = 200;
33 static const int kHttpPartialContent = 206;
35 // Define the number of bytes in a megabyte.
36 static const int kMegabyte = 1024 * 1024;
38 // Minimum capacity of the buffer in forward or backward direction.
40 // 2MB is an arbitrary limit; it just seems to be "good enough" in practice.
41 static const int kMinBufferCapacity = 2 * kMegabyte;
43 // Maximum capacity of the buffer in forward or backward direction. This is
44 // effectively the largest single read the code path can handle.
45 // 20MB is an arbitrary limit; it just seems to be "good enough" in practice.
46 static const int kMaxBufferCapacity = 20 * kMegabyte;
48 // Maximum number of bytes outside the buffer we will wait for in order to
49 // fulfill a read. If a read starts more than 2MB away from the data we
50 // currently have in the buffer, we will not wait for buffer to reach the read's
51 // location and will instead reset the request.
52 static const int kForwardWaitThreshold = 2 * kMegabyte;
54 // Computes the suggested backward and forward capacity for the buffer
55 // if one wants to play at |playback_rate| * the natural playback speed.
56 // Use a value of 0 for |bitrate| if it is unknown.
57 static void ComputeTargetBufferWindow(double playback_rate, int bitrate,
58 int* out_backward_capacity,
59 int* out_forward_capacity) {
60 static const int kDefaultBitrate = 200 * 1024 * 8; // 200 Kbps.
61 static const int kMaxBitrate = 20 * kMegabyte * 8; // 20 Mbps.
62 static const double kMaxPlaybackRate = 25.0;
63 static const int kTargetSecondsBufferedAhead = 10;
64 static const int kTargetSecondsBufferedBehind = 2;
66 // Use a default bit rate if unknown and clamp to prevent overflow.
67 if (bitrate <= 0)
68 bitrate = kDefaultBitrate;
69 bitrate = std::min(bitrate, kMaxBitrate);
71 // Only scale the buffer window for playback rates greater than 1.0 in
72 // magnitude and clamp to prevent overflow.
73 bool backward_playback = false;
74 if (playback_rate < 0.0) {
75 backward_playback = true;
76 playback_rate *= -1.0;
79 playback_rate = std::max(playback_rate, 1.0);
80 playback_rate = std::min(playback_rate, kMaxPlaybackRate);
82 int bytes_per_second = (bitrate / 8.0) * playback_rate;
84 // Clamp between kMinBufferCapacity and kMaxBufferCapacity.
85 *out_forward_capacity = std::max(
86 kTargetSecondsBufferedAhead * bytes_per_second, kMinBufferCapacity);
87 *out_backward_capacity = std::max(
88 kTargetSecondsBufferedBehind * bytes_per_second, kMinBufferCapacity);
90 *out_forward_capacity = std::min(*out_forward_capacity, kMaxBufferCapacity);
91 *out_backward_capacity = std::min(*out_backward_capacity, kMaxBufferCapacity);
93 if (backward_playback)
94 std::swap(*out_forward_capacity, *out_backward_capacity);
97 BufferedResourceLoader::BufferedResourceLoader(
98 const GURL& url,
99 CORSMode cors_mode,
100 int64 first_byte_position,
101 int64 last_byte_position,
102 DeferStrategy strategy,
103 int bitrate,
104 double playback_rate,
105 MediaLog* media_log)
106 : buffer_(kMinBufferCapacity, kMinBufferCapacity),
107 loader_failed_(false),
108 defer_strategy_(strategy),
109 might_be_reused_from_cache_in_future_(true),
110 range_supported_(false),
111 saved_forward_capacity_(0),
112 url_(url),
113 cors_mode_(cors_mode),
114 first_byte_position_(first_byte_position),
115 last_byte_position_(last_byte_position),
116 single_origin_(true),
117 offset_(0),
118 content_length_(kPositionNotSpecified),
119 instance_size_(kPositionNotSpecified),
120 read_position_(0),
121 read_size_(0),
122 read_buffer_(NULL),
123 first_offset_(0),
124 last_offset_(0),
125 bitrate_(bitrate),
126 playback_rate_(playback_rate),
127 media_log_(media_log),
128 cancel_upon_deferral_(false) {
130 // Set the initial capacity of |buffer_| based on |bitrate_| and
131 // |playback_rate_|.
132 UpdateBufferWindow();
135 BufferedResourceLoader::~BufferedResourceLoader() {}
137 void BufferedResourceLoader::Start(
138 const StartCB& start_cb,
139 const LoadingStateChangedCB& loading_cb,
140 const ProgressCB& progress_cb,
141 WebFrame* frame) {
142 // Make sure we have not started.
143 DCHECK(start_cb_.is_null());
144 DCHECK(loading_cb_.is_null());
145 DCHECK(progress_cb_.is_null());
146 DCHECK(!start_cb.is_null());
147 DCHECK(!loading_cb.is_null());
148 DCHECK(!progress_cb.is_null());
149 CHECK(frame);
151 start_cb_ = start_cb;
152 loading_cb_ = loading_cb;
153 progress_cb_ = progress_cb;
155 if (first_byte_position_ != kPositionNotSpecified) {
156 // TODO(hclam): server may not support range request so |offset_| may not
157 // equal to |first_byte_position_|.
158 offset_ = first_byte_position_;
161 // Prepare the request.
162 WebURLRequest request(url_);
163 // TODO(mkwst): Split this into video/audio.
164 request.setRequestContext(WebURLRequest::RequestContextVideo);
166 if (IsRangeRequest()) {
167 request.setHTTPHeaderField(
168 WebString::fromUTF8(net::HttpRequestHeaders::kRange),
169 WebString::fromUTF8(net::HttpByteRange::Bounded(
170 first_byte_position_, last_byte_position_).GetHeaderValue()));
173 frame->setReferrerForRequest(request, blink::WebURL());
175 // Disable compression, compression for audio/video doesn't make sense...
176 request.setHTTPHeaderField(
177 WebString::fromUTF8(net::HttpRequestHeaders::kAcceptEncoding),
178 WebString::fromUTF8("identity;q=1, *;q=0"));
180 // Check for our test WebURLLoader.
181 scoped_ptr<WebURLLoader> loader;
182 if (test_loader_) {
183 loader = test_loader_.Pass();
184 } else {
185 WebURLLoaderOptions options;
186 if (cors_mode_ == kUnspecified) {
187 options.allowCredentials = true;
188 options.crossOriginRequestPolicy =
189 WebURLLoaderOptions::CrossOriginRequestPolicyAllow;
190 } else {
191 options.exposeAllResponseHeaders = true;
192 // The author header set is empty, no preflight should go ahead.
193 options.preflightPolicy = WebURLLoaderOptions::PreventPreflight;
194 options.crossOriginRequestPolicy =
195 WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
196 if (cors_mode_ == kUseCredentials)
197 options.allowCredentials = true;
199 loader.reset(frame->createAssociatedURLLoader(options));
202 // Start the resource loading.
203 loader->loadAsynchronously(request, this);
204 active_loader_.reset(new ActiveLoader(loader.Pass()));
205 loading_cb_.Run(kLoading);
208 void BufferedResourceLoader::Stop() {
209 // Reset callbacks.
210 start_cb_.Reset();
211 loading_cb_.Reset();
212 progress_cb_.Reset();
213 read_cb_.Reset();
215 // Cancel and reset any active loaders.
216 active_loader_.reset();
219 void BufferedResourceLoader::Read(
220 int64 position,
221 int read_size,
222 uint8* buffer,
223 const ReadCB& read_cb) {
224 DCHECK(start_cb_.is_null());
225 DCHECK(read_cb_.is_null());
226 DCHECK(!read_cb.is_null());
227 DCHECK(buffer);
228 DCHECK_GT(read_size, 0);
230 // Save the parameter of reading.
231 read_cb_ = read_cb;
232 read_position_ = position;
233 read_size_ = read_size;
234 read_buffer_ = buffer;
236 // Reads should immediately fail if the loader also failed.
237 if (loader_failed_) {
238 DoneRead(kFailed, 0);
239 return;
242 // If we're attempting to read past the end of the file, return a zero
243 // indicating EOF.
245 // This can happen with callees that read in fixed-sized amounts for parsing
246 // or at the end of chunked 200 responses when we discover the actual length
247 // of the file.
248 if (instance_size_ != kPositionNotSpecified &&
249 instance_size_ <= read_position_) {
250 DVLOG(1) << "Appear to have seeked beyond EOS; returning 0.";
251 DoneRead(kOk, 0);
252 return;
255 // Make sure |offset_| and |read_position_| does not differ by a large
256 // amount.
257 if (read_position_ > offset_ + kint32max ||
258 read_position_ < offset_ + kint32min) {
259 DoneRead(kCacheMiss, 0);
260 return;
263 // Make sure |read_size_| is not too large for the buffer to ever be able to
264 // fulfill the read request.
265 if (read_size_ > kMaxBufferCapacity) {
266 DoneRead(kFailed, 0);
267 return;
270 // Prepare the parameters.
271 first_offset_ = read_position_ - offset_;
272 last_offset_ = first_offset_ + read_size_;
274 // If we can serve the request now, do the actual read.
275 if (CanFulfillRead()) {
276 ReadInternal();
277 UpdateDeferBehavior();
278 return;
281 // If we expect the read request to be fulfilled later, expand capacity as
282 // necessary and disable deferring.
283 if (WillFulfillRead()) {
284 // Advance offset as much as possible to create additional capacity.
285 int advance = std::min(first_offset_, buffer_.forward_bytes());
286 bool ret = buffer_.Seek(advance);
287 DCHECK(ret);
289 offset_ += advance;
290 first_offset_ -= advance;
291 last_offset_ -= advance;
293 // Expand capacity to accomodate a read that extends past the normal
294 // capacity.
296 // This can happen when reading in a large seek index or when the
297 // first byte of a read request falls within kForwardWaitThreshold.
298 if (last_offset_ > buffer_.forward_capacity()) {
299 saved_forward_capacity_ = buffer_.forward_capacity();
300 buffer_.set_forward_capacity(last_offset_);
303 // Make sure we stop deferring now that there's additional capacity.
304 DCHECK(!ShouldDefer())
305 << "Capacity was not adjusted properly to prevent deferring.";
306 UpdateDeferBehavior();
308 return;
311 // Make a callback to report failure.
312 DoneRead(kCacheMiss, 0);
315 int64 BufferedResourceLoader::content_length() {
316 return content_length_;
319 int64 BufferedResourceLoader::instance_size() {
320 return instance_size_;
323 bool BufferedResourceLoader::range_supported() {
324 return range_supported_;
327 /////////////////////////////////////////////////////////////////////////////
328 // blink::WebURLLoaderClient implementation.
329 void BufferedResourceLoader::willSendRequest(
330 WebURLLoader* loader,
331 WebURLRequest& newRequest,
332 const WebURLResponse& redirectResponse) {
334 // The load may have been stopped and |start_cb| is destroyed.
335 // In this case we shouldn't do anything.
336 if (start_cb_.is_null()) {
337 // Set the url in the request to an invalid value (empty url).
338 newRequest.setURL(blink::WebURL());
339 return;
342 // Only allow |single_origin_| if we haven't seen a different origin yet.
343 if (single_origin_)
344 single_origin_ = url_.GetOrigin() == GURL(newRequest.url()).GetOrigin();
346 url_ = newRequest.url();
349 void BufferedResourceLoader::didSendData(
350 WebURLLoader* loader,
351 unsigned long long bytes_sent,
352 unsigned long long total_bytes_to_be_sent) {
353 NOTIMPLEMENTED();
356 void BufferedResourceLoader::didReceiveResponse(
357 WebURLLoader* loader,
358 const WebURLResponse& response) {
359 DVLOG(1) << "didReceiveResponse: HTTP/"
360 << (response.httpVersion() == WebURLResponse::HTTP_0_9 ? "0.9" :
361 response.httpVersion() == WebURLResponse::HTTP_1_0 ? "1.0" :
362 response.httpVersion() == WebURLResponse::HTTP_1_1 ? "1.1" :
363 "Unknown")
364 << " " << response.httpStatusCode();
365 DCHECK(active_loader_.get());
367 // The loader may have been stopped and |start_cb| is destroyed.
368 // In this case we shouldn't do anything.
369 if (start_cb_.is_null())
370 return;
372 uint32 reasons = GetReasonsForUncacheability(response);
373 might_be_reused_from_cache_in_future_ = reasons == 0;
374 UMA_HISTOGRAM_BOOLEAN("Media.CacheUseful", reasons == 0);
375 int shift = 0;
376 int max_enum = base::bits::Log2Ceiling(kMaxReason);
377 while (reasons) {
378 DCHECK_LT(shift, max_enum); // Sanity check.
379 if (reasons & 0x1) {
380 UMA_HISTOGRAM_ENUMERATION("Media.UncacheableReason",
381 shift,
382 max_enum); // PRESUBMIT_IGNORE_UMA_MAX
385 reasons >>= 1;
386 ++shift;
389 // Expected content length can be |kPositionNotSpecified|, in that case
390 // |content_length_| is not specified and this is a streaming response.
391 content_length_ = response.expectedContentLength();
393 // We make a strong assumption that when we reach here we have either
394 // received a response from HTTP/HTTPS protocol or the request was
395 // successful (in particular range request). So we only verify the partial
396 // response for HTTP and HTTPS protocol.
397 if (url_.SchemeIsHTTPOrHTTPS()) {
398 bool partial_response = (response.httpStatusCode() == kHttpPartialContent);
399 bool ok_response = (response.httpStatusCode() == kHttpOK);
401 if (IsRangeRequest()) {
402 // Check to see whether the server supports byte ranges.
403 std::string accept_ranges =
404 response.httpHeaderField("Accept-Ranges").utf8();
405 range_supported_ = (accept_ranges.find("bytes") != std::string::npos);
407 // If we have verified the partial response and it is correct, we will
408 // return kOk. It's also possible for a server to support range requests
409 // without advertising "Accept-Ranges: bytes".
410 if (partial_response && VerifyPartialResponse(response)) {
411 range_supported_ = true;
412 } else if (ok_response && first_byte_position_ == 0 &&
413 last_byte_position_ == kPositionNotSpecified) {
414 // We accept a 200 response for a Range:0- request, trusting the
415 // Accept-Ranges header, because Apache thinks that's a reasonable thing
416 // to return.
417 instance_size_ = content_length_;
418 } else {
419 MEDIA_LOG(ERROR, media_log_)
420 << "Failed loading buffered resource using range request due to "
421 "invalid server response. HTTP status code="
422 << response.httpStatusCode();
423 DoneStart(kFailed);
424 return;
426 } else {
427 instance_size_ = content_length_;
428 if (response.httpStatusCode() != kHttpOK) {
429 // We didn't request a range but server didn't reply with "200 OK".
430 MEDIA_LOG(ERROR, media_log_)
431 << "Failed loading buffered resource due to invalid server "
432 "response. HTTP status code=" << response.httpStatusCode();
433 DoneStart(kFailed);
434 return;
438 } else {
439 CHECK_EQ(instance_size_, kPositionNotSpecified);
440 if (content_length_ != kPositionNotSpecified) {
441 if (first_byte_position_ == kPositionNotSpecified)
442 instance_size_ = content_length_;
443 else if (last_byte_position_ == kPositionNotSpecified)
444 instance_size_ = content_length_ + first_byte_position_;
448 // Calls with a successful response.
449 DoneStart(kOk);
452 void BufferedResourceLoader::didReceiveData(
453 WebURLLoader* loader,
454 const char* data,
455 int data_length,
456 int encoded_data_length) {
457 DVLOG(1) << "didReceiveData: " << data_length << " bytes";
458 DCHECK(active_loader_.get());
459 DCHECK_GT(data_length, 0);
461 buffer_.Append(reinterpret_cast<const uint8*>(data), data_length);
463 // If there is an active read request, try to fulfill the request.
464 if (HasPendingRead() && CanFulfillRead())
465 ReadInternal();
467 // At last see if the buffer is full and we need to defer the downloading.
468 UpdateDeferBehavior();
470 // Consume excess bytes from our in-memory buffer if necessary.
471 if (buffer_.forward_bytes() > buffer_.forward_capacity()) {
472 int excess = buffer_.forward_bytes() - buffer_.forward_capacity();
473 bool success = buffer_.Seek(excess);
474 DCHECK(success);
475 offset_ += first_offset_ + excess;
478 // Notify latest progress and buffered offset.
479 progress_cb_.Run(offset_ + buffer_.forward_bytes() - 1);
480 Log();
483 void BufferedResourceLoader::didDownloadData(
484 blink::WebURLLoader* loader,
485 int dataLength,
486 int encoded_data_length) {
487 NOTIMPLEMENTED();
490 void BufferedResourceLoader::didReceiveCachedMetadata(
491 WebURLLoader* loader,
492 const char* data,
493 int data_length) {
494 NOTIMPLEMENTED();
497 void BufferedResourceLoader::didFinishLoading(
498 WebURLLoader* loader,
499 double finishTime,
500 int64_t total_encoded_data_length) {
501 DVLOG(1) << "didFinishLoading";
502 DCHECK(active_loader_.get());
504 // We're done with the loader.
505 active_loader_.reset();
506 loading_cb_.Run(kLoadingFinished);
508 // If we didn't know the |instance_size_| we do now.
509 if (instance_size_ == kPositionNotSpecified) {
510 instance_size_ = offset_ + buffer_.forward_bytes();
513 // If there is a start callback, run it.
514 if (!start_cb_.is_null()) {
515 DCHECK(read_cb_.is_null())
516 << "Shouldn't have a read callback during start";
517 DoneStart(kOk);
518 return;
521 // Don't leave read callbacks hanging around.
522 if (HasPendingRead()) {
523 // Try to fulfill with what is in the buffer.
524 if (CanFulfillRead())
525 ReadInternal();
526 else
527 DoneRead(kCacheMiss, 0);
531 void BufferedResourceLoader::didFail(
532 WebURLLoader* loader,
533 const WebURLError& error) {
534 DVLOG(1) << "didFail: reason=" << error.reason
535 << ", isCancellation=" << error.isCancellation
536 << ", domain=" << error.domain.utf8().data()
537 << ", localizedDescription="
538 << error.localizedDescription.utf8().data();
539 DCHECK(active_loader_.get());
540 MEDIA_LOG(ERROR, media_log_)
541 << "Failed loading buffered resource. Error code=" << error.reason;
543 // We don't need to continue loading after failure.
545 // Keep it alive until we exit this method so that |error| remains valid.
546 scoped_ptr<ActiveLoader> active_loader = active_loader_.Pass();
547 loader_failed_ = true;
548 loading_cb_.Run(kLoadingFailed);
550 // Don't leave start callbacks hanging around.
551 if (!start_cb_.is_null()) {
552 DCHECK(read_cb_.is_null())
553 << "Shouldn't have a read callback during start";
554 DoneStart(kFailed);
555 return;
558 // Don't leave read callbacks hanging around.
559 if (HasPendingRead()) {
560 DoneRead(kFailed, 0);
564 bool BufferedResourceLoader::HasSingleOrigin() const {
565 DCHECK(start_cb_.is_null())
566 << "Start() must complete before calling HasSingleOrigin()";
567 return single_origin_;
570 bool BufferedResourceLoader::DidPassCORSAccessCheck() const {
571 DCHECK(start_cb_.is_null())
572 << "Start() must complete before calling DidPassCORSAccessCheck()";
573 return !loader_failed_ && cors_mode_ != kUnspecified;
576 void BufferedResourceLoader::UpdateDeferStrategy(DeferStrategy strategy) {
577 if (!might_be_reused_from_cache_in_future_ && strategy == kNeverDefer)
578 strategy = kCapacityDefer;
579 defer_strategy_ = strategy;
580 UpdateDeferBehavior();
583 void BufferedResourceLoader::SetPlaybackRate(double playback_rate) {
584 playback_rate_ = playback_rate;
586 // This is a pause so don't bother updating the buffer window as we'll likely
587 // get unpaused in the future.
588 if (playback_rate_ == 0.0)
589 return;
591 // Abort any cancellations in progress if playback starts.
592 if (playback_rate_ > 0 && cancel_upon_deferral_)
593 cancel_upon_deferral_ = false;
595 UpdateBufferWindow();
598 void BufferedResourceLoader::SetBitrate(int bitrate) {
599 DCHECK(bitrate >= 0);
600 bitrate_ = bitrate;
601 UpdateBufferWindow();
604 /////////////////////////////////////////////////////////////////////////////
605 // Helper methods.
607 void BufferedResourceLoader::UpdateBufferWindow() {
608 int backward_capacity;
609 int forward_capacity;
610 ComputeTargetBufferWindow(
611 playback_rate_, bitrate_, &backward_capacity, &forward_capacity);
613 // This does not evict data from the buffer if the new capacities are less
614 // than the current capacities; the new limits will be enforced after the
615 // existing excess buffered data is consumed.
616 buffer_.set_backward_capacity(backward_capacity);
617 buffer_.set_forward_capacity(forward_capacity);
620 void BufferedResourceLoader::UpdateDeferBehavior() {
621 if (!active_loader_)
622 return;
624 SetDeferred(ShouldDefer());
627 void BufferedResourceLoader::SetDeferred(bool deferred) {
628 if (active_loader_->deferred() == deferred)
629 return;
631 active_loader_->SetDeferred(deferred);
632 loading_cb_.Run(deferred ? kLoadingDeferred : kLoading);
634 if (deferred && cancel_upon_deferral_)
635 CancelUponDeferral();
638 bool BufferedResourceLoader::ShouldDefer() const {
639 switch(defer_strategy_) {
640 case kNeverDefer:
641 return false;
643 case kReadThenDefer:
644 DCHECK(read_cb_.is_null() || last_offset_ > buffer_.forward_bytes())
645 << "We shouldn't stop deferring if we can fulfill the read";
646 return read_cb_.is_null();
648 case kCapacityDefer:
649 return buffer_.forward_bytes() >= buffer_.forward_capacity();
651 NOTREACHED();
652 return false;
655 bool BufferedResourceLoader::CanFulfillRead() const {
656 // If we are reading too far in the backward direction.
657 if (first_offset_ < 0 && (first_offset_ + buffer_.backward_bytes()) < 0)
658 return false;
660 // If the start offset is too far ahead.
661 if (first_offset_ >= buffer_.forward_bytes())
662 return false;
664 // At the point, we verified that first byte requested is within the buffer.
665 // If the request has completed, then just returns with what we have now.
666 if (!active_loader_)
667 return true;
669 // If the resource request is still active, make sure the whole requested
670 // range is covered.
671 if (last_offset_ > buffer_.forward_bytes())
672 return false;
674 return true;
677 bool BufferedResourceLoader::WillFulfillRead() const {
678 // Trying to read too far behind.
679 if (first_offset_ < 0 && (first_offset_ + buffer_.backward_bytes()) < 0)
680 return false;
682 // Trying to read too far ahead.
683 if ((first_offset_ - buffer_.forward_bytes()) >= kForwardWaitThreshold)
684 return false;
686 // The resource request has completed, there's no way we can fulfill the
687 // read request.
688 if (!active_loader_)
689 return false;
691 return true;
694 void BufferedResourceLoader::ReadInternal() {
695 // Seek to the first byte requested.
696 bool ret = buffer_.Seek(first_offset_);
697 DCHECK(ret);
699 // Then do the read.
700 int read = buffer_.Read(read_buffer_, read_size_);
701 offset_ += first_offset_ + read;
703 // And report with what we have read.
704 DoneRead(kOk, read);
707 int64 BufferedResourceLoader::first_byte_position() const {
708 return first_byte_position_;
711 // static
712 bool BufferedResourceLoader::ParseContentRange(
713 const std::string& content_range_str, int64* first_byte_position,
714 int64* last_byte_position, int64* instance_size) {
715 const std::string kUpThroughBytesUnit = "bytes ";
716 if (content_range_str.find(kUpThroughBytesUnit) != 0)
717 return false;
718 std::string range_spec =
719 content_range_str.substr(kUpThroughBytesUnit.length());
720 size_t dash_offset = range_spec.find("-");
721 size_t slash_offset = range_spec.find("/");
723 if (dash_offset == std::string::npos || slash_offset == std::string::npos ||
724 slash_offset < dash_offset || slash_offset + 1 == range_spec.length()) {
725 return false;
727 if (!base::StringToInt64(range_spec.substr(0, dash_offset),
728 first_byte_position) ||
729 !base::StringToInt64(range_spec.substr(dash_offset + 1,
730 slash_offset - dash_offset - 1),
731 last_byte_position)) {
732 return false;
734 if (slash_offset == range_spec.length() - 2 &&
735 range_spec[slash_offset + 1] == '*') {
736 *instance_size = kPositionNotSpecified;
737 } else {
738 if (!base::StringToInt64(range_spec.substr(slash_offset + 1),
739 instance_size)) {
740 return false;
743 if (*last_byte_position < *first_byte_position ||
744 (*instance_size != kPositionNotSpecified &&
745 *last_byte_position >= *instance_size)) {
746 return false;
749 return true;
752 void BufferedResourceLoader::CancelUponDeferral() {
753 cancel_upon_deferral_ = true;
754 if (active_loader_ && active_loader_->deferred())
755 active_loader_.reset();
758 bool BufferedResourceLoader::VerifyPartialResponse(
759 const WebURLResponse& response) {
760 int64 first_byte_position, last_byte_position, instance_size;
761 if (!ParseContentRange(response.httpHeaderField("Content-Range").utf8(),
762 &first_byte_position, &last_byte_position,
763 &instance_size)) {
764 return false;
767 if (instance_size != kPositionNotSpecified) {
768 instance_size_ = instance_size;
771 if (first_byte_position_ != kPositionNotSpecified &&
772 first_byte_position_ != first_byte_position) {
773 return false;
776 // TODO(hclam): I should also check |last_byte_position|, but since
777 // we will never make such a request that it is ok to leave it unimplemented.
778 return true;
781 void BufferedResourceLoader::DoneRead(Status status, int bytes_read) {
782 if (saved_forward_capacity_) {
783 buffer_.set_forward_capacity(saved_forward_capacity_);
784 saved_forward_capacity_ = 0;
786 read_position_ = 0;
787 read_size_ = 0;
788 read_buffer_ = NULL;
789 first_offset_ = 0;
790 last_offset_ = 0;
791 Log();
793 base::ResetAndReturn(&read_cb_).Run(status, bytes_read);
797 void BufferedResourceLoader::DoneStart(Status status) {
798 base::ResetAndReturn(&start_cb_).Run(status);
801 bool BufferedResourceLoader::IsRangeRequest() const {
802 return first_byte_position_ != kPositionNotSpecified;
805 void BufferedResourceLoader::Log() {
806 media_log_->AddEvent(
807 media_log_->CreateBufferedExtentsChangedEvent(
808 offset_ - buffer_.backward_bytes(),
809 offset_,
810 offset_ + buffer_.forward_bytes()));
813 } // namespace media