summaryrefslogtreecommitdiffstats
path: root/webkit/glue/media
diff options
context:
space:
mode:
authorKristian Monsen <kristianm@google.com>2011-05-11 20:53:37 +0100
committerKristian Monsen <kristianm@google.com>2011-05-16 13:54:48 +0100
commit21d179b334e59e9a3bfcaed4c4430bef1bc5759d (patch)
tree64e2bb6da27af6a5c93ca34f6051584aafbfcb9e /webkit/glue/media
parent0c63f00edd6ed0482fd5cbcea937ca088baf7858 (diff)
downloadexternal_chromium-21d179b334e59e9a3bfcaed4c4430bef1bc5759d.zip
external_chromium-21d179b334e59e9a3bfcaed4c4430bef1bc5759d.tar.gz
external_chromium-21d179b334e59e9a3bfcaed4c4430bef1bc5759d.tar.bz2
Merge Chromium at 10.0.621.0: Initial merge by git.
Change-Id: I070cc91c608dfa4a968a5a54c173260765ac8097
Diffstat (limited to 'webkit/glue/media')
-rw-r--r--webkit/glue/media/audio_decoder.cc76
-rw-r--r--webkit/glue/media/audio_decoder.h20
-rw-r--r--webkit/glue/media/buffered_data_source.cc590
-rw-r--r--webkit/glue/media/buffered_data_source.h219
-rw-r--r--webkit/glue/media/buffered_data_source_unittest.cc548
-rw-r--r--webkit/glue/media/buffered_resource_loader.cc578
-rw-r--r--webkit/glue/media/buffered_resource_loader.h246
-rw-r--r--webkit/glue/media/buffered_resource_loader_unittest.cc486
-rw-r--r--webkit/glue/media/media_resource_loader_bridge_factory.cc82
-rw-r--r--webkit/glue/media/media_resource_loader_bridge_factory.h76
-rw-r--r--webkit/glue/media/media_resource_loader_bridge_factory_unittest.cc44
-rw-r--r--webkit/glue/media/mock_media_resource_loader_bridge_factory.h36
-rw-r--r--webkit/glue/media/simple_data_source.cc142
-rw-r--r--webkit/glue/media/simple_data_source.h76
-rw-r--r--webkit/glue/media/simple_data_source_unittest.cc95
15 files changed, 1679 insertions, 1635 deletions
diff --git a/webkit/glue/media/audio_decoder.cc b/webkit/glue/media/audio_decoder.cc
new file mode 100644
index 0000000..3fc05c9
--- /dev/null
+++ b/webkit/glue/media/audio_decoder.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/media/audio_decoder.h"
+
+#include <vector>
+#include "base/basictypes.h"
+#include "base/string_util.h"
+#include "base/time.h"
+#include "media/filters/audio_file_reader.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebAudioBus.h"
+
+using media::AudioFileReader;
+using media::InMemoryDataReader;
+using std::vector;
+using WebKit::WebAudioBus;
+
+namespace webkit_glue {
+
+// Decode in-memory audio file data.
+bool DecodeAudioFileData(
+ WebKit::WebAudioBus* destination_bus,
+ const char* data, size_t data_size, double sample_rate) {
+ DCHECK(destination_bus);
+ if (!destination_bus)
+ return false;
+
+ // Uses the FFmpeg library for audio file reading.
+ InMemoryDataReader data_reader(data, data_size);
+ AudioFileReader reader(&data_reader);
+
+ if (!reader.Open())
+ return false;
+
+ size_t number_of_channels = reader.channels();
+ double file_sample_rate = reader.sample_rate();
+ double duration = reader.duration().InSecondsF();
+ size_t number_of_frames = static_cast<size_t>(reader.number_of_frames());
+
+ // TODO(crogers) : do sample-rate conversion with FFmpeg.
+ // For now, we're ignoring the requested 'sample_rate' and returning
+ // the WebAudioBus at the file's sample-rate.
+ // double destination_sample_rate =
+ // (sample_rate != 0.0) ? sample_rate : file_sample_rate;
+ double destination_sample_rate = file_sample_rate;
+
+ DLOG(INFO) << "Decoding file data -"
+ << " data: " << data
+ << " data size: " << data_size
+ << " duration: " << duration
+ << " number of frames: " << number_of_frames
+ << " sample rate: " << file_sample_rate
+ << " number of channels: " << number_of_channels;
+
+ // Change to destination sample-rate.
+ number_of_frames = static_cast<size_t>(number_of_frames *
+ (destination_sample_rate / file_sample_rate));
+
+ // Allocate and configure the output audio channel data.
+ destination_bus->initialize(number_of_channels,
+ number_of_frames,
+ destination_sample_rate);
+
+ // Wrap the channel pointers which will receive the decoded PCM audio.
+ vector<float*> audio_data;
+ audio_data.reserve(number_of_channels);
+ for (size_t i = 0; i < number_of_channels; ++i) {
+ audio_data.push_back(destination_bus->channelData(i));
+ }
+
+ // Decode the audio file data.
+ return reader.Read(audio_data, number_of_frames);
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/media/audio_decoder.h b/webkit/glue/media/audio_decoder.h
new file mode 100644
index 0000000..57cc90b
--- /dev/null
+++ b/webkit/glue/media/audio_decoder.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_MEDIA_AUDIO_DECODER_H_
+#define WEBKIT_GLUE_MEDIA_AUDIO_DECODER_H_
+
+#include "base/basictypes.h"
+
+namespace WebKit { class WebAudioBus; }
+
+namespace webkit_glue {
+
+// Decode in-memory audio file data.
+bool DecodeAudioFileData(WebKit::WebAudioBus* destination_bus, const char* data,
+ size_t data_size, double sample_rate);
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_MEDIA_AUDIO_DECODER_H_
diff --git a/webkit/glue/media/buffered_data_source.cc b/webkit/glue/media/buffered_data_source.cc
index 29c86ac..b6efa16 100644
--- a/webkit/glue/media/buffered_data_source.cc
+++ b/webkit/glue/media/buffered_data_source.cc
@@ -2,46 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/callback.h"
-#include "base/compiler_specific.h"
-#include "base/message_loop.h"
-#include "base/process_util.h"
-#include "base/stl_util-inl.h"
-#include "base/string_util.h"
+#include "webkit/glue/media/buffered_data_source.h"
+
#include "media/base/filter_host.h"
-#include "media/base/media_format.h"
-#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
-#include "net/http/http_response_headers.h"
-#include "webkit/glue/media/buffered_data_source.h"
#include "webkit/glue/webkit_glue.h"
-#include "webkit/glue/webmediaplayer_impl.h"
-
-namespace {
-const char kHttpScheme[] = "http";
-const char kHttpsScheme[] = "https";
-const char kDataScheme[] = "data";
-const int64 kPositionNotSpecified = -1;
-const int kHttpOK = 200;
-const int kHttpPartialContent = 206;
+using WebKit::WebFrame;
-// Define the number of bytes in a megabyte.
-const size_t kMegabyte = 1024 * 1024;
-
-// Backward capacity of the buffer, by default 2MB.
-const size_t kBackwardCapcity = 2 * kMegabyte;
-
-// Forward capacity of the buffer, by default 10MB.
-const size_t kForwardCapacity = 10 * kMegabyte;
-
-// The threshold of bytes that we should wait until the data arrives in the
-// future instead of restarting a new connection. This number is defined in the
-// number of bytes, we should determine this value from typical connection speed
-// and amount of time for a suitable wait. Now I just make a guess for this
-// number to be 2MB.
-// TODO(hclam): determine a better value for this.
-const int kForwardWaitThreshold = 2 * kMegabyte;
+namespace {
// Defines how long we should wait for more data before we declare a connection
// timeout and start a new request.
@@ -58,479 +27,18 @@ const int kReadTrials = 3;
// of FFmpeg.
const int kInitialReadBufferSize = 32768;
-// Returns true if |url| operates on HTTP protocol.
-bool IsHttpProtocol(const GURL& url) {
- return url.SchemeIs(kHttpScheme) || url.SchemeIs(kHttpsScheme);
-}
-
-bool IsDataProtocol(const GURL& url) {
- return url.SchemeIs(kDataScheme);
-}
-
-} // namespace
+} // namespace
namespace webkit_glue {
-/////////////////////////////////////////////////////////////////////////////
-// BufferedResourceLoader
-BufferedResourceLoader::BufferedResourceLoader(
- webkit_glue::MediaResourceLoaderBridgeFactory* bridge_factory,
- const GURL& url,
- int64 first_byte_position,
- int64 last_byte_position)
- : buffer_(new media::SeekableBuffer(kBackwardCapcity, kForwardCapacity)),
- deferred_(false),
- defer_allowed_(true),
- completed_(false),
- range_requested_(false),
- partial_response_(false),
- bridge_factory_(bridge_factory),
- url_(url),
- first_byte_position_(first_byte_position),
- last_byte_position_(last_byte_position),
- start_callback_(NULL),
- bridge_(NULL),
- offset_(0),
- content_length_(kPositionNotSpecified),
- instance_size_(kPositionNotSpecified),
- read_callback_(NULL),
- read_position_(0),
- read_size_(0),
- read_buffer_(NULL),
- first_offset_(0),
- last_offset_(0) {
-}
-
-BufferedResourceLoader::~BufferedResourceLoader() {
-}
-
-void BufferedResourceLoader::Start(net::CompletionCallback* start_callback,
- NetworkEventCallback* event_callback) {
- // Make sure we have not started.
- DCHECK(!bridge_.get());
- DCHECK(!start_callback_.get());
- DCHECK(!event_callback_.get());
- DCHECK(start_callback);
- DCHECK(event_callback);
-
- start_callback_.reset(start_callback);
- event_callback_.reset(event_callback);
-
- if (first_byte_position_ != kPositionNotSpecified) {
- range_requested_ = true;
- // TODO(hclam): server may not support range request so |offset_| may not
- // equal to |first_byte_position_|.
- offset_ = first_byte_position_;
- }
-
- // Creates the bridge on render thread since we can only access
- // ResourceDispatcher on this thread.
- bridge_.reset(
- bridge_factory_->CreateBridge(
- url_,
- net::LOAD_NORMAL,
- first_byte_position_,
- last_byte_position_));
-
- // Increment the reference count right before we start the request. This
- // reference will be release when this request has ended.
- AddRef();
-
- // And start the resource loading.
- bridge_->Start(this);
-}
-
-void BufferedResourceLoader::Stop() {
- // Reset callbacks.
- start_callback_.reset();
- event_callback_.reset();
- read_callback_.reset();
-
- // Use the internal buffer to signal that we have been stopped.
- // TODO(hclam): Not so pretty to do this.
- if (!buffer_.get())
- return;
-
- // Destroy internal buffer.
- buffer_.reset();
-
- if (bridge_.get()) {
- // Cancel the request. This method call will cancel the request
- // asynchronously. We may still get data or messages until we receive
- // a response completed message.
- if (deferred_)
- bridge_->SetDefersLoading(false);
- deferred_ = false;
- bridge_->Cancel();
- }
-}
-
-void BufferedResourceLoader::Read(int64 position,
- int read_size,
- uint8* buffer,
- net::CompletionCallback* read_callback) {
- DCHECK(!read_callback_.get());
- DCHECK(buffer_.get());
- DCHECK(read_callback);
- DCHECK(buffer);
-
- // Save the parameter of reading.
- read_callback_.reset(read_callback);
- read_position_ = position;
- read_size_ = read_size;
- read_buffer_ = buffer;
- // If read position is beyond the instance size, we cannot read there.
- if (instance_size_ != kPositionNotSpecified &&
- instance_size_ <= read_position_) {
- DoneRead(0);
- return;
- }
-
- // Make sure |offset_| and |read_position_| does not differ by a large
- // amount.
- if (read_position_ > offset_ + kint32max ||
- read_position_ < offset_ + kint32min) {
- DoneRead(net::ERR_CACHE_MISS);
- return;
- }
-
- // Prepare the parameters.
- first_offset_ = static_cast<int>(read_position_ - offset_);
- last_offset_ = first_offset_ + read_size_;
-
- // If we can serve the request now, do the actual read.
- if (CanFulfillRead()) {
- ReadInternal();
- DisableDeferIfNeeded();
- return;
- }
-
- // If we expected the read request to be fulfilled later, returns
- // immediately and let more data to flow in.
- if (WillFulfillRead())
- return;
-
- // Make a callback to report failure.
- DoneRead(net::ERR_CACHE_MISS);
-}
-
-int64 BufferedResourceLoader::GetBufferedFirstBytePosition() {
- if (buffer_.get())
- return offset_ - static_cast<int>(buffer_->backward_bytes());
- return kPositionNotSpecified;
-}
-
-int64 BufferedResourceLoader::GetBufferedLastBytePosition() {
- if (buffer_.get())
- return offset_ + static_cast<int>(buffer_->forward_bytes()) - 1;
- return kPositionNotSpecified;
-}
-
-void BufferedResourceLoader::SetAllowDefer(bool is_allowed) {
- defer_allowed_ = is_allowed;
- DisableDeferIfNeeded();
-}
-
-/////////////////////////////////////////////////////////////////////////////
-// BufferedResourceLoader,
-// webkit_glue::ResourceLoaderBridge::Peer implementations
-bool BufferedResourceLoader::OnReceivedRedirect(
- const GURL& new_url,
- const webkit_glue::ResourceResponseInfo& info,
- bool* has_new_first_party_for_cookies,
- GURL* new_first_party_for_cookies) {
- DCHECK(bridge_.get());
-
- // Save the new URL.
- url_ = new_url;
- // TODO(wtc): should we return a new first party for cookies URL?
- *has_new_first_party_for_cookies = false;
-
- // The load may have been stopped and |start_callback| is destroyed.
- // In this case we shouldn't do anything.
- if (!start_callback_.get())
- return true;
-
- if (!IsProtocolSupportedForMedia(new_url)) {
- DoneStart(net::ERR_ADDRESS_INVALID);
- Stop();
- return false;
- }
- return true;
-}
-
-void BufferedResourceLoader::OnReceivedResponse(
- const webkit_glue::ResourceResponseInfo& info,
- bool content_filtered) {
- DCHECK(bridge_.get());
-
- // The loader may have been stopped and |start_callback| is destroyed.
- // In this case we shouldn't do anything.
- if (!start_callback_.get())
- return;
-
- // We make a strong assumption that when we reach here we have either
- // received a response from HTTP/HTTPS protocol or the request was
- // successful (in particular range request). So we only verify the partial
- // response for HTTP and HTTPS protocol.
- if (IsHttpProtocol(url_)) {
- int error = net::OK;
- if (!info.headers) {
- // We expect to receive headers because this is a HTTP or HTTPS protocol,
- // if not report failure.
- error = net::ERR_INVALID_RESPONSE;
- } else {
- if (info.headers->response_code() == kHttpPartialContent)
- partial_response_ = true;
-
- if (range_requested_ && partial_response_) {
- // If we have verified the partial response and it is correct, we will
- // return net::OK.
- if (!VerifyPartialResponse(info))
- error = net::ERR_INVALID_RESPONSE;
- } else if (info.headers->response_code() != kHttpOK) {
- // We didn't request a range but server didn't reply with "200 OK".
- error = net::ERR_FAILED;
- }
- }
-
- if (error != net::OK) {
- DoneStart(error);
- Stop();
- return;
- }
- } else {
- // For any protocol other than HTTP and HTTPS, assume range request is
- // always fulfilled.
- partial_response_ = range_requested_;
- }
-
- // |info.content_length| can be -1, in that case |content_length_| is
- // not specified and this is a streaming response.
- content_length_ = info.content_length;
-
- // If we have not requested a range, then the size of the instance is equal
- // to the content length.
- if (!partial_response_)
- instance_size_ = content_length_;
-
- // Calls with a successful response.
- DoneStart(net::OK);
-}
-
-void BufferedResourceLoader::OnReceivedData(const char* data, int len) {
- DCHECK(bridge_.get());
-
- // If this loader has been stopped, |buffer_| would be destroyed.
- // In this case we shouldn't do anything.
- if (!buffer_.get())
- return;
-
- // Writes more data to |buffer_|.
- buffer_->Append(reinterpret_cast<const uint8*>(data), len);
-
- // If there is an active read request, try to fulfill the request.
- if (HasPendingRead() && CanFulfillRead()) {
- ReadInternal();
- } else if (!defer_allowed_) {
- // If we're not allowed to defer, slide the buffer window forward instead
- // of deferring.
- if (buffer_->forward_bytes() > buffer_->forward_capacity()) {
- size_t excess = buffer_->forward_bytes() - buffer_->forward_capacity();
- bool success = buffer_->Seek(excess);
- DCHECK(success);
- offset_ += first_offset_ + excess;
- }
- }
-
- // At last see if the buffer is full and we need to defer the downloading.
- EnableDeferIfNeeded();
-
- // Notify that we have received some data.
- NotifyNetworkEvent();
-}
-
-void BufferedResourceLoader::OnCompletedRequest(
- const URLRequestStatus& status,
- const std::string& security_info,
- const base::Time& completion_time) {
- DCHECK(bridge_.get());
-
- // Saves the information that the request has completed.
- completed_ = true;
-
- // If there is a start callback, calls it.
- if (start_callback_.get()) {
- DoneStart(status.os_error());
- }
-
- // If there is a pending read but the request has ended, returns with what
- // we have.
- if (HasPendingRead()) {
- // Make sure we have a valid buffer before we satisfy a read request.
- DCHECK(buffer_.get());
-
- if (status.is_success()) {
- // Try to fulfill with what is in the buffer.
- if (CanFulfillRead())
- ReadInternal();
- else
- DoneRead(net::ERR_CACHE_MISS);
- } else {
- // If the request has failed, then fail the read.
- DoneRead(net::ERR_FAILED);
- }
- }
-
- // There must not be any outstanding read request.
- DCHECK(!HasPendingRead());
-
- // Notify that network response is completed.
- NotifyNetworkEvent();
-
- // We incremented the reference count when the loader was started. We balance
- // that reference here so that we get destroyed. This is also the only safe
- // place to destroy the ResourceLoaderBridge.
- bridge_.reset();
- Release();
-}
-
-/////////////////////////////////////////////////////////////////////////////
-// BufferedResourceLoader, private
-void BufferedResourceLoader::EnableDeferIfNeeded() {
- if (!defer_allowed_)
- return;
-
- if (!deferred_ &&
- buffer_->forward_bytes() >= buffer_->forward_capacity()) {
- deferred_ = true;
-
- if (bridge_.get())
- bridge_->SetDefersLoading(true);
-
- NotifyNetworkEvent();
- }
-}
-
-void BufferedResourceLoader::DisableDeferIfNeeded() {
- if (deferred_ &&
- (!defer_allowed_ ||
- buffer_->forward_bytes() < buffer_->forward_capacity() / 2)) {
- deferred_ = false;
-
- if (bridge_.get())
- bridge_->SetDefersLoading(false);
-
- NotifyNetworkEvent();
- }
-}
-
-bool BufferedResourceLoader::CanFulfillRead() {
- // If we are reading too far in the backward direction.
- if (first_offset_ < 0 &&
- first_offset_ + static_cast<int>(buffer_->backward_bytes()) < 0)
- return false;
-
- // If the start offset is too far ahead.
- if (first_offset_ >= static_cast<int>(buffer_->forward_bytes()))
- return false;
-
- // At the point, we verified that first byte requested is within the buffer.
- // If the request has completed, then just returns with what we have now.
- if (completed_)
- return true;
-
- // If the resource request is still active, make sure the whole requested
- // range is covered.
- if (last_offset_ > static_cast<int>(buffer_->forward_bytes()))
- return false;
-
- return true;
-}
-
-bool BufferedResourceLoader::WillFulfillRead() {
- // Reading too far in the backward direction.
- if (first_offset_ < 0 &&
- first_offset_ + static_cast<int>(buffer_->backward_bytes()) < 0)
- return false;
-
- // Try to read too far ahead.
- if (last_offset_ > kForwardWaitThreshold)
- return false;
-
- // The resource request has completed, there's no way we can fulfill the
- // read request.
- if (completed_)
- return false;
-
- return true;
-}
-
-void BufferedResourceLoader::ReadInternal() {
- // Seek to the first byte requested.
- bool ret = buffer_->Seek(first_offset_);
- DCHECK(ret);
-
- // Then do the read.
- int read = static_cast<int>(buffer_->Read(read_buffer_, read_size_));
- offset_ += first_offset_ + read;
-
- // And report with what we have read.
- DoneRead(read);
-}
-
-bool BufferedResourceLoader::VerifyPartialResponse(
- const ResourceResponseInfo& info) {
- int64 first_byte_position, last_byte_position, instance_size;
- if (!info.headers->GetContentRange(&first_byte_position,
- &last_byte_position,
- &instance_size)) {
- return false;
- }
-
- if (instance_size != kPositionNotSpecified)
- instance_size_ = instance_size;
-
- if (first_byte_position_ != -1 &&
- first_byte_position_ != first_byte_position) {
- return false;
- }
-
- // TODO(hclam): I should also check |last_byte_position|, but since
- // we will never make such a request that it is ok to leave it unimplemented.
- return true;
-}
-
-void BufferedResourceLoader::DoneRead(int error) {
- read_callback_->RunWithParams(Tuple1<int>(error));
- read_callback_.reset();
- read_position_ = 0;
- read_size_ = 0;
- read_buffer_ = NULL;
- first_offset_ = 0;
- last_offset_ = 0;
-}
-
-void BufferedResourceLoader::DoneStart(int error) {
- start_callback_->RunWithParams(Tuple1<int>(error));
- start_callback_.reset();
-}
-
-void BufferedResourceLoader::NotifyNetworkEvent() {
- if (event_callback_.get())
- event_callback_->Run();
-}
-
-/////////////////////////////////////////////////////////////////////////////
-// BufferedDataSource
BufferedDataSource::BufferedDataSource(
MessageLoop* render_loop,
- webkit_glue::MediaResourceLoaderBridgeFactory* bridge_factory)
+ WebFrame* frame)
: total_bytes_(kPositionNotSpecified),
loaded_(false),
streaming_(false),
+ frame_(frame),
single_origin_(true),
- bridge_factory_(bridge_factory),
loader_(NULL),
network_activity_(false),
initialize_callback_(NULL),
@@ -558,7 +66,7 @@ BufferedResourceLoader* BufferedDataSource::CreateResourceLoader(
int64 first_byte_position, int64 last_byte_position) {
DCHECK(MessageLoop::current() == render_loop_);
- return new BufferedResourceLoader(bridge_factory_.get(), url_,
+ return new BufferedResourceLoader(url_,
first_byte_position,
last_byte_position);
}
@@ -571,7 +79,7 @@ base::TimeDelta BufferedDataSource::GetTimeoutMilliseconds() {
}
/////////////////////////////////////////////////////////////////////////////
-// BufferedDataSource, media::MediaFilter implementation
+// media::Filter implementation.
void BufferedDataSource::Initialize(const std::string& url,
media::FilterCallback* callback) {
// Saves the url.
@@ -602,7 +110,7 @@ bool BufferedDataSource::IsUrlSupported(const std::string& url) {
GURL gurl(url);
// This data source doesn't support data:// protocol so reject it.
- return IsProtocolSupportedForMedia(gurl) && !IsDataProtocol(gurl);
+ return IsProtocolSupportedForMedia(gurl) && !gurl.SchemeIs(kDataScheme);
}
void BufferedDataSource::Stop(media::FilterCallback* callback) {
@@ -626,7 +134,7 @@ void BufferedDataSource::SetPlaybackRate(float playback_rate) {
}
/////////////////////////////////////////////////////////////////////////////
-// BufferedDataSource, media::DataSource implementation
+// media::DataSource implementation.
void BufferedDataSource::Read(int64 position, size_t size, uint8* data,
media::DataSource::ReadCallback* read_callback) {
render_loop_->PostTask(FROM_HERE,
@@ -658,20 +166,21 @@ void BufferedDataSource::Abort() {
// If we are told to abort, immediately return from any pending read
// with an error.
if (read_callback_.get()) {
- {
AutoLock auto_lock(lock_);
DoneRead_Locked(net::ERR_FAILED);
- }
- CleanupTask();
}
+
+ CleanupTask();
+ frame_ = NULL;
}
/////////////////////////////////////////////////////////////////////////////
-// BufferedDataSource, render thread tasks
+// Render thread tasks.
void BufferedDataSource::InitializeTask() {
DCHECK(MessageLoop::current() == render_loop_);
DCHECK(!loader_.get());
- DCHECK(!stopped_on_render_loop_);
+ if (stopped_on_render_loop_)
+ return;
// Kick starts the watch dog task that will handle connection timeout.
// We run the watch dog 2 times faster the actual timeout so as to catch
@@ -681,24 +190,24 @@ void BufferedDataSource::InitializeTask() {
this,
&BufferedDataSource::WatchDogTask);
- if (IsHttpProtocol(url_)) {
- // Fetch only first 1024 bytes as this usually covers the header portion
- // of a media file that gives enough information about the codecs, etc.
- // This also serve as a probe to determine server capability to serve
- // range request.
- // TODO(hclam): Do some experiments for the best approach.
- loader_ = CreateResourceLoader(0, 1024);
+ if (url_.SchemeIs(kHttpScheme) || url_.SchemeIs(kHttpsScheme)) {
+ // Do an unbounded range request starting at the beginning. If the server
+ // responds with 200 instead of 206 we'll fall back into a streaming mode.
+ loader_ = CreateResourceLoader(0, kPositionNotSpecified);
loader_->Start(
NewCallback(this, &BufferedDataSource::HttpInitialStartCallback),
- NewCallback(this, &BufferedDataSource::NetworkEventCallback));
+ NewCallback(this, &BufferedDataSource::NetworkEventCallback),
+ frame_);
} else {
// For all other protocols, assume they support range request. We fetch
// the full range of the resource to obtain the instance size because
// we won't be served HTTP headers.
- loader_ = CreateResourceLoader(-1, -1);
+ loader_ = CreateResourceLoader(kPositionNotSpecified,
+ kPositionNotSpecified);
loader_->Start(
NewCallback(this, &BufferedDataSource::NonHttpInitialStartCallback),
- NewCallback(this, &BufferedDataSource::NetworkEventCallback));
+ NewCallback(this, &BufferedDataSource::NetworkEventCallback),
+ frame_);
}
}
@@ -706,11 +215,6 @@ void BufferedDataSource::ReadTask(
int64 position, int read_size, uint8* buffer,
media::DataSource::ReadCallback* read_callback) {
DCHECK(MessageLoop::current() == render_loop_);
-
- // If CleanupTask() was executed we should return immediately. We check this
- // variable to prevent doing any actual work after clean up was done. We do
- // not check |stop_signal_received_| because anything use of it has to be
- // within |lock_| which is not desirable.
if (stopped_on_render_loop_)
return;
@@ -731,8 +235,6 @@ void BufferedDataSource::ReadTask(
void BufferedDataSource::CleanupTask() {
DCHECK(MessageLoop::current() == render_loop_);
-
- // If we have already stopped, do nothing.
if (stopped_on_render_loop_)
return;
@@ -757,13 +259,6 @@ void BufferedDataSource::CleanupTask() {
void BufferedDataSource::RestartLoadingTask() {
DCHECK(MessageLoop::current() == render_loop_);
-
- // This variable is set in CleanupTask(). We check this and do an early
- // return. The sequence of actions which enable this conditions is:
- // 1. Stop() is called from the pipeline.
- // 2. ReadCallback() is called from the resource loader.
- // 3. CleanupTask() is executed.
- // 4. RestartLoadingTask() is executed.
if (stopped_on_render_loop_)
return;
@@ -771,16 +266,18 @@ void BufferedDataSource::RestartLoadingTask() {
if (!read_callback_.get())
return;
- loader_ = CreateResourceLoader(read_position_, -1);
+ loader_ = CreateResourceLoader(read_position_, kPositionNotSpecified);
loader_->SetAllowDefer(!media_is_paused_);
loader_->Start(
NewCallback(this, &BufferedDataSource::PartialReadStartCallback),
- NewCallback(this, &BufferedDataSource::NetworkEventCallback));
+ NewCallback(this, &BufferedDataSource::NetworkEventCallback),
+ frame_);
}
void BufferedDataSource::WatchDogTask() {
DCHECK(MessageLoop::current() == render_loop_);
- DCHECK(!stopped_on_render_loop_);
+ if (stopped_on_render_loop_)
+ return;
// We only care if there is an active read request.
if (!read_callback_.get())
@@ -802,11 +299,12 @@ void BufferedDataSource::WatchDogTask() {
// Stops the current loader and creates a new resource loader and
// retry the request.
loader_->Stop();
- loader_ = CreateResourceLoader(read_position_, -1);
+ loader_ = CreateResourceLoader(read_position_, kPositionNotSpecified);
loader_->SetAllowDefer(!media_is_paused_);
loader_->Start(
NewCallback(this, &BufferedDataSource::PartialReadStartCallback),
- NewCallback(this, &BufferedDataSource::NetworkEventCallback));
+ NewCallback(this, &BufferedDataSource::NetworkEventCallback),
+ frame_);
}
void BufferedDataSource::SetPlaybackRateTask(float playback_rate) {
@@ -829,7 +327,7 @@ void BufferedDataSource::SetPlaybackRateTask(float playback_rate) {
// prior to make this method call.
void BufferedDataSource::ReadInternal() {
DCHECK(MessageLoop::current() == render_loop_);
- DCHECK(loader_.get());
+ DCHECK(loader_);
// First we prepare the intermediate read buffer for BufferedResourceLoader
// to write to.
@@ -872,9 +370,7 @@ void BufferedDataSource::DoneInitialization_Locked() {
}
/////////////////////////////////////////////////////////////////////////////
-// BufferedDataSource, callback methods.
-// These methods are called on the render thread for the events reported by
-// BufferedResourceLoader.
+// BufferedResourceLoader callback methods.
void BufferedDataSource::HttpInitialStartCallback(int error) {
DCHECK(MessageLoop::current() == render_loop_);
DCHECK(loader_.get());
@@ -901,10 +397,12 @@ void BufferedDataSource::HttpInitialStartCallback(int error) {
// Assuming that the Range header was causing the problem. Retry without
// the Range header.
using_range_request_ = false;
- loader_ = CreateResourceLoader(-1, -1);
+ loader_ = CreateResourceLoader(kPositionNotSpecified,
+ kPositionNotSpecified);
loader_->Start(
NewCallback(this, &BufferedDataSource::HttpInitialStartCallback),
- NewCallback(this, &BufferedDataSource::NetworkEventCallback));
+ NewCallback(this, &BufferedDataSource::NetworkEventCallback),
+ frame_);
return;
}
@@ -1058,10 +556,10 @@ void BufferedDataSource::NetworkEventCallback() {
return;
bool network_activity = loader_->network_activity();
- int64 buffered_last_byte_position = loader_->GetBufferedLastBytePosition();
+ int64 buffered_position = loader_->GetBufferedPosition();
// If we get an unspecified value, return immediately.
- if (buffered_last_byte_position == kPositionNotSpecified)
+ if (buffered_position == kPositionNotSpecified)
return;
// We need to prevent calling to filter host and running the callback if
@@ -1080,7 +578,7 @@ void BufferedDataSource::NetworkEventCallback() {
network_activity_ = network_activity;
host()->SetNetworkActivity(network_activity);
}
- host()->SetBufferedBytes(buffered_last_byte_position + 1);
+ host()->SetBufferedBytes(buffered_position + 1);
}
} // namespace webkit_glue
diff --git a/webkit/glue/media/buffered_data_source.h b/webkit/glue/media/buffered_data_source.h
index 2af9e84..1c4e3fa 100644
--- a/webkit/glue/media/buffered_data_source.h
+++ b/webkit/glue/media/buffered_data_source.h
@@ -10,220 +10,18 @@
#include "base/callback.h"
#include "base/lock.h"
#include "base/scoped_ptr.h"
-#include "base/timer.h"
-#include "base/condition_variable.h"
-#include "googleurl/src/gurl.h"
-#include "media/base/filters.h"
-#include "media/base/media_format.h"
-#include "media/base/pipeline.h"
-#include "media/base/seekable_buffer.h"
-#include "net/base/completion_callback.h"
-#include "net/base/file_stream.h"
-#include "webkit/glue/media/media_resource_loader_bridge_factory.h"
-#include "webkit/glue/media/web_data_source.h"
-#include "webkit/glue/webmediaplayer_impl.h"
+#include "webkit/glue/media/buffered_resource_loader.h"
namespace webkit_glue {
-/////////////////////////////////////////////////////////////////////////////
-// BufferedResourceLoader
-// This class works inside demuxer thread and render thread. It contains a
-// resource loader bridge and does the actual resource loading. This object
-// does buffering internally, it defers the resource loading if buffer is
-// full and un-defers the resource loading if it is under buffered.
-class BufferedResourceLoader :
- public base::RefCountedThreadSafe<BufferedResourceLoader>,
- public webkit_glue::ResourceLoaderBridge::Peer {
- public:
- typedef Callback0::Type NetworkEventCallback;
-
- // |bridge_factory| - Factory to create a ResourceLoaderBridge.
- // |url| - URL for the resource to be loaded.
- // |first_byte_position| - First byte to start loading from, -1 for not
- // specified.
- // |last_byte_position| - Last byte to be loaded, -1 for not specified.
- BufferedResourceLoader(
- webkit_glue::MediaResourceLoaderBridgeFactory* bridge_factory,
- const GURL& url,
- int64 first_byte_position,
- int64 last_byte_position);
-
- // Start the resource loading with the specified URL and range.
- // This method operates in asynchronous mode. Once there's a response from the
- // server, success or fail |callback| is called with the result.
- // |callback| is called with the following values:
- // - net::OK
- // The request has started successfully.
- // - net::ERR_FAILED
- // The request has failed because of an error with the network.
- // - net::ERR_INVALID_RESPONSE
- // An invalid response is received from the server.
- // - (Anything else)
- // An error code that indicates the request has failed.
- // |event_callback| is called when the response is completed, data is
- // received, the request is suspended or resumed.
- virtual void Start(net::CompletionCallback* callback,
- NetworkEventCallback* event_callback);
-
- // Stop this loader, cancels and request and release internal buffer.
- virtual void Stop();
-
- // Reads the specified |read_size| from |position| into |buffer| and when
- // the operation is done invoke |callback| with number of bytes read or an
- // error code.
- // |callback| is called with the following values:
- // - (Anything greater than or equal 0)
- // Read was successful with the indicated number of bytes read.
- // - net::ERR_FAILED
- // The read has failed because of an error with the network.
- // - net::ERR_CACHE_MISS
- // The read was made too far away from the current buffered position.
- virtual void Read(int64 position, int read_size,
- uint8* buffer, net::CompletionCallback* callback);
-
- // Returns the position of the first byte buffered. Returns -1 if such value
- // is not available.
- virtual int64 GetBufferedFirstBytePosition();
-
- // Returns the position of the last byte buffered. Returns -1 if such value
- // is not available.
- virtual int64 GetBufferedLastBytePosition();
-
- // Sets whether deferring data is allowed or disallowed.
- virtual void SetAllowDefer(bool is_allowed);
-
- // Gets the content length in bytes of the instance after this loader has been
- // started. If this value is -1, then content length is unknown.
- virtual int64 content_length() { return content_length_; }
-
- // Gets the original size of the file requested. If this value is -1, then
- // the size is unknown.
- virtual int64 instance_size() { return instance_size_; }
-
- // Returns true if the response for this loader is a partial response.
- // It means a 206 response in HTTP/HTTPS protocol.
- virtual bool partial_response() { return partial_response_; }
-
- // Returns true if network is currently active.
- virtual bool network_activity() { return !completed_ && !deferred_; }
-
- // Returns resulting URL.
- virtual const GURL& url() { return url_; }
-
- /////////////////////////////////////////////////////////////////////////////
- // webkit_glue::ResourceLoaderBridge::Peer implementations.
- virtual void OnUploadProgress(uint64 position, uint64 size) {}
- virtual bool OnReceivedRedirect(
- const GURL& new_url,
- const webkit_glue::ResourceResponseInfo& info,
- bool* has_new_first_party_for_cookies,
- GURL* new_first_party_for_cookies);
- virtual void OnReceivedResponse(
- const webkit_glue::ResourceResponseInfo& info,
- bool content_filtered);
- virtual void OnDownloadedData(int len) {}
- virtual void OnReceivedData(const char* data, int len);
- virtual void OnCompletedRequest(
- const URLRequestStatus& status,
- const std::string& security_info,
- const base::Time& completion_time);
-
- protected:
- friend class base::RefCountedThreadSafe<BufferedResourceLoader>;
-
- virtual ~BufferedResourceLoader();
-
- private:
- friend class BufferedResourceLoaderTest;
-
- // Defer the resource loading if the buffer is full.
- void EnableDeferIfNeeded();
-
- // Disable defer loading if we are under-buffered.
- void DisableDeferIfNeeded();
-
- // Returns true if the current read request can be fulfilled by what is in
- // the buffer.
- bool CanFulfillRead();
-
- // Returns true if the current read request will be fulfilled in the future.
- bool WillFulfillRead();
-
- // Method that does the actual read and calls the |read_callbac_|, assuming
- // the request range is in |buffer_|.
- void ReadInternal();
-
- // If we have made a range request, verify the response from the server.
- bool VerifyPartialResponse(const ResourceResponseInfo& info);
-
- // Done with read. Invokes the read callback and reset parameters for the
- // read request.
- void DoneRead(int error);
-
- // Done with start. Invokes the start callback and reset it.
- void DoneStart(int error);
-
- // Calls |event_callback_| in terms of a network event.
- void NotifyNetworkEvent();
-
- bool HasPendingRead() { return read_callback_.get() != NULL; }
-
- // A sliding window of buffer.
- scoped_ptr<media::SeekableBuffer> buffer_;
-
- // True if resource loading was deferred.
- bool deferred_;
-
- // True if resource loader is allowed to defer, false otherwise.
- bool defer_allowed_;
-
- // True if resource loading has completed.
- bool completed_;
-
- // True if a range request was made.
- bool range_requested_;
-
- // True if response data received is a partial range.
- bool partial_response_;
-
- webkit_glue::MediaResourceLoaderBridgeFactory* bridge_factory_;
- GURL url_;
- int64 first_byte_position_;
- int64 last_byte_position_;
-
- // Callback method that listens to network events.
- scoped_ptr<NetworkEventCallback> event_callback_;
-
- // Members used during request start.
- scoped_ptr<net::CompletionCallback> start_callback_;
- scoped_ptr<webkit_glue::ResourceLoaderBridge> bridge_;
- int64 offset_;
- int64 content_length_;
- int64 instance_size_;
-
- // Members used during a read operation. They should be reset after each
- // read has completed or failed.
- scoped_ptr<net::CompletionCallback> read_callback_;
- int64 read_position_;
- int read_size_;
- uint8* read_buffer_;
-
- // Offsets of the requested first byte and last byte in |buffer_|. They are
- // written by VerifyRead().
- int first_offset_;
- int last_offset_;
-
- DISALLOW_COPY_AND_ASSIGN(BufferedResourceLoader);
-};
class BufferedDataSource : public WebDataSource {
public:
- BufferedDataSource(
- MessageLoop* render_loop,
- webkit_glue::MediaResourceLoaderBridgeFactory* bridge_factory);
+ BufferedDataSource(MessageLoop* render_loop,
+ WebKit::WebFrame* frame);
virtual ~BufferedDataSource();
- // media::MediaFilter implementation.
+ // media::Filter implementation.
virtual void Initialize(const std::string& url,
media::FilterCallback* callback);
virtual bool IsUrlSupported(const std::string& url);
@@ -247,7 +45,6 @@ class BufferedDataSource : public WebDataSource {
virtual void Abort();
protected:
-
// A factory method to create a BufferedResourceLoader based on the read
// parameters. We can override this file to object a mock
// BufferedResourceLoader for testing.
@@ -338,12 +135,12 @@ class BufferedDataSource : public WebDataSource {
// i.e. range request is not supported.
bool streaming_;
+ // A webframe for loading.
+ WebKit::WebFrame* frame_;
+
// True if the media resource has a single origin.
bool single_origin_;
- // A factory object to produce ResourceLoaderBridge.
- scoped_ptr<webkit_glue::MediaResourceLoaderBridgeFactory> bridge_factory_;
-
// A resource loader for the media resource.
scoped_refptr<BufferedResourceLoader> loader_;
@@ -385,7 +182,7 @@ class BufferedDataSource : public WebDataSource {
bool stop_signal_received_;
// This variable is set by CleanupTask() that indicates this object is stopped
- // on the render thread.
+ // on the render thread and work should no longer progress.
bool stopped_on_render_loop_;
// This variable is true when we are in a paused state and false when we
diff --git a/webkit/glue/media/buffered_data_source_unittest.cc b/webkit/glue/media/buffered_data_source_unittest.cc
index 81103b2..dcb11ec 100644
--- a/webkit/glue/media/buffered_data_source_unittest.cc
+++ b/webkit/glue/media/buffered_data_source_unittest.cc
@@ -4,22 +4,18 @@
#include <algorithm>
-#include "base/callback.h"
-#include "base/format_macros.h"
-#include "base/message_loop.h"
-#include "base/string_util.h"
-#include "base/stringprintf.h"
-#include "media/base/filters.h"
+#include "base/test/test_timeouts.h"
#include "media/base/mock_filter_host.h"
#include "media/base/mock_filters.h"
#include "net/base/net_errors.h"
-#include "net/http/http_response_headers.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLError.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLResponse.h"
#include "webkit/glue/media/buffered_data_source.h"
-#include "webkit/glue/media/mock_media_resource_loader_bridge_factory.h"
-#include "webkit/glue/mock_resource_loader_bridge.h"
+#include "webkit/mocks/mock_webframe.h"
using ::testing::_;
using ::testing::Assign;
+using ::testing::AtLeast;
using ::testing::DeleteArg;
using ::testing::DoAll;
using ::testing::InSequence;
@@ -49,476 +45,36 @@ enum NetworkState {
namespace webkit_glue {
-// Submit a request completed event to the resource loader due to request
-// being canceled. Pretending the event is from external.
-ACTION_P(RequestCanceled, loader) {
- URLRequestStatus status;
- status.set_status(URLRequestStatus::CANCELED);
- status.set_os_error(net::ERR_ABORTED);
- loader->OnCompletedRequest(status, "", base::Time());
-}
-
-class BufferedResourceLoaderTest : public testing::Test {
+// A mock BufferedDataSource to inject mock BufferedResourceLoader through
+// CreateResourceLoader() method.
+class MockBufferedDataSource : public BufferedDataSource {
public:
- BufferedResourceLoaderTest() {
- bridge_.reset(new StrictMock<MockResourceLoaderBridge>());
-
- for (int i = 0; i < kDataSize; ++i)
- data_[i] = i;
- }
-
- ~BufferedResourceLoaderTest() {
- if (bridge_.get())
- EXPECT_CALL(*bridge_, OnDestroy());
- EXPECT_CALL(bridge_factory_, OnDestroy());
- }
-
- void Initialize(const char* url, int first_position, int last_position) {
- gurl_ = GURL(url);
- first_position_ = first_position;
- last_position_ = last_position;
-
- loader_ = new BufferedResourceLoader(&bridge_factory_, gurl_,
- first_position_, last_position_);
- }
-
- void SetLoaderBuffer(size_t forward_capacity, size_t backward_capacity) {
- loader_->buffer_.reset(
- new media::SeekableBuffer(backward_capacity, forward_capacity));
- }
-
- void Start() {
- InSequence s;
- EXPECT_CALL(bridge_factory_,
- CreateBridge(gurl_, _, first_position_, last_position_))
- .WillOnce(Return(bridge_.get()));
- EXPECT_CALL(*bridge_, Start(loader_.get()));
- loader_->Start(
- NewCallback(this, &BufferedResourceLoaderTest::StartCallback),
- NewCallback(this, &BufferedResourceLoaderTest::NetworkCallback));
- }
-
- void FullResponse(int64 instance_size) {
- EXPECT_CALL(*this, StartCallback(net::OK));
- ResourceResponseInfo info;
- std::string header = base::StringPrintf("HTTP/1.1 200 OK\n"
- "Content-Length: %" PRId64,
- instance_size);
- replace(header.begin(), header.end(), '\n', '\0');
- info.headers = new net::HttpResponseHeaders(header);
- info.content_length = instance_size;
- loader_->OnReceivedResponse(info, false);
- EXPECT_EQ(instance_size, loader_->content_length());
- EXPECT_EQ(instance_size, loader_->instance_size());
- EXPECT_FALSE(loader_->partial_response());
- }
-
- void PartialResponse(int64 first_position, int64 last_position,
- int64 instance_size) {
- EXPECT_CALL(*this, StartCallback(net::OK));
- int64 content_length = last_position - first_position + 1;
- ResourceResponseInfo info;
- std::string header = base::StringPrintf("HTTP/1.1 206 Partial Content\n"
- "Content-Range: bytes "
- "%" PRId64 "-%" PRId64 "/%" PRId64,
- first_position,
- last_position,
- instance_size);
- replace(header.begin(), header.end(), '\n', '\0');
- info.headers = new net::HttpResponseHeaders(header);
- info.content_length = content_length;
- loader_->OnReceivedResponse(info, false);
- EXPECT_EQ(content_length, loader_->content_length());
- EXPECT_EQ(instance_size, loader_->instance_size());
- EXPECT_TRUE(loader_->partial_response());
- }
-
- void StopWhenLoad() {
- InSequence s;
- EXPECT_CALL(*bridge_, Cancel())
- .WillOnce(RequestCanceled(loader_));
- EXPECT_CALL(*bridge_, OnDestroy())
- .WillOnce(Invoke(this, &BufferedResourceLoaderTest::ReleaseBridge));
- loader_->Stop();
- }
-
- void ReleaseBridge() {
- ignore_result(bridge_.release());
- }
-
- // Helper method to write to |loader_| from |data_|.
- void WriteLoader(int position, int size) {
- EXPECT_CALL(*this, NetworkCallback())
- .RetiresOnSaturation();
- loader_->OnReceivedData(reinterpret_cast<char*>(data_ + position), size);
- }
-
- // Helper method to read from |loader_|.
- void ReadLoader(int64 position, int size, uint8* buffer) {
- loader_->Read(position, size, buffer,
- NewCallback(this, &BufferedResourceLoaderTest::ReadCallback));
- }
-
- // Verifis that data in buffer[0...size] is equal to data_[pos...pos+size].
- void VerifyBuffer(uint8* buffer, int pos, int size) {
- EXPECT_EQ(0, memcmp(buffer, data_ + pos, size));
- }
-
- // Helper method to disallow deferring in |loader_|.
- void DisallowLoaderDefer() {
- if (loader_->deferred_) {
- EXPECT_CALL(*bridge_, SetDefersLoading(false));
- EXPECT_CALL(*this, NetworkCallback());
- }
- loader_->SetAllowDefer(false);
+ MockBufferedDataSource(
+ MessageLoop* message_loop, WebFrame* frame)
+ : BufferedDataSource(message_loop, frame) {
}
- // Helper method to allow deferring in |loader_|.
- void AllowLoaderDefer() {
- loader_->SetAllowDefer(true);
+ virtual base::TimeDelta GetTimeoutMilliseconds() {
+ return base::TimeDelta::FromMilliseconds(
+ TestTimeouts::tiny_timeout_ms());
}
- MOCK_METHOD1(StartCallback, void(int error));
- MOCK_METHOD1(ReadCallback, void(int error));
- MOCK_METHOD0(NetworkCallback, void());
-
- protected:
- GURL gurl_;
- int64 first_position_;
- int64 last_position_;
-
- scoped_refptr<BufferedResourceLoader> loader_;
- StrictMock<MockMediaResourceLoaderBridgeFactory> bridge_factory_;
- scoped_ptr<StrictMock<MockResourceLoaderBridge> > bridge_;
-
- uint8 data_[kDataSize];
+ MOCK_METHOD2(CreateResourceLoader,
+ BufferedResourceLoader*(int64 first_position,
+ int64 last_position));
private:
- DISALLOW_COPY_AND_ASSIGN(BufferedResourceLoaderTest);
+ DISALLOW_COPY_AND_ASSIGN(MockBufferedDataSource);
};
-TEST_F(BufferedResourceLoaderTest, StartStop) {
- Initialize(kHttpUrl, -1, -1);
- Start();
- StopWhenLoad();
-}
-
-// Tests that HTTP header is missing in the response.
-TEST_F(BufferedResourceLoaderTest, MissingHttpHeader) {
- Initialize(kHttpUrl, -1, -1);
- Start();
-
- EXPECT_CALL(*this, StartCallback(net::ERR_INVALID_RESPONSE));
- EXPECT_CALL(*bridge_, Cancel())
- .WillOnce(RequestCanceled(loader_));
- EXPECT_CALL(*bridge_, OnDestroy())
- .WillOnce(Invoke(this, &BufferedResourceLoaderTest::ReleaseBridge));
-
- ResourceResponseInfo info;
- loader_->OnReceivedResponse(info, false);
-}
-
-// Tests that a bad HTTP response is recived, e.g. file not found.
-TEST_F(BufferedResourceLoaderTest, BadHttpResponse) {
- Initialize(kHttpUrl, -1, -1);
- Start();
-
- EXPECT_CALL(*this, StartCallback(net::ERR_FAILED));
- EXPECT_CALL(*bridge_, Cancel())
- .WillOnce(RequestCanceled(loader_));
- EXPECT_CALL(*bridge_, OnDestroy())
- .WillOnce(Invoke(this, &BufferedResourceLoaderTest::ReleaseBridge));
-
- ResourceResponseInfo info;
- info.headers = new net::HttpResponseHeaders("HTTP/1.1 404 Not Found\n");
- loader_->OnReceivedResponse(info, false);
-}
-
-// Tests that partial content is requested but not fulfilled.
-TEST_F(BufferedResourceLoaderTest, NotPartialResponse) {
- Initialize(kHttpUrl, 100, -1);
- Start();
- FullResponse(1024);
- StopWhenLoad();
-}
-
-// Tests that a 200 response is received.
-TEST_F(BufferedResourceLoaderTest, FullResponse) {
- Initialize(kHttpUrl, -1, -1);
- Start();
- FullResponse(1024);
- StopWhenLoad();
-}
-
-// Tests that a partial content response is received.
-TEST_F(BufferedResourceLoaderTest, PartialResponse) {
- Initialize(kHttpUrl, 100, 200);
- Start();
- PartialResponse(100, 200, 1024);
- StopWhenLoad();
-}
-
-// Tests that an invalid partial response is received.
-TEST_F(BufferedResourceLoaderTest, InvalidPartialResponse) {
- Initialize(kHttpUrl, 0, 10);
- Start();
-
- EXPECT_CALL(*this, StartCallback(net::ERR_INVALID_RESPONSE));
- EXPECT_CALL(*bridge_, Cancel())
- .WillOnce(RequestCanceled(loader_));
- EXPECT_CALL(*bridge_, OnDestroy())
- .WillOnce(Invoke(this, &BufferedResourceLoaderTest::ReleaseBridge));
-
- ResourceResponseInfo info;
- std::string header = base::StringPrintf("HTTP/1.1 206 Partial Content\n"
- "Content-Range: bytes %d-%d/%d",
- 1, 10, 1024);
- replace(header.begin(), header.end(), '\n', '\0');
- info.headers = new net::HttpResponseHeaders(header);
- info.content_length = 10;
- loader_->OnReceivedResponse(info, false);
-}
-
-// Tests the logic of sliding window for data buffering and reading.
-TEST_F(BufferedResourceLoaderTest, BufferAndRead) {
- Initialize(kHttpUrl, 10, 29);
- Start();
- PartialResponse(10, 29, 30);
-
- uint8 buffer[10];
- InSequence s;
-
- // Writes 10 bytes and read them back.
- WriteLoader(10, 10);
- EXPECT_CALL(*this, ReadCallback(10));
- ReadLoader(10, 10, buffer);
- VerifyBuffer(buffer, 10, 10);
-
- // Writes 10 bytes and read 2 times.
- WriteLoader(20, 10);
- EXPECT_CALL(*this, ReadCallback(5));
- ReadLoader(20, 5, buffer);
- VerifyBuffer(buffer, 20, 5);
- EXPECT_CALL(*this, ReadCallback(5));
- ReadLoader(25, 5, buffer);
- VerifyBuffer(buffer, 25, 5);
-
- // Read backward within buffer.
- EXPECT_CALL(*this, ReadCallback(10));
- ReadLoader(10, 10, buffer);
- VerifyBuffer(buffer, 10, 10);
-
- // Read backward outside buffer.
- EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
- ReadLoader(9, 10, buffer);
-
- // Response has completed.
- EXPECT_CALL(*this, NetworkCallback());
- EXPECT_CALL(*bridge_, OnDestroy())
- .WillOnce(Invoke(this, &BufferedResourceLoaderTest::ReleaseBridge));
- URLRequestStatus status;
- status.set_status(URLRequestStatus::SUCCESS);
- loader_->OnCompletedRequest(status, "", base::Time());
-
- // Try to read 10 from position 25 will just return with 5 bytes.
- EXPECT_CALL(*this, ReadCallback(5));
- ReadLoader(25, 10, buffer);
- VerifyBuffer(buffer, 25, 5);
-
- // Try to read outside buffered range after request has completed.
- EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
- ReadLoader(5, 10, buffer);
-
- // Try to read beyond the instance size.
- EXPECT_CALL(*this, ReadCallback(0));
- ReadLoader(30, 10, buffer);
-}
-
-TEST_F(BufferedResourceLoaderTest, ReadOutsideBuffer) {
- Initialize(kHttpUrl, 10, 0x00FFFFFF);
- Start();
- PartialResponse(10, 0x00FFFFFF, 0x01000000);
-
- uint8 buffer[10];
- InSequence s;
-
- // Read very far aheard will get a cache miss.
- EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
- ReadLoader(0x00FFFFFF, 1, buffer);
-
- // The following call will not call ReadCallback() because it is waiting for
- // data to arrive.
- ReadLoader(10, 10, buffer);
-
- // Writing to loader will fulfill the read request.
- EXPECT_CALL(*this, ReadCallback(10));
- WriteLoader(10, 20);
- VerifyBuffer(buffer, 10, 10);
-
- // The following call cannot be fulfilled now.
- ReadLoader(25, 10, buffer);
-
- EXPECT_CALL(*this, ReadCallback(5));
- EXPECT_CALL(*this, NetworkCallback());
- EXPECT_CALL(*bridge_, OnDestroy())
- .WillOnce(Invoke(this, &BufferedResourceLoaderTest::ReleaseBridge));
- URLRequestStatus status;
- status.set_status(URLRequestStatus::SUCCESS);
- loader_->OnCompletedRequest(status, "", base::Time());
-}
-
-TEST_F(BufferedResourceLoaderTest, RequestFailedWhenRead) {
- Initialize(kHttpUrl, 10, 29);
- Start();
- PartialResponse(10, 29, 30);
-
- uint8 buffer[10];
- InSequence s;
-
- ReadLoader(10, 10, buffer);
- EXPECT_CALL(*this, ReadCallback(net::ERR_FAILED));
- EXPECT_CALL(*this, NetworkCallback());
- EXPECT_CALL(*bridge_, OnDestroy())
- .WillOnce(Invoke(this, &BufferedResourceLoaderTest::ReleaseBridge));
- URLRequestStatus status;
- status.set_status(URLRequestStatus::FAILED);
- loader_->OnCompletedRequest(status, "", base::Time());
-}
-
-// Tests the logic of caching data to disk when media is paused.
-TEST_F(BufferedResourceLoaderTest, AllowDefer_NoDataReceived) {
- Initialize(kHttpUrl, 10, 99);
- SetLoaderBuffer(10, 20);
- Start();
- PartialResponse(10, 99, 100);
-
- // Start in undeferred state, then disallow defer, then allow defer
- // without receiving data in between.
- DisallowLoaderDefer();
- AllowLoaderDefer();
- StopWhenLoad();
-}
-
-TEST_F(BufferedResourceLoaderTest, AllowDefer_ReadSameWindow) {
- Initialize(kHttpUrl, 10, 99);
- SetLoaderBuffer(10, 20);
- Start();
- PartialResponse(10, 99, 100);
-
- uint8 buffer[10];
-
- // Start in undeferred state, disallow defer, receive data but don't shift
- // buffer window, then allow defer and read.
- DisallowLoaderDefer();
- WriteLoader(10, 10);
- AllowLoaderDefer();
-
- EXPECT_CALL(*this, ReadCallback(10));
- ReadLoader(10, 10, buffer);
- VerifyBuffer(buffer, 10, 10);
- StopWhenLoad();
-}
-
-TEST_F(BufferedResourceLoaderTest, AllowDefer_ReadPastWindow) {
- Initialize(kHttpUrl, 10, 99);
- SetLoaderBuffer(10, 20);
- Start();
- PartialResponse(10, 99, 100);
-
- uint8 buffer[10];
-
- // Not deferred, disallow defer, received data and shift buffer window,
- // allow defer, then read in area outside of buffer window.
- DisallowLoaderDefer();
- WriteLoader(10, 10);
- WriteLoader(20, 50);
- AllowLoaderDefer();
-
- EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
- ReadLoader(10, 10, buffer);
- StopWhenLoad();
-}
-
-TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredNoDataReceived) {
- Initialize(kHttpUrl, 10, 99);
- SetLoaderBuffer(10, 20);
- Start();
- PartialResponse(10, 99, 100);
-
- uint8 buffer[10];
-
- // Start in deferred state, then disallow defer, receive no data, and
- // allow defer and read.
- EXPECT_CALL(*bridge_, SetDefersLoading(true));
- EXPECT_CALL(*this, NetworkCallback());
- WriteLoader(10, 40);
-
- DisallowLoaderDefer();
- AllowLoaderDefer();
-
- EXPECT_CALL(*this, ReadCallback(10));
- ReadLoader(20, 10, buffer);
- VerifyBuffer(buffer, 20, 10);
- StopWhenLoad();
-}
-
-TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredReadSameWindow) {
- Initialize(kHttpUrl, 10, 99);
- SetLoaderBuffer(10, 20);
- Start();
- PartialResponse(10, 99, 100);
-
- uint8 buffer[10];
-
- // Start in deferred state, disallow defer, receive data and shift buffer
- // window, allow defer, and read in a place that's still in the window.
- EXPECT_CALL(*bridge_, SetDefersLoading(true));
- EXPECT_CALL(*this, NetworkCallback());
- WriteLoader(10, 30);
-
- DisallowLoaderDefer();
- WriteLoader(40, 5);
- AllowLoaderDefer();
-
- EXPECT_CALL(*this, ReadCallback(10));
- ReadLoader(20, 10, buffer);
- VerifyBuffer(buffer, 20, 10);
- StopWhenLoad();
-}
-
-TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredReadPastWindow) {
- Initialize(kHttpUrl, 10, 99);
- SetLoaderBuffer(10, 20);
- Start();
- PartialResponse(10, 99, 100);
-
- uint8 buffer[10];
-
- // Start in deferred state, disallow defer, receive data and shift buffer
- // window, allow defer, and read outside of the buffer window.
- EXPECT_CALL(*bridge_, SetDefersLoading(true));
- EXPECT_CALL(*this, NetworkCallback());
- WriteLoader(10, 40);
-
- DisallowLoaderDefer();
- WriteLoader(50, 20);
- WriteLoader(70, 40);
- AllowLoaderDefer();
-
- EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
- ReadLoader(20, 5, buffer);
- StopWhenLoad();
-}
-
-// TODO(hclam): add unit test for defer loading.
-
class MockBufferedResourceLoader : public BufferedResourceLoader {
public:
- MockBufferedResourceLoader() : BufferedResourceLoader(NULL, GURL(), 0, 0) {
+ MockBufferedResourceLoader() : BufferedResourceLoader(GURL(), 0, 0) {
}
- MOCK_METHOD2(Start, void(net::CompletionCallback* read_callback,
- NetworkEventCallback* network_callback));
+ MOCK_METHOD3(Start, void(net::CompletionCallback* read_callback,
+ NetworkEventCallback* network_callback,
+ WebFrame* frame));
MOCK_METHOD0(Stop, void());
MOCK_METHOD4(Read, void(int64 position, int read_size, uint8* buffer,
net::CompletionCallback* callback));
@@ -536,33 +92,10 @@ class MockBufferedResourceLoader : public BufferedResourceLoader {
DISALLOW_COPY_AND_ASSIGN(MockBufferedResourceLoader);
};
-// A mock BufferedDataSource to inject mock BufferedResourceLoader through
-// CreateResourceLoader() method.
-class MockBufferedDataSource : public BufferedDataSource {
- public:
- MockBufferedDataSource(
- MessageLoop* message_loop, MediaResourceLoaderBridgeFactory* factory)
- : BufferedDataSource(message_loop, factory) {
- }
-
- virtual base::TimeDelta GetTimeoutMilliseconds() {
- // It is 100 ms because we don't want the test to run too long.
- return base::TimeDelta::FromMilliseconds(100);
- }
-
- MOCK_METHOD2(CreateResourceLoader, BufferedResourceLoader*(
- int64 first_position, int64 last_position));
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MockBufferedDataSource);
-};
-
class BufferedDataSourceTest : public testing::Test {
public:
BufferedDataSourceTest() {
message_loop_ = MessageLoop::current();
- bridge_factory_.reset(
- new StrictMock<MockMediaResourceLoaderBridgeFactory>());
// Prepare test data.
for (size_t i = 0; i < sizeof(data_); ++i) {
@@ -571,20 +104,14 @@ class BufferedDataSourceTest : public testing::Test {
}
virtual ~BufferedDataSourceTest() {
- if (data_source_) {
- // Release the bridge factory because we don't own it.
- // Expects bridge factory to be destroyed along with data source.
- EXPECT_CALL(*bridge_factory_, OnDestroy())
- .WillOnce(Invoke(this,
- &BufferedDataSourceTest::ReleaseBridgeFactory));
- }
+ ignore_result(frame_.release());
}
void ExpectCreateAndStartResourceLoader(int start_error) {
EXPECT_CALL(*data_source_, CreateResourceLoader(_, _))
.WillOnce(Return(loader_.get()));
- EXPECT_CALL(*loader_, Start(NotNull(), NotNull()))
+ EXPECT_CALL(*loader_, Start(NotNull(), NotNull(), NotNull()))
.WillOnce(
DoAll(Assign(&error_, start_error),
Invoke(this,
@@ -597,15 +124,10 @@ class BufferedDataSourceTest : public testing::Test {
// Saves the url first.
gurl_ = GURL(url);
- media::MediaFormat url_format;
- url_format.SetAsString(media::MediaFormat::kMimeType,
- media::mime_type::kURL);
- url_format.SetAsString(media::MediaFormat::kURL, url);
- data_source_ = new MockBufferedDataSource(MessageLoop::current(),
- bridge_factory_.get());
- CHECK(data_source_);
+ frame_.reset(new NiceMock<MockWebFrame>());
- // There is no need to provide a message loop to data source.
+ data_source_ = new MockBufferedDataSource(MessageLoop::current(),
+ frame_.get());
data_source_->set_host(&host_);
scoped_refptr<NiceMock<MockBufferedResourceLoader> > first_loader(
@@ -631,7 +153,7 @@ class BufferedDataSourceTest : public testing::Test {
// Replace loader_ with a new instance.
loader_ = new NiceMock<MockBufferedResourceLoader>();
- // Create and start Make sure Start() is called the new loader.
+ // Create and start. Make sure Start() is called on the new loader.
ExpectCreateAndStartResourceLoader(net::OK);
// Update initialization variable since we know the second loader will
@@ -705,13 +227,10 @@ class BufferedDataSourceTest : public testing::Test {
message_loop_->RunAllPending();
}
- void ReleaseBridgeFactory() {
- ignore_result(bridge_factory_.release());
- }
-
void InvokeStartCallback(
net::CompletionCallback* callback,
- BufferedResourceLoader::NetworkEventCallback* network_callback) {
+ BufferedResourceLoader::NetworkEventCallback* network_callback,
+ WebFrame* frame) {
callback->RunWithParams(Tuple1<int>(error_));
delete callback;
// TODO(hclam): Save this callback.
@@ -790,7 +309,7 @@ class BufferedDataSourceTest : public testing::Test {
.WillOnce(Return(new_loader));
// 3. Then the new loader will be started.
- EXPECT_CALL(*new_loader, Start(NotNull(), NotNull()))
+ EXPECT_CALL(*new_loader, Start(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(Assign(&error_, net::OK),
Invoke(this,
&BufferedDataSourceTest::InvokeStartCallback)));
@@ -856,7 +375,7 @@ class BufferedDataSourceTest : public testing::Test {
// 3. Then the new loader will be started and respond to queries about
// whether this is a partial response using the value of the previous
// loader.
- EXPECT_CALL(*new_loader, Start(NotNull(), NotNull()))
+ EXPECT_CALL(*new_loader, Start(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(Assign(&error_, net::OK),
Invoke(this,
&BufferedDataSourceTest::InvokeStartCallback)));
@@ -889,10 +408,9 @@ class BufferedDataSourceTest : public testing::Test {
MOCK_METHOD1(ReadCallback, void(size_t size));
- scoped_ptr<StrictMock<MockMediaResourceLoaderBridgeFactory> >
- bridge_factory_;
scoped_refptr<NiceMock<MockBufferedResourceLoader> > loader_;
scoped_refptr<MockBufferedDataSource> data_source_;
+ scoped_ptr<NiceMock<MockWebFrame> > frame_;
StrictMock<media::MockFilterHost> host_;
GURL gurl_;
diff --git a/webkit/glue/media/buffered_resource_loader.cc b/webkit/glue/media/buffered_resource_loader.cc
new file mode 100644
index 0000000..61aac72
--- /dev/null
+++ b/webkit/glue/media/buffered_resource_loader.cc
@@ -0,0 +1,578 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/media/buffered_resource_loader.h"
+
+#include "base/format_macros.h"
+#include "base/string_util.h"
+#include "net/base/net_errors.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebKit.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebKitClient.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLError.h"
+#include "webkit/glue/multipart_response_delegate.h"
+#include "webkit/glue/webkit_glue.h"
+
+using WebKit::WebFrame;
+using WebKit::WebString;
+using WebKit::WebURLError;
+using WebKit::WebURLLoader;
+using WebKit::WebURLRequest;
+using WebKit::WebURLResponse;
+using webkit_glue::MultipartResponseDelegate;
+
+namespace {
+
+const int kHttpOK = 200;
+const int kHttpPartialContent = 206;
+
+// Define the number of bytes in a megabyte.
+const size_t kMegabyte = 1024 * 1024;
+
+// Backward capacity of the buffer, by default 2MB.
+const size_t kBackwardCapcity = 2 * kMegabyte;
+
+// Forward capacity of the buffer, by default 10MB.
+const size_t kForwardCapacity = 10 * kMegabyte;
+
+// The threshold of bytes that we should wait until the data arrives in the
+// future instead of restarting a new connection. This number is defined in the
+// number of bytes, we should determine this value from typical connection speed
+// and amount of time for a suitable wait. Now I just make a guess for this
+// number to be 2MB.
+// TODO(hclam): determine a better value for this.
+const int kForwardWaitThreshold = 2 * kMegabyte;
+
+} // namespace
+
+namespace webkit_glue {
+
+BufferedResourceLoader::BufferedResourceLoader(
+ const GURL& url,
+ int64 first_byte_position,
+ int64 last_byte_position)
+ : buffer_(new media::SeekableBuffer(kBackwardCapcity, kForwardCapacity)),
+ deferred_(false),
+ defer_allowed_(true),
+ completed_(false),
+ range_requested_(false),
+ partial_response_(false),
+ url_(url),
+ first_byte_position_(first_byte_position),
+ last_byte_position_(last_byte_position),
+ start_callback_(NULL),
+ offset_(0),
+ content_length_(kPositionNotSpecified),
+ instance_size_(kPositionNotSpecified),
+ read_callback_(NULL),
+ read_position_(0),
+ read_size_(0),
+ read_buffer_(NULL),
+ first_offset_(0),
+ last_offset_(0),
+ keep_test_loader_(false) {
+}
+
+BufferedResourceLoader::~BufferedResourceLoader() {
+ if (!completed_ && url_loader_.get())
+ url_loader_->cancel();
+}
+
+void BufferedResourceLoader::Start(net::CompletionCallback* start_callback,
+ NetworkEventCallback* event_callback,
+ WebFrame* frame) {
+ // Make sure we have not started.
+ DCHECK(!start_callback_.get());
+ DCHECK(!event_callback_.get());
+ DCHECK(start_callback);
+ DCHECK(event_callback);
+ CHECK(frame);
+
+ start_callback_.reset(start_callback);
+ event_callback_.reset(event_callback);
+
+ if (first_byte_position_ != kPositionNotSpecified) {
+ range_requested_ = true;
+ // TODO(hclam): server may not support range request so |offset_| may not
+ // equal to |first_byte_position_|.
+ offset_ = first_byte_position_;
+ }
+
+ // Increment the reference count right before we start the request. This
+ // reference will be release when this request has ended.
+ AddRef();
+
+ // Prepare the request.
+ WebURLRequest request(url_);
+ request.setTargetType(WebURLRequest::TargetIsMedia);
+ request.setHTTPHeaderField(WebString::fromUTF8("Range"),
+ WebString::fromUTF8(GenerateHeaders(
+ first_byte_position_,
+ last_byte_position_)));
+ frame->setReferrerForRequest(request, WebKit::WebURL());
+
+ // This flag is for unittests as we don't want to reset |url_loader|
+ if (!keep_test_loader_)
+ url_loader_.reset(frame->createAssociatedURLLoader());
+
+ // Start the resource loading.
+ url_loader_->loadAsynchronously(request, this);
+}
+
+void BufferedResourceLoader::Stop() {
+ // Reset callbacks.
+ start_callback_.reset();
+ event_callback_.reset();
+ read_callback_.reset();
+
+ // Use the internal buffer to signal that we have been stopped.
+ // TODO(hclam): Not so pretty to do this.
+ if (!buffer_.get())
+ return;
+
+ // Destroy internal buffer.
+ buffer_.reset();
+
+ if (url_loader_.get()) {
+ if (deferred_)
+ url_loader_->setDefersLoading(false);
+ deferred_ = false;
+
+ if (!completed_) {
+ url_loader_->cancel();
+ completed_ = true;
+ }
+ }
+}
+
+void BufferedResourceLoader::Read(int64 position,
+ int read_size,
+ uint8* buffer,
+ net::CompletionCallback* read_callback) {
+ DCHECK(!read_callback_.get());
+ DCHECK(buffer_.get());
+ DCHECK(read_callback);
+ DCHECK(buffer);
+
+ // Save the parameter of reading.
+ read_callback_.reset(read_callback);
+ read_position_ = position;
+ read_size_ = read_size;
+ read_buffer_ = buffer;
+
+ // If read position is beyond the instance size, we cannot read there.
+ if (instance_size_ != kPositionNotSpecified &&
+ instance_size_ <= read_position_) {
+ DoneRead(0);
+ return;
+ }
+
+ // Make sure |offset_| and |read_position_| does not differ by a large
+ // amount.
+ if (read_position_ > offset_ + kint32max ||
+ read_position_ < offset_ + kint32min) {
+ DoneRead(net::ERR_CACHE_MISS);
+ return;
+ }
+
+ // Prepare the parameters.
+ first_offset_ = static_cast<int>(read_position_ - offset_);
+ last_offset_ = first_offset_ + read_size_;
+
+ // If we can serve the request now, do the actual read.
+ if (CanFulfillRead()) {
+ ReadInternal();
+ DisableDeferIfNeeded();
+ return;
+ }
+
+ // If we expected the read request to be fulfilled later, returns
+ // immediately and let more data to flow in.
+ if (WillFulfillRead())
+ return;
+
+ // Make a callback to report failure.
+ DoneRead(net::ERR_CACHE_MISS);
+}
+
+int64 BufferedResourceLoader::GetBufferedPosition() {
+ if (buffer_.get())
+ return offset_ + static_cast<int>(buffer_->forward_bytes()) - 1;
+ return kPositionNotSpecified;
+}
+
+void BufferedResourceLoader::SetAllowDefer(bool is_allowed) {
+ defer_allowed_ = is_allowed;
+ DisableDeferIfNeeded();
+}
+
+int64 BufferedResourceLoader::content_length() {
+ return content_length_;
+}
+
+int64 BufferedResourceLoader::instance_size() {
+ return instance_size_;
+}
+
+bool BufferedResourceLoader::partial_response() {
+ return partial_response_;
+}
+
+bool BufferedResourceLoader::network_activity() {
+ return !completed_ && !deferred_;
+}
+
+const GURL& BufferedResourceLoader::url() {
+ return url_;
+}
+
+void BufferedResourceLoader::SetURLLoaderForTest(WebURLLoader* mock_loader) {
+ url_loader_.reset(mock_loader);
+ keep_test_loader_ = true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// WebKit::WebURLLoaderClient implementation.
+void BufferedResourceLoader::willSendRequest(
+ WebURLLoader* loader,
+ WebURLRequest& newRequest,
+ const WebURLResponse& redirectResponse) {
+
+ // The load may have been stopped and |start_callback| is destroyed.
+ // In this case we shouldn't do anything.
+ if (!start_callback_.get()) {
+ // Set the url in the request to an invalid value (empty url).
+ newRequest.setURL(WebKit::WebURL());
+ return;
+ }
+
+ if (!IsProtocolSupportedForMedia(newRequest.url())) {
+ // Set the url in the request to an invalid value (empty url).
+ newRequest.setURL(WebKit::WebURL());
+ DoneStart(net::ERR_ADDRESS_INVALID);
+ Stop();
+ return;
+ }
+
+ url_ = newRequest.url();
+}
+
+void BufferedResourceLoader::didSendData(
+ WebURLLoader* loader,
+ unsigned long long bytes_sent,
+ unsigned long long total_bytes_to_be_sent) {
+ NOTIMPLEMENTED();
+}
+
+void BufferedResourceLoader::didReceiveResponse(
+ WebURLLoader* loader,
+ const WebURLResponse& response) {
+
+ // The loader may have been stopped and |start_callback| is destroyed.
+ // In this case we shouldn't do anything.
+ if (!start_callback_.get())
+ return;
+
+ // We make a strong assumption that when we reach here we have either
+ // received a response from HTTP/HTTPS protocol or the request was
+ // successful (in particular range request). So we only verify the partial
+ // response for HTTP and HTTPS protocol.
+ if (url_.SchemeIs(kHttpScheme) || url_.SchemeIs(kHttpsScheme)) {
+ int error = net::OK;
+
+ if (response.httpStatusCode() == kHttpPartialContent)
+ partial_response_ = true;
+
+ if (range_requested_ && partial_response_) {
+ // If we have verified the partial response and it is correct, we will
+ // return net::OK.
+ if (!VerifyPartialResponse(response))
+ error = net::ERR_INVALID_RESPONSE;
+ } else if (response.httpStatusCode() != kHttpOK) {
+ // We didn't request a range but server didn't reply with "200 OK".
+ error = net::ERR_FAILED;
+ }
+
+ if (error != net::OK) {
+ DoneStart(error);
+ Stop();
+ return;
+ }
+ } else {
+ // For any protocol other than HTTP and HTTPS, assume range request is
+ // always fulfilled.
+ partial_response_ = range_requested_;
+ }
+
+ // Expected content length can be |kPositionNotSpecified|, in that case
+ // |content_length_| is not specified and this is a streaming response.
+ content_length_ = response.expectedContentLength();
+
+ // If we have not requested a range, then the size of the instance is equal
+ // to the content length.
+ if (!partial_response_)
+ instance_size_ = content_length_;
+
+ // Calls with a successful response.
+ DoneStart(net::OK);
+}
+
+void BufferedResourceLoader::didReceiveData(
+ WebURLLoader* loader,
+ const char* data,
+ int data_length) {
+ DCHECK(!completed_);
+ DCHECK_GT(data_length, 0);
+
+ // If this loader has been stopped, |buffer_| would be destroyed.
+ // In this case we shouldn't do anything.
+ if (!buffer_.get())
+ return;
+
+ // Writes more data to |buffer_|.
+ buffer_->Append(reinterpret_cast<const uint8*>(data), data_length);
+
+ // If there is an active read request, try to fulfill the request.
+ if (HasPendingRead() && CanFulfillRead()) {
+ ReadInternal();
+ } else if (!defer_allowed_) {
+ // If we're not allowed to defer, slide the buffer window forward instead
+ // of deferring.
+ if (buffer_->forward_bytes() > buffer_->forward_capacity()) {
+ size_t excess = buffer_->forward_bytes() - buffer_->forward_capacity();
+ bool success = buffer_->Seek(excess);
+ DCHECK(success);
+ offset_ += first_offset_ + excess;
+ }
+ }
+
+ // At last see if the buffer is full and we need to defer the downloading.
+ EnableDeferIfNeeded();
+
+ // Notify that we have received some data.
+ NotifyNetworkEvent();
+}
+
+void BufferedResourceLoader::didDownloadData(
+ WebKit::WebURLLoader* loader,
+ int dataLength) {
+ NOTIMPLEMENTED();
+}
+
+void BufferedResourceLoader::didReceiveCachedMetadata(
+ WebURLLoader* loader,
+ const char* data,
+ int data_length) {
+ NOTIMPLEMENTED();
+}
+
+void BufferedResourceLoader::didFinishLoading(
+ WebURLLoader* loader,
+ double finishTime) {
+ DCHECK(!completed_);
+ completed_ = true;
+
+ // If there is a start callback, calls it.
+ if (start_callback_.get()) {
+ DoneStart(net::OK);
+ }
+
+ // If there is a pending read but the request has ended, returns with what
+ // we have.
+ if (HasPendingRead()) {
+ // Make sure we have a valid buffer before we satisfy a read request.
+ DCHECK(buffer_.get());
+
+ // Try to fulfill with what is in the buffer.
+ if (CanFulfillRead())
+ ReadInternal();
+ else
+ DoneRead(net::ERR_CACHE_MISS);
+ }
+
+ // There must not be any outstanding read request.
+ DCHECK(!HasPendingRead());
+
+ // Notify that network response is completed.
+ NotifyNetworkEvent();
+
+ url_loader_.reset();
+ Release();
+}
+
+void BufferedResourceLoader::didFail(
+ WebURLLoader* loader,
+ const WebURLError& error) {
+ DCHECK(!completed_);
+ completed_ = true;
+
+ // If there is a start callback, calls it.
+ if (start_callback_.get()) {
+ DoneStart(error.reason);
+ }
+
+ // If there is a pending read but the request failed, return with the
+ // reason for the error.
+ if (HasPendingRead()) {
+ DoneRead(error.reason);
+ }
+
+ // Notify that network response is completed.
+ NotifyNetworkEvent();
+
+ url_loader_.reset();
+ Release();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Helper methods.
+void BufferedResourceLoader::EnableDeferIfNeeded() {
+ if (!defer_allowed_)
+ return;
+
+ if (!deferred_ &&
+ buffer_->forward_bytes() >= buffer_->forward_capacity()) {
+ deferred_ = true;
+
+ if (url_loader_.get())
+ url_loader_->setDefersLoading(true);
+
+ NotifyNetworkEvent();
+ }
+}
+
+void BufferedResourceLoader::DisableDeferIfNeeded() {
+ if (deferred_ &&
+ (!defer_allowed_ ||
+ buffer_->forward_bytes() < buffer_->forward_capacity() / 2)) {
+ deferred_ = false;
+
+ if (url_loader_.get())
+ url_loader_->setDefersLoading(false);
+
+ NotifyNetworkEvent();
+ }
+}
+
+bool BufferedResourceLoader::CanFulfillRead() {
+ // If we are reading too far in the backward direction.
+ if (first_offset_ < 0 &&
+ first_offset_ + static_cast<int>(buffer_->backward_bytes()) < 0)
+ return false;
+
+ // If the start offset is too far ahead.
+ if (first_offset_ >= static_cast<int>(buffer_->forward_bytes()))
+ return false;
+
+ // At the point, we verified that first byte requested is within the buffer.
+ // If the request has completed, then just returns with what we have now.
+ if (completed_)
+ return true;
+
+ // If the resource request is still active, make sure the whole requested
+ // range is covered.
+ if (last_offset_ > static_cast<int>(buffer_->forward_bytes()))
+ return false;
+
+ return true;
+}
+
+bool BufferedResourceLoader::WillFulfillRead() {
+ // Reading too far in the backward direction.
+ if (first_offset_ < 0 &&
+ first_offset_ + static_cast<int>(buffer_->backward_bytes()) < 0)
+ return false;
+
+ // Try to read too far ahead.
+ if (last_offset_ > kForwardWaitThreshold)
+ return false;
+
+ // The resource request has completed, there's no way we can fulfill the
+ // read request.
+ if (completed_)
+ return false;
+
+ return true;
+}
+
+void BufferedResourceLoader::ReadInternal() {
+ // Seek to the first byte requested.
+ bool ret = buffer_->Seek(first_offset_);
+ DCHECK(ret);
+
+ // Then do the read.
+ int read = static_cast<int>(buffer_->Read(read_buffer_, read_size_));
+ offset_ += first_offset_ + read;
+
+ // And report with what we have read.
+ DoneRead(read);
+}
+
+bool BufferedResourceLoader::VerifyPartialResponse(
+ const WebURLResponse& response) {
+ int first_byte_position, last_byte_position, instance_size;
+
+ if (!MultipartResponseDelegate::ReadContentRanges(response,
+ &first_byte_position,
+ &last_byte_position,
+ &instance_size)) {
+ return false;
+ }
+
+ if (instance_size != kPositionNotSpecified) {
+ instance_size_ = instance_size;
+ }
+
+ if (first_byte_position_ != kPositionNotSpecified &&
+ first_byte_position_ != first_byte_position) {
+ return false;
+ }
+
+ // TODO(hclam): I should also check |last_byte_position|, but since
+ // we will never make such a request that it is ok to leave it unimplemented.
+ return true;
+}
+
+std::string BufferedResourceLoader::GenerateHeaders(
+ int64 first_byte_position,
+ int64 last_byte_position) {
+ // Construct the value for the range header.
+ std::string header;
+ if (first_byte_position > kPositionNotSpecified &&
+ last_byte_position > kPositionNotSpecified) {
+ if (first_byte_position <= last_byte_position) {
+ header = base::StringPrintf("bytes=%" PRId64 "-%" PRId64,
+ first_byte_position,
+ last_byte_position);
+ }
+ } else if (first_byte_position > kPositionNotSpecified) {
+ header = base::StringPrintf("bytes=%" PRId64 "-",
+ first_byte_position);
+ } else if (last_byte_position > kPositionNotSpecified) {
+ NOTIMPLEMENTED() << "Suffix range not implemented";
+ }
+ return header;
+}
+
+void BufferedResourceLoader::DoneRead(int error) {
+ read_callback_->RunWithParams(Tuple1<int>(error));
+ read_callback_.reset();
+ read_position_ = 0;
+ read_size_ = 0;
+ read_buffer_ = NULL;
+ first_offset_ = 0;
+ last_offset_ = 0;
+}
+
+void BufferedResourceLoader::DoneStart(int error) {
+ start_callback_->RunWithParams(Tuple1<int>(error));
+ start_callback_.reset();
+}
+
+void BufferedResourceLoader::NotifyNetworkEvent() {
+ if (event_callback_.get())
+ event_callback_->Run();
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/media/buffered_resource_loader.h b/webkit/glue/media/buffered_resource_loader.h
new file mode 100644
index 0000000..09b05e6
--- /dev/null
+++ b/webkit/glue/media/buffered_resource_loader.h
@@ -0,0 +1,246 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_MEDIA_BUFFERED_RESOURCE_LOADER_H_
+#define WEBKIT_GLUE_MEDIA_BUFFERED_RESOURCE_LOADER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/lock.h"
+#include "base/scoped_ptr.h"
+#include "base/timer.h"
+#include "googleurl/src/gurl.h"
+#include "media/base/seekable_buffer.h"
+#include "net/base/file_stream.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLLoader.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLLoaderClient.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h"
+#include "webkit/glue/media/web_data_source.h"
+#include "webkit/glue/webmediaplayer_impl.h"
+
+namespace webkit_glue {
+
+const int64 kPositionNotSpecified = -1;
+
+const char kHttpScheme[] = "http";
+const char kHttpsScheme[] = "https";
+const char kDataScheme[] = "data";
+
+// This class works inside demuxer thread and render thread. It contains a
+// WebURLLoader and does the actual resource loading. This object does
+// buffering internally, it defers the resource loading if buffer is full
+// and un-defers the resource loading if it is under buffered.
+class BufferedResourceLoader :
+ public base::RefCountedThreadSafe<BufferedResourceLoader>,
+ public WebKit::WebURLLoaderClient {
+ public:
+ typedef Callback0::Type NetworkEventCallback;
+
+ // |url| - URL for the resource to be loaded.
+ // |first_byte_position| - First byte to start loading from,
+ // |kPositionNotSpecified| for not specified.
+ // |last_byte_position| - Last byte to be loaded,
+ // |kPositionNotSpecified| for not specified.
+ BufferedResourceLoader(const GURL& url,
+ int64 first_byte_position,
+ int64 last_byte_position);
+
+ // Start the resource loading with the specified URL and range.
+ // This method operates in asynchronous mode. Once there's a response from the
+ // server, success or fail |callback| is called with the result.
+ // |callback| is called with the following values:
+ // - net::OK
+ // The request has started successfully.
+ // - net::ERR_FAILED
+ // The request has failed because of an error with the network.
+ // - net::ERR_INVALID_RESPONSE
+ // An invalid response is received from the server.
+ // - (Anything else)
+ // An error code that indicates the request has failed.
+ // |event_callback| is called when the response is completed, data is
+ // received, the request is suspended or resumed.
+ virtual void Start(net::CompletionCallback* callback,
+ NetworkEventCallback* event_callback,
+ WebKit::WebFrame* frame);
+
+ // Stop this loader, cancels and request and release internal buffer.
+ virtual void Stop();
+
+ // Reads the specified |read_size| from |position| into |buffer| and when
+ // the operation is done invoke |callback| with number of bytes read or an
+ // error code.
+ // |callback| is called with the following values:
+ // - (Anything greater than or equal 0)
+ // Read was successful with the indicated number of bytes read.
+ // - net::ERR_FAILED
+ // The read has failed because of an error with the network.
+ // - net::ERR_CACHE_MISS
+ // The read was made too far away from the current buffered position.
+ virtual void Read(int64 position, int read_size,
+ uint8* buffer, net::CompletionCallback* callback);
+
+ // Returns the position of the last byte buffered. Returns
+ // |kPositionNotSpecified| if such value is not available.
+ virtual int64 GetBufferedPosition();
+
+ // Sets whether deferring data is allowed or disallowed.
+ virtual void SetAllowDefer(bool is_allowed);
+
+ // Gets the content length in bytes of the instance after this loader has been
+ // started. If this value is |kPositionNotSpecified|, then content length is
+ // unknown.
+ virtual int64 content_length();
+
+ // Gets the original size of the file requested. If this value is
+ // |kPositionNotSpecified|, then the size is unknown.
+ virtual int64 instance_size();
+
+ // Returns true if the response for this loader is a partial response.
+ // It means a 206 response in HTTP/HTTPS protocol.
+ virtual bool partial_response();
+
+ // Returns true if network is currently active.
+ virtual bool network_activity();
+
+ // Returns resulting URL.
+ virtual const GURL& url();
+
+ // Used to inject a mock used for unittests.
+ virtual void SetURLLoaderForTest(WebKit::WebURLLoader* mock_loader);
+
+ // WebKit::WebURLLoaderClient implementation.
+ virtual void willSendRequest(
+ WebKit::WebURLLoader* loader,
+ WebKit::WebURLRequest& newRequest,
+ const WebKit::WebURLResponse& redirectResponse);
+ virtual void didSendData(
+ WebKit::WebURLLoader* loader,
+ unsigned long long bytesSent,
+ unsigned long long totalBytesToBeSent);
+ virtual void didReceiveResponse(
+ WebKit::WebURLLoader* loader,
+ const WebKit::WebURLResponse& response);
+ virtual void didDownloadData(
+ WebKit::WebURLLoader* loader,
+ int dataLength);
+ virtual void didReceiveData(
+ WebKit::WebURLLoader* loader,
+ const char* data,
+ int dataLength);
+ virtual void didReceiveCachedMetadata(
+ WebKit::WebURLLoader* loader,
+ const char* data, int dataLength);
+ virtual void didFinishLoading(
+ WebKit::WebURLLoader* loader,
+ double finishTime);
+ virtual void didFail(
+ WebKit::WebURLLoader* loader,
+ const WebKit::WebURLError&);
+
+ protected:
+ friend class base::RefCountedThreadSafe<BufferedResourceLoader>;
+
+ virtual ~BufferedResourceLoader();
+
+ private:
+ friend class BufferedResourceLoaderTest;
+
+ // Defer the resource loading if the buffer is full.
+ void EnableDeferIfNeeded();
+
+ // Disable defer loading if we are under-buffered.
+ void DisableDeferIfNeeded();
+
+ // Returns true if the current read request can be fulfilled by what is in
+ // the buffer.
+ bool CanFulfillRead();
+
+ // Returns true if the current read request will be fulfilled in the future.
+ bool WillFulfillRead();
+
+ // Method that does the actual read and calls the |read_callback_|, assuming
+ // the request range is in |buffer_|.
+ void ReadInternal();
+
+ // If we have made a range request, verify the response from the server.
+ bool VerifyPartialResponse(const WebKit::WebURLResponse& response);
+
+ // Returns the value for a range request header using parameters
+ // |first_byte_position| and |last_byte_position|. Negative numbers other
+ // than |kPositionNotSpecified| are not allowed for |first_byte_position| and
+ // |last_byte_position|. |first_byte_position| should always be less than or
+ // equal to |last_byte_position| if they are both not |kPositionNotSpecified|.
+ // Empty string is returned on invalid parameters.
+ std::string GenerateHeaders(int64 first_byte_position,
+ int64 last_byte_position);
+
+ // Done with read. Invokes the read callback and reset parameters for the
+ // read request.
+ void DoneRead(int error);
+
+ // Done with start. Invokes the start callback and reset it.
+ void DoneStart(int error);
+
+ // Calls |event_callback_| in terms of a network event.
+ void NotifyNetworkEvent();
+
+ bool HasPendingRead() { return read_callback_.get() != NULL; }
+
+ // A sliding window of buffer.
+ scoped_ptr<media::SeekableBuffer> buffer_;
+
+ // True if resource loading was deferred.
+ bool deferred_;
+
+ // True if resource loader is allowed to defer, false otherwise.
+ bool defer_allowed_;
+
+ // True if resource loading has completed.
+ bool completed_;
+
+ // True if a range request was made.
+ bool range_requested_;
+
+ // True if response data received is a partial range.
+ bool partial_response_;
+
+ // Does the work of loading and sends data back to this client.
+ scoped_ptr<WebKit::WebURLLoader> url_loader_;
+
+ GURL url_;
+ int64 first_byte_position_;
+ int64 last_byte_position_;
+
+ // Callback method that listens to network events.
+ scoped_ptr<NetworkEventCallback> event_callback_;
+
+ // Members used during request start.
+ scoped_ptr<net::CompletionCallback> start_callback_;
+ int64 offset_;
+ int64 content_length_;
+ int64 instance_size_;
+
+ // Members used during a read operation. They should be reset after each
+ // read has completed or failed.
+ scoped_ptr<net::CompletionCallback> read_callback_;
+ int64 read_position_;
+ int read_size_;
+ uint8* read_buffer_;
+
+ // Offsets of the requested first byte and last byte in |buffer_|. They are
+ // written by Read().
+ int first_offset_;
+ int last_offset_;
+
+ // Used to ensure mocks for unittests are used instead of reset in Start().
+ bool keep_test_loader_;
+
+ DISALLOW_COPY_AND_ASSIGN(BufferedResourceLoader);
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_MEDIA_BUFFERED_RESOURCE_LOADER_H_
diff --git a/webkit/glue/media/buffered_resource_loader_unittest.cc b/webkit/glue/media/buffered_resource_loader_unittest.cc
new file mode 100644
index 0000000..8e64c26
--- /dev/null
+++ b/webkit/glue/media/buffered_resource_loader_unittest.cc
@@ -0,0 +1,486 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+
+#include "base/format_macros.h"
+#include "base/stringprintf.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_util.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrameClient.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLError.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLResponse.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
+#include "webkit/glue/media/buffered_resource_loader.h"
+#include "webkit/mocks/mock_webframe.h"
+#include "webkit/mocks/mock_weburlloader.h"
+
+using ::testing::_;
+using ::testing::Assign;
+using ::testing::AtLeast;
+using ::testing::DeleteArg;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+using ::testing::NotNull;
+using ::testing::Return;
+using ::testing::ReturnRef;
+using ::testing::SetArgumentPointee;
+using ::testing::StrictMock;
+using ::testing::NiceMock;
+using ::testing::WithArgs;
+
+using WebKit::WebURLError;
+using WebKit::WebFrameClient;
+using WebKit::WebURLResponse;
+using WebKit::WebView;
+
+namespace {
+
+const char* kHttpUrl = "http://test";
+const int kDataSize = 1024;
+const int kHttpOK = 200;
+const int kHttpPartialContent = 206;
+
+enum NetworkState {
+ NONE,
+ LOADED,
+ LOADING
+};
+
+} // namespace
+
+namespace webkit_glue {
+
+// Submit a request completed event to the resource loader due to request
+// being canceled. Pretending the event is from external.
+ACTION_P(RequestCanceled, loader) {
+ WebURLError error;
+ error.reason = net::ERR_ABORTED;
+ error.domain = WebString::fromUTF8(net::kErrorDomain);
+ loader->didFail(NULL, error);
+}
+
+class BufferedResourceLoaderTest : public testing::Test {
+ public:
+ BufferedResourceLoaderTest() {
+ url_loader_ = new NiceMock<MockWebURLLoader>();
+
+ for (int i = 0; i < kDataSize; ++i)
+ data_[i] = i;
+ }
+
+ virtual ~BufferedResourceLoaderTest() {
+ ignore_result(frame_.release());
+ }
+
+ void Initialize(const char* url, int first_position, int last_position) {
+ gurl_ = GURL(url);
+ first_position_ = first_position;
+ last_position_ = last_position;
+
+ frame_.reset(new NiceMock<MockWebFrame>());
+
+ loader_ = new BufferedResourceLoader(gurl_,
+ first_position_, last_position_);
+ loader_->SetURLLoaderForTest(url_loader_);
+ }
+
+ void SetLoaderBuffer(size_t forward_capacity, size_t backward_capacity) {
+ loader_->buffer_.reset(
+ new media::SeekableBuffer(backward_capacity, forward_capacity));
+ }
+
+ void Start() {
+ InSequence s;
+ EXPECT_CALL(*url_loader_, loadAsynchronously(_, loader_.get()));
+ loader_->Start(
+ NewCallback(this, &BufferedResourceLoaderTest::StartCallback),
+ NewCallback(this, &BufferedResourceLoaderTest::NetworkCallback),
+ frame_.get());
+ }
+
+ void FullResponse(int64 instance_size) {
+ EXPECT_CALL(*this, StartCallback(net::OK));
+
+ WebURLResponse response(gurl_);
+ response.setHTTPHeaderField(WebString::fromUTF8("Content-Length"),
+ WebString::fromUTF8(base::StringPrintf("%"
+ PRId64, instance_size)));
+ response.setExpectedContentLength(instance_size);
+ response.setHTTPStatusCode(kHttpOK);
+ loader_->didReceiveResponse(url_loader_, response);
+ EXPECT_EQ(instance_size, loader_->content_length());
+ EXPECT_EQ(instance_size, loader_->instance_size());
+ EXPECT_FALSE(loader_->partial_response());
+ }
+
+ void PartialResponse(int64 first_position, int64 last_position,
+ int64 instance_size) {
+ EXPECT_CALL(*this, StartCallback(net::OK));
+ int64 content_length = last_position - first_position + 1;
+
+ WebURLResponse response(gurl_);
+ response.setHTTPHeaderField(WebString::fromUTF8("Content-Range"),
+ WebString::fromUTF8(base::StringPrintf("bytes "
+ "%" PRId64 "-%" PRId64 "/%" PRId64,
+ first_position,
+ last_position,
+ instance_size)));
+ response.setExpectedContentLength(content_length);
+ response.setHTTPStatusCode(kHttpPartialContent);
+ loader_->didReceiveResponse(url_loader_, response);
+ EXPECT_EQ(content_length, loader_->content_length());
+ EXPECT_EQ(instance_size, loader_->instance_size());
+ EXPECT_TRUE(loader_->partial_response());
+ }
+
+ void StopWhenLoad() {
+ InSequence s;
+ EXPECT_CALL(*url_loader_, cancel())
+ .WillOnce(RequestCanceled(loader_));
+ loader_->Stop();
+ }
+
+ // Helper method to write to |loader_| from |data_|.
+ void WriteLoader(int position, int size) {
+ EXPECT_CALL(*this, NetworkCallback())
+ .RetiresOnSaturation();
+ loader_->didReceiveData(url_loader_,
+ reinterpret_cast<char*>(data_ + position), size);
+ }
+
+ // Helper method to read from |loader_|.
+ void ReadLoader(int64 position, int size, uint8* buffer) {
+ loader_->Read(position, size, buffer,
+ NewCallback(this, &BufferedResourceLoaderTest::ReadCallback));
+ }
+
+ // Verifis that data in buffer[0...size] is equal to data_[pos...pos+size].
+ void VerifyBuffer(uint8* buffer, int pos, int size) {
+ EXPECT_EQ(0, memcmp(buffer, data_ + pos, size));
+ }
+
+ // Helper method to disallow deferring in |loader_|.
+ void DisallowLoaderDefer() {
+ if (loader_->deferred_) {
+ EXPECT_CALL(*url_loader_, setDefersLoading(false));
+ EXPECT_CALL(*this, NetworkCallback());
+ }
+ loader_->SetAllowDefer(false);
+ }
+
+ // Helper method to allow deferring in |loader_|.
+ void AllowLoaderDefer() {
+ loader_->SetAllowDefer(true);
+ }
+
+ MOCK_METHOD1(StartCallback, void(int error));
+ MOCK_METHOD1(ReadCallback, void(int error));
+ MOCK_METHOD0(NetworkCallback, void());
+
+ protected:
+ GURL gurl_;
+ int64 first_position_;
+ int64 last_position_;
+
+ scoped_refptr<BufferedResourceLoader> loader_;
+ NiceMock<MockWebURLLoader>* url_loader_;
+ scoped_ptr<NiceMock<MockWebFrame> > frame_;
+
+ uint8 data_[kDataSize];
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BufferedResourceLoaderTest);
+};
+
+TEST_F(BufferedResourceLoaderTest, StartStop) {
+ Initialize(kHttpUrl, -1, -1);
+ Start();
+ StopWhenLoad();
+}
+
+// Tests that a bad HTTP response is recived, e.g. file not found.
+TEST_F(BufferedResourceLoaderTest, BadHttpResponse) {
+ Initialize(kHttpUrl, -1, -1);
+ Start();
+
+ EXPECT_CALL(*this, StartCallback(net::ERR_FAILED));
+ EXPECT_CALL(*url_loader_, cancel())
+ .WillOnce(RequestCanceled(loader_));
+
+ WebURLResponse response(gurl_);
+ response.setHTTPStatusCode(404);
+ response.setHTTPStatusText("Not Found\n");
+ loader_->didReceiveResponse(url_loader_, response);
+}
+
+// Tests that partial content is requested but not fulfilled.
+TEST_F(BufferedResourceLoaderTest, NotPartialResponse) {
+ Initialize(kHttpUrl, 100, -1);
+ Start();
+ FullResponse(1024);
+ StopWhenLoad();
+}
+
+// Tests that a 200 response is received.
+TEST_F(BufferedResourceLoaderTest, FullResponse) {
+ Initialize(kHttpUrl, -1, -1);
+ Start();
+ FullResponse(1024);
+ StopWhenLoad();
+}
+
+// Tests that a partial content response is received.
+TEST_F(BufferedResourceLoaderTest, PartialResponse) {
+ Initialize(kHttpUrl, 100, 200);
+ Start();
+ PartialResponse(100, 200, 1024);
+ StopWhenLoad();
+}
+
+// Tests that an invalid partial response is received.
+TEST_F(BufferedResourceLoaderTest, InvalidPartialResponse) {
+ Initialize(kHttpUrl, 0, 10);
+ Start();
+
+ EXPECT_CALL(*this, StartCallback(net::ERR_INVALID_RESPONSE));
+ EXPECT_CALL(*url_loader_, cancel())
+ .WillOnce(RequestCanceled(loader_));
+
+ WebURLResponse response(gurl_);
+ response.setHTTPHeaderField(WebString::fromUTF8("Content-Range"),
+ WebString::fromUTF8(base::StringPrintf("bytes "
+ "%d-%d/%d", 1, 10, 1024)));
+ response.setExpectedContentLength(10);
+ response.setHTTPStatusCode(kHttpPartialContent);
+ loader_->didReceiveResponse(url_loader_, response);
+}
+
+// Tests the logic of sliding window for data buffering and reading.
+TEST_F(BufferedResourceLoaderTest, BufferAndRead) {
+ Initialize(kHttpUrl, 10, 29);
+ Start();
+ PartialResponse(10, 29, 30);
+
+ uint8 buffer[10];
+ InSequence s;
+
+ // Writes 10 bytes and read them back.
+ WriteLoader(10, 10);
+ EXPECT_CALL(*this, ReadCallback(10));
+ ReadLoader(10, 10, buffer);
+ VerifyBuffer(buffer, 10, 10);
+
+ // Writes 10 bytes and read 2 times.
+ WriteLoader(20, 10);
+ EXPECT_CALL(*this, ReadCallback(5));
+ ReadLoader(20, 5, buffer);
+ VerifyBuffer(buffer, 20, 5);
+ EXPECT_CALL(*this, ReadCallback(5));
+ ReadLoader(25, 5, buffer);
+ VerifyBuffer(buffer, 25, 5);
+
+ // Read backward within buffer.
+ EXPECT_CALL(*this, ReadCallback(10));
+ ReadLoader(10, 10, buffer);
+ VerifyBuffer(buffer, 10, 10);
+
+ // Read backward outside buffer.
+ EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
+ ReadLoader(9, 10, buffer);
+
+ // Response has completed.
+ EXPECT_CALL(*this, NetworkCallback());
+ loader_->didFinishLoading(url_loader_, 0);
+
+ // Try to read 10 from position 25 will just return with 5 bytes.
+ EXPECT_CALL(*this, ReadCallback(5));
+ ReadLoader(25, 10, buffer);
+ VerifyBuffer(buffer, 25, 5);
+
+ // Try to read outside buffered range after request has completed.
+ EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
+ ReadLoader(5, 10, buffer);
+
+ // Try to read beyond the instance size.
+ EXPECT_CALL(*this, ReadCallback(0));
+ ReadLoader(30, 10, buffer);
+}
+
+TEST_F(BufferedResourceLoaderTest, ReadOutsideBuffer) {
+ Initialize(kHttpUrl, 10, 0x00FFFFFF);
+ Start();
+ PartialResponse(10, 0x00FFFFFF, 0x01000000);
+
+ uint8 buffer[10];
+ InSequence s;
+
+ // Read very far aheard will get a cache miss.
+ EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
+ ReadLoader(0x00FFFFFF, 1, buffer);
+
+ // The following call will not call ReadCallback() because it is waiting for
+ // data to arrive.
+ ReadLoader(10, 10, buffer);
+
+ // Writing to loader will fulfill the read request.
+ EXPECT_CALL(*this, ReadCallback(10));
+ WriteLoader(10, 20);
+ VerifyBuffer(buffer, 10, 10);
+
+ // The following call cannot be fulfilled now.
+ ReadLoader(25, 10, buffer);
+
+ EXPECT_CALL(*this, ReadCallback(5));
+ EXPECT_CALL(*this, NetworkCallback());
+ loader_->didFinishLoading(url_loader_, 0);
+}
+
+TEST_F(BufferedResourceLoaderTest, RequestFailedWhenRead) {
+ Initialize(kHttpUrl, 10, 29);
+ Start();
+ PartialResponse(10, 29, 30);
+
+ uint8 buffer[10];
+ InSequence s;
+
+ ReadLoader(10, 10, buffer);
+ EXPECT_CALL(*this, ReadCallback(net::ERR_FAILED));
+ EXPECT_CALL(*this, NetworkCallback());
+ WebURLError error;
+ error.reason = net::ERR_FAILED;
+ loader_->didFail(url_loader_, error);
+}
+
+// Tests the logic of caching data to disk when media is paused.
+TEST_F(BufferedResourceLoaderTest, AllowDefer_NoDataReceived) {
+ Initialize(kHttpUrl, 10, 99);
+ SetLoaderBuffer(10, 20);
+ Start();
+ PartialResponse(10, 99, 100);
+
+ // Start in undeferred state, then disallow defer, then allow defer
+ // without receiving data in between.
+ DisallowLoaderDefer();
+ AllowLoaderDefer();
+ StopWhenLoad();
+}
+
+TEST_F(BufferedResourceLoaderTest, AllowDefer_ReadSameWindow) {
+ Initialize(kHttpUrl, 10, 99);
+ SetLoaderBuffer(10, 20);
+ Start();
+ PartialResponse(10, 99, 100);
+
+ uint8 buffer[10];
+
+ // Start in undeferred state, disallow defer, receive data but don't shift
+ // buffer window, then allow defer and read.
+ DisallowLoaderDefer();
+ WriteLoader(10, 10);
+ AllowLoaderDefer();
+
+ EXPECT_CALL(*this, ReadCallback(10));
+ ReadLoader(10, 10, buffer);
+ VerifyBuffer(buffer, 10, 10);
+ StopWhenLoad();
+}
+
+TEST_F(BufferedResourceLoaderTest, AllowDefer_ReadPastWindow) {
+ Initialize(kHttpUrl, 10, 99);
+ SetLoaderBuffer(10, 20);
+ Start();
+ PartialResponse(10, 99, 100);
+
+ uint8 buffer[10];
+
+ // Not deferred, disallow defer, received data and shift buffer window,
+ // allow defer, then read in area outside of buffer window.
+ DisallowLoaderDefer();
+ WriteLoader(10, 10);
+ WriteLoader(20, 50);
+ AllowLoaderDefer();
+
+ EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
+ ReadLoader(10, 10, buffer);
+ StopWhenLoad();
+}
+
+TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredNoDataReceived) {
+ Initialize(kHttpUrl, 10, 99);
+ SetLoaderBuffer(10, 20);
+ Start();
+ PartialResponse(10, 99, 100);
+
+ uint8 buffer[10];
+
+ // Start in deferred state, then disallow defer, receive no data, and
+ // allow defer and read.
+ EXPECT_CALL(*url_loader_, setDefersLoading(true));
+ EXPECT_CALL(*this, NetworkCallback());
+ WriteLoader(10, 40);
+
+ DisallowLoaderDefer();
+ AllowLoaderDefer();
+
+ EXPECT_CALL(*this, ReadCallback(10));
+ ReadLoader(20, 10, buffer);
+ VerifyBuffer(buffer, 20, 10);
+ StopWhenLoad();
+}
+
+TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredReadSameWindow) {
+ Initialize(kHttpUrl, 10, 99);
+ SetLoaderBuffer(10, 20);
+ Start();
+ PartialResponse(10, 99, 100);
+
+ uint8 buffer[10];
+
+ // Start in deferred state, disallow defer, receive data and shift buffer
+ // window, allow defer, and read in a place that's still in the window.
+ EXPECT_CALL(*url_loader_, setDefersLoading(true));
+ EXPECT_CALL(*this, NetworkCallback());
+ WriteLoader(10, 30);
+
+ DisallowLoaderDefer();
+ WriteLoader(40, 5);
+ AllowLoaderDefer();
+
+ EXPECT_CALL(*this, ReadCallback(10));
+ ReadLoader(20, 10, buffer);
+ VerifyBuffer(buffer, 20, 10);
+ StopWhenLoad();
+}
+
+TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredReadPastWindow) {
+ Initialize(kHttpUrl, 10, 99);
+ SetLoaderBuffer(10, 20);
+ Start();
+ PartialResponse(10, 99, 100);
+
+ uint8 buffer[10];
+
+ // Start in deferred state, disallow defer, receive data and shift buffer
+ // window, allow defer, and read outside of the buffer window.
+ EXPECT_CALL(*url_loader_, setDefersLoading(true));
+ EXPECT_CALL(*this, NetworkCallback());
+ WriteLoader(10, 40);
+
+ DisallowLoaderDefer();
+ WriteLoader(50, 20);
+ WriteLoader(70, 40);
+ AllowLoaderDefer();
+
+ EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
+ ReadLoader(20, 5, buffer);
+ StopWhenLoad();
+}
+// TODO(hclam): add unit test for defer loading.
+
+} // namespace webkit_glue
+
diff --git a/webkit/glue/media/media_resource_loader_bridge_factory.cc b/webkit/glue/media/media_resource_loader_bridge_factory.cc
deleted file mode 100644
index 3fb9d65..0000000
--- a/webkit/glue/media/media_resource_loader_bridge_factory.cc
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "webkit/glue/media/media_resource_loader_bridge_factory.h"
-
-#include "base/format_macros.h"
-#include "base/string_util.h"
-#include "base/stringprintf.h"
-
-namespace {
-
-// A constant for an unknown position.
-const int64 kPositionNotSpecified = -1;
-
-} // namespace
-
-namespace webkit_glue {
-
-MediaResourceLoaderBridgeFactory::MediaResourceLoaderBridgeFactory(
- const GURL& referrer,
- const std::string& frame_origin,
- const std::string& main_frame_origin,
- int origin_pid,
- int appcache_host_id,
- int32 routing_id)
- : referrer_(referrer),
- frame_origin_(frame_origin),
- main_frame_origin_(main_frame_origin),
- origin_pid_(origin_pid),
- appcache_host_id_(appcache_host_id),
- routing_id_(routing_id) {
-}
-
-MediaResourceLoaderBridgeFactory::~MediaResourceLoaderBridgeFactory() {}
-
-ResourceLoaderBridge* MediaResourceLoaderBridgeFactory::CreateBridge(
- const GURL& url,
- int load_flags,
- int64 first_byte_position,
- int64 last_byte_position) {
- webkit_glue::ResourceLoaderBridge::RequestInfo request_info;
- request_info.method = "GET";
- request_info.url = url;
- request_info.first_party_for_cookies = url;
- request_info.referrer = referrer_;
- request_info.frame_origin = frame_origin_;
- request_info.main_frame_origin = main_frame_origin_;
- request_info.headers = GenerateHeaders(first_byte_position,
- last_byte_position);
- request_info.load_flags = load_flags;
- request_info.requestor_pid = origin_pid_;
- request_info.request_type = ResourceType::MEDIA;
- request_info.appcache_host_id = appcache_host_id_;
- request_info.routing_id = routing_id_;
- return webkit_glue::ResourceLoaderBridge::Create(request_info);
-}
-
-MediaResourceLoaderBridgeFactory::MediaResourceLoaderBridgeFactory() {}
-
-// static
-const std::string MediaResourceLoaderBridgeFactory::GenerateHeaders (
- int64 first_byte_position, int64 last_byte_position) {
- // Construct the range header.
- std::string header;
- if (first_byte_position > kPositionNotSpecified &&
- last_byte_position > kPositionNotSpecified) {
- if (first_byte_position <= last_byte_position) {
- header = base::StringPrintf("Range: bytes=%" PRId64 "-%" PRId64,
- first_byte_position,
- last_byte_position);
- }
- } else if (first_byte_position > kPositionNotSpecified) {
- header = base::StringPrintf("Range: bytes=%" PRId64 "-",
- first_byte_position);
- } else if (last_byte_position > kPositionNotSpecified) {
- NOTIMPLEMENTED() << "Suffix range not implemented";
- }
- return header;
-}
-
-} // namespace webkit_glue
diff --git a/webkit/glue/media/media_resource_loader_bridge_factory.h b/webkit/glue/media/media_resource_loader_bridge_factory.h
deleted file mode 100644
index ccacdc6..0000000
--- a/webkit/glue/media/media_resource_loader_bridge_factory.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef WEBKIT_GLUE_MEDIA_MEDIA_RESOURCE_LOADER_BRIDGE_FACTORY_H_
-#define WEBKIT_GLUE_MEDIA_MEDIA_RESOURCE_LOADER_BRIDGE_FACTORY_H_
-
-#include "base/gtest_prod_util.h"
-#include "webkit/glue/resource_loader_bridge.h"
-
-namespace webkit_glue {
-
-// A factory used to create a ResourceLoaderBridge for the media player.
-// This factory is used also for testing. Testing code can use this class and
-// override CreateBridge() to inject a mock ResourceLoaderBridge for code that
-// interacts with it, e.g. BufferedDataSource.
-class MediaResourceLoaderBridgeFactory {
- public:
- MediaResourceLoaderBridgeFactory(
- const GURL& referrer,
- const std::string& frame_origin,
- const std::string& main_frame_origin,
- int origin_pid,
- int appcache_host_id,
- int32 routing_id);
-
- virtual ~MediaResourceLoaderBridgeFactory();
-
- // Factory method to create a ResourceLoaderBridge with the following
- // parameters:
- // |url| - URL of the resource to be loaded.
- // |load_flags| - Load flags for this loading.
- // |first_byte_position| - First byte position for a range request, -1 if not.
- // |last_byte_position| - Last byte position for a range request, -1 if not.
- virtual ResourceLoaderBridge* CreateBridge(
- const GURL& url,
- int load_flags,
- int64 first_byte_position,
- int64 last_byte_position);
-
- protected:
- // An empty constructor only used by inherited classes.
- MediaResourceLoaderBridgeFactory();
-
- private:
- FRIEND_TEST_ALL_PREFIXES(MediaResourceLoaderBridgeFactoryTest,
- GenerateHeaders);
-
- // Returns a range request header using parameters |first_byte_position| and
- // |last_byte_position|.
- // Negative numbers other than -1 are not allowed for |first_byte_position|
- // and |last_byte_position|. |first_byte_position| should always be less than
- // or equal to |last_byte_position| if they are both not -1.
- // Here's a list of valid parameters:
- // |first_byte_position| |last_byte_position|
- // 0 1000
- // 4096 4096
- // 0 -1
- // -1 -1
- // Empty string is returned on invalid parameters.
- static const std::string GenerateHeaders(int64 first_byte_position,
- int64 last_byte_position);
-
- GURL first_party_for_cookies_;
- GURL referrer_;
- std::string frame_origin_;
- std::string main_frame_origin_;
- std::string headers_;
- int origin_pid_;
- int appcache_host_id_;
- int32 routing_id_;
-};
-
-} // namespace webkit_glue
-
-#endif // WEBKIT_GLUE_MEDIA_MEDIA_RESOURCE_LOADER_BRIDGE_FACTORY_H_
diff --git a/webkit/glue/media/media_resource_loader_bridge_factory_unittest.cc b/webkit/glue/media/media_resource_loader_bridge_factory_unittest.cc
deleted file mode 100644
index 4c0126b..0000000
--- a/webkit/glue/media/media_resource_loader_bridge_factory_unittest.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/http/http_util.h"
-#include "webkit/glue/media/media_resource_loader_bridge_factory.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace webkit_glue {
-
-TEST(MediaResourceLoaderBridgeFactoryTest, GenerateHeaders) {
- static const struct {
- const bool success;
- const int64 first_byte_position;
- const int64 last_byte_position;
- } data[] = {
- { false, -1, -1 },
- { false, -5, 0 },
- { false, 100, 0 },
- { true, 0, -1 },
- { true, 0, 0 },
- { true, 100, 100 },
- { true, 50, -1 },
- { true, 10000, -1 },
- { true, 50, 100 },
- };
-
- for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) {
- std::string headers = MediaResourceLoaderBridgeFactory::GenerateHeaders(
- data[i].first_byte_position, data[i].last_byte_position);
- std::vector<net::HttpByteRange> ranges;
- bool ret = net::HttpUtil::ParseRanges(headers, &ranges);
- EXPECT_EQ(data[i].success, ret);
- if (ret) {
- EXPECT_EQ(1u, ranges.size());
- EXPECT_EQ(data[i].first_byte_position,
- ranges[0].first_byte_position());
- EXPECT_EQ(data[i].last_byte_position,
- ranges[0].last_byte_position());
- }
- }
-}
-
-} // namespace webkit_glue
diff --git a/webkit/glue/media/mock_media_resource_loader_bridge_factory.h b/webkit/glue/media/mock_media_resource_loader_bridge_factory.h
deleted file mode 100644
index 3c0a3ae..0000000
--- a/webkit/glue/media/mock_media_resource_loader_bridge_factory.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef WEBKIT_GLUE_MEDIA_MOCK_MEDIA_RESOURCE_LOADER_BRIDGE_FACTORY_H_
-#define WEBKIT_GLUE_MEDIA_MOCK_MEDIA_RESOURCE_LOADER_BRIDGE_FACTORY_H_
-
-#include "testing/gmock/include/gmock/gmock.h"
-#include "webkit/glue/media/media_resource_loader_bridge_factory.h"
-
-namespace webkit_glue {
-
-class MockMediaResourceLoaderBridgeFactory
- : public webkit_glue::MediaResourceLoaderBridgeFactory {
- public:
- MockMediaResourceLoaderBridgeFactory() {
- }
-
- virtual ~MockMediaResourceLoaderBridgeFactory() {
- OnDestroy();
- }
-
- MOCK_METHOD4(CreateBridge,
- webkit_glue::ResourceLoaderBridge*(const GURL& url,
- int load_flags,
- int64 first_byte_position,
- int64 last_byte_position));
- MOCK_METHOD0(OnDestroy, void());
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MockMediaResourceLoaderBridgeFactory);
-};
-
-} // namespace webkit_glue
-
-#endif // WEBKIT_GLUE_MEDIA_MOCK_MEDIA_RESOURCE_LOADER_BRIDGE_FACTORY_H_
diff --git a/webkit/glue/media/simple_data_source.cc b/webkit/glue/media/simple_data_source.cc
index 291928e..8869e28 100644
--- a/webkit/glue/media/simple_data_source.cc
+++ b/webkit/glue/media/simple_data_source.cc
@@ -2,40 +2,35 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "webkit/glue/media/simple_data_source.h"
+
#include "base/message_loop.h"
#include "base/process_util.h"
#include "media/base/filter_host.h"
-#include "net/base/load_flags.h"
#include "net/base/data_url.h"
-#include "net/http/http_response_headers.h"
+#include "net/base/load_flags.h"
#include "net/url_request/url_request_status.h"
-#include "webkit/glue/media/simple_data_source.h"
-#include "webkit/glue/resource_loader_bridge.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebKit.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebKitClient.h"
#include "webkit/glue/webkit_glue.h"
namespace {
-const char kHttpScheme[] = "http";
-const char kHttpsScheme[] = "https";
const char kDataScheme[] = "data";
-// A helper method that accepts only HTTP, HTTPS and FILE protocol.
-bool IsDataProtocol(const GURL& url) {
- return url.SchemeIs(kDataScheme);
-}
-
} // namespace
namespace webkit_glue {
SimpleDataSource::SimpleDataSource(
MessageLoop* render_loop,
- webkit_glue::MediaResourceLoaderBridgeFactory* bridge_factory)
+ WebKit::WebFrame* frame)
: render_loop_(render_loop),
- bridge_factory_(bridge_factory),
+ frame_(frame),
size_(-1),
single_origin_(true),
- state_(UNINITIALIZED) {
+ state_(UNINITIALIZED),
+ keep_test_loader_(false) {
DCHECK(render_loop);
}
@@ -108,34 +103,59 @@ bool SimpleDataSource::IsStreaming() {
return false;
}
-bool SimpleDataSource::OnReceivedRedirect(
- const GURL& new_url,
- const webkit_glue::ResourceResponseInfo& info,
- bool* has_new_first_party_for_cookies,
- GURL* new_first_party_for_cookies) {
+void SimpleDataSource::SetURLLoaderForTest(WebKit::WebURLLoader* mock_loader) {
+ url_loader_.reset(mock_loader);
+ keep_test_loader_ = true;
+}
+
+void SimpleDataSource::willSendRequest(
+ WebKit::WebURLLoader* loader,
+ WebKit::WebURLRequest& newRequest,
+ const WebKit::WebURLResponse& redirectResponse) {
DCHECK(MessageLoop::current() == render_loop_);
- single_origin_ = url_.GetOrigin() == new_url.GetOrigin();
+ single_origin_ = url_.GetOrigin() == GURL(newRequest.url()).GetOrigin();
- // TODO(wtc): should we return a new first party for cookies URL?
- *has_new_first_party_for_cookies = false;
- return true;
+ url_ = newRequest.url();
}
-void SimpleDataSource::OnReceivedResponse(
- const webkit_glue::ResourceResponseInfo& info,
- bool content_filtered) {
+void SimpleDataSource::didSendData(
+ WebKit::WebURLLoader* loader,
+ unsigned long long bytesSent,
+ unsigned long long totalBytesToBeSent) {
+ NOTIMPLEMENTED();
+}
+
+void SimpleDataSource::didReceiveResponse(
+ WebKit::WebURLLoader* loader,
+ const WebKit::WebURLResponse& response) {
DCHECK(MessageLoop::current() == render_loop_);
- size_ = info.content_length;
+ size_ = response.expectedContentLength();
+}
+
+void SimpleDataSource::didDownloadData(
+ WebKit::WebURLLoader* loader,
+ int dataLength) {
+ NOTIMPLEMENTED();
}
-void SimpleDataSource::OnReceivedData(const char* data, int len) {
+void SimpleDataSource::didReceiveData(
+ WebKit::WebURLLoader* loader,
+ const char* data,
+ int data_length) {
DCHECK(MessageLoop::current() == render_loop_);
- data_.append(data, len);
+ data_.append(data, data_length);
}
-void SimpleDataSource::OnCompletedRequest(const URLRequestStatus& status,
- const std::string& security_info,
- const base::Time& completion_time) {
+void SimpleDataSource::didReceiveCachedMetadata(
+ WebKit::WebURLLoader* loader,
+ const char* data,
+ int dataLength) {
+ NOTIMPLEMENTED();
+}
+
+void SimpleDataSource::didFinishLoading(
+ WebKit::WebURLLoader* loader,
+ double finishTime) {
DCHECK(MessageLoop::current() == render_loop_);
AutoLock auto_lock(lock_);
// It's possible this gets called after Stop(), in which case |host_| is no
@@ -143,10 +163,8 @@ void SimpleDataSource::OnCompletedRequest(const URLRequestStatus& status,
if (state_ == STOPPED)
return;
- // Otherwise we should be initializing and have created a bridge.
+ // Otherwise we should be initializing and have created a WebURLLoader.
DCHECK_EQ(state_, INITIALIZING);
- DCHECK(bridge_.get());
- bridge_.reset();
// If we don't get a content length or the request has failed, report it
// as a network error.
@@ -154,7 +172,29 @@ void SimpleDataSource::OnCompletedRequest(const URLRequestStatus& status,
size_ = data_.length();
DCHECK(static_cast<size_t>(size_) == data_.length());
- DoneInitialization_Locked(status.is_success());
+ DoneInitialization_Locked(true);
+}
+
+void SimpleDataSource::didFail(
+ WebKit::WebURLLoader* loader,
+ const WebKit::WebURLError& error) {
+ DCHECK(MessageLoop::current() == render_loop_);
+ AutoLock auto_lock(lock_);
+ // It's possible this gets called after Stop(), in which case |host_| is no
+ // longer valid.
+ if (state_ == STOPPED)
+ return;
+
+ // Otherwise we should be initializing and have created a WebURLLoader.
+ DCHECK_EQ(state_, INITIALIZING);
+
+ // If we don't get a content length or the request has failed, report it
+ // as a network error.
+ if (size_ == -1)
+ size_ = data_.length();
+ DCHECK(static_cast<size_t>(size_) == data_.length());
+
+ DoneInitialization_Locked(false);
}
bool SimpleDataSource::HasSingleOrigin() {
@@ -164,7 +204,7 @@ bool SimpleDataSource::HasSingleOrigin() {
void SimpleDataSource::Abort() {
DCHECK(MessageLoop::current() == render_loop_);
- NOTIMPLEMENTED();
+ frame_ = NULL;
}
void SimpleDataSource::SetURL(const GURL& url) {
@@ -183,9 +223,11 @@ void SimpleDataSource::StartTask() {
if (state_ == STOPPED)
return;
+ CHECK(frame_);
+
DCHECK_EQ(state_, INITIALIZING);
- if (IsDataProtocol(url_)) {
+ if (url_.SchemeIs(kDataScheme)) {
// If this using data protocol, we just need to decode it.
std::string mime_type, charset;
bool success = net::DataURL::Parse(url_, &mime_type, &charset, &data_);
@@ -194,10 +236,18 @@ void SimpleDataSource::StartTask() {
size_ = data_.length();
DoneInitialization_Locked(success);
} else {
- // Create our bridge and start loading the resource.
- bridge_.reset(bridge_factory_->CreateBridge(
- url_, net::LOAD_BYPASS_CACHE, -1, -1));
- bridge_->Start(this);
+ // Prepare the request.
+ WebKit::WebURLRequest request(url_);
+ request.setTargetType(WebKit::WebURLRequest::TargetIsMedia);
+
+ frame_->setReferrerForRequest(request, WebKit::WebURL());
+
+ // This flag is for unittests as we don't want to reset |url_loader|
+ if (!keep_test_loader_)
+ url_loader_.reset(frame_->createAssociatedURLLoader());
+
+ // Start the resource loading.
+ url_loader_->loadAsynchronously(request, this);
}
}
@@ -207,9 +257,9 @@ void SimpleDataSource::CancelTask() {
DCHECK_EQ(state_, STOPPED);
// Cancel any pending requests.
- if (bridge_.get()) {
- bridge_->Cancel();
- bridge_.reset();
+ if (url_loader_.get()) {
+ url_loader_->cancel();
+ url_loader_.reset();
}
}
@@ -220,7 +270,7 @@ void SimpleDataSource::DoneInitialization_Locked(bool success) {
host()->SetTotalBytes(size_);
host()->SetBufferedBytes(size_);
// If scheme is file or data, say we are loaded.
- host()->SetLoaded(url_.SchemeIsFile() || IsDataProtocol(url_));
+ host()->SetLoaded(url_.SchemeIsFile() || url_.SchemeIs(kDataScheme));
} else {
host()->SetError(media::PIPELINE_ERROR_NETWORK);
}
diff --git a/webkit/glue/media/simple_data_source.h b/webkit/glue/media/simple_data_source.h
index ff1e247..f4c83e6 100644
--- a/webkit/glue/media/simple_data_source.h
+++ b/webkit/glue/media/simple_data_source.h
@@ -10,10 +10,17 @@
#ifndef WEBKIT_GLUE_MEDIA_SIMPLE_DATA_SOURCE_H_
#define WEBKIT_GLUE_MEDIA_SIMPLE_DATA_SOURCE_H_
+#include <algorithm>
+#include <string>
+
#include "base/message_loop.h"
#include "base/scoped_ptr.h"
#include "media/base/filters.h"
-#include "webkit/glue/media/media_resource_loader_bridge_factory.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLLoader.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLLoaderClient.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLResponse.h"
#include "webkit/glue/media/web_data_source.h"
class MessageLoop;
@@ -22,17 +29,15 @@ class WebMediaPlayerDelegateImpl;
namespace webkit_glue {
class SimpleDataSource : public WebDataSource,
- public webkit_glue::ResourceLoaderBridge::Peer {
+ public WebKit::WebURLLoaderClient {
public:
- SimpleDataSource(
- MessageLoop* render_loop,
- webkit_glue::MediaResourceLoaderBridgeFactory* bridge_factory);
+ SimpleDataSource(MessageLoop* render_loop, WebKit::WebFrame* frame);
virtual ~SimpleDataSource();
- // MediaFilter implementation.
+ // media::Filter implementation.
virtual void Stop(media::FilterCallback* callback);
- // DataSource implementation.
+ // media::DataSource implementation.
virtual void Initialize(const std::string& url,
media::FilterCallback* callback);
virtual const media::MediaFormat& media_format();
@@ -41,21 +46,37 @@ class SimpleDataSource : public WebDataSource,
virtual bool GetSize(int64* size_out);
virtual bool IsStreaming();
- // webkit_glue::ResourceLoaderBridge::Peer implementation.
- virtual void OnUploadProgress(uint64 position, uint64 size) {}
- virtual bool OnReceivedRedirect(
- const GURL& new_url,
- const webkit_glue::ResourceResponseInfo& info,
- bool* has_new_first_party_for_cookies,
- GURL* new_first_party_for_cookies);
- virtual void OnReceivedResponse(
- const webkit_glue::ResourceResponseInfo& info,
- bool content_filtered);
- virtual void OnDownloadedData(int len) {}
- virtual void OnReceivedData(const char* data, int len);
- virtual void OnCompletedRequest(const URLRequestStatus& status,
- const std::string& security_info,
- const base::Time& completion_time);
+ // Used to inject a mock used for unittests.
+ virtual void SetURLLoaderForTest(WebKit::WebURLLoader* mock_loader);
+
+ // WebKit::WebURLLoaderClient implementations.
+ virtual void willSendRequest(
+ WebKit::WebURLLoader* loader,
+ WebKit::WebURLRequest& newRequest,
+ const WebKit::WebURLResponse& redirectResponse);
+ virtual void didSendData(
+ WebKit::WebURLLoader* loader,
+ unsigned long long bytesSent,
+ unsigned long long totalBytesToBeSent);
+ virtual void didReceiveResponse(
+ WebKit::WebURLLoader* loader,
+ const WebKit::WebURLResponse& response);
+ virtual void didDownloadData(
+ WebKit::WebURLLoader* loader,
+ int dataLength);
+ virtual void didReceiveData(
+ WebKit::WebURLLoader* loader,
+ const char* data,
+ int dataLength);
+ virtual void didReceiveCachedMetadata(
+ WebKit::WebURLLoader* loader,
+ const char* data, int dataLength);
+ virtual void didFinishLoading(
+ WebKit::WebURLLoader* loader,
+ double finishTime);
+ virtual void didFail(
+ WebKit::WebURLLoader* loader,
+ const WebKit::WebURLError&);
// webkit_glue::WebDataSource implementation.
virtual bool HasSingleOrigin();
@@ -77,11 +98,11 @@ class SimpleDataSource : public WebDataSource,
// Primarily used for asserting the bridge is loading on the render thread.
MessageLoop* render_loop_;
- // Factory to create a bridge.
- scoped_ptr<webkit_glue::MediaResourceLoaderBridgeFactory> bridge_factory_;
+ // A webframe for loading.
+ WebKit::WebFrame* frame_;
- // Bridge used to load the media resource.
- scoped_ptr<webkit_glue::ResourceLoaderBridge> bridge_;
+ // Does the work of loading and sends data back to this client.
+ scoped_ptr<WebKit::WebURLLoader> url_loader_;
media::MediaFormat media_format_;
GURL url_;
@@ -104,6 +125,9 @@ class SimpleDataSource : public WebDataSource,
// Filter callbacks.
scoped_ptr<media::FilterCallback> initialize_callback_;
+ // Used to ensure mocks for unittests are used instead of reset in Start().
+ bool keep_test_loader_;
+
DISALLOW_COPY_AND_ASSIGN(SimpleDataSource);
};
diff --git a/webkit/glue/media/simple_data_source_unittest.cc b/webkit/glue/media/simple_data_source_unittest.cc
index 537798f..55dc913 100644
--- a/webkit/glue/media/simple_data_source_unittest.cc
+++ b/webkit/glue/media/simple_data_source_unittest.cc
@@ -6,9 +6,15 @@
#include "media/base/filters.h"
#include "media/base/mock_filter_host.h"
#include "media/base/mock_filters.h"
-#include "webkit/glue/media/mock_media_resource_loader_bridge_factory.h"
+#include "net/base/net_errors.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLError.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLLoader.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLResponse.h"
#include "webkit/glue/media/simple_data_source.h"
-#include "webkit/glue/mock_resource_loader_bridge.h"
+#include "webkit/mocks/mock_webframe.h"
+#include "webkit/mocks/mock_weburlloader.h"
using ::testing::_;
using ::testing::DoAll;
@@ -21,6 +27,11 @@ using ::testing::SetArgumentPointee;
using ::testing::StrictMock;
using ::testing::WithArgs;
+using WebKit::WebURLError;
+using WebKit::WebURLLoader;
+using WebKit::WebURLRequest;
+using WebKit::WebURLResponse;
+
namespace {
const int kDataSize = 1024;
@@ -39,68 +50,56 @@ namespace webkit_glue {
class SimpleDataSourceTest : public testing::Test {
public:
SimpleDataSourceTest() {
- bridge_factory_.reset(
- new NiceMock<MockMediaResourceLoaderBridgeFactory>());
- bridge_.reset(new NiceMock<MockResourceLoaderBridge>());
-
for (int i = 0; i < kDataSize; ++i) {
data_[i] = i;
}
}
virtual ~SimpleDataSourceTest() {
- if (bridge_.get())
- EXPECT_CALL(*bridge_, OnDestroy());
- if (bridge_factory_.get())
- EXPECT_CALL(*bridge_factory_, OnDestroy());
+ ignore_result(frame_.release());
}
void InitializeDataSource(const char* url) {
+ gurl_ = GURL(url);
+
+ frame_.reset(new NiceMock<MockWebFrame>());
+ url_loader_ = new NiceMock<MockWebURLLoader>();
+
data_source_ = new SimpleDataSource(MessageLoop::current(),
- bridge_factory_.get());
- CHECK(data_source_);
+ frame_.get());
// There is no need to provide a message loop to data source.
data_source_->set_host(&host_);
+ data_source_->SetURLLoaderForTest(url_loader_);
- // First a bridge is created.
InSequence s;
- EXPECT_CALL(*bridge_factory_, CreateBridge(GURL(url), _, -1, -1))
- .WillOnce(Return(bridge_.get()));
- EXPECT_CALL(*bridge_, Start(data_source_.get()))
- .WillOnce(Return(true));
data_source_->Initialize(url, callback_.NewCallback());
-
MessageLoop::current()->RunAllPending();
}
void RequestSucceeded(bool is_loaded) {
- ResourceResponseInfo info;
- info.content_length = kDataSize;
+ WebURLResponse response(gurl_);
+ response.setExpectedContentLength(kDataSize);
- data_source_->OnReceivedResponse(info, false);
+ data_source_->didReceiveResponse(NULL, response);
int64 size;
EXPECT_TRUE(data_source_->GetSize(&size));
EXPECT_EQ(kDataSize, size);
- for (int i = 0; i < kDataSize; ++i)
- data_source_->OnReceivedData(data_ + i, 1);
+ for (int i = 0; i < kDataSize; ++i) {
+ data_source_->didReceiveData(NULL, data_ + i, 1);
+ }
EXPECT_CALL(host_, SetLoaded(is_loaded));
InSequence s;
- EXPECT_CALL(*bridge_, OnDestroy())
- .WillOnce(Invoke(this, &SimpleDataSourceTest::ReleaseBridge));
EXPECT_CALL(host_, SetTotalBytes(kDataSize));
EXPECT_CALL(host_, SetBufferedBytes(kDataSize));
EXPECT_CALL(callback_, OnFilterCallback());
EXPECT_CALL(callback_, OnCallbackDestroyed());
- URLRequestStatus status;
- status.set_status(URLRequestStatus::SUCCESS);
- status.set_os_error(0);
- data_source_->OnCompletedRequest(status, "", base::Time());
+ data_source_->didFinishLoading(NULL, 0);
// Let the tasks to be executed.
MessageLoop::current()->RunAllPending();
@@ -108,28 +107,23 @@ class SimpleDataSourceTest : public testing::Test {
void RequestFailed() {
InSequence s;
- EXPECT_CALL(*bridge_, OnDestroy())
- .WillOnce(Invoke(this, &SimpleDataSourceTest::ReleaseBridge));
EXPECT_CALL(host_, SetError(media::PIPELINE_ERROR_NETWORK));
EXPECT_CALL(callback_, OnFilterCallback());
EXPECT_CALL(callback_, OnCallbackDestroyed());
- URLRequestStatus status;
- status.set_status(URLRequestStatus::FAILED);
- status.set_os_error(100);
- data_source_->OnCompletedRequest(status, "", base::Time());
+ WebURLError error;
+ error.reason = net::ERR_FAILED;
+ data_source_->didFail(NULL, error);
// Let the tasks to be executed.
MessageLoop::current()->RunAllPending();
}
void DestroyDataSource() {
- EXPECT_CALL(*bridge_factory_, OnDestroy())
- .WillOnce(Invoke(this, &SimpleDataSourceTest::ReleaseBridgeFactory));
-
StrictMock<media::MockFilterCallback> callback;
EXPECT_CALL(callback, OnFilterCallback());
EXPECT_CALL(callback, OnCallbackDestroyed());
+
data_source_->Stop(callback.NewCallback());
MessageLoop::current()->RunAllPending();
@@ -148,23 +142,17 @@ class SimpleDataSourceTest : public testing::Test {
}
}
- void ReleaseBridge() {
- ignore_result(bridge_.release());
- }
-
- void ReleaseBridgeFactory() {
- ignore_result(bridge_factory_.release());
- }
-
MOCK_METHOD1(ReadCallback, void(size_t size));
protected:
+ GURL gurl_;
scoped_ptr<MessageLoop> message_loop_;
- scoped_ptr<NiceMock<MockMediaResourceLoaderBridgeFactory> > bridge_factory_;
- scoped_ptr<NiceMock<MockResourceLoaderBridge> > bridge_;
+ NiceMock<MockWebURLLoader>* url_loader_;
scoped_refptr<SimpleDataSource> data_source_;
StrictMock<media::MockFilterHost> host_;
StrictMock<media::MockFilterCallback> callback_;
+ scoped_ptr<NiceMock<MockWebFrame> > frame_;
+
char data_[kDataSize];
DISALLOW_COPY_AND_ASSIGN(SimpleDataSourceTest);
@@ -189,13 +177,16 @@ TEST_F(SimpleDataSourceTest, InitializeFile) {
}
TEST_F(SimpleDataSourceTest, InitializeData) {
+ frame_.reset(new NiceMock<MockWebFrame>());
+ url_loader_ = new NiceMock<MockWebURLLoader>();
+
data_source_ = new SimpleDataSource(MessageLoop::current(),
- bridge_factory_.get());
+ frame_.get());
EXPECT_TRUE(data_source_->IsUrlSupported(kDataUrl));
- CHECK(data_source_);
// There is no need to provide a message loop to data source.
data_source_->set_host(&host_);
+ data_source_->SetURLLoaderForTest(url_loader_);
EXPECT_CALL(host_, SetLoaded(true));
EXPECT_CALL(host_, SetTotalBytes(sizeof(kDataUrlDecoded)));
@@ -218,9 +209,7 @@ TEST_F(SimpleDataSourceTest, RequestFailed) {
TEST_F(SimpleDataSourceTest, StopWhenDownloading) {
InitializeDataSource(kHttpUrl);
- EXPECT_CALL(*bridge_, Cancel());
- EXPECT_CALL(*bridge_, OnDestroy())
- .WillOnce(Invoke(this, &SimpleDataSourceTest::ReleaseBridge));
+ EXPECT_CALL(*url_loader_, cancel());
EXPECT_CALL(callback_, OnCallbackDestroyed());
DestroyDataSource();
}