3177209033a2c7134a954cfe96b78dacf6c8ad59
[platform/framework/web/crosswalk.git] / src / content / renderer / media / buffered_resource_loader.cc
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.
4
5 #include "content/renderer/media/buffered_resource_loader.h"
6
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 "content/renderer/media/cache_util.h"
13 #include "media/base/media_log.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"
21
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;
29
30 namespace content {
31
32 static const int kHttpOK = 200;
33 static const int kHttpPartialContent = 206;
34
35 // Define the number of bytes in a megabyte.
36 static const int kMegabyte = 1024 * 1024;
37
38 // Minimum capacity of the buffer in forward or backward direction.
39 //
40 // 2MB is an arbitrary limit; it just seems to be "good enough" in practice.
41 static const int kMinBufferCapacity = 2 * kMegabyte;
42
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;
47
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;
53
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(float 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 float kMaxPlaybackRate = 25.0;
63   static const int kTargetSecondsBufferedAhead = 10;
64   static const int kTargetSecondsBufferedBehind = 2;
65
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);
70
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.0f) {
75     backward_playback = true;
76     playback_rate *= -1.0f;
77   }
78
79   playback_rate = std::max(playback_rate, 1.0f);
80   playback_rate = std::min(playback_rate, kMaxPlaybackRate);
81
82   int bytes_per_second = (bitrate / 8.0) * playback_rate;
83
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);
89
90   *out_forward_capacity = std::min(*out_forward_capacity, kMaxBufferCapacity);
91   *out_backward_capacity = std::min(*out_backward_capacity, kMaxBufferCapacity);
92
93   if (backward_playback)
94     std::swap(*out_forward_capacity, *out_backward_capacity);
95 }
96
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     float playback_rate,
105     media::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
129   // Set the initial capacity of |buffer_| based on |bitrate_| and
130   // |playback_rate_|.
131   UpdateBufferWindow();
132 }
133
134 BufferedResourceLoader::~BufferedResourceLoader() {}
135
136 void BufferedResourceLoader::Start(
137     const StartCB& start_cb,
138     const LoadingStateChangedCB& loading_cb,
139     const ProgressCB& progress_cb,
140     WebFrame* frame) {
141   // Make sure we have not started.
142   DCHECK(start_cb_.is_null());
143   DCHECK(loading_cb_.is_null());
144   DCHECK(progress_cb_.is_null());
145   DCHECK(!start_cb.is_null());
146   DCHECK(!loading_cb.is_null());
147   DCHECK(!progress_cb.is_null());
148   CHECK(frame);
149
150   start_cb_ = start_cb;
151   loading_cb_ = loading_cb;
152   progress_cb_ = progress_cb;
153
154   if (first_byte_position_ != kPositionNotSpecified) {
155     // TODO(hclam): server may not support range request so |offset_| may not
156     // equal to |first_byte_position_|.
157     offset_ = first_byte_position_;
158   }
159
160   // Prepare the request.
161   WebURLRequest request(url_);
162   request.setTargetType(WebURLRequest::TargetIsMedia);
163
164   if (IsRangeRequest()) {
165     request.setHTTPHeaderField(
166         WebString::fromUTF8(net::HttpRequestHeaders::kRange),
167         WebString::fromUTF8(net::HttpByteRange::Bounded(
168             first_byte_position_, last_byte_position_).GetHeaderValue()));
169   }
170
171   frame->setReferrerForRequest(request, blink::WebURL());
172
173   // Disable compression, compression for audio/video doesn't make sense...
174   request.setHTTPHeaderField(
175       WebString::fromUTF8(net::HttpRequestHeaders::kAcceptEncoding),
176       WebString::fromUTF8("identity;q=1, *;q=0"));
177
178   // Check for our test WebURLLoader.
179   scoped_ptr<WebURLLoader> loader;
180   if (test_loader_) {
181     loader = test_loader_.Pass();
182   } else {
183     WebURLLoaderOptions options;
184     if (cors_mode_ == kUnspecified) {
185       options.allowCredentials = true;
186       options.crossOriginRequestPolicy =
187           WebURLLoaderOptions::CrossOriginRequestPolicyAllow;
188     } else {
189       options.exposeAllResponseHeaders = true;
190       // The author header set is empty, no preflight should go ahead.
191       options.preflightPolicy = WebURLLoaderOptions::PreventPreflight;
192       options.crossOriginRequestPolicy =
193           WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
194       if (cors_mode_ == kUseCredentials)
195         options.allowCredentials = true;
196     }
197     loader.reset(frame->createAssociatedURLLoader(options));
198   }
199
200   // Start the resource loading.
201   loader->loadAsynchronously(request, this);
202   active_loader_.reset(new ActiveLoader(loader.Pass()));
203   loading_cb_.Run(kLoading);
204 }
205
206 void BufferedResourceLoader::Stop() {
207   // Reset callbacks.
208   start_cb_.Reset();
209   loading_cb_.Reset();
210   progress_cb_.Reset();
211   read_cb_.Reset();
212
213   // Cancel and reset any active loaders.
214   active_loader_.reset();
215 }
216
217 void BufferedResourceLoader::Read(
218     int64 position,
219     int read_size,
220     uint8* buffer,
221     const ReadCB& read_cb) {
222   DCHECK(start_cb_.is_null());
223   DCHECK(read_cb_.is_null());
224   DCHECK(!read_cb.is_null());
225   DCHECK(buffer);
226   DCHECK_GT(read_size, 0);
227
228   // Save the parameter of reading.
229   read_cb_ = read_cb;
230   read_position_ = position;
231   read_size_ = read_size;
232   read_buffer_ = buffer;
233
234   // Reads should immediately fail if the loader also failed.
235   if (loader_failed_) {
236     DoneRead(kFailed, 0);
237     return;
238   }
239
240   // If we're attempting to read past the end of the file, return a zero
241   // indicating EOF.
242   //
243   // This can happen with callees that read in fixed-sized amounts for parsing
244   // or at the end of chunked 200 responses when we discover the actual length
245   // of the file.
246   if (instance_size_ != kPositionNotSpecified &&
247       instance_size_ <= read_position_) {
248     DVLOG(1) << "Appear to have seeked beyond EOS; returning 0.";
249     DoneRead(kOk, 0);
250     return;
251   }
252
253   // Make sure |offset_| and |read_position_| does not differ by a large
254   // amount.
255   if (read_position_ > offset_ + kint32max ||
256       read_position_ < offset_ + kint32min) {
257     DoneRead(kCacheMiss, 0);
258     return;
259   }
260
261   // Make sure |read_size_| is not too large for the buffer to ever be able to
262   // fulfill the read request.
263   if (read_size_ > kMaxBufferCapacity) {
264     DoneRead(kFailed, 0);
265     return;
266   }
267
268   // Prepare the parameters.
269   first_offset_ = read_position_ - offset_;
270   last_offset_ = first_offset_ + read_size_;
271
272   // If we can serve the request now, do the actual read.
273   if (CanFulfillRead()) {
274     ReadInternal();
275     UpdateDeferBehavior();
276     return;
277   }
278
279   // If we expect the read request to be fulfilled later, expand capacity as
280   // necessary and disable deferring.
281   if (WillFulfillRead()) {
282     // Advance offset as much as possible to create additional capacity.
283     int advance = std::min(first_offset_, buffer_.forward_bytes());
284     bool ret = buffer_.Seek(advance);
285     DCHECK(ret);
286
287     offset_ += advance;
288     first_offset_ -= advance;
289     last_offset_ -= advance;
290
291     // Expand capacity to accomodate a read that extends past the normal
292     // capacity.
293     //
294     // This can happen when reading in a large seek index or when the
295     // first byte of a read request falls within kForwardWaitThreshold.
296     if (last_offset_ > buffer_.forward_capacity()) {
297       saved_forward_capacity_ = buffer_.forward_capacity();
298       buffer_.set_forward_capacity(last_offset_);
299     }
300
301     // Make sure we stop deferring now that there's additional capacity.
302     DCHECK(!ShouldDefer())
303         << "Capacity was not adjusted properly to prevent deferring.";
304     UpdateDeferBehavior();
305
306     return;
307   }
308
309   // Make a callback to report failure.
310   DoneRead(kCacheMiss, 0);
311 }
312
313 int64 BufferedResourceLoader::content_length() {
314   return content_length_;
315 }
316
317 int64 BufferedResourceLoader::instance_size() {
318   return instance_size_;
319 }
320
321 bool BufferedResourceLoader::range_supported() {
322   return range_supported_;
323 }
324
325 /////////////////////////////////////////////////////////////////////////////
326 // blink::WebURLLoaderClient implementation.
327 void BufferedResourceLoader::willSendRequest(
328     WebURLLoader* loader,
329     WebURLRequest& newRequest,
330     const WebURLResponse& redirectResponse) {
331
332   // The load may have been stopped and |start_cb| is destroyed.
333   // In this case we shouldn't do anything.
334   if (start_cb_.is_null()) {
335     // Set the url in the request to an invalid value (empty url).
336     newRequest.setURL(blink::WebURL());
337     return;
338   }
339
340   // Only allow |single_origin_| if we haven't seen a different origin yet.
341   if (single_origin_)
342     single_origin_ = url_.GetOrigin() == GURL(newRequest.url()).GetOrigin();
343
344   url_ = newRequest.url();
345 }
346
347 void BufferedResourceLoader::didSendData(
348     WebURLLoader* loader,
349     unsigned long long bytes_sent,
350     unsigned long long total_bytes_to_be_sent) {
351   NOTIMPLEMENTED();
352 }
353
354 void BufferedResourceLoader::didReceiveResponse(
355     WebURLLoader* loader,
356     const WebURLResponse& response) {
357   DVLOG(1) << "didReceiveResponse: HTTP/"
358            << (response.httpVersion() == WebURLResponse::HTTP_0_9 ? "0.9" :
359                response.httpVersion() == WebURLResponse::HTTP_1_0 ? "1.0" :
360                response.httpVersion() == WebURLResponse::HTTP_1_1 ? "1.1" :
361                "Unknown")
362            << " " << response.httpStatusCode();
363   DCHECK(active_loader_.get());
364
365   // The loader may have been stopped and |start_cb| is destroyed.
366   // In this case we shouldn't do anything.
367   if (start_cb_.is_null())
368     return;
369
370   uint32 reasons = GetReasonsForUncacheability(response);
371   might_be_reused_from_cache_in_future_ = reasons == 0;
372   UMA_HISTOGRAM_BOOLEAN("Media.CacheUseful", reasons == 0);
373   int shift = 0;
374   int max_enum = base::bits::Log2Ceiling(kMaxReason);
375   while (reasons) {
376     DCHECK_LT(shift, max_enum);  // Sanity check.
377     if (reasons & 0x1)
378       UMA_HISTOGRAM_ENUMERATION("Media.UncacheableReason", shift, max_enum);
379     reasons >>= 1;
380     ++shift;
381   }
382
383   // Expected content length can be |kPositionNotSpecified|, in that case
384   // |content_length_| is not specified and this is a streaming response.
385   content_length_ = response.expectedContentLength();
386
387   // We make a strong assumption that when we reach here we have either
388   // received a response from HTTP/HTTPS protocol or the request was
389   // successful (in particular range request). So we only verify the partial
390   // response for HTTP and HTTPS protocol.
391   if (url_.SchemeIs(kHttpScheme) || url_.SchemeIs(kHttpsScheme)) {
392     bool partial_response = (response.httpStatusCode() == kHttpPartialContent);
393     bool ok_response = (response.httpStatusCode() == kHttpOK);
394
395     if (IsRangeRequest()) {
396       // Check to see whether the server supports byte ranges.
397       std::string accept_ranges =
398           response.httpHeaderField("Accept-Ranges").utf8();
399       range_supported_ = (accept_ranges.find("bytes") != std::string::npos);
400
401       // If we have verified the partial response and it is correct, we will
402       // return kOk. It's also possible for a server to support range requests
403       // without advertising "Accept-Ranges: bytes".
404       if (partial_response && VerifyPartialResponse(response)) {
405         range_supported_ = true;
406       } else if (ok_response && first_byte_position_ == 0 &&
407                  last_byte_position_ == kPositionNotSpecified) {
408         // We accept a 200 response for a Range:0- request, trusting the
409         // Accept-Ranges header, because Apache thinks that's a reasonable thing
410         // to return.
411         instance_size_ = content_length_;
412       } else {
413         DoneStart(kFailed);
414         return;
415       }
416     } else {
417       instance_size_ = content_length_;
418       if (response.httpStatusCode() != kHttpOK) {
419         // We didn't request a range but server didn't reply with "200 OK".
420         DoneStart(kFailed);
421         return;
422       }
423     }
424
425   } else {
426     CHECK_EQ(instance_size_, kPositionNotSpecified);
427     if (content_length_ != kPositionNotSpecified) {
428       if (first_byte_position_ == kPositionNotSpecified)
429         instance_size_ = content_length_;
430       else if (last_byte_position_ == kPositionNotSpecified)
431         instance_size_ = content_length_ + first_byte_position_;
432     }
433   }
434
435   // Calls with a successful response.
436   DoneStart(kOk);
437 }
438
439 void BufferedResourceLoader::didReceiveData(
440     WebURLLoader* loader,
441     const char* data,
442     int data_length,
443     int encoded_data_length) {
444   DVLOG(1) << "didReceiveData: " << data_length << " bytes";
445   DCHECK(active_loader_.get());
446   DCHECK_GT(data_length, 0);
447
448   buffer_.Append(reinterpret_cast<const uint8*>(data), data_length);
449
450   // If there is an active read request, try to fulfill the request.
451   if (HasPendingRead() && CanFulfillRead())
452     ReadInternal();
453
454   // At last see if the buffer is full and we need to defer the downloading.
455   UpdateDeferBehavior();
456
457   // Consume excess bytes from our in-memory buffer if necessary.
458   if (buffer_.forward_bytes() > buffer_.forward_capacity()) {
459     int excess = buffer_.forward_bytes() - buffer_.forward_capacity();
460     bool success = buffer_.Seek(excess);
461     DCHECK(success);
462     offset_ += first_offset_ + excess;
463   }
464
465   // Notify latest progress and buffered offset.
466   progress_cb_.Run(offset_ + buffer_.forward_bytes() - 1);
467   Log();
468 }
469
470 void BufferedResourceLoader::didDownloadData(
471     blink::WebURLLoader* loader,
472     int dataLength,
473     int encoded_data_length) {
474   NOTIMPLEMENTED();
475 }
476
477 void BufferedResourceLoader::didReceiveCachedMetadata(
478     WebURLLoader* loader,
479     const char* data,
480     int data_length) {
481   NOTIMPLEMENTED();
482 }
483
484 void BufferedResourceLoader::didFinishLoading(
485     WebURLLoader* loader,
486     double finishTime,
487     int64_t total_encoded_data_length) {
488   DVLOG(1) << "didFinishLoading";
489   DCHECK(active_loader_.get());
490
491   // We're done with the loader.
492   active_loader_.reset();
493   loading_cb_.Run(kLoadingFinished);
494
495   // If we didn't know the |instance_size_| we do now.
496   if (instance_size_ == kPositionNotSpecified) {
497     instance_size_ = offset_ + buffer_.forward_bytes();
498   }
499
500   // If there is a start callback, run it.
501   if (!start_cb_.is_null()) {
502     DCHECK(read_cb_.is_null())
503         << "Shouldn't have a read callback during start";
504     DoneStart(kOk);
505     return;
506   }
507
508   // Don't leave read callbacks hanging around.
509   if (HasPendingRead()) {
510     // Try to fulfill with what is in the buffer.
511     if (CanFulfillRead())
512       ReadInternal();
513     else
514       DoneRead(kCacheMiss, 0);
515   }
516 }
517
518 void BufferedResourceLoader::didFail(
519     WebURLLoader* loader,
520     const WebURLError& error) {
521   DVLOG(1) << "didFail: reason=" << error.reason
522            << ", isCancellation=" << error.isCancellation
523            << ", domain=" << error.domain.utf8().data()
524            << ", localizedDescription="
525            << error.localizedDescription.utf8().data();
526   DCHECK(active_loader_.get());
527
528   // We don't need to continue loading after failure.
529   //
530   // Keep it alive until we exit this method so that |error| remains valid.
531   scoped_ptr<ActiveLoader> active_loader = active_loader_.Pass();
532   loader_failed_ = true;
533   loading_cb_.Run(kLoadingFailed);
534
535   // Don't leave start callbacks hanging around.
536   if (!start_cb_.is_null()) {
537     DCHECK(read_cb_.is_null())
538         << "Shouldn't have a read callback during start";
539     DoneStart(kFailed);
540     return;
541   }
542
543   // Don't leave read callbacks hanging around.
544   if (HasPendingRead()) {
545     DoneRead(kFailed, 0);
546   }
547 }
548
549 bool BufferedResourceLoader::HasSingleOrigin() const {
550   DCHECK(start_cb_.is_null())
551       << "Start() must complete before calling HasSingleOrigin()";
552   return single_origin_;
553 }
554
555 bool BufferedResourceLoader::DidPassCORSAccessCheck() const {
556   DCHECK(start_cb_.is_null())
557       << "Start() must complete before calling DidPassCORSAccessCheck()";
558   return !loader_failed_ && cors_mode_ != kUnspecified;
559 }
560
561 void BufferedResourceLoader::UpdateDeferStrategy(DeferStrategy strategy) {
562   if (!might_be_reused_from_cache_in_future_ && strategy == kNeverDefer)
563     strategy = kCapacityDefer;
564   defer_strategy_ = strategy;
565   UpdateDeferBehavior();
566 }
567
568 void BufferedResourceLoader::SetPlaybackRate(float playback_rate) {
569   playback_rate_ = playback_rate;
570
571   // This is a pause so don't bother updating the buffer window as we'll likely
572   // get unpaused in the future.
573   if (playback_rate_ == 0.0)
574     return;
575
576   UpdateBufferWindow();
577 }
578
579 void BufferedResourceLoader::SetBitrate(int bitrate) {
580   DCHECK(bitrate >= 0);
581   bitrate_ = bitrate;
582   UpdateBufferWindow();
583 }
584
585 /////////////////////////////////////////////////////////////////////////////
586 // Helper methods.
587
588 void BufferedResourceLoader::UpdateBufferWindow() {
589   int backward_capacity;
590   int forward_capacity;
591   ComputeTargetBufferWindow(
592       playback_rate_, bitrate_, &backward_capacity, &forward_capacity);
593
594   // This does not evict data from the buffer if the new capacities are less
595   // than the current capacities; the new limits will be enforced after the
596   // existing excess buffered data is consumed.
597   buffer_.set_backward_capacity(backward_capacity);
598   buffer_.set_forward_capacity(forward_capacity);
599 }
600
601 void BufferedResourceLoader::UpdateDeferBehavior() {
602   if (!active_loader_)
603     return;
604
605   SetDeferred(ShouldDefer());
606 }
607
608 void BufferedResourceLoader::SetDeferred(bool deferred) {
609   if (active_loader_->deferred() == deferred)
610     return;
611
612   active_loader_->SetDeferred(deferred);
613   loading_cb_.Run(deferred ? kLoadingDeferred : kLoading);
614 }
615
616 bool BufferedResourceLoader::ShouldDefer() const {
617   switch(defer_strategy_) {
618     case kNeverDefer:
619       return false;
620
621     case kReadThenDefer:
622       DCHECK(read_cb_.is_null() || last_offset_ > buffer_.forward_bytes())
623           << "We shouldn't stop deferring if we can fulfill the read";
624       return read_cb_.is_null();
625
626     case kCapacityDefer:
627       return buffer_.forward_bytes() >= buffer_.forward_capacity();
628   }
629   NOTREACHED();
630   return false;
631 }
632
633 bool BufferedResourceLoader::CanFulfillRead() const {
634   // If we are reading too far in the backward direction.
635   if (first_offset_ < 0 && (first_offset_ + buffer_.backward_bytes()) < 0)
636     return false;
637
638   // If the start offset is too far ahead.
639   if (first_offset_ >= buffer_.forward_bytes())
640     return false;
641
642   // At the point, we verified that first byte requested is within the buffer.
643   // If the request has completed, then just returns with what we have now.
644   if (!active_loader_)
645     return true;
646
647   // If the resource request is still active, make sure the whole requested
648   // range is covered.
649   if (last_offset_ > buffer_.forward_bytes())
650     return false;
651
652   return true;
653 }
654
655 bool BufferedResourceLoader::WillFulfillRead() const {
656   // Trying to read too far behind.
657   if (first_offset_ < 0 && (first_offset_ + buffer_.backward_bytes()) < 0)
658     return false;
659
660   // Trying to read too far ahead.
661   if ((first_offset_ - buffer_.forward_bytes()) >= kForwardWaitThreshold)
662     return false;
663
664   // The resource request has completed, there's no way we can fulfill the
665   // read request.
666   if (!active_loader_)
667     return false;
668
669   return true;
670 }
671
672 void BufferedResourceLoader::ReadInternal() {
673   // Seek to the first byte requested.
674   bool ret = buffer_.Seek(first_offset_);
675   DCHECK(ret);
676
677   // Then do the read.
678   int read = buffer_.Read(read_buffer_, read_size_);
679   offset_ += first_offset_ + read;
680
681   // And report with what we have read.
682   DoneRead(kOk, read);
683 }
684
685 int64 BufferedResourceLoader::first_byte_position() const {
686   return first_byte_position_;
687 }
688
689 // static
690 bool BufferedResourceLoader::ParseContentRange(
691     const std::string& content_range_str, int64* first_byte_position,
692     int64* last_byte_position, int64* instance_size) {
693   const std::string kUpThroughBytesUnit = "bytes ";
694   if (content_range_str.find(kUpThroughBytesUnit) != 0)
695     return false;
696   std::string range_spec =
697       content_range_str.substr(kUpThroughBytesUnit.length());
698   size_t dash_offset = range_spec.find("-");
699   size_t slash_offset = range_spec.find("/");
700
701   if (dash_offset == std::string::npos || slash_offset == std::string::npos ||
702       slash_offset < dash_offset || slash_offset + 1 == range_spec.length()) {
703     return false;
704   }
705   if (!base::StringToInt64(range_spec.substr(0, dash_offset),
706                            first_byte_position) ||
707       !base::StringToInt64(range_spec.substr(dash_offset + 1,
708                                              slash_offset - dash_offset - 1),
709                            last_byte_position)) {
710     return false;
711   }
712   if (slash_offset == range_spec.length() - 2 &&
713       range_spec[slash_offset + 1] == '*') {
714     *instance_size = kPositionNotSpecified;
715   } else {
716     if (!base::StringToInt64(range_spec.substr(slash_offset + 1),
717                              instance_size)) {
718       return false;
719     }
720   }
721   if (*last_byte_position < *first_byte_position ||
722       (*instance_size != kPositionNotSpecified &&
723        *last_byte_position >= *instance_size)) {
724     return false;
725   }
726
727   return true;
728 }
729
730 bool BufferedResourceLoader::VerifyPartialResponse(
731     const WebURLResponse& response) {
732   int64 first_byte_position, last_byte_position, instance_size;
733   if (!ParseContentRange(response.httpHeaderField("Content-Range").utf8(),
734                          &first_byte_position, &last_byte_position,
735                          &instance_size)) {
736     return false;
737   }
738
739   if (instance_size != kPositionNotSpecified) {
740     instance_size_ = instance_size;
741   }
742
743   if (first_byte_position_ != kPositionNotSpecified &&
744       first_byte_position_ != first_byte_position) {
745     return false;
746   }
747
748   // TODO(hclam): I should also check |last_byte_position|, but since
749   // we will never make such a request that it is ok to leave it unimplemented.
750   return true;
751 }
752
753 void BufferedResourceLoader::DoneRead(Status status, int bytes_read) {
754   if (saved_forward_capacity_) {
755     buffer_.set_forward_capacity(saved_forward_capacity_);
756     saved_forward_capacity_ = 0;
757   }
758   read_position_ = 0;
759   read_size_ = 0;
760   read_buffer_ = NULL;
761   first_offset_ = 0;
762   last_offset_ = 0;
763   Log();
764
765   base::ResetAndReturn(&read_cb_).Run(status, bytes_read);
766 }
767
768
769 void BufferedResourceLoader::DoneStart(Status status) {
770   base::ResetAndReturn(&start_cb_).Run(status);
771 }
772
773 bool BufferedResourceLoader::IsRangeRequest() const {
774   return first_byte_position_ != kPositionNotSpecified;
775 }
776
777 void BufferedResourceLoader::Log() {
778   media_log_->AddEvent(
779       media_log_->CreateBufferedExtentsChangedEvent(
780           offset_ - buffer_.backward_bytes(),
781           offset_,
782           offset_ + buffer_.forward_bytes()));
783 }
784
785 }  // namespace content