diff options
author | rdsmith@chromium.org <rdsmith@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-04 18:20:56 +0000 |
---|---|---|
committer | rdsmith@chromium.org <rdsmith@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-04 18:20:56 +0000 |
commit | d7db4f62b576074b5b5f62d156e335855dc51bdb (patch) | |
tree | ab1dbcbdf199ac0a8eaec3e2ad49753d216d42b3 /content | |
parent | f7bc735b65da49b5df9fb82a69dcbab08ebb1061 (diff) | |
download | chromium_src-d7db4f62b576074b5b5f62d156e335855dc51bdb.zip chromium_src-d7db4f62b576074b5b5f62d156e335855dc51bdb.tar.gz chromium_src-d7db4f62b576074b5b5f62d156e335855dc51bdb.tar.bz2 |
Use ByteStream in downloads system to decouple source and sink.
BUG=123192
BUG=93006
BUG=111588
Review URL: https://chromiumcodereview.appspot.com/10392111
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@140328 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
29 files changed, 951 insertions, 1110 deletions
diff --git a/content/browser/download/base_file.cc b/content/browser/download/base_file.cc index 9516fb3..05dc9d3 100644 --- a/content/browser/download/base_file.cc +++ b/content/browser/download/base_file.cc @@ -272,13 +272,6 @@ net::Error BaseFile::AppendDataToFile(const char* data, size_t data_len) { download_stats::APPEND_TO_DETACHED_FILE_COUNT); } - if (bound_net_log_.IsLoggingAllEvents()) { - bound_net_log_.AddEvent( - net::NetLog::TYPE_DOWNLOAD_FILE_WRITTEN, - make_scoped_refptr(new net::NetLogIntegerParameter( - "byte_count", data_len))); - } - if (!file_stream_.get()) return LOG_ERROR("get", net::ERR_INVALID_HANDLE); diff --git a/content/browser/download/byte_stream.cc b/content/browser/download/byte_stream.cc index a5e06a7..381ae23 100644 --- a/content/browser/download/byte_stream.cc +++ b/content/browser/download/byte_stream.cc @@ -23,7 +23,7 @@ static const int kFractionBufferBeforeSending = 3; // before we update the input window. static const int kFractionReadBeforeWindowUpdate = 3; -class ByteStreamOutputImpl; +class ByteStreamReaderImpl; // A poor man's weak pointer; a RefCountedThreadSafe boolean that can be // cleared in an object destructor and accessed to check for object @@ -44,30 +44,30 @@ struct LifetimeFlag : public base::RefCountedThreadSafe<LifetimeFlag> { DISALLOW_COPY_AND_ASSIGN(LifetimeFlag); }; -// For both ByteStreamInputImpl and ByteStreamOutputImpl, Construction and +// For both ByteStreamWriterImpl and ByteStreamReaderImpl, Construction and // SetPeer may happen anywhere; all other operations on each class must // happen in the context of their SequencedTaskRunner. -class ByteStreamInputImpl : public content::ByteStreamInput { +class ByteStreamWriterImpl : public content::ByteStreamWriter { public: - ByteStreamInputImpl(scoped_refptr<base::SequencedTaskRunner> task_runner, + ByteStreamWriterImpl(scoped_refptr<base::SequencedTaskRunner> task_runner, scoped_refptr<LifetimeFlag> lifetime_flag, size_t buffer_size); - virtual ~ByteStreamInputImpl(); + virtual ~ByteStreamWriterImpl(); // Must be called before any operations are performed. - void SetPeer(ByteStreamOutputImpl* peer, + void SetPeer(ByteStreamReaderImpl* peer, scoped_refptr<base::SequencedTaskRunner> peer_task_runner, scoped_refptr<LifetimeFlag> peer_lifetime_flag); - // Overridden from ByteStreamInput. + // Overridden from ByteStreamWriter. virtual bool Write(scoped_refptr<net::IOBuffer> buffer, size_t byte_count) OVERRIDE; virtual void Close(content::DownloadInterruptReason status) OVERRIDE; virtual void RegisterCallback(const base::Closure& source_callback) OVERRIDE; - // PostTask target from |ByteStreamOutputImpl::MaybeUpdateInput|. + // PostTask target from |ByteStreamReaderImpl::MaybeUpdateInput|. static void UpdateWindow(scoped_refptr<LifetimeFlag> lifetime_flag, - ByteStreamInputImpl* target, + ByteStreamWriterImpl* target, size_t bytes_consumed); private: @@ -102,36 +102,36 @@ class ByteStreamInputImpl : public content::ByteStreamInput { // Only valid to access on peer_task_runner_ if // |*peer_lifetime_flag_ == true| - ByteStreamOutputImpl* peer_; + ByteStreamReaderImpl* peer_; }; -class ByteStreamOutputImpl : public content::ByteStreamOutput { +class ByteStreamReaderImpl : public content::ByteStreamReader { public: - ByteStreamOutputImpl(scoped_refptr<base::SequencedTaskRunner> task_runner, + ByteStreamReaderImpl(scoped_refptr<base::SequencedTaskRunner> task_runner, scoped_refptr<LifetimeFlag> lifetime_flag, size_t buffer_size); - virtual ~ByteStreamOutputImpl(); + virtual ~ByteStreamReaderImpl(); // Must be called before any operations are performed. - void SetPeer(ByteStreamInputImpl* peer, + void SetPeer(ByteStreamWriterImpl* peer, scoped_refptr<base::SequencedTaskRunner> peer_task_runner, scoped_refptr<LifetimeFlag> peer_lifetime_flag); - // Overridden from ByteStreamOutput. + // Overridden from ByteStreamReader. virtual StreamState Read(scoped_refptr<net::IOBuffer>* data, size_t* length) OVERRIDE; virtual content::DownloadInterruptReason GetStatus() const OVERRIDE; virtual void RegisterCallback(const base::Closure& sink_callback) OVERRIDE; - // PostTask target from |ByteStreamInputImpl::MaybePostToPeer| and - // |ByteStreamInputImpl::Close|. + // PostTask target from |ByteStreamWriterImpl::MaybePostToPeer| and + // |ByteStreamWriterImpl::Close|. // Receive data from our peer. // static because it may be called after the object it is targeting // has been destroyed. It may not access |*target| // if |*object_lifetime_flag| is false. static void TransferData( scoped_refptr<LifetimeFlag> object_lifetime_flag, - ByteStreamOutputImpl* target, + ByteStreamReaderImpl* target, scoped_ptr<ContentVector> transfer_buffer, size_t transfer_buffer_bytes, bool source_complete, @@ -178,10 +178,10 @@ class ByteStreamOutputImpl : public content::ByteStreamOutput { // Only valid to access on peer_task_runner_ if // |*peer_lifetime_flag_ == true| - ByteStreamInputImpl* peer_; + ByteStreamWriterImpl* peer_; }; -ByteStreamInputImpl::ByteStreamInputImpl( +ByteStreamWriterImpl::ByteStreamWriterImpl( scoped_refptr<base::SequencedTaskRunner> task_runner, scoped_refptr<LifetimeFlag> lifetime_flag, size_t buffer_size) @@ -195,12 +195,12 @@ ByteStreamInputImpl::ByteStreamInputImpl( my_lifetime_flag_->is_alive_ = true; } -ByteStreamInputImpl::~ByteStreamInputImpl() { +ByteStreamWriterImpl::~ByteStreamWriterImpl() { my_lifetime_flag_->is_alive_ = false; } -void ByteStreamInputImpl::SetPeer( - ByteStreamOutputImpl* peer, +void ByteStreamWriterImpl::SetPeer( + ByteStreamReaderImpl* peer, scoped_refptr<base::SequencedTaskRunner> peer_task_runner, scoped_refptr<LifetimeFlag> peer_lifetime_flag) { peer_ = peer; @@ -208,7 +208,7 @@ void ByteStreamInputImpl::SetPeer( peer_lifetime_flag_ = peer_lifetime_flag; } -bool ByteStreamInputImpl::Write( +bool ByteStreamWriterImpl::Write( scoped_refptr<net::IOBuffer> buffer, size_t byte_count) { DCHECK(my_task_runner_->RunsTasksOnCurrentThread()); @@ -222,21 +222,21 @@ bool ByteStreamInputImpl::Write( return (input_contents_size_ + output_size_used_ <= total_buffer_size_); } -void ByteStreamInputImpl::Close( +void ByteStreamWriterImpl::Close( content::DownloadInterruptReason status) { DCHECK(my_task_runner_->RunsTasksOnCurrentThread()); PostToPeer(true, status); } -void ByteStreamInputImpl::RegisterCallback( +void ByteStreamWriterImpl::RegisterCallback( const base::Closure& source_callback) { DCHECK(my_task_runner_->RunsTasksOnCurrentThread()); space_available_callback_ = source_callback; } // static -void ByteStreamInputImpl::UpdateWindow( - scoped_refptr<LifetimeFlag> lifetime_flag, ByteStreamInputImpl* target, +void ByteStreamWriterImpl::UpdateWindow( + scoped_refptr<LifetimeFlag> lifetime_flag, ByteStreamWriterImpl* target, size_t bytes_consumed) { // If the target object isn't alive anymore, we do nothing. if (!lifetime_flag->is_alive_) return; @@ -244,7 +244,7 @@ void ByteStreamInputImpl::UpdateWindow( target->UpdateWindowInternal(bytes_consumed); } -void ByteStreamInputImpl::UpdateWindowInternal(size_t bytes_consumed) { +void ByteStreamWriterImpl::UpdateWindowInternal(size_t bytes_consumed) { DCHECK(my_task_runner_->RunsTasksOnCurrentThread()); DCHECK_GE(output_size_used_, bytes_consumed); output_size_used_ -= bytes_consumed; @@ -259,7 +259,7 @@ void ByteStreamInputImpl::UpdateWindowInternal(size_t bytes_consumed) { space_available_callback_.Run(); } -void ByteStreamInputImpl::PostToPeer( +void ByteStreamWriterImpl::PostToPeer( bool complete, content::DownloadInterruptReason status) { DCHECK(my_task_runner_->RunsTasksOnCurrentThread()); // Valid contexts in which to call. @@ -276,7 +276,7 @@ void ByteStreamInputImpl::PostToPeer( } peer_task_runner_->PostTask( FROM_HERE, base::Bind( - &ByteStreamOutputImpl::TransferData, + &ByteStreamReaderImpl::TransferData, peer_lifetime_flag_, peer_, base::Passed(transfer_buffer.Pass()), @@ -285,7 +285,7 @@ void ByteStreamInputImpl::PostToPeer( status)); } -ByteStreamOutputImpl::ByteStreamOutputImpl( +ByteStreamReaderImpl::ByteStreamReaderImpl( scoped_refptr<base::SequencedTaskRunner> task_runner, scoped_refptr<LifetimeFlag> lifetime_flag, size_t buffer_size) @@ -300,12 +300,12 @@ ByteStreamOutputImpl::ByteStreamOutputImpl( my_lifetime_flag_->is_alive_ = true; } -ByteStreamOutputImpl::~ByteStreamOutputImpl() { +ByteStreamReaderImpl::~ByteStreamReaderImpl() { my_lifetime_flag_->is_alive_ = false; } -void ByteStreamOutputImpl::SetPeer( - ByteStreamInputImpl* peer, +void ByteStreamReaderImpl::SetPeer( + ByteStreamWriterImpl* peer, scoped_refptr<base::SequencedTaskRunner> peer_task_runner, scoped_refptr<LifetimeFlag> peer_lifetime_flag) { peer_ = peer; @@ -313,8 +313,8 @@ void ByteStreamOutputImpl::SetPeer( peer_lifetime_flag_ = peer_lifetime_flag; } -ByteStreamOutputImpl::StreamState -ByteStreamOutputImpl::Read(scoped_refptr<net::IOBuffer>* data, +ByteStreamReaderImpl::StreamState +ByteStreamReaderImpl::Read(scoped_refptr<net::IOBuffer>* data, size_t* length) { DCHECK(my_task_runner_->RunsTasksOnCurrentThread()); @@ -334,13 +334,13 @@ ByteStreamOutputImpl::Read(scoped_refptr<net::IOBuffer>* data, } content::DownloadInterruptReason -ByteStreamOutputImpl::GetStatus() const { +ByteStreamReaderImpl::GetStatus() const { DCHECK(my_task_runner_->RunsTasksOnCurrentThread()); DCHECK(received_status_); return status_; } -void ByteStreamOutputImpl::RegisterCallback( +void ByteStreamReaderImpl::RegisterCallback( const base::Closure& sink_callback) { DCHECK(my_task_runner_->RunsTasksOnCurrentThread()); @@ -348,9 +348,9 @@ void ByteStreamOutputImpl::RegisterCallback( } // static -void ByteStreamOutputImpl::TransferData( +void ByteStreamReaderImpl::TransferData( scoped_refptr<LifetimeFlag> object_lifetime_flag, - ByteStreamOutputImpl* target, + ByteStreamReaderImpl* target, scoped_ptr<ContentVector> transfer_buffer, size_t buffer_size, bool source_complete, @@ -362,7 +362,7 @@ void ByteStreamOutputImpl::TransferData( transfer_buffer.Pass(), buffer_size, source_complete, status); } -void ByteStreamOutputImpl::TransferDataInternal( +void ByteStreamReaderImpl::TransferDataInternal( scoped_ptr<ContentVector> transfer_buffer, size_t buffer_size, bool source_complete, @@ -393,7 +393,7 @@ void ByteStreamOutputImpl::TransferDataInternal( // Decide whether or not to send the input a window update. // Currently we do that whenever we've got unreported consumption // greater than 1/3 of total size. -void ByteStreamOutputImpl::MaybeUpdateInput() { +void ByteStreamReaderImpl::MaybeUpdateInput() { DCHECK(my_task_runner_->RunsTasksOnCurrentThread()); if (unreported_consumed_bytes_ <= @@ -402,7 +402,7 @@ void ByteStreamOutputImpl::MaybeUpdateInput() { peer_task_runner_->PostTask( FROM_HERE, base::Bind( - &ByteStreamInputImpl::UpdateWindow, + &ByteStreamWriterImpl::UpdateWindow, peer_lifetime_flag_, peer_, unreported_consumed_bytes_)); @@ -413,22 +413,22 @@ void ByteStreamOutputImpl::MaybeUpdateInput() { namespace content { -ByteStreamOutput::~ByteStreamOutput() { } +ByteStreamReader::~ByteStreamReader() { } -ByteStreamInput::~ByteStreamInput() { } +ByteStreamWriter::~ByteStreamWriter() { } void CreateByteStream( scoped_refptr<base::SequencedTaskRunner> input_task_runner, scoped_refptr<base::SequencedTaskRunner> output_task_runner, size_t buffer_size, - scoped_ptr<ByteStreamInput>* input, - scoped_ptr<ByteStreamOutput>* output) { + scoped_ptr<ByteStreamWriter>* input, + scoped_ptr<ByteStreamReader>* output) { scoped_refptr<LifetimeFlag> input_flag(new LifetimeFlag()); scoped_refptr<LifetimeFlag> output_flag(new LifetimeFlag()); - ByteStreamInputImpl* in = new ByteStreamInputImpl( + ByteStreamWriterImpl* in = new ByteStreamWriterImpl( input_task_runner, input_flag, buffer_size); - ByteStreamOutputImpl* out = new ByteStreamOutputImpl( + ByteStreamReaderImpl* out = new ByteStreamReaderImpl( output_task_runner, output_flag, buffer_size); in->SetPeer(out, output_task_runner, output_flag); diff --git a/content/browser/download/byte_stream.h b/content/browser/download/byte_stream.h index 8265b8d..f5be75a 100644 --- a/content/browser/download/byte_stream.h +++ b/content/browser/download/byte_stream.h @@ -26,15 +26,15 @@ namespace content { // sink, which may be on different threads. It is intended to be the // only connection between source and sink; they need have no // direct awareness of each other aside from the byte stream. The source and -// the sink have different interfaces to a byte stream, |ByteStreamInput| -// and |ByteStreamOutput|. A pair of connected interfaces is generated by +// the sink have different interfaces to a byte stream, |ByteStreamWriter| +// and |ByteStreamReader|. A pair of connected interfaces is generated by // calling |CreateByteStream|. // -// The source adds bytes to the bytestream via |ByteStreamInput::Write| -// and the sink retrieves bytes already written via |ByteStreamOutput::Read|. +// The source adds bytes to the bytestream via |ByteStreamWriter::Write| +// and the sink retrieves bytes already written via |ByteStreamReader::Read|. // // When the source has no more data to add, it will call -// |ByteStreamInput::Close| to indicate that. Errors at the source +// |ByteStreamWriter::Close| to indicate that. Errors at the source // are indicated to the sink via a non-DOWNLOAD_INTERRUPT_REASON_NONE code. // // Normally the source is not managed after the relationship is setup; @@ -58,9 +58,9 @@ namespace content { // // Class methods are virtual to allow mocking for tests; these classes // aren't intended to be base classes for other classes. -class CONTENT_EXPORT ByteStreamInput { +class CONTENT_EXPORT ByteStreamWriter { public: - virtual ~ByteStreamInput() = 0; + virtual ~ByteStreamWriter() = 0; // Always adds the data passed into the ByteStream. Returns true // if more data may be added without exceeding the class limit @@ -75,21 +75,22 @@ public: // Register a callback to be called when the stream transitions from // full to having space available. The callback will always be - // called on the task runner associated with the ByteStreamInput. + // called on the task runner associated with the ByteStreamWriter. // This callback will only be called if a call to Write has previously // returned false (i.e. the ByteStream has been filled). // Multiple calls to this function are supported, though note that it // is the callers responsibility to handle races with space becoming // available (i.e. in the case of that race either of the before // or after callbacks may be called). + // The callback will not be called after ByteStreamWriter destruction. virtual void RegisterCallback(const base::Closure& source_callback) = 0; }; -class CONTENT_EXPORT ByteStreamOutput { +class CONTENT_EXPORT ByteStreamReader { public: enum StreamState { STREAM_EMPTY, STREAM_HAS_DATA, STREAM_COMPLETE }; - virtual ~ByteStreamOutput() = 0; + virtual ~ByteStreamReader() = 0; // Returns STREAM_EMPTY if there is no data on the ByteStream and // Close() has not been called, and STREAM_COMPLETE if there @@ -109,6 +110,7 @@ class CONTENT_EXPORT ByteStreamOutput { // though note that it is the callers responsibility to handle races // with data becoming available (i.e. in the case of that race // either of the before or after callbacks may be called). + // The callback will not be called after ByteStreamReader destruction. virtual void RegisterCallback(const base::Closure& sink_callback) = 0; }; @@ -116,8 +118,8 @@ CONTENT_EXPORT void CreateByteStream( scoped_refptr<base::SequencedTaskRunner> input_task_runner, scoped_refptr<base::SequencedTaskRunner> output_task_runner, size_t buffer_size, - scoped_ptr<ByteStreamInput>* input, - scoped_ptr<ByteStreamOutput>* output); + scoped_ptr<ByteStreamWriter>* input, + scoped_ptr<ByteStreamReader>* output); } // namespace content diff --git a/content/browser/download/byte_stream_unittest.cc b/content/browser/download/byte_stream_unittest.cc index 44bca9a..483641a 100644 --- a/content/browser/download/byte_stream_unittest.cc +++ b/content/browser/download/byte_stream_unittest.cc @@ -85,7 +85,7 @@ class ByteStreamTest : public testing::Test { // ByteStream, returning the result of the ByteStream::Write. // Separate function to avoid duplication of buffer_size in test // calls. - bool Write(content::ByteStreamInput* byte_stream_input, + bool Write(content::ByteStreamWriter* byte_stream_input, size_t buffer_size) { return byte_stream_input->Write(NewIOBuffer(buffer_size), buffer_size); } @@ -139,11 +139,11 @@ ByteStreamTest::ByteStreamTest() : producing_seed_key_(0), consuming_seed_key_(0) { } -// Confirm that filling and emptying the pipe works properly, and that +// Confirm that filling and emptying the stream works properly, and that // we get full triggers when we expect. TEST_F(ByteStreamTest, ByteStream_PushBack) { - scoped_ptr<content::ByteStreamInput> byte_stream_input; - scoped_ptr<content::ByteStreamOutput> byte_stream_output; + scoped_ptr<content::ByteStreamWriter> byte_stream_input; + scoped_ptr<content::ByteStreamReader> byte_stream_output; content::CreateByteStream( message_loop_.message_loop_proxy(), message_loop_.message_loop_proxy(), 3 * 1024, &byte_stream_input, &byte_stream_output); @@ -163,27 +163,27 @@ TEST_F(ByteStreamTest, ByteStream_PushBack) { // have the same contents? scoped_refptr<net::IOBuffer> output_io_buffer; size_t output_length; - EXPECT_EQ(content::ByteStreamOutput::STREAM_HAS_DATA, + EXPECT_EQ(content::ByteStreamReader::STREAM_HAS_DATA, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_TRUE(ValidateIOBuffer(output_io_buffer, output_length)); - EXPECT_EQ(content::ByteStreamOutput::STREAM_HAS_DATA, + EXPECT_EQ(content::ByteStreamReader::STREAM_HAS_DATA, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_TRUE(ValidateIOBuffer(output_io_buffer, output_length)); - EXPECT_EQ(content::ByteStreamOutput::STREAM_HAS_DATA, + EXPECT_EQ(content::ByteStreamReader::STREAM_HAS_DATA, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_TRUE(ValidateIOBuffer(output_io_buffer, output_length)); - EXPECT_EQ(content::ByteStreamOutput::STREAM_HAS_DATA, + EXPECT_EQ(content::ByteStreamReader::STREAM_HAS_DATA, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_TRUE(ValidateIOBuffer(output_io_buffer, output_length)); - EXPECT_EQ(content::ByteStreamOutput::STREAM_HAS_DATA, + EXPECT_EQ(content::ByteStreamReader::STREAM_HAS_DATA, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_TRUE(ValidateIOBuffer(output_io_buffer, output_length)); - EXPECT_EQ(content::ByteStreamOutput::STREAM_COMPLETE, + EXPECT_EQ(content::ByteStreamReader::STREAM_COMPLETE, byte_stream_output->Read(&output_io_buffer, &output_length)); } @@ -191,8 +191,8 @@ TEST_F(ByteStreamTest, ByteStream_PushBack) { // that we're getting pushback even when data's split across the two // objects TEST_F(ByteStreamTest, ByteStream_PushBackSplit) { - scoped_ptr<content::ByteStreamInput> byte_stream_input; - scoped_ptr<content::ByteStreamOutput> byte_stream_output; + scoped_ptr<content::ByteStreamWriter> byte_stream_input; + scoped_ptr<content::ByteStreamReader> byte_stream_output; content::CreateByteStream( message_loop_.message_loop_proxy(), message_loop_.message_loop_proxy(), 9 * 1024, &byte_stream_input, &byte_stream_output); @@ -214,35 +214,35 @@ TEST_F(ByteStreamTest, ByteStream_PushBackSplit) { // have the same contents? scoped_refptr<net::IOBuffer> output_io_buffer; size_t output_length; - EXPECT_EQ(content::ByteStreamOutput::STREAM_HAS_DATA, + EXPECT_EQ(content::ByteStreamReader::STREAM_HAS_DATA, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_TRUE(ValidateIOBuffer(output_io_buffer, output_length)); - EXPECT_EQ(content::ByteStreamOutput::STREAM_HAS_DATA, + EXPECT_EQ(content::ByteStreamReader::STREAM_HAS_DATA, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_TRUE(ValidateIOBuffer(output_io_buffer, output_length)); - EXPECT_EQ(content::ByteStreamOutput::STREAM_HAS_DATA, + EXPECT_EQ(content::ByteStreamReader::STREAM_HAS_DATA, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_TRUE(ValidateIOBuffer(output_io_buffer, output_length)); - EXPECT_EQ(content::ByteStreamOutput::STREAM_HAS_DATA, + EXPECT_EQ(content::ByteStreamReader::STREAM_HAS_DATA, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_TRUE(ValidateIOBuffer(output_io_buffer, output_length)); - EXPECT_EQ(content::ByteStreamOutput::STREAM_HAS_DATA, + EXPECT_EQ(content::ByteStreamReader::STREAM_HAS_DATA, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_TRUE(ValidateIOBuffer(output_io_buffer, output_length)); - EXPECT_EQ(content::ByteStreamOutput::STREAM_EMPTY, + EXPECT_EQ(content::ByteStreamReader::STREAM_EMPTY, byte_stream_output->Read(&output_io_buffer, &output_length)); } // Confirm that a Close() notification transmits in-order -// with data on the pipe. +// with data on the stream. TEST_F(ByteStreamTest, ByteStream_CompleteTransmits) { - scoped_ptr<content::ByteStreamInput> byte_stream_input; - scoped_ptr<content::ByteStreamOutput> byte_stream_output; + scoped_ptr<content::ByteStreamWriter> byte_stream_input; + scoped_ptr<content::ByteStreamReader> byte_stream_output; scoped_refptr<net::IOBuffer> output_io_buffer; size_t output_length; @@ -251,11 +251,11 @@ TEST_F(ByteStreamTest, ByteStream_CompleteTransmits) { content::CreateByteStream( message_loop_.message_loop_proxy(), message_loop_.message_loop_proxy(), 3 * 1024, &byte_stream_input, &byte_stream_output); - EXPECT_EQ(content::ByteStreamOutput::STREAM_EMPTY, + EXPECT_EQ(content::ByteStreamReader::STREAM_EMPTY, byte_stream_output->Read(&output_io_buffer, &output_length)); byte_stream_input->Close(content::DOWNLOAD_INTERRUPT_REASON_NONE); message_loop_.RunAllPending(); - ASSERT_EQ(content::ByteStreamOutput::STREAM_COMPLETE, + ASSERT_EQ(content::ByteStreamReader::STREAM_COMPLETE, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE, byte_stream_output->GetStatus()); @@ -264,15 +264,15 @@ TEST_F(ByteStreamTest, ByteStream_CompleteTransmits) { content::CreateByteStream( message_loop_.message_loop_proxy(), message_loop_.message_loop_proxy(), 3 * 1024, &byte_stream_input, &byte_stream_output); - EXPECT_EQ(content::ByteStreamOutput::STREAM_EMPTY, + EXPECT_EQ(content::ByteStreamReader::STREAM_EMPTY, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_TRUE(Write(byte_stream_input.get(), 1024)); byte_stream_input->Close(content::DOWNLOAD_INTERRUPT_REASON_NONE); message_loop_.RunAllPending(); - EXPECT_EQ(content::ByteStreamOutput::STREAM_HAS_DATA, + EXPECT_EQ(content::ByteStreamReader::STREAM_HAS_DATA, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_TRUE(ValidateIOBuffer(output_io_buffer, output_length)); - ASSERT_EQ(content::ByteStreamOutput::STREAM_COMPLETE, + ASSERT_EQ(content::ByteStreamReader::STREAM_COMPLETE, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE, byte_stream_output->GetStatus()); @@ -281,12 +281,12 @@ TEST_F(ByteStreamTest, ByteStream_CompleteTransmits) { content::CreateByteStream( message_loop_.message_loop_proxy(), message_loop_.message_loop_proxy(), 3 * 1024, &byte_stream_input, &byte_stream_output); - EXPECT_EQ(content::ByteStreamOutput::STREAM_EMPTY, + EXPECT_EQ(content::ByteStreamReader::STREAM_EMPTY, byte_stream_output->Read(&output_io_buffer, &output_length)); byte_stream_input->Close( content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED); message_loop_.RunAllPending(); - ASSERT_EQ(content::ByteStreamOutput::STREAM_COMPLETE, + ASSERT_EQ(content::ByteStreamReader::STREAM_COMPLETE, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, byte_stream_output->GetStatus()); @@ -295,16 +295,16 @@ TEST_F(ByteStreamTest, ByteStream_CompleteTransmits) { content::CreateByteStream( message_loop_.message_loop_proxy(), message_loop_.message_loop_proxy(), 3 * 1024, &byte_stream_input, &byte_stream_output); - EXPECT_EQ(content::ByteStreamOutput::STREAM_EMPTY, + EXPECT_EQ(content::ByteStreamReader::STREAM_EMPTY, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_TRUE(Write(byte_stream_input.get(), 1024)); byte_stream_input->Close( content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED); message_loop_.RunAllPending(); - EXPECT_EQ(content::ByteStreamOutput::STREAM_HAS_DATA, + EXPECT_EQ(content::ByteStreamReader::STREAM_HAS_DATA, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_TRUE(ValidateIOBuffer(output_io_buffer, output_length)); - ASSERT_EQ(content::ByteStreamOutput::STREAM_COMPLETE, + ASSERT_EQ(content::ByteStreamReader::STREAM_COMPLETE, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, byte_stream_output->GetStatus()); @@ -316,8 +316,8 @@ TEST_F(ByteStreamTest, ByteStream_SinkCallback) { EXPECT_CALL(*task_runner.get(), RunsTasksOnCurrentThread()) .WillRepeatedly(Return(true)); - scoped_ptr<content::ByteStreamInput> byte_stream_input; - scoped_ptr<content::ByteStreamOutput> byte_stream_output; + scoped_ptr<content::ByteStreamWriter> byte_stream_input; + scoped_ptr<content::ByteStreamReader> byte_stream_output; content::CreateByteStream( message_loop_.message_loop_proxy(), task_runner, 10000, &byte_stream_input, &byte_stream_output); @@ -327,7 +327,7 @@ TEST_F(ByteStreamTest, ByteStream_SinkCallback) { base::Closure intermediate_callback; // Note that the specifics of when the callbacks are called with regard - // to how much data is pushed onto the pipe is not (currently) part + // to how much data is pushed onto the stream is not (currently) part // of the interface contract. If it becomes part of the contract, the // tests below should get much more precise. @@ -353,10 +353,10 @@ TEST_F(ByteStreamTest, ByteStream_SinkCallback) { EXPECT_EQ(1, num_callbacks); // Check data and stream state. - EXPECT_EQ(content::ByteStreamOutput::STREAM_HAS_DATA, + EXPECT_EQ(content::ByteStreamReader::STREAM_HAS_DATA, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_TRUE(ValidateIOBuffer(output_io_buffer, output_length)); - EXPECT_EQ(content::ByteStreamOutput::STREAM_EMPTY, + EXPECT_EQ(content::ByteStreamReader::STREAM_EMPTY, byte_stream_output->Read(&output_io_buffer, &output_length)); // Confirm callback *isn't* called at less than 33% (by lack of @@ -366,7 +366,7 @@ TEST_F(ByteStreamTest, ByteStream_SinkCallback) { // This reflects an implementation artifact that data goes with callbacks, // which should not be considered part of the interface guarantee. - EXPECT_EQ(content::ByteStreamOutput::STREAM_EMPTY, + EXPECT_EQ(content::ByteStreamReader::STREAM_EMPTY, byte_stream_output->Read(&output_io_buffer, &output_length)); } @@ -377,8 +377,8 @@ TEST_F(ByteStreamTest, ByteStream_SourceCallback) { EXPECT_CALL(*task_runner.get(), RunsTasksOnCurrentThread()) .WillRepeatedly(Return(true)); - scoped_ptr<content::ByteStreamInput> byte_stream_input; - scoped_ptr<content::ByteStreamOutput> byte_stream_output; + scoped_ptr<content::ByteStreamWriter> byte_stream_input; + scoped_ptr<content::ByteStreamReader> byte_stream_output; content::CreateByteStream( task_runner, message_loop_.message_loop_proxy(), 10000, &byte_stream_input, &byte_stream_output); @@ -388,7 +388,7 @@ TEST_F(ByteStreamTest, ByteStream_SourceCallback) { base::Closure intermediate_callback; // Note that the specifics of when the callbacks are called with regard - // to how much data is pulled from the pipe is not (currently) part + // to how much data is pulled from the stream is not (currently) part // of the interface contract. If it becomes part of the contract, the // tests below should get much more precise. @@ -406,7 +406,7 @@ TEST_F(ByteStreamTest, ByteStream_SourceCallback) { // Allow bytes to transition (needed for message passing implementation), // and get and validate the data. message_loop_.RunAllPending(); - EXPECT_EQ(content::ByteStreamOutput::STREAM_HAS_DATA, + EXPECT_EQ(content::ByteStreamReader::STREAM_HAS_DATA, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_TRUE(ValidateIOBuffer(output_io_buffer, output_length)); @@ -417,7 +417,7 @@ TEST_F(ByteStreamTest, ByteStream_SourceCallback) { // Grab data, triggering callback. Recorded on dispatch, but doesn't // happen because it's caught by the mock. - EXPECT_EQ(content::ByteStreamOutput::STREAM_HAS_DATA, + EXPECT_EQ(content::ByteStreamReader::STREAM_HAS_DATA, byte_stream_output->Read(&output_io_buffer, &output_length)); ::testing::Mock::VerifyAndClearExpectations(task_runner.get()); EXPECT_CALL(*task_runner.get(), RunsTasksOnCurrentThread()) @@ -433,13 +433,13 @@ TEST_F(ByteStreamTest, ByteStream_SourceCallback) { EXPECT_CALL(*task_runner.get(), PostDelayedTask(_, _, 0)) .WillOnce(DoAll(SaveArg<1>(&intermediate_callback), Return(true))); - EXPECT_EQ(content::ByteStreamOutput::STREAM_HAS_DATA, + EXPECT_EQ(content::ByteStreamReader::STREAM_HAS_DATA, byte_stream_output->Read(&output_io_buffer, &output_length)); ::testing::Mock::VerifyAndClearExpectations(task_runner.get()); EXPECT_CALL(*task_runner.get(), RunsTasksOnCurrentThread()) .WillRepeatedly(Return(true)); EXPECT_TRUE(ValidateIOBuffer(output_io_buffer, output_length)); - EXPECT_EQ(content::ByteStreamOutput::STREAM_EMPTY, + EXPECT_EQ(content::ByteStreamReader::STREAM_EMPTY, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_EQ(1, num_callbacks); intermediate_callback.Run(); @@ -455,8 +455,8 @@ TEST_F(ByteStreamTest, ByteStream_SinkInterrupt) { EXPECT_CALL(*task_runner.get(), RunsTasksOnCurrentThread()) .WillRepeatedly(Return(true)); - scoped_ptr<content::ByteStreamInput> byte_stream_input; - scoped_ptr<content::ByteStreamOutput> byte_stream_output; + scoped_ptr<content::ByteStreamWriter> byte_stream_input; + scoped_ptr<content::ByteStreamReader> byte_stream_output; content::CreateByteStream( message_loop_.message_loop_proxy(), task_runner, 10000, &byte_stream_input, &byte_stream_output); @@ -494,10 +494,10 @@ TEST_F(ByteStreamTest, ByteStream_SinkInterrupt) { EXPECT_EQ(1, num_alt_callbacks); // Final cleanup. - EXPECT_EQ(content::ByteStreamOutput::STREAM_HAS_DATA, + EXPECT_EQ(content::ByteStreamReader::STREAM_HAS_DATA, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_TRUE(ValidateIOBuffer(output_io_buffer, output_length)); - EXPECT_EQ(content::ByteStreamOutput::STREAM_EMPTY, + EXPECT_EQ(content::ByteStreamReader::STREAM_EMPTY, byte_stream_output->Read(&output_io_buffer, &output_length)); } @@ -509,8 +509,8 @@ TEST_F(ByteStreamTest, ByteStream_SourceInterrupt) { EXPECT_CALL(*task_runner.get(), RunsTasksOnCurrentThread()) .WillRepeatedly(Return(true)); - scoped_ptr<content::ByteStreamInput> byte_stream_input; - scoped_ptr<content::ByteStreamOutput> byte_stream_output; + scoped_ptr<content::ByteStreamWriter> byte_stream_input; + scoped_ptr<content::ByteStreamReader> byte_stream_output; content::CreateByteStream( task_runner, message_loop_.message_loop_proxy(), 10000, &byte_stream_input, &byte_stream_output); @@ -529,7 +529,7 @@ TEST_F(ByteStreamTest, ByteStream_SourceInterrupt) { message_loop_.RunAllPending(); // Initial get should not trigger callback. - EXPECT_EQ(content::ByteStreamOutput::STREAM_HAS_DATA, + EXPECT_EQ(content::ByteStreamReader::STREAM_HAS_DATA, byte_stream_output->Read(&output_io_buffer, &output_length)); EXPECT_TRUE(ValidateIOBuffer(output_io_buffer, output_length)); message_loop_.RunAllPending(); @@ -540,7 +540,7 @@ TEST_F(ByteStreamTest, ByteStream_SourceInterrupt) { Return(true))); // Second get *should* trigger callback. - EXPECT_EQ(content::ByteStreamOutput::STREAM_HAS_DATA, + EXPECT_EQ(content::ByteStreamReader::STREAM_HAS_DATA, byte_stream_output->Read(&output_io_buffer, &output_length)); ::testing::Mock::VerifyAndClearExpectations(task_runner.get()); EXPECT_CALL(*task_runner.get(), RunsTasksOnCurrentThread()) @@ -559,13 +559,13 @@ TEST_F(ByteStreamTest, ByteStream_SourceInterrupt) { EXPECT_CALL(*task_runner.get(), PostDelayedTask(_, _, 0)) .WillOnce(DoAll(SaveArg<1>(&intermediate_callback), Return(true))); - EXPECT_EQ(content::ByteStreamOutput::STREAM_HAS_DATA, + EXPECT_EQ(content::ByteStreamReader::STREAM_HAS_DATA, byte_stream_output->Read(&output_io_buffer, &output_length)); ::testing::Mock::VerifyAndClearExpectations(task_runner.get()); EXPECT_CALL(*task_runner.get(), RunsTasksOnCurrentThread()) .WillRepeatedly(Return(true)); EXPECT_TRUE(ValidateIOBuffer(output_io_buffer, output_length)); - EXPECT_EQ(content::ByteStreamOutput::STREAM_EMPTY, + EXPECT_EQ(content::ByteStreamReader::STREAM_EMPTY, byte_stream_output->Read(&output_io_buffer, &output_length)); } @@ -576,8 +576,8 @@ TEST_F(ByteStreamTest, ByteStream_ZeroCallback) { EXPECT_CALL(*task_runner.get(), RunsTasksOnCurrentThread()) .WillRepeatedly(Return(true)); - scoped_ptr<content::ByteStreamInput> byte_stream_input; - scoped_ptr<content::ByteStreamOutput> byte_stream_output; + scoped_ptr<content::ByteStreamWriter> byte_stream_input; + scoped_ptr<content::ByteStreamReader> byte_stream_output; content::CreateByteStream( message_loop_.message_loop_proxy(), task_runner, 10000, &byte_stream_input, &byte_stream_output); diff --git a/content/browser/download/download_buffer.cc b/content/browser/download/download_buffer.cc deleted file mode 100644 index c8d5c80..0000000 --- a/content/browser/download/download_buffer.cc +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2011 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 "content/browser/download/download_buffer.h" - -#include "net/base/io_buffer.h" - -namespace content { - -net::IOBuffer* AssembleData(const ContentVector& contents, size_t* num_bytes) { - if (num_bytes) - *num_bytes = 0; - - size_t data_len; - // Determine how large a buffer we need. - size_t assembly_buffer_length = 0; - for (size_t i = 0; i < contents.size(); ++i) { - data_len = contents[i].second; - assembly_buffer_length += data_len; - } - - // 0-length IOBuffers are not allowed. - if (assembly_buffer_length == 0) - return NULL; - - net::IOBuffer* assembly_buffer = new net::IOBuffer(assembly_buffer_length); - - // Copy the data into |assembly_buffer|. - size_t bytes_copied = 0; - for (size_t i = 0; i < contents.size(); ++i) { - net::IOBuffer* data = contents[i].first; - data_len = contents[i].second; - DCHECK(data != NULL); - DCHECK_LE(bytes_copied + data_len, assembly_buffer_length); - memcpy(assembly_buffer->data() + bytes_copied, data->data(), data_len); - bytes_copied += data_len; - } - DCHECK_EQ(assembly_buffer_length, bytes_copied); - - if (num_bytes) - *num_bytes = assembly_buffer_length; - - return assembly_buffer; -} - -DownloadBuffer::DownloadBuffer() { -} - -DownloadBuffer::~DownloadBuffer() { -} - -size_t DownloadBuffer::AddData(net::IOBuffer* io_buffer, size_t byte_count) { - base::AutoLock auto_lock(lock_); - contents_.push_back(std::make_pair(io_buffer, byte_count)); - return contents_.size(); -} - -ContentVector* DownloadBuffer::ReleaseContents() { - base::AutoLock auto_lock(lock_); - ContentVector* other_contents = new ContentVector; - other_contents->swap(contents_); - return other_contents; -} - -size_t DownloadBuffer::size() const { - base::AutoLock auto_lock(lock_); - return contents_.size(); -} - -} // namespace content diff --git a/content/browser/download/download_buffer.h b/content/browser/download/download_buffer.h deleted file mode 100644 index 35d4a56..0000000 --- a/content/browser/download/download_buffer.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2012 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 CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_BUFFER_H_ -#define CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_BUFFER_H_ -#pragma once - -#include <vector> - -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" -#include "content/common/content_export.h" - -namespace net { -class IOBuffer; -} - -namespace content { - -typedef std::pair<scoped_refptr<net::IOBuffer>, size_t> ContentElement; -typedef std::vector<ContentElement> ContentVector; - -// Helper function for ContentVector. -// Assembles the data from |contents| and returns it in an |IOBuffer|. -// The number of bytes in the |IOBuffer| is returned in |*num_bytes| -// (if |num_bytes| is not NULL). -// If |contents| is empty, returns NULL as an |IOBuffer| is not allowed -// to have 0 bytes. -CONTENT_EXPORT net::IOBuffer* AssembleData(const ContentVector& contents, - size_t* num_bytes); - -// |DownloadBuffer| is a thread-safe wrapper around |ContentVector|. -// -// It is created and populated on the IO thread, and passed to the -// FILE thread for writing. In order to avoid flooding the FILE thread with -// too many small write messages, each write is appended to the -// |DownloadBuffer| while waiting for the task to run on the FILE -// thread. Access to the write buffers is synchronized via the lock. -class CONTENT_EXPORT DownloadBuffer - : public base::RefCountedThreadSafe<DownloadBuffer> { - public: - DownloadBuffer(); - - // Adds data to the buffers. - // Returns the number of |IOBuffer|s in the ContentVector. - size_t AddData(net::IOBuffer* io_buffer, size_t byte_count); - - // Retrieves the ContentVector of buffers, clearing the contents. - // The caller takes ownership. - ContentVector* ReleaseContents(); - - // Gets the number of |IOBuffers| we have. - size_t size() const; - - private: - friend class base::RefCountedThreadSafe<DownloadBuffer>; - - // Do not allow explicit destruction by anyone else. - ~DownloadBuffer(); - - mutable base::Lock lock_; - ContentVector contents_; - - DISALLOW_COPY_AND_ASSIGN(DownloadBuffer); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_BUFFER_H_ diff --git a/content/browser/download/download_buffer_unittest.cc b/content/browser/download/download_buffer_unittest.cc deleted file mode 100644 index 137e612..0000000 --- a/content/browser/download/download_buffer_unittest.cc +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) 2011 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 "content/browser/download/download_buffer.h" - -#include <string.h> - -#include "net/base/io_buffer.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace content { - -namespace { - -const char* kTestData[] = { - "Four score and twenty years ago", - "Our four (or five) fathers", - "Danced and sang in the woods" -}; -const size_t kTestDataQty = arraysize(kTestData); - -class DownloadBufferTest : public testing::Test { - public: - DownloadBufferTest() { - } - ~DownloadBufferTest() { - } - - void CreateBuffer() { - EXPECT_EQ(0u, content_buffer_.size()); - - for (size_t i = 0; i < kTestDataQty; ++i) { - net::StringIOBuffer* io_buffer = new net::StringIOBuffer(kTestData[i]); - content_buffer_.push_back(std::make_pair(io_buffer, io_buffer->size())); - EXPECT_EQ(i + 1, content_buffer_.size()); - } - } - - ContentVector* content_buffer() { - return &content_buffer_; - } - - private: - ContentVector content_buffer_; -}; - -TEST_F(DownloadBufferTest, CreateContentVector) { - CreateBuffer(); - - ContentVector* contents = content_buffer(); - - EXPECT_EQ(kTestDataQty, content_buffer()->size()); - EXPECT_EQ(kTestDataQty, contents->size()); - - for (size_t i = 0; i < kTestDataQty; ++i) { - size_t io_buffer_size = strlen(kTestData[i]); - EXPECT_STREQ(kTestData[i], (*contents)[i].first->data()); - EXPECT_EQ(io_buffer_size, (*contents)[i].second); - } -} - -TEST_F(DownloadBufferTest, CreateDownloadBuffer) { - scoped_refptr<DownloadBuffer> content_buffer(new DownloadBuffer); - EXPECT_EQ(0u, content_buffer->size()); - - for (size_t i = 0; i < kTestDataQty; ++i) { - net::StringIOBuffer* io_buffer = new net::StringIOBuffer(kTestData[i]); - EXPECT_EQ(i + 1, content_buffer->AddData(io_buffer, io_buffer->size())); - } - scoped_ptr<ContentVector> contents(content_buffer->ReleaseContents()); - - EXPECT_EQ(0u, content_buffer->size()); - EXPECT_EQ(kTestDataQty, contents->size()); - - for (size_t i = 0; i < kTestDataQty; ++i) { - size_t io_buffer_size = strlen(kTestData[i]); - EXPECT_STREQ(kTestData[i], (*contents)[i].first->data()); - EXPECT_EQ(io_buffer_size, (*contents)[i].second); - } -} - -TEST_F(DownloadBufferTest, AssembleData) { - CreateBuffer(); - - size_t assembled_bytes = 0; - scoped_refptr<net::IOBuffer> assembled_buffer = - AssembleData(*content_buffer(), &assembled_bytes); - - // Did not change the content buffer. - EXPECT_EQ(kTestDataQty, content_buffer()->size()); - - // Verify the data. - size_t total_size = 0; - for (size_t i = 0; i < kTestDataQty; ++i) { - size_t len = strlen(kTestData[i]); - // assembled_buffer->data() may not be NULL-terminated, so we can't use - // EXPECT_STREQ(). - EXPECT_EQ( - 0, memcmp(kTestData[i], assembled_buffer->data() + total_size, len)); - total_size += len; - } - // Verify the data length. - EXPECT_EQ(total_size, assembled_bytes); -} - -TEST_F(DownloadBufferTest, AssembleDataWithEmptyBuffer) { - ContentVector buffer; - EXPECT_EQ(0u, buffer.size()); - - size_t assembled_bytes = 0; - scoped_refptr<net::IOBuffer> assembled_buffer = - AssembleData(buffer, &assembled_bytes); - - // Did not change the content buffer. - EXPECT_EQ(0u, buffer.size()); - EXPECT_EQ(0u, assembled_bytes); - EXPECT_TRUE(NULL == assembled_buffer.get()); -} - -} // namespace - -} // namespace content diff --git a/content/browser/download/download_file.h b/content/browser/download/download_file.h index 0b1f5ff..ef6e64e 100644 --- a/content/browser/download/download_file.h +++ b/content/browser/download/download_file.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -30,11 +30,6 @@ class CONTENT_EXPORT DownloadFile { // Returns net::OK on success, or a network error code on failure. virtual net::Error Initialize() = 0; - // Write a new chunk of data to the file. - // Returns net::OK on success (all bytes written to the file), - // or a network error code on failure. - virtual net::Error AppendDataToFile(const char* data, size_t data_len) = 0; - // Rename the download file. // Returns net::OK on success, or a network error code on failure. virtual net::Error Rename(const FilePath& full_path) = 0; @@ -45,9 +40,6 @@ class CONTENT_EXPORT DownloadFile { // Abort the download and automatically close the file. virtual void Cancel() = 0; - // Indicate that the download has finished. No new data will be received. - virtual void Finish() = 0; - // Informs the OS that this file came from the internet. virtual void AnnotateWithSourceInformation() = 0; diff --git a/content/browser/download/download_file_impl.cc b/content/browser/download/download_file_impl.cc index ae8d4ec..ee588b8 100644 --- a/content/browser/download/download_file_impl.cc +++ b/content/browser/download/download_file_impl.cc @@ -6,20 +6,30 @@ #include <string> +#include "base/bind.h" #include "base/file_util.h" +#include "base/message_loop_proxy.h" +#include "base/time.h" +#include "content/browser/download/byte_stream.h" #include "content/browser/download/download_create_info.h" +#include "content/browser/download/download_interrupt_reasons_impl.h" +#include "content/browser/download/download_net_log_parameters.h" #include "content/browser/power_save_blocker.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/download_manager.h" +#include "content/browser/download/download_stats.h" +#include "net/base/io_buffer.h" using content::BrowserThread; using content::DownloadId; using content::DownloadManager; const int kUpdatePeriodMs = 500; +const int kMaxTimeBlockingFileThreadMs = 1000; DownloadFileImpl::DownloadFileImpl( const DownloadCreateInfo* info, + scoped_ptr<content::ByteStreamReader> stream, DownloadRequestHandleInterface* request_handle, DownloadManager* download_manager, bool calculate_hash, @@ -33,21 +43,35 @@ DownloadFileImpl::DownloadFileImpl( info->save_info.hash_state, info->save_info.file_stream, bound_net_log), + stream_reader_(stream.Pass()), id_(info->download_id), request_handle_(request_handle), download_manager_(download_manager), + bytes_seen_(0), + bound_net_log_(bound_net_log), + weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), power_save_blocker_(power_save_blocker.Pass()) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); } DownloadFileImpl::~DownloadFileImpl() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); } // BaseFile delegated functions. net::Error DownloadFileImpl::Initialize() { update_timer_.reset(new base::RepeatingTimer<DownloadFileImpl>()); - return file_.Initialize(); + net::Error result = file_.Initialize(); + if (result != net::OK) + return result; + + stream_reader_->RegisterCallback( + base::Bind(&DownloadFileImpl::StreamActive, weak_factory_.GetWeakPtr())); + + download_start_ = base::TimeTicks::Now(); + // Initial pull from the straw. + StreamActive(); + + return result; } net::Error DownloadFileImpl::AppendDataToFile(const char* data, @@ -72,11 +96,6 @@ void DownloadFileImpl::Cancel() { file_.Cancel(); } -void DownloadFileImpl::Finish() { - file_.Finish(); - update_timer_.reset(); -} - void DownloadFileImpl::AnnotateWithSourceInformation() { file_.AnnotateWithSourceInformation(); } @@ -135,6 +154,114 @@ std::string DownloadFileImpl::DebugString() const { file_.DebugString().c_str()); } +void DownloadFileImpl::StreamActive() { + base::TimeTicks start(base::TimeTicks::Now()); + base::TimeTicks now; + scoped_refptr<net::IOBuffer> incoming_data; + size_t incoming_data_size = 0; + size_t total_incoming_data_size = 0; + size_t num_buffers = 0; + content::ByteStreamReader::StreamState state( + content::ByteStreamReader::STREAM_EMPTY); + content::DownloadInterruptReason reason = + content::DOWNLOAD_INTERRUPT_REASON_NONE; + base::TimeDelta delta( + base::TimeDelta::FromMilliseconds(kMaxTimeBlockingFileThreadMs)); + + // Take care of any file local activity required. + do { + state = stream_reader_->Read(&incoming_data, &incoming_data_size); + + net::Error result = net::OK; + switch (state) { + case content::ByteStreamReader::STREAM_EMPTY: + break; + case content::ByteStreamReader::STREAM_HAS_DATA: + { + ++num_buffers; + base::TimeTicks write_start(base::TimeTicks::Now()); + result = AppendDataToFile( + incoming_data.get()->data(), incoming_data_size); + disk_writes_time_ += (base::TimeTicks::Now() - write_start); + total_incoming_data_size += incoming_data_size; + reason = content::ConvertNetErrorToInterruptReason( + result, content::DOWNLOAD_INTERRUPT_FROM_DISK); + } + break; + case content::ByteStreamReader::STREAM_COMPLETE: + { + reason = stream_reader_->GetStatus(); + SendUpdate(); + base::TimeTicks close_start(base::TimeTicks::Now()); + file_.Finish(); + base::TimeTicks now(base::TimeTicks::Now()); + disk_writes_time_ += (now - close_start); + download_stats::RecordFileBandwidth( + bytes_seen_, disk_writes_time_, now - download_start_); + update_timer_.reset(); + } + break; + default: + NOTREACHED(); + break; + } + now = base::TimeTicks::Now(); + } while (state == content::ByteStreamReader::STREAM_HAS_DATA && + reason == content::DOWNLOAD_INTERRUPT_REASON_NONE && + now - start <= delta); + + // If we're stopping to yield the thread, post a task so we come back. + if (state == content::ByteStreamReader::STREAM_HAS_DATA && + now - start > delta) { + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + base::Bind(&DownloadFileImpl::StreamActive, + weak_factory_.GetWeakPtr())); + } + + if (total_incoming_data_size) + download_stats::RecordFileThreadReceiveBuffers(num_buffers); + bytes_seen_ += total_incoming_data_size; + + download_stats::RecordContiguousWriteTime(now - start); + + // Take care of communication with our controller. + if (reason != content::DOWNLOAD_INTERRUPT_REASON_NONE) { + // Error case for both upstream source and file write. + // Shut down processing and signal an error to our controller. + // Our controller will clean us up. + stream_reader_->RegisterCallback(base::Closure()); + weak_factory_.InvalidateWeakPtrs(); + if (download_manager_.get()) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&DownloadManager::OnDownloadInterrupted, + download_manager_, id_.local(), + BytesSoFar(), GetHashState(), reason)); + } + } else if (state == content::ByteStreamReader::STREAM_COMPLETE) { + // Signal successful completion and shut down processing. + stream_reader_->RegisterCallback(base::Closure()); + weak_factory_.InvalidateWeakPtrs(); + if (download_manager_.get()) { + std::string hash; + if (!GetHash(&hash) || file_.IsEmptyHash(hash)) + hash.clear(); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&DownloadManager::OnResponseCompleted, + download_manager_, id_.local(), + BytesSoFar(), hash)); + } + } + if (bound_net_log_.IsLoggingAllEvents()) { + bound_net_log_.AddEvent( + net::NetLog::TYPE_DOWNLOAD_STREAM_DRAINED, + make_scoped_refptr(new download_net_logs::FileStreamDrainedParameters( + total_incoming_data_size, num_buffers))); + } +} + void DownloadFileImpl::SendUpdate() { if (download_manager_.get()) { BrowserThread::PostTask( diff --git a/content/browser/download/download_file_impl.h b/content/browser/download/download_file_impl.h index ab39c14..93d6329 100644 --- a/content/browser/download/download_file_impl.h +++ b/content/browser/download/download_file_impl.h @@ -10,15 +10,20 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/time.h" #include "base/timer.h" #include "content/browser/download/base_file.h" +#include "content/browser/download/byte_stream.h" #include "content/browser/download/download_request_handle.h" +#include "net/base/net_log.h" class PowerSaveBlocker; struct DownloadCreateInfo; namespace content { +class ByteStreamReader; class DownloadManager; } @@ -27,6 +32,7 @@ class CONTENT_EXPORT DownloadFileImpl : virtual public content::DownloadFile { // Takes ownership of the object pointed to by |request_handle|. // |bound_net_log| will be used for logging the download file's events. DownloadFileImpl(const DownloadCreateInfo* info, + scoped_ptr<content::ByteStreamReader> stream, DownloadRequestHandleInterface* request_handle, content::DownloadManager* download_manager, bool calculate_hash, @@ -36,12 +42,9 @@ class CONTENT_EXPORT DownloadFileImpl : virtual public content::DownloadFile { // DownloadFile functions. virtual net::Error Initialize() OVERRIDE; - virtual net::Error AppendDataToFile(const char* data, - size_t data_len) OVERRIDE; virtual net::Error Rename(const FilePath& full_path) OVERRIDE; virtual void Detach() OVERRIDE; virtual void Cancel() OVERRIDE; - virtual void Finish() OVERRIDE; virtual void AnnotateWithSourceInformation() OVERRIDE; virtual FilePath FullPath() const OVERRIDE; virtual bool InProgress() const OVERRIDE; @@ -55,13 +58,28 @@ class CONTENT_EXPORT DownloadFileImpl : virtual public content::DownloadFile { virtual const content::DownloadId& GlobalId() const OVERRIDE; virtual std::string DebugString() const OVERRIDE; + protected: + // For test class overrides. + virtual net::Error AppendDataToFile(const char* data, + size_t data_len); + private: + // Called when there's some activity on stream_reader_ that needs to be + // handled. + void StreamActive(); + // Send updates on our progress. void SendUpdate(); // The base file instance. BaseFile file_; + // The stream through which data comes. + // TODO(rdsmith): Move this into BaseFile; requires using the same + // stream semantics in SavePackage. Alternatively, replace SaveFile + // with DownloadFile and get rid of BaseFile. + scoped_ptr<content::ByteStreamReader> stream_reader_; + // The unique identifier for this download, assigned at creation by // the DownloadFileManager for its internal record keeping. content::DownloadId id_; @@ -76,6 +94,15 @@ class CONTENT_EXPORT DownloadFileImpl : virtual public content::DownloadFile { // DownloadManager this download belongs to. scoped_refptr<content::DownloadManager> download_manager_; + // Statistics + size_t bytes_seen_; + base::TimeDelta disk_writes_time_; + base::TimeTicks download_start_; + + net::BoundNetLog bound_net_log_; + + base::WeakPtrFactory<DownloadFileImpl> weak_factory_; + // RAII handle to keep the system from sleeping while we're downloading. scoped_ptr<PowerSaveBlocker> power_save_blocker_; diff --git a/content/browser/download/download_file_manager.cc b/content/browser/download/download_file_manager.cc index 40f1acb..f74cb28 100644 --- a/content/browser/download/download_file_manager.cc +++ b/content/browser/download/download_file_manager.cc @@ -13,7 +13,6 @@ #include "base/stl_util.h" #include "base/utf_string_conversions.h" #include "content/browser/download/base_file.h" -#include "content/browser/download/download_buffer.h" #include "content/browser/download/download_create_info.h" #include "content/browser/download/download_file_impl.h" #include "content/browser/download/download_interrupt_reasons_impl.h" @@ -41,6 +40,7 @@ class DownloadFileFactoryImpl virtual content::DownloadFile* CreateFile( DownloadCreateInfo* info, + scoped_ptr<content::ByteStreamReader> stream, const DownloadRequestHandle& request_handle, DownloadManager* download_manager, bool calculate_hash, @@ -49,12 +49,13 @@ class DownloadFileFactoryImpl DownloadFile* DownloadFileFactoryImpl::CreateFile( DownloadCreateInfo* info, + scoped_ptr<content::ByteStreamReader> stream, const DownloadRequestHandle& request_handle, DownloadManager* download_manager, bool calculate_hash, const net::BoundNetLog& bound_net_log) { return new DownloadFileImpl( - info, new DownloadRequestHandle(request_handle), + info, stream.Pass(), new DownloadRequestHandle(request_handle), download_manager, calculate_hash, scoped_ptr<PowerSaveBlocker>( new PowerSaveBlocker( @@ -87,19 +88,19 @@ void DownloadFileManager::OnShutdown() { } void DownloadFileManager::CreateDownloadFile( - DownloadCreateInfo* info, const DownloadRequestHandle& request_handle, + scoped_ptr<DownloadCreateInfo> info, + scoped_ptr<content::ByteStreamReader> stream, + const DownloadRequestHandle& request_handle, DownloadManager* download_manager, bool get_hash, const net::BoundNetLog& bound_net_log) { - DCHECK(info); + DCHECK(info.get()); VLOG(20) << __FUNCTION__ << "()" << " info = " << info->DebugString(); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - // Life of |info| ends here. No more references to it after this method. - scoped_ptr<DownloadCreateInfo> infop(info); - // Create the download file. scoped_ptr<DownloadFile> download_file(download_file_factory_->CreateFile( - info, request_handle, download_manager, get_hash, bound_net_log)); + info.get(), stream.Pass(), request_handle, download_manager, + get_hash, bound_net_log)); net::Error init_result = download_file->Initialize(); if (net::OK != init_result) { @@ -152,9 +153,11 @@ void DownloadFileManager::UpdateInProgressDownloads() { } void DownloadFileManager::StartDownload( - DownloadCreateInfo* info, const DownloadRequestHandle& request_handle) { + scoped_ptr<DownloadCreateInfo> info, + scoped_ptr<content::ByteStreamReader> stream, + const DownloadRequestHandle& request_handle) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK(info); + DCHECK(info.get()); DownloadManager* manager = request_handle.GetDownloadManager(); DCHECK(manager); // Checked in |DownloadResourceHandler::StartOnUIThread()|. @@ -162,114 +165,17 @@ void DownloadFileManager::StartDownload( // |bound_net_log| will be used for logging the both the download item's and // the download file's events. net::BoundNetLog bound_net_log = - manager->CreateDownloadItem(info, request_handle); + manager->CreateDownloadItem(info.get(), request_handle); bool hash_needed = manager->GenerateFileHash(); BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(&DownloadFileManager::CreateDownloadFile, this, - info, request_handle, make_scoped_refptr(manager), + base::Passed(info.Pass()), base::Passed(stream.Pass()), + request_handle, + make_scoped_refptr(manager), hash_needed, bound_net_log)); } -// We don't forward an update to the UI thread here, since we want to throttle -// the UI update rate via a periodic timer. If the user has cancelled the -// download (in the UI thread), we may receive a few more updates before the IO -// thread gets the cancel message: we just delete the data since the -// DownloadFile has been deleted. -void DownloadFileManager::UpdateDownload( - DownloadId global_id, content::DownloadBuffer* buffer) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - scoped_ptr<content::ContentVector> contents(buffer->ReleaseContents()); - - download_stats::RecordFileThreadReceiveBuffers(contents->size()); - - DownloadFile* download_file = GetDownloadFile(global_id); - bool had_error = false; - for (size_t i = 0; i < contents->size(); ++i) { - net::IOBuffer* data = (*contents)[i].first; - const int data_len = (*contents)[i].second; - if (!had_error && download_file) { - net::Error write_result = - download_file->AppendDataToFile(data->data(), data_len); - if (write_result != net::OK) { - // Write failed: interrupt the download. - DownloadManager* download_manager = - download_file->GetDownloadManager(); - had_error = true; - - int64 bytes_downloaded = download_file->BytesSoFar(); - std::string hash_state(download_file->GetHashState()); - - // Calling this here in case we get more data, to avoid - // processing data after an error. That could lead to - // files that are corrupted if the later processing succeeded. - CancelDownload(global_id); - download_file = NULL; // Was deleted in |CancelDownload|. - - if (download_manager) { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&DownloadManager::OnDownloadInterrupted, - download_manager, - global_id.local(), - bytes_downloaded, - hash_state, - content::ConvertNetErrorToInterruptReason( - write_result, - content::DOWNLOAD_INTERRUPT_FROM_DISK))); - } - } - } - data->Release(); - } -} - -void DownloadFileManager::OnResponseCompleted( - DownloadId global_id, - content::DownloadInterruptReason reason, - const std::string& security_info) { - VLOG(20) << __FUNCTION__ << "()" << " id = " << global_id - << " reason = " << InterruptReasonDebugString(reason) - << " security_info = \"" << security_info << "\""; - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - DownloadFile* download_file = GetDownloadFile(global_id); - if (!download_file) - return; - - download_file->Finish(); - - DownloadManager* download_manager = download_file->GetDownloadManager(); - if (!download_manager) { - CancelDownload(global_id); - return; - } - - if (reason == content::DOWNLOAD_INTERRUPT_REASON_NONE) { - std::string hash; - if (!download_file->GetHash(&hash) || - BaseFile::IsEmptyHash(hash)) { - hash.clear(); - } - - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&DownloadManager::OnResponseCompleted, - download_manager, global_id.local(), - download_file->BytesSoFar(), hash)); - } else { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&DownloadManager::OnDownloadInterrupted, - download_manager, - global_id.local(), - download_file->BytesSoFar(), - download_file->GetHashState(), - reason)); - } - // We need to keep the download around until the UI thread has finalized - // the name. -} - // This method will be sent via a user action, or shutdown on the UI thread, and // run on the download thread. Since this message has been sent from the UI // thread, the download may have already completed and won't exist in our map. diff --git a/content/browser/download/download_file_manager.h b/content/browser/download/download_file_manager.h index 965a93c..f7737e9 100644 --- a/content/browser/download/download_file_manager.h +++ b/content/browser/download/download_file_manager.h @@ -61,7 +61,7 @@ class DownloadRequestHandle; class FilePath; namespace content { -class DownloadBuffer; +class ByteStreamReader; class DownloadFile; class DownloadManager; } @@ -86,6 +86,7 @@ class CONTENT_EXPORT DownloadFileManager virtual content::DownloadFile* CreateFile( DownloadCreateInfo* info, + scoped_ptr<content::ByteStreamReader> stream, const DownloadRequestHandle& request_handle, content::DownloadManager* download_manager, bool calculate_hash, @@ -101,23 +102,10 @@ class CONTENT_EXPORT DownloadFileManager virtual void Shutdown(); // Called on UI thread to make DownloadFileManager start the download. - virtual void StartDownload(DownloadCreateInfo* info, + virtual void StartDownload(scoped_ptr<DownloadCreateInfo> info, + scoped_ptr<content::ByteStreamReader> stream, const DownloadRequestHandle& request_handle); - // Handlers for notifications sent from the IO thread and run on the - // FILE thread. - virtual void UpdateDownload(content::DownloadId global_id, - content::DownloadBuffer* buffer); - - // |reason| is the reason for interruption, if one occurs. - // |security_info| contains SSL information (cert_id, cert_status, - // security_bits, ssl_connection_status), which can be used to - // fine-tune the error message. It is empty if the transaction - // was not performed securely. - virtual void OnResponseCompleted(content::DownloadId global_id, - content::DownloadInterruptReason reason, - const std::string& security_info); - // Handlers for notifications sent from the UI thread and run on the // FILE thread. These are both terminal actions with respect to the // download file, as far as the DownloadFileManager is concerned -- if @@ -186,7 +174,8 @@ class CONTENT_EXPORT DownloadFileManager // Creates DownloadFile on FILE thread and continues starting the download // process. - void CreateDownloadFile(DownloadCreateInfo* info, + void CreateDownloadFile(scoped_ptr<DownloadCreateInfo> info, + scoped_ptr<content::ByteStreamReader> stream, const DownloadRequestHandle& request_handle, content::DownloadManager* download_manager, bool hash_needed, diff --git a/content/browser/download/download_file_manager_unittest.cc b/content/browser/download/download_file_manager_unittest.cc index c8556ba..0845bc6 100644 --- a/content/browser/download/download_file_manager_unittest.cc +++ b/content/browser/download/download_file_manager_unittest.cc @@ -10,7 +10,7 @@ #include "base/scoped_temp_dir.h" #include "base/string_number_conversions.h" #include "content/browser/browser_thread_impl.h" -#include "content/browser/download/download_buffer.h" +#include "content/browser/download/byte_stream.h" #include "content/browser/download/download_create_info.h" #include "content/browser/download/download_interrupt_reasons_impl.h" #include "content/browser/download/download_request_handle.h" @@ -53,6 +53,7 @@ class MockDownloadFileFactory : virtual content::DownloadFile* CreateFile( DownloadCreateInfo* info, + scoped_ptr<content::ByteStreamReader> stream, const DownloadRequestHandle& request_handle, content::DownloadManager* download_manager, bool calculate_hash, @@ -66,6 +67,7 @@ class MockDownloadFileFactory : content::DownloadFile* MockDownloadFileFactory::CreateFile( DownloadCreateInfo* info, + scoped_ptr<content::ByteStreamReader> stream, const DownloadRequestHandle& request_handle, content::DownloadManager* download_manager, bool calculate_hash, @@ -136,8 +138,7 @@ class DownloadFileManagerTest : public testing::Test { // calling Release() on |download_manager_| won't ever result in its // destructor being called and we get a leak. DownloadFileManagerTest() - : download_buffer_(new content::DownloadBuffer()), - ui_thread_(BrowserThread::UI, &loop_), + : ui_thread_(BrowserThread::UI, &loop_), file_thread_(BrowserThread::FILE, &loop_) { } @@ -175,7 +176,8 @@ class DownloadFileManagerTest : public testing::Test { // Start a download. // |info| is the information needed to create a new download file. // |id| is the download ID of the new download file. - void StartDownload(DownloadCreateInfo* info, const DownloadId& id) { + void StartDownload(scoped_ptr<DownloadCreateInfo> info, + const DownloadId& id) { // Expected call sequence: // StartDownload // DownloadManager::CreateDownloadItem @@ -191,7 +193,7 @@ class DownloadFileManagerTest : public testing::Test { info->download_id = id; // Set expectations and return values. - EXPECT_CALL(*download_manager_, CreateDownloadItem(info, _)) + EXPECT_CALL(*download_manager_, CreateDownloadItem(info.get(), _)) .Times(1) .WillOnce(Return(net::BoundNetLog())); EXPECT_CALL(*download_manager_, GenerateFileHash()) @@ -199,94 +201,13 @@ class DownloadFileManagerTest : public testing::Test { .WillRepeatedly(Return(false)); EXPECT_CALL(*download_manager_, StartDownload(id.local())); - download_file_manager_->StartDownload(info, *request_handle_); + download_file_manager_->StartDownload( + info.Pass(), scoped_ptr<content::ByteStreamReader>(), *request_handle_); ProcessAllPendingMessages(); ClearExpectations(id); } - // Creates a new |net::IOBuffer| of size |length| with |data|, and attaches - // it to the download buffer. - bool UpdateBuffer(const char* data, size_t length) { - // We are passing ownership of this buffer to the download file manager. - // NOTE: we are padding io_buffer with one extra character so that the - // mock testing framework can treat it as a NULL-delimited string. - net::IOBuffer* io_buffer = new net::IOBuffer(length + 1); - // We need |AddRef()| because we do a |Release()| in |UpdateDownload()|. - io_buffer->AddRef(); - memcpy(io_buffer->data(), data, length); - io_buffer->data()[length] = 0; - - download_buffer_->AddData(io_buffer, length); - - return true; - } - - // Updates the download file. - // |id| is the download ID of the download file. - // |data| is the buffer containing the data. - // |length| is the length of the data buffer. - // |error_to_insert| is the error to simulate. For no error, use net::OK. - void UpdateDownload(const DownloadId& id, - const char* data, - size_t length, - net::Error error_to_insert) { - // Expected call sequence: - // Create DownloadBuffer - // UpdateDownload - // GetDownloadFile - // DownloadFile::AppendDataToFile - // - // On error: - // DownloadFile::GetDownloadManager - // DownloadFile::BytesSoFar - // CancelDownload - // Process one message in the message loop - // DownloadManager::OnDownloadInterrupted - EXPECT_TRUE(UpdateBuffer(data, length)); - MockDownloadFile* file = download_file_factory_->GetExistingFile(id); - ASSERT_TRUE(file != NULL); - byte_count_[id] += length; - - // Make sure our comparison string is NULL-terminated. - char* expected_data = new char[length + 1]; - memcpy(expected_data, data, length); - expected_data[length] = 0; - - EXPECT_CALL(*file, AppendDataToFile(StrEq(expected_data), length)) - .Times(1) - .WillOnce(Return(error_to_insert)); - - if (error_to_insert != net::OK) { - EXPECT_CALL(*file, GetDownloadManager()) - .Times(AtLeast(1)); - EXPECT_CALL(*file, BytesSoFar()) - .Times(AtLeast(1)) - .WillRepeatedly(Return(byte_count_[id])); - EXPECT_CALL(*file, GetHashState()) - .Times(AtLeast(1)); - EXPECT_CALL(*file, Cancel()); - } - - download_file_manager_->UpdateDownload(id, download_buffer_.get()); - - if (error_to_insert != net::OK) { - EXPECT_CALL(*download_manager_, - OnDownloadInterrupted( - id.local(), - byte_count_[id], - "", - content::ConvertNetErrorToInterruptReason( - error_to_insert, content::DOWNLOAD_INTERRUPT_FROM_DISK))); - - ProcessAllPendingMessages(); - ++error_count_[id]; - } - - ClearExpectations(id); - delete [] expected_data; - } - // Renames the download file. // |id| is the download ID of the download file. // |new_path| is the new file path. @@ -357,67 +278,6 @@ class DownloadFileManagerTest : public testing::Test { } } - // Finish the download operation. - // |id| is the download ID of the download file. - // |reason| is the interrupt reason to inject. For no interrupt, use - // DOWNLOAD_INTERRUPT_REASON_NONE. - // |security_string| is the extra security information, if interrupted. - void OnResponseCompleted(const DownloadId& id, - content::DownloadInterruptReason reason, - const std::string& security_string) { - // OnResponseCompleted - // GetDownloadFile - // DownloadFile::Finish - // DownloadFile::GetDownloadManager - // DownloadFile::BytesSoFar - // Process one message in the message loop - // - // OK: - // DownloadFile::GetHash - // DownloadManager::OnResponseCompleted - // - // On Error: - // DownloadManager::OnDownloadInterrupted - MockDownloadFile* file = download_file_factory_->GetExistingFile(id); - ASSERT_TRUE(file != NULL); - - EXPECT_CALL(*file, Finish()); - EXPECT_CALL(*file, GetDownloadManager()); - if (reason == content::DOWNLOAD_INTERRUPT_REASON_NONE) { - EXPECT_CALL(*file, GetHash(_)) - .WillOnce(Return(false)); - } else { - EXPECT_CALL(*file, GetHashState()); - } - EXPECT_CALL(*file, BytesSoFar()) - .Times(AtLeast(1)) - .WillRepeatedly(Return(byte_count_[id])); - - download_file_manager_->OnResponseCompleted(id, reason, security_string); - - if (reason == content::DOWNLOAD_INTERRUPT_REASON_NONE) { - EXPECT_CALL(*download_manager_, - OnResponseCompleted(id.local(), byte_count_[id], "")) - .Times(1); - } else { - EXPECT_EQ(0, error_count_[id]); - - EXPECT_CALL(*download_manager_, - OnDownloadInterrupted(id.local(), - byte_count_[id], - "", - reason)) - .Times(1); - } - - ProcessAllPendingMessages(); - - if (reason != content::DOWNLOAD_INTERRUPT_REASON_NONE) - ++error_count_[id]; - - ClearExpectations(id); - } - void CleanUp(DownloadId id) { // Expected calls: // DownloadFileManager::CancelDownload @@ -440,7 +300,6 @@ class DownloadFileManagerTest : public testing::Test { scoped_ptr<MockDownloadRequestHandle> request_handle_; MockDownloadFileFactory* download_file_factory_; scoped_refptr<DownloadFileManager> download_file_manager_; - scoped_refptr<content::DownloadBuffer> download_buffer_; // Per-download statistics. std::map<DownloadId, int64> byte_count_; @@ -470,127 +329,65 @@ const int DownloadFileManagerTest::kDummyChildId = 3; const int DownloadFileManagerTest::kDummyRequestId = 67; TEST_F(DownloadFileManagerTest, CancelAtStart) { - DownloadCreateInfo* info = new DownloadCreateInfo; + scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); - StartDownload(info, dummy_id); + StartDownload(info.Pass(), dummy_id); CleanUp(dummy_id); } TEST_F(DownloadFileManagerTest, CancelBeforeFinished) { // Same as StartDownload, at first. - DownloadCreateInfo* info = new DownloadCreateInfo; - DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); - - StartDownload(info, dummy_id); - - UpdateDownload(dummy_id, kTestData1, strlen(kTestData1), net::OK); - UpdateDownload(dummy_id, kTestData2, strlen(kTestData2), net::OK); - UpdateDownload(dummy_id, kTestData3, strlen(kTestData3), net::OK); - - CleanUp(dummy_id); -} - -TEST_F(DownloadFileManagerTest, DownloadWithError) { - // Same as StartDownload, at first. - DownloadCreateInfo* info = new DownloadCreateInfo; - DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); - - StartDownload(info, dummy_id); - - UpdateDownload(dummy_id, kTestData1, strlen(kTestData1), net::OK); - UpdateDownload(dummy_id, kTestData2, strlen(kTestData2), - net::ERR_FILE_NO_SPACE); -} - -TEST_F(DownloadFileManagerTest, CompleteDownload) { - // Same as StartDownload, at first. - DownloadCreateInfo* info = new DownloadCreateInfo; + scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); - StartDownload(info, dummy_id); - - UpdateDownload(dummy_id, kTestData1, strlen(kTestData1), net::OK); - UpdateDownload(dummy_id, kTestData2, strlen(kTestData2), net::OK); - UpdateDownload(dummy_id, kTestData3, strlen(kTestData3), net::OK); - - OnResponseCompleted(dummy_id, content::DOWNLOAD_INTERRUPT_REASON_NONE, ""); - - CleanUp(dummy_id); -} - -TEST_F(DownloadFileManagerTest, CompleteDownloadWithError) { - // Same as StartDownload, at first. - DownloadCreateInfo* info = new DownloadCreateInfo; - DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); - - StartDownload(info, dummy_id); - - UpdateDownload(dummy_id, kTestData1, strlen(kTestData1), net::OK); - UpdateDownload(dummy_id, kTestData2, strlen(kTestData2), net::OK); - UpdateDownload(dummy_id, kTestData3, strlen(kTestData3), net::OK); - - OnResponseCompleted(dummy_id, - content::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED, - ""); + StartDownload(info.Pass(), dummy_id); CleanUp(dummy_id); } TEST_F(DownloadFileManagerTest, RenameInProgress) { // Same as StartDownload, at first. - DownloadCreateInfo* info = new DownloadCreateInfo; + scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); ScopedTempDir download_dir; ASSERT_TRUE(download_dir.CreateUniqueTempDir()); - StartDownload(info, dummy_id); + StartDownload(info.Pass(), dummy_id); - UpdateDownload(dummy_id, kTestData1, strlen(kTestData1), net::OK); - UpdateDownload(dummy_id, kTestData2, strlen(kTestData2), net::OK); FilePath foo(download_dir.path().Append(FILE_PATH_LITERAL("foo.txt"))); RenameFile(dummy_id, foo, foo, net::OK, IN_PROGRESS, OVERWRITE); - UpdateDownload(dummy_id, kTestData3, strlen(kTestData3), net::OK); - - OnResponseCompleted(dummy_id, content::DOWNLOAD_INTERRUPT_REASON_NONE, ""); CleanUp(dummy_id); } TEST_F(DownloadFileManagerTest, RenameInProgressWithUniquification) { // Same as StartDownload, at first. - DownloadCreateInfo* info = new DownloadCreateInfo; + scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); ScopedTempDir download_dir; ASSERT_TRUE(download_dir.CreateUniqueTempDir()); - StartDownload(info, dummy_id); + StartDownload(info.Pass(), dummy_id); - UpdateDownload(dummy_id, kTestData1, strlen(kTestData1), net::OK); - UpdateDownload(dummy_id, kTestData2, strlen(kTestData2), net::OK); FilePath foo(download_dir.path().Append(FILE_PATH_LITERAL("foo.txt"))); FilePath unique_foo(foo.InsertBeforeExtension(FILE_PATH_LITERAL(" (1)"))); ASSERT_EQ(0, file_util::WriteFile(foo, "", 0)); RenameFile(dummy_id, foo, unique_foo, net::OK, IN_PROGRESS, DONT_OVERWRITE); - UpdateDownload(dummy_id, kTestData3, strlen(kTestData3), net::OK); - - OnResponseCompleted(dummy_id, content::DOWNLOAD_INTERRUPT_REASON_NONE, ""); CleanUp(dummy_id); } TEST_F(DownloadFileManagerTest, RenameInProgressWithError) { // Same as StartDownload, at first. - DownloadCreateInfo* info = new DownloadCreateInfo; + scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); ScopedTempDir download_dir; ASSERT_TRUE(download_dir.CreateUniqueTempDir()); - StartDownload(info, dummy_id); + StartDownload(info.Pass(), dummy_id); - UpdateDownload(dummy_id, kTestData1, strlen(kTestData1), net::OK); - UpdateDownload(dummy_id, kTestData2, strlen(kTestData2), net::OK); FilePath foo(download_dir.path().Append(FILE_PATH_LITERAL("foo.txt"))); RenameFile(dummy_id, foo, foo, net::ERR_FILE_PATH_TOO_LONG, IN_PROGRESS, OVERWRITE); @@ -598,41 +395,14 @@ TEST_F(DownloadFileManagerTest, RenameInProgressWithError) { CleanUp(dummy_id); } -TEST_F(DownloadFileManagerTest, RenameCompleting) { +TEST_F(DownloadFileManagerTest, RenameWithUniquification) { // Same as StartDownload, at first. - DownloadCreateInfo* info = new DownloadCreateInfo; + scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); ScopedTempDir download_dir; ASSERT_TRUE(download_dir.CreateUniqueTempDir()); - StartDownload(info, dummy_id); - - UpdateDownload(dummy_id, kTestData1, strlen(kTestData1), net::OK); - UpdateDownload(dummy_id, kTestData2, strlen(kTestData2), net::OK); - UpdateDownload(dummy_id, kTestData3, strlen(kTestData3), net::OK); - - OnResponseCompleted(dummy_id, content::DOWNLOAD_INTERRUPT_REASON_NONE, ""); - - FilePath foo(download_dir.path().Append(FILE_PATH_LITERAL("foo.txt"))); - RenameFile(dummy_id, foo, foo, net::OK, COMPLETE, OVERWRITE); - - CleanUp(dummy_id); -} - -TEST_F(DownloadFileManagerTest, RenameCompletingWithUniquification) { - // Same as StartDownload, at first. - DownloadCreateInfo* info = new DownloadCreateInfo; - DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); - ScopedTempDir download_dir; - ASSERT_TRUE(download_dir.CreateUniqueTempDir()); - - StartDownload(info, dummy_id); - - UpdateDownload(dummy_id, kTestData1, strlen(kTestData1), net::OK); - UpdateDownload(dummy_id, kTestData2, strlen(kTestData2), net::OK); - UpdateDownload(dummy_id, kTestData3, strlen(kTestData3), net::OK); - - OnResponseCompleted(dummy_id, content::DOWNLOAD_INTERRUPT_REASON_NONE, ""); + StartDownload(info.Pass(), dummy_id); FilePath foo(download_dir.path().Append(FILE_PATH_LITERAL("foo.txt"))); FilePath unique_foo(foo.InsertBeforeExtension(FILE_PATH_LITERAL(" (1)"))); @@ -645,45 +415,18 @@ TEST_F(DownloadFileManagerTest, RenameCompletingWithUniquification) { CleanUp(dummy_id); } -TEST_F(DownloadFileManagerTest, RenameCompletingWithError) { - // Same as StartDownload, at first. - DownloadCreateInfo* info = new DownloadCreateInfo; - DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); - ScopedTempDir download_dir; - ASSERT_TRUE(download_dir.CreateUniqueTempDir()); - - StartDownload(info, dummy_id); - - UpdateDownload(dummy_id, kTestData1, strlen(kTestData1), net::OK); - UpdateDownload(dummy_id, kTestData2, strlen(kTestData2), net::OK); - UpdateDownload(dummy_id, kTestData3, strlen(kTestData3), net::OK); - - OnResponseCompleted(dummy_id, content::DOWNLOAD_INTERRUPT_REASON_NONE, ""); - - FilePath foo(download_dir.path().Append(FILE_PATH_LITERAL("foo.txt"))); - RenameFile(dummy_id, foo, foo, net::ERR_FILE_PATH_TOO_LONG, - COMPLETE, OVERWRITE); - - CleanUp(dummy_id); -} - TEST_F(DownloadFileManagerTest, RenameTwice) { // Same as StartDownload, at first. - DownloadCreateInfo* info = new DownloadCreateInfo; + scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); ScopedTempDir download_dir; ASSERT_TRUE(download_dir.CreateUniqueTempDir()); - StartDownload(info, dummy_id); + StartDownload(info.Pass(), dummy_id); - UpdateDownload(dummy_id, kTestData1, strlen(kTestData1), net::OK); - UpdateDownload(dummy_id, kTestData2, strlen(kTestData2), net::OK); FilePath crfoo(download_dir.path().Append( FILE_PATH_LITERAL("foo.txt.crdownload"))); RenameFile(dummy_id, crfoo, crfoo, net::OK, IN_PROGRESS, OVERWRITE); - UpdateDownload(dummy_id, kTestData3, strlen(kTestData3), net::OK); - - OnResponseCompleted(dummy_id, content::DOWNLOAD_INTERRUPT_REASON_NONE, ""); FilePath foo(download_dir.path().Append(FILE_PATH_LITERAL("foo.txt"))); RenameFile(dummy_id, foo, foo, net::OK, COMPLETE, OVERWRITE); @@ -693,22 +436,17 @@ TEST_F(DownloadFileManagerTest, RenameTwice) { TEST_F(DownloadFileManagerTest, TwoDownloads) { // Same as StartDownload, at first. - DownloadCreateInfo* info = new DownloadCreateInfo; - DownloadCreateInfo* info2 = new DownloadCreateInfo; + scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); + scoped_ptr<DownloadCreateInfo> info2(new DownloadCreateInfo); DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); DownloadId dummy_id2(download_manager_.get(), kDummyDownloadId2); ScopedTempDir download_dir; ASSERT_TRUE(download_dir.CreateUniqueTempDir()); - StartDownload(info, dummy_id); - - UpdateDownload(dummy_id, kTestData1, strlen(kTestData1), net::OK); - - StartDownload(info2, dummy_id2); + StartDownload(info.Pass(), dummy_id); - UpdateDownload(dummy_id, kTestData2, strlen(kTestData2), net::OK); + StartDownload(info2.Pass(), dummy_id2); - UpdateDownload(dummy_id2, kTestData4, strlen(kTestData4), net::OK); FilePath crbar(download_dir.path().Append( FILE_PATH_LITERAL("bar.txt.crdownload"))); RenameFile(dummy_id2, crbar, crbar, net::OK, IN_PROGRESS, OVERWRITE); @@ -717,14 +455,6 @@ TEST_F(DownloadFileManagerTest, TwoDownloads) { FILE_PATH_LITERAL("foo.txt.crdownload"))); RenameFile(dummy_id, crfoo, crfoo, net::OK, IN_PROGRESS, OVERWRITE); - UpdateDownload(dummy_id, kTestData3, strlen(kTestData3), net::OK); - - UpdateDownload(dummy_id2, kTestData5, strlen(kTestData5), net::OK); - UpdateDownload(dummy_id2, kTestData6, strlen(kTestData6), net::OK); - - OnResponseCompleted(dummy_id2, content::DOWNLOAD_INTERRUPT_REASON_NONE, ""); - - OnResponseCompleted(dummy_id, content::DOWNLOAD_INTERRUPT_REASON_NONE, ""); FilePath bar(download_dir.path().Append(FILE_PATH_LITERAL("bar.txt"))); RenameFile(dummy_id2, bar, bar, net::OK, COMPLETE, OVERWRITE); diff --git a/content/browser/download/download_file_unittest.cc b/content/browser/download/download_file_unittest.cc index 87526b8..9d1c661 100644 --- a/content/browser/download/download_file_unittest.cc +++ b/content/browser/download/download_file_unittest.cc @@ -6,14 +6,17 @@ #include "base/message_loop.h" #include "base/string_number_conversions.h" #include "content/browser/browser_thread_impl.h" +#include "content/browser/download/byte_stream.h" #include "content/browser/download/download_create_info.h" #include "content/browser/download/download_file_impl.h" #include "content/browser/download/download_request_handle.h" #include "content/browser/power_save_blocker.h" +#include "content/public/browser/download_interrupt_reasons.h" #include "content/public/browser/download_manager.h" #include "content/public/test/mock_download_manager.h" #include "net/base/file_stream.h" #include "net/base/net_errors.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using content::BrowserThread; @@ -21,9 +24,28 @@ using content::BrowserThreadImpl; using content::DownloadFile; using content::DownloadId; using content::DownloadManager; -using testing::_; -using testing::AnyNumber; -using testing::StrictMock; +using ::testing::_; +using ::testing::AnyNumber; +using ::testing::DoAll; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::StrictMock; + +namespace { + +class MockByteStreamReader : public content::ByteStreamReader { + public: + MockByteStreamReader() {} + ~MockByteStreamReader() {} + + // ByteStream functions + MOCK_METHOD2(Read, content::ByteStreamReader::StreamState( + scoped_refptr<net::IOBuffer>*, size_t*)); + MOCK_CONST_METHOD0(GetStatus, content::DownloadInterruptReason()); + MOCK_METHOD1(RegisterCallback, void(const base::Closure&)); +}; + +} // namespace DownloadId::Domain kValidIdDomain = "valid DownloadId::Domain"; @@ -77,45 +99,133 @@ class DownloadFileTest : public testing::Test { ui_thread_.message_loop()->RunAllPending(); } - virtual void CreateDownloadFile(scoped_ptr<DownloadFile>* file, - int offset, - bool calculate_hash) { + // Mock calls to this function are forwarded here. + void RegisterCallback(base::Closure sink_callback) { + sink_callback_ = sink_callback; + } + + virtual bool CreateDownloadFile(int offset, bool calculate_hash) { + // There can be only one. + DCHECK(!download_file_.get()); + + input_stream_ = new StrictMock<MockByteStreamReader>(); + + // TODO: Need to actually create a function that'll set the variables + // based on the inputs from the callback. + EXPECT_CALL(*input_stream_, RegisterCallback(_)) + .WillOnce(Invoke(this, &DownloadFileTest::RegisterCallback)) + .RetiresOnSaturation(); + DownloadCreateInfo info; info.download_id = DownloadId(kValidIdDomain, kDummyDownloadId + offset); // info.request_handle default constructed to null. info.save_info.file_stream = file_stream_; - file->reset( - new DownloadFileImpl(&info, new DownloadRequestHandle(), - download_manager_, calculate_hash, - scoped_ptr<PowerSaveBlocker>(NULL).Pass(), - net::BoundNetLog())); + download_file_.reset( + new DownloadFileImpl( + &info, + scoped_ptr<content::ByteStreamReader>(input_stream_).Pass(), + new DownloadRequestHandle(), + download_manager_, calculate_hash, + scoped_ptr<PowerSaveBlocker>(NULL).Pass(), + net::BoundNetLog())); + + EXPECT_CALL(*input_stream_, Read(_, _)) + .WillOnce(Return(content::ByteStreamReader::STREAM_EMPTY)) + .RetiresOnSaturation(); + net::Error result = download_file_->Initialize(); + ::testing::Mock::VerifyAndClearExpectations(input_stream_); + return result == net::OK; } - virtual void DestroyDownloadFile(scoped_ptr<DownloadFile>* file, int offset) { - EXPECT_EQ(kDummyDownloadId + offset, (*file)->Id()); - EXPECT_EQ(download_manager_, (*file)->GetDownloadManager()); - EXPECT_FALSE((*file)->InProgress()); + virtual void DestroyDownloadFile(int offset) { + EXPECT_EQ(kDummyDownloadId + offset, download_file_->Id()); + EXPECT_EQ(download_manager_, download_file_->GetDownloadManager()); + EXPECT_FALSE(download_file_->InProgress()); EXPECT_EQ(static_cast<int64>(expected_data_.size()), - (*file)->BytesSoFar()); + download_file_->BytesSoFar()); // Make sure the data has been properly written to disk. std::string disk_data; - EXPECT_TRUE(file_util::ReadFileToString((*file)->FullPath(), + EXPECT_TRUE(file_util::ReadFileToString(download_file_->FullPath(), &disk_data)); EXPECT_EQ(expected_data_, disk_data); // Make sure the Browser and File threads outlive the DownloadFile // to satisfy thread checks inside it. - file->reset(); + download_file_.reset(); } - void AppendDataToFile(scoped_ptr<DownloadFile>* file, - const std::string& data) { - EXPECT_TRUE((*file)->InProgress()); - (*file)->AppendDataToFile(data.data(), data.size()); - expected_data_ += data; - EXPECT_EQ(static_cast<int64>(expected_data_.size()), - (*file)->BytesSoFar()); + // Setup the stream to do be a data append; don't actually trigger + // the callback or do verifications. + void SetupDataAppend(const char **data_chunks, size_t num_chunks, + ::testing::Sequence s) { + DCHECK(input_stream_); + for (size_t i = 0; i < num_chunks; i++) { + const char *source_data = data_chunks[i]; + size_t length = strlen(source_data); + scoped_refptr<net::IOBuffer> data = new net::IOBuffer(length); + memcpy(data->data(), source_data, length); + EXPECT_CALL(*input_stream_, Read(_, _)) + .InSequence(s) + .WillOnce(DoAll(SetArgPointee<0>(data), + SetArgPointee<1>(length), + Return(content::ByteStreamReader::STREAM_HAS_DATA))) + .RetiresOnSaturation(); + expected_data_ += source_data; + } + } + + void VerifyStreamAndSize() { + ::testing::Mock::VerifyAndClearExpectations(input_stream_); + int64 size; + EXPECT_TRUE(file_util::GetFileSize(download_file_->FullPath(), &size)); + EXPECT_EQ(expected_data_.size(), static_cast<size_t>(size)); + } + + // TODO(rdsmith): Manage full percentage issues properly. + void AppendDataToFile(const char **data_chunks, size_t num_chunks) { + ::testing::Sequence s1; + SetupDataAppend(data_chunks, num_chunks, s1); + EXPECT_CALL(*input_stream_, Read(_, _)) + .InSequence(s1) + .WillOnce(Return(content::ByteStreamReader::STREAM_EMPTY)) + .RetiresOnSaturation(); + sink_callback_.Run(); + VerifyStreamAndSize(); + } + + void SetupFinishStream(content::DownloadInterruptReason interrupt_reason, + ::testing::Sequence s) { + EXPECT_CALL(*input_stream_, Read(_, _)) + .InSequence(s) + .WillOnce(Return(content::ByteStreamReader::STREAM_COMPLETE)) + .RetiresOnSaturation(); + EXPECT_CALL(*input_stream_, GetStatus()) + .InSequence(s) + .WillOnce(Return(interrupt_reason)) + .RetiresOnSaturation(); + EXPECT_CALL(*input_stream_, RegisterCallback(_)) + .RetiresOnSaturation(); + } + + void FinishStream(content::DownloadInterruptReason interrupt_reason, + bool check_download_manager) { + ::testing::Sequence s1; + SetupFinishStream(interrupt_reason, s1); + sink_callback_.Run(); + VerifyStreamAndSize(); + if (check_download_manager) { + EXPECT_CALL(*download_manager_, OnResponseCompleted(_, _, _)); + loop_.RunAllPending(); + ::testing::Mock::VerifyAndClearExpectations(download_manager_.get()); + EXPECT_CALL(*(download_manager_.get()), + UpdateDownload( + DownloadId(kValidIdDomain, kDummyDownloadId + 0).local(), + _, _, _)) + .Times(AnyNumber()) + .WillRepeatedly(Invoke(this, + &DownloadFileTest::SetUpdateDownloadInfo)); + } } protected: @@ -126,6 +236,13 @@ class DownloadFileTest : public testing::Test { // DownloadFile instance we are testing. scoped_ptr<DownloadFile> download_file_; + // Stream for sending data into the download file. + // Owned by download_file_; will be alive for lifetime of download_file_. + StrictMock<MockByteStreamReader>* input_stream_; + + // Sink callback data for stream. + base::Closure sink_callback_; + // Latest update sent to the download manager. int64 bytes_; int64 bytes_per_sec_; @@ -157,8 +274,7 @@ const int DownloadFileTest::kDummyRequestId = 67; // Rename the file before any data is downloaded, after some has, after it all // has, and after it's closed. TEST_F(DownloadFileTest, RenameFileFinal) { - CreateDownloadFile(&download_file_, 0, true); - ASSERT_EQ(net::OK, download_file_->Initialize()); + ASSERT_TRUE(CreateDownloadFile(0, true)); FilePath initial_path(download_file_->FullPath()); EXPECT_TRUE(file_util::PathExists(initial_path)); FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1")); @@ -176,8 +292,8 @@ TEST_F(DownloadFileTest, RenameFileFinal) { EXPECT_TRUE(file_util::PathExists(path_1)); // Download the data. - AppendDataToFile(&download_file_, kTestData1); - AppendDataToFile(&download_file_, kTestData2); + const char* chunks1[] = { kTestData1, kTestData2 }; + AppendDataToFile(chunks1, 2); // Rename the file after downloading some data. EXPECT_EQ(net::OK, download_file_->Rename(path_2)); @@ -188,7 +304,8 @@ TEST_F(DownloadFileTest, RenameFileFinal) { EXPECT_FALSE(file_util::PathExists(path_1)); EXPECT_TRUE(file_util::PathExists(path_2)); - AppendDataToFile(&download_file_, kTestData3); + const char* chunks2[] = { kTestData3 }; + AppendDataToFile(chunks2, 1); // Rename the file after downloading all the data. EXPECT_EQ(net::OK, download_file_->Rename(path_3)); @@ -202,8 +319,8 @@ TEST_F(DownloadFileTest, RenameFileFinal) { // Should not be able to get the hash until the file is closed. std::string hash; EXPECT_FALSE(download_file_->GetHash(&hash)); - - download_file_->Finish(); + FinishStream(content::DOWNLOAD_INTERRUPT_REASON_NONE, true); + loop_.RunAllPending(); // Rename the file after downloading all the data and closing the file. EXPECT_EQ(net::OK, download_file_->Rename(path_4)); @@ -218,17 +335,100 @@ TEST_F(DownloadFileTest, RenameFileFinal) { EXPECT_TRUE(download_file_->GetHash(&hash)); EXPECT_EQ(kDataHash, base::HexEncode(hash.data(), hash.size())); - DestroyDownloadFile(&download_file_, 0); + DestroyDownloadFile(0); +} + +// Various tests of the StreamActive method. +TEST_F(DownloadFileTest, StreamEmptySuccess) { + ASSERT_TRUE(CreateDownloadFile(0, true)); + FilePath initial_path(download_file_->FullPath()); + EXPECT_TRUE(file_util::PathExists(initial_path)); + + // Test that calling the sink_callback_ on an empty stream shouldn't + // do anything. + AppendDataToFile(NULL, 0); + ::testing::Mock::VerifyAndClearExpectations(download_manager_.get()); + EXPECT_CALL(*(download_manager_.get()), + UpdateDownload( + DownloadId(kValidIdDomain, kDummyDownloadId + 0).local(), + _, _, _)) + .Times(AnyNumber()) + .WillRepeatedly(Invoke(this, &DownloadFileTest::SetUpdateDownloadInfo)); + + // Finish the download this way and make sure we see it on the + // DownloadManager. + EXPECT_CALL(*(download_manager_.get()), + OnResponseCompleted(DownloadId(kValidIdDomain, + kDummyDownloadId + 0).local(), + 0, _)); + FinishStream(content::DOWNLOAD_INTERRUPT_REASON_NONE, false); + + DestroyDownloadFile(0); +} + +TEST_F(DownloadFileTest, StreamEmptyError) { + ASSERT_TRUE(CreateDownloadFile(0, true)); + FilePath initial_path(download_file_->FullPath()); + EXPECT_TRUE(file_util::PathExists(initial_path)); + + // Finish the download in error and make sure we see it on the + // DownloadManager. + EXPECT_CALL(*(download_manager_.get()), + OnDownloadInterrupted( + DownloadId(kValidIdDomain, kDummyDownloadId + 0).local(), + 0, _, + content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED)); + FinishStream(content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, false); + + DestroyDownloadFile(0); +} + +TEST_F(DownloadFileTest, StreamNonEmptySuccess) { + ASSERT_TRUE(CreateDownloadFile(0, true)); + FilePath initial_path(download_file_->FullPath()); + EXPECT_TRUE(file_util::PathExists(initial_path)); + + const char* chunks1[] = { kTestData1, kTestData2 }; + ::testing::Sequence s1; + SetupDataAppend(chunks1, 2, s1); + SetupFinishStream(content::DOWNLOAD_INTERRUPT_REASON_NONE, s1); + EXPECT_CALL(*(download_manager_.get()), + OnResponseCompleted(DownloadId(kValidIdDomain, + kDummyDownloadId + 0).local(), + strlen(kTestData1) + strlen(kTestData2), + _)); + sink_callback_.Run(); + VerifyStreamAndSize(); + DestroyDownloadFile(0); +} + +TEST_F(DownloadFileTest, StreamNonEmptyError) { + ASSERT_TRUE(CreateDownloadFile(0, true)); + FilePath initial_path(download_file_->FullPath()); + EXPECT_TRUE(file_util::PathExists(initial_path)); + + const char* chunks1[] = { kTestData1, kTestData2 }; + ::testing::Sequence s1; + SetupDataAppend(chunks1, 2, s1); + SetupFinishStream(content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, + s1); + EXPECT_CALL(*(download_manager_.get()), + OnDownloadInterrupted( + DownloadId(kValidIdDomain, kDummyDownloadId + 0).local(), + strlen(kTestData1) + strlen(kTestData2), _, + content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED)); + sink_callback_.Run(); + VerifyStreamAndSize(); + DestroyDownloadFile(0); } // Send some data, wait 3/4s of a second, run the message loop, and // confirm the values the DownloadManager received are correct. TEST_F(DownloadFileTest, ConfirmUpdate) { - CreateDownloadFile(&download_file_, 0, true); - ASSERT_EQ(net::OK, download_file_->Initialize()); + CreateDownloadFile(0, true); - AppendDataToFile(&download_file_, kTestData1); - AppendDataToFile(&download_file_, kTestData2); + const char* chunks1[] = { kTestData1, kTestData2 }; + AppendDataToFile(chunks1, 2); // Run the message loops for 750ms and check for results. loop_.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(), @@ -239,6 +439,6 @@ TEST_F(DownloadFileTest, ConfirmUpdate) { bytes_); EXPECT_EQ(download_file_->GetHashState(), hash_state_); - download_file_->Finish(); - DestroyDownloadFile(&download_file_, 0); + FinishStream(content::DOWNLOAD_INTERRUPT_REASON_NONE, true); + DestroyDownloadFile(0); } diff --git a/content/browser/download/download_item_impl_unittest.cc b/content/browser/download/download_item_impl_unittest.cc index 3f223fc..abe5c57 100644 --- a/content/browser/download/download_item_impl_unittest.cc +++ b/content/browser/download/download_item_impl_unittest.cc @@ -5,6 +5,7 @@ #include "base/message_loop.h" #include "base/stl_util.h" #include "base/threading/thread.h" +#include "content/browser/download/byte_stream.h" #include "content/browser/download/download_create_info.h" #include "content/browser/download/download_file_manager.h" #include "content/browser/download/download_item_impl.h" @@ -58,8 +59,21 @@ class MockRequestHandle : public DownloadRequestHandleInterface { class MockDownloadFileFactory : public DownloadFileManager::DownloadFileFactory { public: - MOCK_METHOD5(CreateFile, + content::DownloadFile* CreateFile( + DownloadCreateInfo* info, + scoped_ptr<content::ByteStreamReader> stream_reader, + const DownloadRequestHandle& request_handle, + DownloadManager* mgr, + bool calculate_hash, + const net::BoundNetLog& bound_net_log) { + return MockCreateFile( + info, stream_reader.get(), request_handle, mgr, calculate_hash, + bound_net_log); + } + + MOCK_METHOD6(MockCreateFile, content::DownloadFile*(DownloadCreateInfo*, + content::ByteStreamReader*, const DownloadRequestHandle&, DownloadManager*, bool, @@ -70,12 +84,15 @@ class MockDownloadFileManager : public DownloadFileManager { public: MockDownloadFileManager(); MOCK_METHOD0(Shutdown, void()); - MOCK_METHOD2(StartDownload, - void(DownloadCreateInfo*, const DownloadRequestHandle&)); - MOCK_METHOD2(UpdateDownload, void(DownloadId, content::DownloadBuffer*)); - MOCK_METHOD3(OnResponseCompleted, - void(DownloadId, content::DownloadInterruptReason, - const std::string&)); + MOCK_METHOD3(MockStartDownload, + void(DownloadCreateInfo*, content::ByteStreamReader*, + const DownloadRequestHandle&)); + virtual void StartDownload(scoped_ptr<DownloadCreateInfo> info, + scoped_ptr<content::ByteStreamReader> stream, + const DownloadRequestHandle& request_handle) { + MockStartDownload(info.release(), stream.release(), request_handle); + } + MOCK_METHOD1(CancelDownload, void(DownloadId)); MOCK_METHOD1(CompleteDownload, void(DownloadId)); MOCK_METHOD1(OnDownloadManagerShutdown, void(DownloadManager*)); diff --git a/content/browser/download/download_manager_impl_unittest.cc b/content/browser/download/download_manager_impl_unittest.cc index cc38b29..1be540f 100644 --- a/content/browser/download/download_manager_impl_unittest.cc +++ b/content/browser/download/download_manager_impl_unittest.cc @@ -16,7 +16,6 @@ #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "build/build_config.h" -#include "content/browser/download/download_buffer.h" #include "content/browser/download/download_create_info.h" #include "content/browser/download/download_file_impl.h" #include "content/browser/download/download_file_manager.h" @@ -66,6 +65,10 @@ using ::testing::NiceMock; using ::testing::ReturnRef; using ::testing::Return; +namespace content { +class ByteStreamReader; +} + namespace { FilePath GetTempDownloadPath(const FilePath& suggested_path) { @@ -79,6 +82,7 @@ class MockDownloadFileFactory virtual DownloadFile* CreateFile( DownloadCreateInfo* info, + scoped_ptr<content::ByteStreamReader> stream, const DownloadRequestHandle& request_handle, DownloadManager* download_manager, bool calculate_hash, @@ -87,6 +91,7 @@ class MockDownloadFileFactory DownloadFile* MockDownloadFileFactory::CreateFile( DownloadCreateInfo* info, + scoped_ptr<content::ByteStreamReader> stream, const DownloadRequestHandle& request_handle, DownloadManager* download_manager, bool calculate_hash, @@ -261,8 +266,7 @@ class DownloadManagerTest : public testing::Test { download_manager_(new DownloadManagerImpl( download_manager_delegate_.get(), NULL)), ui_thread_(BrowserThread::UI, &message_loop_), - file_thread_(BrowserThread::FILE, &message_loop_), - download_buffer_(new content::DownloadBuffer) { + file_thread_(BrowserThread::FILE, &message_loop_) { download_manager_->Init(browser_context.get()); download_manager_delegate_->set_download_manager(download_manager_); } @@ -313,23 +317,6 @@ class DownloadManagerTest : public testing::Test { download_manager_->OnTargetPathAvailable(download); } - void UpdateData(int32 id, const char* data, size_t length) { - // We are passing ownership of this buffer to the download file manager. - net::IOBuffer* io_buffer = new net::IOBuffer(length); - // We need |AddRef()| because we do a |Release()| in |UpdateDownload()|. - io_buffer->AddRef(); - memcpy(io_buffer->data(), data, length); - - download_buffer_->AddData(io_buffer, length); - - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, - base::Bind(&DownloadFileManager::UpdateDownload, file_manager_.get(), - DownloadId(kValidIdDomain, id), download_buffer_)); - - message_loop_.RunAllPending(); - } - void OnDownloadInterrupted(int32 download_id, int64 size, const std::string& hash_state, content::DownloadInterruptReason reason) { @@ -356,7 +343,6 @@ class DownloadManagerTest : public testing::Test { MessageLoopForUI message_loop_; content::TestBrowserThread ui_thread_; content::TestBrowserThread file_thread_; - scoped_refptr<content::DownloadBuffer> download_buffer_; ScopedTempDir scoped_download_dir_; DownloadFileManager* file_manager() { @@ -378,6 +364,7 @@ const size_t DownloadManagerTest::kTestDataLen = class DownloadFileWithErrors : public DownloadFileImpl { public: DownloadFileWithErrors(DownloadCreateInfo* info, + scoped_ptr<content::ByteStreamReader> stream, DownloadManager* manager, bool calculate_hash); virtual ~DownloadFileWithErrors() {} @@ -406,10 +393,13 @@ class DownloadFileWithErrors : public DownloadFileImpl { net::Error forced_error_; }; -DownloadFileWithErrors::DownloadFileWithErrors(DownloadCreateInfo* info, - DownloadManager* manager, - bool calculate_hash) +DownloadFileWithErrors::DownloadFileWithErrors( + DownloadCreateInfo* info, + scoped_ptr<content::ByteStreamReader> stream, + DownloadManager* manager, + bool calculate_hash) : DownloadFileImpl(info, + stream.Pass(), new DownloadRequestHandle(), manager, calculate_hash, @@ -575,8 +565,16 @@ TEST_F(DownloadManagerTest, MAYBE_StartDownload) { info->mime_type = kStartDownloadCases[i].mime_type; download_manager_->CreateDownloadItem(info.get(), DownloadRequestHandle()); + scoped_ptr<content::ByteStreamWriter> stream_writer; + scoped_ptr<content::ByteStreamReader> stream_reader; + content::CreateByteStream(message_loop_.message_loop_proxy(), + message_loop_.message_loop_proxy(), + kTestDataLen, &stream_writer, &stream_reader); + DownloadFile* download_file( - new DownloadFileImpl(info.get(), new DownloadRequestHandle(), + new DownloadFileImpl(info.get(), + stream_reader.Pass(), + new DownloadRequestHandle(), download_manager_, false, scoped_ptr<PowerSaveBlocker>(NULL).Pass(), net::BoundNetLog())); @@ -882,6 +880,13 @@ TEST_F(DownloadManagerTest, DownloadFilenameTest) { } } +void WriteCopyToStream(const char *source, + size_t len, content::ByteStreamWriter* stream) { + scoped_refptr<net::IOBuffer> copy(new net::IOBuffer(len)); + memcpy(copy.get()->data(), source, len); + stream->Write(copy, len); +} + TEST_F(DownloadManagerTest, DownloadInterruptTest) { using ::testing::_; using ::testing::CreateFunctor; @@ -900,16 +905,12 @@ TEST_F(DownloadManagerTest, DownloadInterruptTest) { const FilePath cr_path(GetTempDownloadPath(new_path)); MockDownloadFile* download_file(new NiceMock<MockDownloadFile>()); - ON_CALL(*download_file, AppendDataToFile(_, _)) - .WillByDefault(Return(net::OK)); // |download_file| is owned by DownloadFileManager. AddMockDownloadToFileManager(info->download_id.local(), download_file); EXPECT_CALL(*download_file, Rename(cr_path)) .WillOnce(Return(net::OK)); - EXPECT_CALL(*download_file, AppendDataToFile(kTestData, kTestDataLen)) - .WillOnce(Return(net::OK)); EXPECT_CALL(*download_file, Cancel()); download_manager_->CreateDownloadItem(info.get(), DownloadRequestHandle()); @@ -919,7 +920,6 @@ TEST_F(DownloadManagerTest, DownloadInterruptTest) { EXPECT_EQ(DownloadItem::IN_PROGRESS, download->GetState()); scoped_ptr<ItemObserver> observer(new ItemObserver(download)); - download_file->AppendDataToFile(kTestData, kTestDataLen); ContinueDownloadWithPath(download, new_path); message_loop_.RunAllPending(); EXPECT_TRUE(GetActiveDownloadItem(0) != NULL); @@ -968,6 +968,12 @@ TEST_F(DownloadManagerTest, MAYBE_DownloadFileErrorTest) { path, base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_WRITE)); + // Make sure the DFM exists; we'll need it later. + // TODO(rdsmith): This is ugly and should be rewritten, but a large + // rewrite of this test is in progress in a different CL, so doing it + // the hacky way for now. + (void) file_manager(); + // Normally, the download system takes ownership of info, and is // responsible for deleting it. In these unit tests, however, we // don't call the function that deletes it, so we do so ourselves. @@ -979,14 +985,17 @@ TEST_F(DownloadManagerTest, MAYBE_DownloadFileErrorTest) { info->total_bytes = static_cast<int64>(kTestDataLen * 3); info->save_info.file_path = path; info->save_info.file_stream.reset(stream); + scoped_ptr<content::ByteStreamWriter> stream_input; + scoped_ptr<content::ByteStreamReader> stream_output; + content::CreateByteStream(message_loop_.message_loop_proxy(), + message_loop_.message_loop_proxy(), + kTestDataLen, &stream_input, &stream_output); // Create a download file that we can insert errors into. - DownloadFileWithErrors* download_file(new DownloadFileWithErrors( - info.get(), download_manager_, false)); + scoped_ptr<DownloadFileWithErrors> download_file(new DownloadFileWithErrors( + info.get(), stream_output.Pass(), download_manager_, false)); download_file->Initialize(); - AddDownloadToFileManager(local_id, download_file); - // |download_file| is owned by DownloadFileManager. download_manager_->CreateDownloadItem(info.get(), DownloadRequestHandle()); DownloadItem* download = GetActiveDownloadItem(0); @@ -996,7 +1005,7 @@ TEST_F(DownloadManagerTest, MAYBE_DownloadFileErrorTest) { scoped_ptr<ItemObserver> observer(new ItemObserver(download)); // Add some data before finalizing the file name. - UpdateData(local_id, kTestData, kTestDataLen); + WriteCopyToStream(kTestData, kTestDataLen, stream_input.get()); // Finalize the file name. ContinueDownloadWithPath(download, path); @@ -1004,11 +1013,12 @@ TEST_F(DownloadManagerTest, MAYBE_DownloadFileErrorTest) { EXPECT_TRUE(GetActiveDownloadItem(0) != NULL); // Add more data. - UpdateData(local_id, kTestData, kTestDataLen); + WriteCopyToStream(kTestData, kTestDataLen, stream_input.get()); // Add more data, but an error occurs. download_file->set_forced_error(net::ERR_FAILED); - UpdateData(local_id, kTestData, kTestDataLen); + WriteCopyToStream(kTestData, kTestDataLen, stream_input.get()); + message_loop_.RunAllPending(); // Check the state. The download should have been interrupted. EXPECT_TRUE(GetActiveDownloadItem(0) == NULL); @@ -1045,15 +1055,11 @@ TEST_F(DownloadManagerTest, DownloadCancelTest) { const FilePath cr_path(GetTempDownloadPath(new_path)); MockDownloadFile* download_file(new NiceMock<MockDownloadFile>()); - ON_CALL(*download_file, AppendDataToFile(_, _)) - .WillByDefault(Return(net::OK)); AddMockDownloadToFileManager(info->download_id.local(), download_file); // |download_file| is owned by DownloadFileManager. EXPECT_CALL(*download_file, Rename(cr_path)) .WillOnce(Return(net::OK)); - EXPECT_CALL(*download_file, AppendDataToFile(kTestData, kTestDataLen)) - .WillOnce(Return(net::OK)); EXPECT_CALL(*download_file, Cancel()); download_manager_->CreateDownloadItem(info.get(), DownloadRequestHandle()); @@ -1068,8 +1074,6 @@ TEST_F(DownloadManagerTest, DownloadCancelTest) { message_loop_.RunAllPending(); EXPECT_TRUE(GetActiveDownloadItem(0) != NULL); - download_file->AppendDataToFile(kTestData, kTestDataLen); - download->Cancel(false); message_loop_.RunAllPending(); @@ -1124,6 +1128,11 @@ TEST_F(DownloadManagerTest, MAYBE_DownloadOverwriteTest) { info->download_id = DownloadId(kValidIdDomain, 0); info->prompt_user_for_save_location = true; info->url_chain.push_back(GURL()); + scoped_ptr<content::ByteStreamWriter> stream_input; + scoped_ptr<content::ByteStreamReader> stream_output; + content::CreateByteStream(message_loop_.message_loop_proxy(), + message_loop_.message_loop_proxy(), + kTestDataLen, &stream_input, &stream_output); download_manager_->CreateDownloadItem(info.get(), DownloadRequestHandle()); @@ -1138,7 +1147,8 @@ TEST_F(DownloadManagerTest, MAYBE_DownloadOverwriteTest) { // name has been chosen, so we need to initialize the download file // properly. DownloadFile* download_file( - new DownloadFileImpl(info.get(), new DownloadRequestHandle(), + new DownloadFileImpl(info.get(), stream_output.Pass(), + new DownloadRequestHandle(), download_manager_, false, scoped_ptr<PowerSaveBlocker>(NULL).Pass(), net::BoundNetLog())); @@ -1152,7 +1162,7 @@ TEST_F(DownloadManagerTest, MAYBE_DownloadOverwriteTest) { message_loop_.RunAllPending(); EXPECT_TRUE(GetActiveDownloadItem(0) != NULL); - download_file->AppendDataToFile(kTestData, kTestDataLen); + WriteCopyToStream(kTestData, kTestDataLen, stream_input.get()); // Finish the download. OnResponseCompleted(0, kTestDataLen, ""); @@ -1198,6 +1208,11 @@ TEST_F(DownloadManagerTest, MAYBE_DownloadRemoveTest) { info->download_id = DownloadId(kValidIdDomain, 0); info->prompt_user_for_save_location = true; info->url_chain.push_back(GURL()); + scoped_ptr<content::ByteStreamWriter> stream_input; + scoped_ptr<content::ByteStreamReader> stream_output; + content::CreateByteStream(message_loop_.message_loop_proxy(), + message_loop_.message_loop_proxy(), + kTestDataLen, &stream_input, &stream_output); download_manager_->CreateDownloadItem(info.get(), DownloadRequestHandle()); @@ -1212,7 +1227,8 @@ TEST_F(DownloadManagerTest, MAYBE_DownloadRemoveTest) { // name has been chosen, so we need to initialize the download file // properly. DownloadFile* download_file( - new DownloadFileImpl(info.get(), new DownloadRequestHandle(), + new DownloadFileImpl(info.get(), stream_output.Pass(), + new DownloadRequestHandle(), download_manager_, false, scoped_ptr<PowerSaveBlocker>(NULL).Pass(), net::BoundNetLog())); @@ -1226,7 +1242,7 @@ TEST_F(DownloadManagerTest, MAYBE_DownloadRemoveTest) { message_loop_.RunAllPending(); EXPECT_TRUE(GetActiveDownloadItem(0) != NULL); - download_file->AppendDataToFile(kTestData, kTestDataLen); + WriteCopyToStream(kTestData, kTestDataLen, stream_input.get()); // Finish the download. OnResponseCompleted(0, kTestDataLen, ""); diff --git a/content/browser/download/download_net_log_parameters.cc b/content/browser/download/download_net_log_parameters.cc index 244a38f..60a5b82 100644 --- a/content/browser/download/download_net_log_parameters.cc +++ b/content/browser/download/download_net_log_parameters.cc @@ -218,6 +218,22 @@ Value* FileOpenedParameters::ToValue() const { FileOpenedParameters::~FileOpenedParameters() {} +FileStreamDrainedParameters::FileStreamDrainedParameters( + size_t stream_size, size_t num_buffers) + : stream_size_(stream_size), num_buffers_(num_buffers) { +} + +Value* FileStreamDrainedParameters::ToValue() const { + DictionaryValue* dict = new DictionaryValue(); + + dict->SetInteger("stream_size", static_cast<int>(stream_size_)); + dict->SetInteger("num_buffers", static_cast<int>(num_buffers_)); + + return dict; +} + +FileStreamDrainedParameters::~FileStreamDrainedParameters() { } + FileRenamedParameters::FileRenamedParameters( const std::string& old_filename, const std::string& new_filename) : old_filename_(old_filename), new_filename_(new_filename) { diff --git a/content/browser/download/download_net_log_parameters.h b/content/browser/download/download_net_log_parameters.h index 5e0cf30..db97083 100644 --- a/content/browser/download/download_net_log_parameters.h +++ b/content/browser/download/download_net_log_parameters.h @@ -181,6 +181,23 @@ class FileOpenedParameters : public net::NetLog::EventParameters { DISALLOW_COPY_AND_ASSIGN(FileOpenedParameters); }; +// NetLog parameters when a DownloadFile is opened. +class FileStreamDrainedParameters : public net::NetLog::EventParameters { + public: + FileStreamDrainedParameters(size_t stream_size, + size_t num_buffers); + virtual base::Value* ToValue() const OVERRIDE; + + protected: + virtual ~FileStreamDrainedParameters(); + + private: + const size_t stream_size_; + const size_t num_buffers_; + + DISALLOW_COPY_AND_ASSIGN(FileStreamDrainedParameters); +}; + // NetLog parameters when a DownloadFile is renamed. class FileRenamedParameters : public net::NetLog::EventParameters { public: diff --git a/content/browser/download/download_resource_handler.cc b/content/browser/download/download_resource_handler.cc index 37f5b16..c255bb8 100644 --- a/content/browser/download/download_resource_handler.cc +++ b/content/browser/download/download_resource_handler.cc @@ -8,15 +8,16 @@ #include "base/bind.h" #include "base/logging.h" +#include "base/message_loop_proxy.h" #include "base/metrics/histogram.h" #include "base/metrics/stats_counters.h" #include "base/stringprintf.h" -#include "content/browser/download/download_buffer.h" #include "content/browser/download/download_create_info.h" #include "content/browser/download/download_file_manager.h" #include "content/browser/download/download_interrupt_reasons_impl.h" #include "content/browser/download/download_manager_impl.h" #include "content/browser/download/download_request_handle.h" +#include "content/browser/download/byte_stream.h" #include "content/browser/download/download_stats.h" #include "content/browser/renderer_host/resource_dispatcher_host_impl.h" #include "content/browser/renderer_host/resource_request_info_impl.h" @@ -39,6 +40,8 @@ using content::ResourceRequestInfoImpl; namespace { +static const int kDownloadByteStreamSize = 100 * 1024; + void CallStartedCBOnUIThread( const DownloadResourceHandler::OnStartedCallback& started_cb, DownloadId id, @@ -50,35 +53,31 @@ void CallStartedCBOnUIThread( started_cb.Run(id, error); } -void StartDownloadOnUIThread( - const scoped_refptr<DownloadFileManager>& download_file_manager, +// Static function in order to prevent any accidental accesses to +// DownloadResourceHandler members from the UI thread. +static void StartOnUIThread( scoped_ptr<DownloadCreateInfo> info, + scoped_ptr<content::ByteStreamReader> stream, + scoped_refptr<DownloadFileManager> download_file_manager, const DownloadRequestHandle& handle, - const base::Callback<void(DownloadId)>& set_download_id_callback) { - DownloadId download_id; + const DownloadResourceHandler::OnStartedCallback& started_cb) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DownloadManager* download_manager = handle.GetDownloadManager(); - if (download_manager) { - // The download manager may be NULL in unittests or if the page closed - // right after starting the download. - download_id = download_manager->delegate()->GetNextId(); - info->download_id = download_id; - - // NOTE: StartDownload triggers creation of the download destination file - // that will hold the downloaded data. The set_download_id_callback - // unblocks the DownloadResourceHandler to begin forwarding network data to - // the download destination file. The sequence of these two steps is - // critical as creation of the downloaded destination file has to happen - // before we attempt to append data to it. Both of those operations happen - // on the FILE thread. - - download_file_manager->StartDownload(info.release(), handle); + if (!download_manager) { + // NULL in unittests or if the page closed right after starting the + // download. + if (!started_cb.is_null()) + started_cb.Run(DownloadId(), net::ERR_ACCESS_DENIED); + return; } + DownloadId download_id = download_manager->delegate()->GetNextId(); + info->download_id = download_id; - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind(set_download_id_callback, download_id)); + download_file_manager->StartDownload(info.Pass(), stream.Pass(), handle); + + if (!started_cb.is_null()) + started_cb.Run(download_id, net::OK); } } // namespace @@ -88,7 +87,7 @@ DownloadResourceHandler::DownloadResourceHandler( int render_view_id, int request_id, const GURL& url, - DownloadFileManager* download_file_manager, + scoped_refptr<DownloadFileManager> download_file_manager, net::URLRequest* request, const DownloadResourceHandler::OnStartedCallback& started_cb, const content::DownloadSaveInfo& save_info) @@ -99,7 +98,6 @@ DownloadResourceHandler::DownloadResourceHandler( request_(request), started_cb_(started_cb), save_info_(save_info), - buffer_(new content::DownloadBuffer), last_buffer_size_(0), bytes_read_(0), pause_count_(0), @@ -128,6 +126,7 @@ bool DownloadResourceHandler::OnResponseStarted( int request_id, content::ResourceResponse* response, bool* defer) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); // There can be only one (call) DCHECK(!on_response_started_called_); on_response_started_called_ = true; @@ -153,6 +152,16 @@ bool DownloadResourceHandler::OnResponseStarted( base::Time::Now(), 0, content_length_, DownloadItem::IN_PROGRESS, request_->net_log(), request_info->has_user_gesture(), request_info->transition_type())); + + // Create the ByteStream for sending data to the download sink. + scoped_ptr<content::ByteStreamReader> stream_reader; + CreateByteStream( + base::MessageLoopProxy::current(), + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), + kDownloadByteStreamSize, &stream_writer_, &stream_reader); + stream_writer_->RegisterCallback( + base::Bind(&DownloadResourceHandler::ResumeRequest, AsWeakPtr())); + info->url_chain = request_->url_chain(); info->referrer_url = GURL(request_->referrer()); info->start_time = base::Time::Now(); @@ -198,19 +207,24 @@ bool DownloadResourceHandler::OnResponseStarted( info->save_info = save_info_; BrowserThread::PostTask( - BrowserThread::UI, - FROM_HERE, - base::Bind( - &StartDownloadOnUIThread, - download_file_manager_, - base::Passed(&info), - request_handle, - base::Bind(&DownloadResourceHandler::SetDownloadID, AsWeakPtr()))); + BrowserThread::UI, FROM_HERE, + base::Bind(&StartOnUIThread, + base::Passed(info.Pass()), + base::Passed(stream_reader.Pass()), + download_file_manager_, + request_handle, + // Pass to StartOnUIThread so that variable + // access is always on IO thread but function + // is called on UI thread. + started_cb_)); + // Guaranteed to be called in StartOnUIThread + started_cb_.Reset(); return true; } void DownloadResourceHandler::CallStartedCB(DownloadId id, net::Error error) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (started_cb_.is_null()) return; BrowserThread::PostTask( @@ -229,6 +243,7 @@ bool DownloadResourceHandler::OnWillStart(int request_id, // writing and deletion. bool DownloadResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size, int min_size) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(buf && buf_size); if (!read_buffer_) { *buf_size = min_size < 0 ? kReadBufSize : min_size; @@ -242,6 +257,7 @@ bool DownloadResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf, // Pass the buffer to the download file writer. bool DownloadResourceHandler::OnReadCompleted(int request_id, int* bytes_read, bool* defer) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (!read_buffer_) { // Ignore spurious OnReadCompleted! Deferring from OnReadCompleted tells // the ResourceDispatcherHost that we did not consume the data. @@ -255,14 +271,6 @@ bool DownloadResourceHandler::OnReadCompleted(int request_id, int* bytes_read, return true; } - if (!download_id_.IsValid()) { - // We can't start saving the data before we create the file on disk and - // have a download id. The request will be un-paused in - // DownloadFileManager::CreateDownloadFile. - *defer = was_deferred_ = true; - return true; - } - base::TimeTicks now(base::TimeTicks::Now()); if (!last_read_time_.is_null()) { double seconds_since_last_read = (now - last_read_time_).InSecondsF(); @@ -281,27 +289,17 @@ bool DownloadResourceHandler::OnReadCompleted(int request_id, int* bytes_read, return true; bytes_read_ += *bytes_read; DCHECK(read_buffer_); - // Swap the data. - net::IOBuffer* io_buffer = NULL; - read_buffer_.swap(&io_buffer); - size_t vector_size = buffer_->AddData(io_buffer, *bytes_read); - bool need_update = (vector_size == 1); // Buffer was empty. - - // We are passing ownership of this buffer to the download file manager. - if (need_update) { - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, - base::Bind(&DownloadFileManager::UpdateDownload, - download_file_manager_, download_id_, buffer_)); - } - // We schedule a pause outside of the read loop if there is too much file - // writing work to do. - if (vector_size > kLoadsToWrite) { + // Take the data ship it down the stream. If the stream is full, pause the + // request; the stream callback will resume it. + if (!stream_writer_->Write(read_buffer_, *bytes_read)) { + PauseRequest(); *defer = was_deferred_ = true; - CheckWriteProgressLater(); + last_stream_pause_time_ = now; } + read_buffer_ = NULL; // Drop our reference. + return true; } @@ -309,37 +307,13 @@ bool DownloadResourceHandler::OnResponseCompleted( int request_id, const net::URLRequestStatus& status, const std::string& security_info) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); VLOG(20) << __FUNCTION__ << "()" << DebugString() << " request_id = " << request_id << " status.status() = " << status.status() << " status.error() = " << status.error(); - int response = status.is_success() ? request_->GetResponseCode() : 0; - if (download_id_.IsValid()) { - OnResponseCompletedInternal(request_id, status, security_info, response); - } else { - // We got cancelled before the task which sets the id ran on the IO thread. - // Wait for it. - BrowserThread::PostTaskAndReply( - BrowserThread::UI, FROM_HERE, - base::Bind(&base::DoNothing), - base::Bind(&DownloadResourceHandler::OnResponseCompletedInternal, - AsWeakPtr(), request_id, status, security_info, response)); - } - // Can't trust request_ being value after this point. - request_ = NULL; - return true; -} + int response_code = status.is_success() ? request_->GetResponseCode() : 0; -void DownloadResourceHandler::OnResponseCompletedInternal( - int request_id, - const net::URLRequestStatus& status, - const std::string& security_info, - int response_code) { - // NOTE: |request_| may be a dangling pointer at this point. - VLOG(20) << __FUNCTION__ << "()" - << " request_id = " << request_id - << " status.status() = " << status.status() - << " status.error() = " << status.error(); net::Error error_code = net::OK; if (status.status() == net::URLRequestStatus::FAILED) error_code = static_cast<net::Error>(status.error()); // Normal case. @@ -383,35 +357,29 @@ void DownloadResourceHandler::OnResponseCompletedInternal( download_stats::RecordAcceptsRanges(accept_ranges_, bytes_read_); - // If the callback was already run on the UI thread, this will be a noop. - CallStartedCB(download_id_, error_code); + CallStartedCB(DownloadId(), error_code); - // We transfer ownership to |DownloadFileManager| to delete |buffer_|, - // so that any functions queued up on the FILE thread are executed - // before deletion. - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, - base::Bind(&DownloadFileManager::OnResponseCompleted, - download_file_manager_, download_id_, reason, security_info)); - buffer_ = NULL; // The buffer is longer needed by |DownloadResourceHandler|. - read_buffer_ = NULL; -} + // Send the info down the stream. Conditional is in case we get + // OnResponseCompleted without OnResponseStarted. + if (stream_writer_.get()) + stream_writer_->Close(reason); -void DownloadResourceHandler::SetDownloadID(DownloadId id) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + stream_writer_.reset(); // We no longer need the stream. + read_buffer_ = NULL; - download_id_ = id; - MaybeResumeRequest(); + // Stats + download_stats::RecordNetworkBandwidth( + bytes_read_, base::TimeTicks::Now() - download_start_time_, + total_pause_time_); - CallStartedCB( - download_id_, - download_id_.IsValid() ? net::OK : net::ERR_ACCESS_DENIED); + return true; } // If the content-length header is not present (or contains something other // than numbers), the incoming content_length is -1 (unknown size). // Set the content length to 0 to indicate unknown size to DownloadManager. void DownloadResourceHandler::SetContentLength(const int64& content_length) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); content_length_ = 0; if (content_length > 0) content_length_ = content_length; @@ -419,22 +387,10 @@ void DownloadResourceHandler::SetContentLength(const int64& content_length) { void DownloadResourceHandler::SetContentDisposition( const std::string& content_disposition) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); content_disposition_ = content_disposition; } -void DownloadResourceHandler::CheckWriteProgress() { - if (!buffer_.get()) - return; // The download completed while we were waiting to run. - - if (buffer_->size() > kLoadsToWrite) { - // We'll come back later and see if it's okay to unpause the request. - CheckWriteProgressLater(); - return; - } - - MaybeResumeRequest(); -} - void DownloadResourceHandler::PauseRequest() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); @@ -446,7 +402,20 @@ void DownloadResourceHandler::ResumeRequest() { DCHECK_LT(0, pause_count_); --pause_count_; - MaybeResumeRequest(); + + if (!was_deferred_) + return; + if (pause_count_ > 0) + return; + + was_deferred_ = false; + if (!last_stream_pause_time_.is_null()) { + total_pause_time_ += (base::TimeTicks::Now() - last_stream_pause_time_); + last_stream_pause_time_ = base::TimeTicks(); + } + ResourceDispatcherHostImpl::Get()->ResumeDeferredRequest( + global_id_.child_id, + global_id_.request_id); } void DownloadResourceHandler::CancelRequest() { @@ -461,7 +430,6 @@ void DownloadResourceHandler::CancelRequest() { std::string DownloadResourceHandler::DebugString() const { return base::StringPrintf("{" " url_ = " "\"%s\"" - " download_id_ = " "%d" " global_id_ = {" " child_id = " "%d" " request_id = " "%d" @@ -472,7 +440,6 @@ std::string DownloadResourceHandler::DebugString() const { request_ ? request_->url().spec().c_str() : "<NULL request>", - download_id_.local(), global_id_.child_id, global_id_.request_id, render_view_id_, @@ -480,38 +447,18 @@ std::string DownloadResourceHandler::DebugString() const { } DownloadResourceHandler::~DownloadResourceHandler() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + // This won't do anything if the callback was called before. // If it goes through, it will likely be because OnWillStart() returned // false somewhere in the chain of resource handlers. - CallStartedCB(download_id_, net::ERR_ACCESS_DENIED); + CallStartedCB(DownloadId(), net::ERR_ACCESS_DENIED); + + // Remove output stream callback if a stream exists. + if (stream_writer_.get()) + stream_writer_->RegisterCallback(base::Closure()); UMA_HISTOGRAM_TIMES("SB2.DownloadDuration", base::TimeTicks::Now() - download_start_time_); } -void DownloadResourceHandler::CheckWriteProgressLater() { - if (!check_write_progress_timer_.IsRunning()) { - check_write_progress_timer_.Start( - FROM_HERE, - base::TimeDelta::FromMilliseconds(kThrottleTimeMs), - this, - &DownloadResourceHandler::CheckWriteProgress); - } -} - -void DownloadResourceHandler::MaybeResumeRequest() { - if (!was_deferred_) - return; - - if (pause_count_ > 0) - return; - if (!download_id_.IsValid()) - return; - if (buffer_.get() && (buffer_->size() > kLoadsToWrite)) - return; - - was_deferred_ = false; - ResourceDispatcherHostImpl::Get()->ResumeDeferredRequest( - global_id_.child_id, - global_id_.request_id); -} diff --git a/content/browser/download/download_resource_handler.h b/content/browser/download/download_resource_handler.h index e82c5838..7f99316 100644 --- a/content/browser/download/download_resource_handler.h +++ b/content/browser/download/download_resource_handler.h @@ -24,7 +24,8 @@ class DownloadRequestHandle; struct DownloadCreateInfo; namespace content { -class DownloadBuffer; +class ByteStreamWriter; +class ByteStreamReader; } namespace net { @@ -38,17 +39,16 @@ class DownloadResourceHandler public: typedef content::DownloadUrlParameters::OnStartedCallback OnStartedCallback; - static const size_t kLoadsToWrite = 100; // number of data buffers queued - // started_cb will be called exactly once on the UI thread. - DownloadResourceHandler(int render_process_host_id, - int render_view_id, - int request_id, - const GURL& url, - DownloadFileManager* download_file_manager, - net::URLRequest* request, - const OnStartedCallback& started_cb, - const content::DownloadSaveInfo& save_info); + DownloadResourceHandler( + int render_process_host_id, + int render_view_id, + int request_id, + const GURL& url, + scoped_refptr<DownloadFileManager> download_file_manager, + net::URLRequest* request, + const OnStartedCallback& started_cb, + const content::DownloadSaveInfo& save_info); virtual bool OnUploadProgress(int request_id, uint64 position, @@ -93,18 +93,11 @@ class DownloadResourceHandler private: virtual ~DownloadResourceHandler(); - void OnResponseCompletedInternal(int request_id, - const net::URLRequestStatus& status, - const std::string& security_info, - int response_code); - - void CheckWriteProgressLater(); - void CheckWriteProgress(); - void MaybeResumeRequest(); + // Arrange for started_cb_ to be called on the UI thread with the + // below values, nulling out started_cb_. Should only be called + // on the IO thread. void CallStartedCB(content::DownloadId id, net::Error error); - void SetDownloadID(content::DownloadId id); - // If the content-length header is not present (or contains something other // than numbers), the incoming content_length is -1 (unknown size). // Set the content length to 0 to indicate unknown size to DownloadManager. @@ -112,23 +105,26 @@ class DownloadResourceHandler void SetContentDisposition(const std::string& content_disposition); - content::DownloadId download_id_; content::GlobalRequestID global_id_; int render_view_id_; - scoped_refptr<net::IOBuffer> read_buffer_; std::string content_disposition_; int64 content_length_; scoped_refptr<DownloadFileManager> download_file_manager_; net::URLRequest* request_; - // This is used only on the UI thread. + // This is read only on the IO thread, but may only + // be called on the UI thread. OnStartedCallback started_cb_; content::DownloadSaveInfo save_info_; - scoped_refptr<content::DownloadBuffer> buffer_; - base::OneShotTimer<DownloadResourceHandler> check_write_progress_timer_; + + // Data flow + scoped_refptr<net::IOBuffer> read_buffer_; // From URLRequest. + scoped_ptr<content::ByteStreamWriter> stream_writer_; // To rest of system. // The following are used to collect stats. base::TimeTicks download_start_time_; base::TimeTicks last_read_time_; + base::TimeTicks last_stream_pause_time_; + base::TimeDelta total_pause_time_; size_t last_buffer_size_; int64 bytes_read_; std::string accept_ranges_; diff --git a/content/browser/download/download_stats.cc b/content/browser/download/download_stats.cc index 40ed912..6332993 100644 --- a/content/browser/download/download_stats.cc +++ b/content/browser/download/download_stats.cc @@ -255,8 +255,7 @@ void RecordDownloadMimeType(const std::string& mime_type_string) { void RecordFileThreadReceiveBuffers(size_t num_buffers) { UMA_HISTOGRAM_CUSTOM_COUNTS( "Download.FileThreadReceiveBuffers", num_buffers, 1, - DownloadResourceHandler::kLoadsToWrite, - DownloadResourceHandler::kLoadsToWrite); + 100, 100); } void RecordBandwidth(double actual_bandwidth, double potential_bandwidth) { @@ -303,6 +302,45 @@ void RecordOpensOutstanding(int size) { 64/*num_buckets*/); } +void RecordContiguousWriteTime(base::TimeDelta time_blocked) { + UMA_HISTOGRAM_TIMES("Download.FileThreadBlockedTime", time_blocked); +} + +void RecordNetworkBandwidth(size_t length, + base::TimeDelta elapsed_time, + base::TimeDelta paused_time) { + size_t non_pause_time_ms = (elapsed_time - paused_time).InMilliseconds(); + if (0u == non_pause_time_ms) + non_pause_time_ms = 1; + + // Note that this will be somewhat higher than sustainable network + // bandwidth because of buffering in the kernel during pauses. + // Using Bytes/s rather than bits/s so that this value is easily + // comparable with BandwidthOverall and BandwidthDisk. + UMA_HISTOGRAM_CUSTOM_COUNTS( + "Download.BandwidthNetworkBytesPerSecond", + (1000 * length / non_pause_time_ms), + 1, 100000000, 50); +} + +void RecordFileBandwidth(size_t length, + base::TimeDelta disk_write_time, + base::TimeDelta elapsed_time) { + size_t elapsed_time_ms = elapsed_time.InMilliseconds(); + if (0u == elapsed_time_ms) + elapsed_time_ms = 1; + size_t disk_write_time_ms = disk_write_time.InMilliseconds(); + if (0u == disk_write_time_ms) + disk_write_time_ms = 1; + + UMA_HISTOGRAM_CUSTOM_COUNTS( + "Download.BandwidthOverallBytesPerSecond", + (1000 * length / elapsed_time_ms), 1, 50000000, 50); + UMA_HISTOGRAM_CUSTOM_COUNTS( + "Download.BandwidthDiskBytesPerSecond", + (1000 * length / disk_write_time_ms), 1, 50000000, 50); +} + void RecordSavePackageEvent(SavePackageEvent event) { UMA_HISTOGRAM_ENUMERATION("Download.SavePackage", event, diff --git a/content/browser/download/download_stats.h b/content/browser/download/download_stats.h index 94bc2e0..7633e51 100644 --- a/content/browser/download/download_stats.h +++ b/content/browser/download/download_stats.h @@ -16,6 +16,7 @@ namespace base { class Time; +class TimeDelta; class TimeTicks; } @@ -137,6 +138,19 @@ void RecordClearAllSize(int size); // Record the number of completed unopened downloads when a download is opened. void RecordOpensOutstanding(int size); +// Record how long we block the file thread at a time. +void RecordContiguousWriteTime(base::TimeDelta time_blocked); + +// Record overall bandwidth stats at the network end. +void RecordNetworkBandwidth(size_t length, + base::TimeDelta elapsed_time, + base::TimeDelta paused_time); + +// Record overall bandwidth stats at the file end. +void RecordFileBandwidth(size_t length, + base::TimeDelta disk_write_time, + base::TimeDelta elapsed_time); + enum SavePackageEvent { // The user has started to save a page as a package. SAVE_PACKAGE_STARTED, diff --git a/content/browser/renderer_host/resource_dispatcher_host_impl.cc b/content/browser/renderer_host/resource_dispatcher_host_impl.cc index 532ce2e..a44e443 100644 --- a/content/browser/renderer_host/resource_dispatcher_host_impl.cc +++ b/content/browser/renderer_host/resource_dispatcher_host_impl.cc @@ -570,7 +570,7 @@ ResourceDispatcherHostImpl::CreateResourceHandlerForDownload( const DownloadResourceHandler::OnStartedCallback& started_cb) { scoped_ptr<ResourceHandler> handler( new DownloadResourceHandler(child_id, route_id, request_id, - request->url(), download_file_manager_.get(), + request->url(), download_file_manager_, request, started_cb, save_info)); if (delegate_) { ScopedVector<ResourceThrottle> throttles; diff --git a/content/browser/renderer_host/resource_dispatcher_host_unittest.cc b/content/browser/renderer_host/resource_dispatcher_host_unittest.cc index 01fc598..f3d71e5 100644 --- a/content/browser/renderer_host/resource_dispatcher_host_unittest.cc +++ b/content/browser/renderer_host/resource_dispatcher_host_unittest.cc @@ -265,6 +265,31 @@ class URLRequestTestDelayedStartJob : public net::URLRequestTestJob { URLRequestTestDelayedStartJob* URLRequestTestDelayedStartJob::list_head_ = NULL; +// This class is a variation on URLRequestTestJob in that it +// returns IO_pending errors before every read, not just the first one. +class URLRequestTestDelayedCompletionJob : public net::URLRequestTestJob { + public: + explicit URLRequestTestDelayedCompletionJob(net::URLRequest* request) + : net::URLRequestTestJob(request) {} + URLRequestTestDelayedCompletionJob(net::URLRequest* request, + bool auto_advance) + : net::URLRequestTestJob(request, auto_advance) {} + URLRequestTestDelayedCompletionJob(net::URLRequest* request, + const std::string& response_headers, + const std::string& response_data, + bool auto_advance) + : net::URLRequestTestJob(request, response_headers, + response_data, auto_advance) {} + + protected: + ~URLRequestTestDelayedCompletionJob() {} + + private: + virtual bool NextReadAsync() OVERRIDE { return true; } +}; + + + // Associated with an URLRequest to determine if the URLRequest gets deleted. class TestUserData : public base::SupportsUserData::Data { public: @@ -378,6 +403,7 @@ class ResourceDispatcherHostTest : public testing::Test, &ResourceDispatcherHostTest::Factory); EnsureTestSchemeIsAllowed(); delay_start_ = false; + delay_complete_ = false; } virtual void TearDown() { @@ -455,6 +481,8 @@ class ResourceDispatcherHostTest : public testing::Test, if (test_fixture_->response_headers_.empty()) { if (delay_start_) { return new URLRequestTestDelayedStartJob(request); + } else if (delay_complete_) { + return new URLRequestTestDelayedCompletionJob(request); } else { return new net::URLRequestTestJob(request); } @@ -463,6 +491,10 @@ class ResourceDispatcherHostTest : public testing::Test, return new URLRequestTestDelayedStartJob( request, test_fixture_->response_headers_, test_fixture_->response_data_, false); + } else if (delay_complete_) { + return new URLRequestTestDelayedCompletionJob( + request, test_fixture_->response_headers_, + test_fixture_->response_data_, false); } else { return new net::URLRequestTestJob(request, test_fixture_->response_headers_, @@ -476,6 +508,10 @@ class ResourceDispatcherHostTest : public testing::Test, delay_start_ = delay_job_start; } + void SetDelayedCompleteJobGeneration(bool delay_job_complete) { + delay_complete_ = delay_job_complete; + } + MessageLoopForIO message_loop_; BrowserThreadImpl ui_thread_; BrowserThreadImpl file_thread_; @@ -492,10 +528,12 @@ class ResourceDispatcherHostTest : public testing::Test, ResourceType::Type resource_type_; static ResourceDispatcherHostTest* test_fixture_; static bool delay_start_; + static bool delay_complete_; }; // Static. ResourceDispatcherHostTest* ResourceDispatcherHostTest::test_fixture_ = NULL; bool ResourceDispatcherHostTest::delay_start_ = false; +bool ResourceDispatcherHostTest::delay_complete_ = false; void ResourceDispatcherHostTest::MakeTestRequest(int render_view_id, int request_id, @@ -1245,8 +1283,16 @@ TEST_F(ResourceDispatcherHostTest, IgnoreCancelForDownloads) { response.size())); std::string response_data("01234567890123456789\x01foobar"); + // Get past sniffing metrics in the BufferedResourceHandler. Note that + // if we don't get past the sniffing metrics, the result will be that + // the BufferedResourceHandler won't have figured out that it's a download, + // won't have constructed a DownloadResourceHandler, and and the request + // will be successfully canceled below, failing the test. + response_data.resize(1025, ' '); + SetResponse(raw_headers, response_data); SetResourceType(ResourceType::MAIN_FRAME); + SetDelayedCompleteJobGeneration(true); HandleScheme("http"); MakeTestRequest(render_view_id, request_id, GURL("http://example.com/blah")); @@ -1279,9 +1325,12 @@ TEST_F(ResourceDispatcherHostTest, CancelRequestsForContext) { std::string raw_headers(net::HttpUtil::AssembleRawHeaders(response.data(), response.size())); std::string response_data("01234567890123456789\x01foobar"); + // Get past sniffing metrics. + response_data.resize(1025, ' '); SetResponse(raw_headers, response_data); SetResourceType(ResourceType::MAIN_FRAME); + SetDelayedCompleteJobGeneration(true); HandleScheme("http"); MakeTestRequest(render_view_id, request_id, GURL("http://example.com/blah")); diff --git a/content/browser/renderer_host/x509_user_cert_resource_handler.cc b/content/browser/renderer_host/x509_user_cert_resource_handler.cc index 828f813d..f66fdb6 100644 --- a/content/browser/renderer_host/x509_user_cert_resource_handler.cc +++ b/content/browser/renderer_host/x509_user_cert_resource_handler.cc @@ -115,9 +115,26 @@ bool X509UserCertResourceHandler::OnResponseCompleted( } void X509UserCertResourceHandler::AssembleResource() { - size_t assembled_bytes = 0; - resource_buffer_ = AssembleData(buffer_, &assembled_bytes); - DCHECK_EQ(content_length_, assembled_bytes); + // 0-length IOBuffers are not allowed. + if (content_length_ == 0) { + resource_buffer_ = NULL; + return; + } + + // Create the new buffer. + resource_buffer_ = new net::IOBuffer(content_length_); + + // Copy the data into it. + size_t bytes_copied = 0; + for (size_t i = 0; i < buffer_.size(); ++i) { + net::IOBuffer* data = buffer_[i].first; + size_t data_len = buffer_[i].second; + DCHECK(data != NULL); + DCHECK_LE(bytes_copied + data_len, content_length_); + memcpy(resource_buffer_->data() + bytes_copied, data->data(), data_len); + bytes_copied += data_len; + } + DCHECK_EQ(content_length_, bytes_copied); } } // namespace content diff --git a/content/browser/renderer_host/x509_user_cert_resource_handler.h b/content/browser/renderer_host/x509_user_cert_resource_handler.h index b89ce4f..240e0f8 100644 --- a/content/browser/renderer_host/x509_user_cert_resource_handler.h +++ b/content/browser/renderer_host/x509_user_cert_resource_handler.h @@ -7,10 +7,12 @@ #pragma once #include <string> +#include <utility> +#include <vector> #include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" #include "base/memory/ref_counted.h" -#include "content/browser/download/download_buffer.h" #include "content/browser/renderer_host/resource_handler.h" #include "googleurl/src/gurl.h" @@ -70,6 +72,9 @@ class X509UserCertResourceHandler : public ResourceHandler { const std::string& sec_info) OVERRIDE; private: + typedef std::vector<std::pair<scoped_refptr<net::IOBuffer>, + size_t> > ContentVector; + void AssembleResource(); GURL url_; diff --git a/content/content_browser.gypi b/content/content_browser.gypi index cfbcba1..010110a 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -265,8 +265,6 @@ 'browser/download/base_file.h', 'browser/download/byte_stream.cc', 'browser/download/byte_stream.h', - 'browser/download/download_buffer.cc', - 'browser/download/download_buffer.h', 'browser/download/download_create_info.cc', 'browser/download/download_create_info.h', 'browser/download/download_file.h', diff --git a/content/content_tests.gypi b/content/content_tests.gypi index c1d4154..b7a742c 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -220,7 +220,6 @@ 'browser/device_orientation/provider_unittest.cc', 'browser/download/base_file_unittest.cc', 'browser/download/byte_stream_unittest.cc', - 'browser/download/download_buffer_unittest.cc', 'browser/download/download_file_manager_unittest.cc', 'browser/download/download_file_unittest.cc', 'browser/download/download_id_unittest.cc', diff --git a/content/test/test_file_error_injector.cc b/content/test/test_file_error_injector.cc index 09d96e0..b7585df 100644 --- a/content/test/test_file_error_injector.cc +++ b/content/test/test_file_error_injector.cc @@ -17,6 +17,10 @@ #include "content/public/browser/download_id.h" #include "googleurl/src/gurl.h" +namespace content { +class ByteStreamReader; +} + namespace { DownloadFileManager* GetDownloadFileManager() { @@ -35,6 +39,7 @@ class DownloadFileWithErrors: public DownloadFileImpl { DownloadFileWithErrors( const DownloadCreateInfo* info, + scoped_ptr<content::ByteStreamReader> stream, DownloadRequestHandleInterface* request_handle, content::DownloadManager* download_manager, bool calculate_hash, @@ -73,6 +78,7 @@ class DownloadFileWithErrors: public DownloadFileImpl { DownloadFileWithErrors::DownloadFileWithErrors( const DownloadCreateInfo* info, + scoped_ptr<content::ByteStreamReader> stream, DownloadRequestHandleInterface* request_handle, content::DownloadManager* download_manager, bool calculate_hash, @@ -81,6 +87,7 @@ DownloadFileWithErrors::DownloadFileWithErrors( const ConstructionCallback& ctor_callback, const DestructionCallback& dtor_callback) : DownloadFileImpl(info, + stream.Pass(), request_handle, download_manager, calculate_hash, @@ -157,6 +164,7 @@ class DownloadFileWithErrorsFactory // DownloadFileFactory interface. virtual content::DownloadFile* CreateFile( DownloadCreateInfo* info, + scoped_ptr<content::ByteStreamReader> stream, const DownloadRequestHandle& request_handle, content::DownloadManager* download_manager, bool calculate_hash, @@ -188,6 +196,7 @@ DownloadFileWithErrorsFactory::~DownloadFileWithErrorsFactory() { content::DownloadFile* DownloadFileWithErrorsFactory::CreateFile( DownloadCreateInfo* info, + scoped_ptr<content::ByteStreamReader> stream, const DownloadRequestHandle& request_handle, content::DownloadManager* download_manager, bool calculate_hash, @@ -206,6 +215,7 @@ content::DownloadFile* DownloadFileWithErrorsFactory::CreateFile( } return new DownloadFileWithErrors(info, + stream.Pass(), new DownloadRequestHandle(request_handle), download_manager, calculate_hash, |