diff options
author | ahendrickson@chromium.org <ahendrickson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-22 19:22:41 +0000 |
---|---|---|
committer | ahendrickson@chromium.org <ahendrickson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-22 19:22:41 +0000 |
commit | 443853c674eeabd922bfcda2caf0411f47c9a93c (patch) | |
tree | 425513cd9998b67aa183aa6f7c9a700761de533f | |
parent | b6dbdce9b4c536b4d0208ab2a0bf39bd219824bf (diff) | |
download | chromium_src-443853c674eeabd922bfcda2caf0411f47c9a93c.zip chromium_src-443853c674eeabd922bfcda2caf0411f47c9a93c.tar.gz chromium_src-443853c674eeabd922bfcda2caf0411f47c9a93c.tar.bz2 |
In order to resume a download some more information needs to be gathered.
This CL adds member data to classes to support this. It is the first of several CLs to implement download resumption.
In addition hash information is propagated from DownloadFile to DownloadItem, because a new DownloadFile will be created when the download is resumed. It will also need to be persisted in order to resume across chrome runs.
The plan is that data will be collected in DownloadResourceHandler::OnResponseStarted() and passed down to DownloadItem.
On resume, it will pass the information from the resuming DownloadItem to ResourceDispatcherHost::BeginDownload(), which will set any required headers.
BUG= 7648
TEST=None
Review URL: http://codereview.chromium.org/8404049
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@115573 0039d316-1c4b-4281-b951-d872f2087c98
34 files changed, 528 insertions, 186 deletions
diff --git a/chrome/browser/download/download_item_unittest.cc b/chrome/browser/download/download_item_unittest.cc index c4d7a31..bb8ee214 100644 --- a/chrome/browser/download/download_item_unittest.cc +++ b/chrome/browser/download/download_item_unittest.cc @@ -159,7 +159,7 @@ TEST_F(DownloadItemTest, NotificationAfterUpdate) { DownloadItem* item = CreateDownloadItem(DownloadItem::IN_PROGRESS); MockObserver observer(item); - item->UpdateProgress(kDownloadChunkSize, kDownloadSpeed); + item->UpdateProgress(kDownloadChunkSize, kDownloadSpeed, ""); ASSERT_TRUE(observer.CheckUpdated()); EXPECT_EQ(kDownloadSpeed, item->CurrentSpeed()); } @@ -202,7 +202,7 @@ TEST_F(DownloadItemTest, NotificationAfterInterrupted) { DownloadItem* item = CreateDownloadItem(DownloadItem::IN_PROGRESS); MockObserver observer(item); - item->Interrupted(kDownloadChunkSize, DOWNLOAD_INTERRUPT_REASON_NONE); + item->Interrupted(kDownloadChunkSize, "", DOWNLOAD_INTERRUPT_REASON_NONE); ASSERT_TRUE(observer.CheckUpdated()); } diff --git a/chrome/browser/download/download_manager_unittest.cc b/chrome/browser/download/download_manager_unittest.cc index ca98843..965924f 100644 --- a/chrome/browser/download/download_manager_unittest.cc +++ b/chrome/browser/download/download_manager_unittest.cc @@ -70,13 +70,15 @@ class MockDownloadFileFactory virtual DownloadFile* CreateFile(DownloadCreateInfo* info, const DownloadRequestHandle& request_handle, - DownloadManager* download_manager) OVERRIDE; + DownloadManager* download_manager, + bool calculate_hash) OVERRIDE; }; DownloadFile* MockDownloadFileFactory::CreateFile( DownloadCreateInfo* info, const DownloadRequestHandle& request_handle, - DownloadManager* download_manager) { + DownloadManager* download_manager, + bool calculate_hash) { NOTREACHED(); return NULL; } @@ -221,8 +223,10 @@ class DownloadManagerTest : public testing::Test { } void OnDownloadInterrupted(int32 download_id, int64 size, + const std::string& hash_state, InterruptReason reason) { - download_manager_->OnDownloadInterrupted(download_id, size, reason); + download_manager_->OnDownloadInterrupted(download_id, size, + hash_state, reason); } // Get the download item with ID |id|. @@ -261,11 +265,13 @@ const size_t DownloadManagerTest::kTestDataLen = // A DownloadFile that we can inject errors into. class DownloadFileWithErrors : public DownloadFileImpl { public: - DownloadFileWithErrors(DownloadCreateInfo* info, DownloadManager* manager); + DownloadFileWithErrors(DownloadCreateInfo* info, + DownloadManager* manager, + bool calculate_hash); virtual ~DownloadFileWithErrors() {} // BaseFile delegated functions. - virtual net::Error Initialize(bool calculate_hash); + virtual net::Error Initialize(); virtual net::Error AppendDataToFile(const char* data, size_t data_len); virtual net::Error Rename(const FilePath& full_path); @@ -288,13 +294,17 @@ class DownloadFileWithErrors : public DownloadFileImpl { }; DownloadFileWithErrors::DownloadFileWithErrors(DownloadCreateInfo* info, - DownloadManager* manager) - : DownloadFileImpl(info, new DownloadRequestHandle(), manager), + DownloadManager* manager, + bool calculate_hash) + : DownloadFileImpl(info, + new DownloadRequestHandle(), + manager, + calculate_hash), forced_error_(net::OK) { } -net::Error DownloadFileWithErrors::Initialize(bool calculate_hash) { - return ReturnError(DownloadFileImpl::Initialize(calculate_hash)); +net::Error DownloadFileWithErrors::Initialize() { + return ReturnError(DownloadFileImpl::Initialize()); } net::Error DownloadFileWithErrors::AppendDataToFile(const char* data, @@ -482,9 +492,9 @@ TEST_F(DownloadManagerTest, MAYBE_StartDownload) { DownloadFile* download_file( new DownloadFileImpl(info.get(), new DownloadRequestHandle(), - download_manager_)); + download_manager_, false)); AddDownloadToFileManager(info->download_id.local(), download_file); - download_file->Initialize(false); + download_file->Initialize(); download_manager_->StartDownload(info->download_id.local()); message_loop_.RunAllPending(); @@ -920,7 +930,7 @@ TEST_F(DownloadManagerTest, DownloadInterruptTest) { EXPECT_TRUE(GetActiveDownloadItem(0) != NULL); int64 error_size = 3; - OnDownloadInterrupted(0, error_size, + OnDownloadInterrupted(0, error_size, "", DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED); message_loop_.RunAllPending(); @@ -986,7 +996,8 @@ TEST_F(DownloadManagerTest, DownloadFileErrorTest) { // Create a download file that we can insert errors into. DownloadFileWithErrors* download_file(new DownloadFileWithErrors( - info.get(), download_manager_)); + info.get(), download_manager_, false)); + download_file->Initialize(); AddDownloadToFileManager(local_id, download_file); // |download_file| is owned by DownloadFileManager. @@ -1160,10 +1171,10 @@ TEST_F(DownloadManagerTest, MAYBE_DownloadOverwriteTest) { // properly. DownloadFile* download_file( new DownloadFileImpl(info.get(), new DownloadRequestHandle(), - download_manager_)); + download_manager_, false)); download_file->Rename(cr_path); // This creates the .crdownload version of the file. - download_file->Initialize(false); + download_file->Initialize(); // |download_file| is owned by DownloadFileManager. AddDownloadToFileManager(info->download_id.local(), download_file); @@ -1237,10 +1248,10 @@ TEST_F(DownloadManagerTest, MAYBE_DownloadRemoveTest) { // properly. DownloadFile* download_file( new DownloadFileImpl(info.get(), new DownloadRequestHandle(), - download_manager_)); + download_manager_, false)); download_file->Rename(cr_path); // This creates the .crdownload version of the file. - download_file->Initialize(false); + download_file->Initialize(); // |download_file| is owned by DownloadFileManager. AddDownloadToFileManager(info->download_id.local(), download_file); diff --git a/content/browser/download/base_file.cc b/content/browser/download/base_file.cc index 41db5ab..ba2c531 100644 --- a/content/browser/download/base_file.cc +++ b/content/browser/download/base_file.cc @@ -7,6 +7,7 @@ #include "base/file_util.h" #include "base/format_macros.h" #include "base/logging.h" +#include "base/pickle.h" #include "base/stringprintf.h" #include "base/threading/thread_restrictions.h" #include "base/utf_string_conversions.h" @@ -193,6 +194,8 @@ BaseFile::BaseFile(const FilePath& full_path, const GURL& source_url, const GURL& referrer_url, int64 received_bytes, + bool calculate_hash, + const std::string& hash_state, const linked_ptr<net::FileStream>& file_stream) : full_path_(full_path), source_url_(source_url), @@ -201,12 +204,21 @@ BaseFile::BaseFile(const FilePath& full_path, bytes_so_far_(received_bytes), start_tick_(base::TimeTicks::Now()), power_save_blocker_(PowerSaveBlocker::kPowerSaveBlockPreventSystemSleep), - calculate_hash_(false), + calculate_hash_(calculate_hash), detached_(false) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); memcpy(sha256_hash_, kEmptySha256Hash, kSha256HashLen); if (file_stream_.get()) file_stream_->EnableErrorStatistics(); + + if (calculate_hash_) { + secure_hash_.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256)); + if ((bytes_so_far_ > 0) && // Not starting at the beginning. + (hash_state != "") && // Reasonably sure we have a hash state. + (!IsEmptyHash(hash_state))) { + SetHashState(hash_state); + } + } } BaseFile::~BaseFile() { @@ -217,15 +229,10 @@ BaseFile::~BaseFile() { Cancel(); // Will delete the file. } -net::Error BaseFile::Initialize(bool calculate_hash) { +net::Error BaseFile::Initialize() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); DCHECK(!detached_); - calculate_hash_ = calculate_hash; - - if (calculate_hash_) - secure_hash_.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256)); - if (full_path_.empty()) { FilePath temp_file; FilePath download_dir = @@ -390,14 +397,36 @@ void BaseFile::Finish() { Close(); } -bool BaseFile::GetSha256Hash(std::string* hash) { +bool BaseFile::GetHash(std::string* hash) { DCHECK(!detached_); hash->assign(reinterpret_cast<const char*>(sha256_hash_), sizeof(sha256_hash_)); return (calculate_hash_ && !in_progress()); } -bool BaseFile::IsEmptySha256Hash(const std::string& hash) { +std::string BaseFile::GetHashState() { + if (!calculate_hash_) + return ""; + + Pickle hash_state; + if (!secure_hash_->Serialize(&hash_state)) + return ""; + + return std::string(reinterpret_cast<const char*>(hash_state.data()), + hash_state.size()); +} + +bool BaseFile::SetHashState(const std::string& hash_state_bytes) { + if (!calculate_hash_) + return false; + + Pickle hash_state(hash_state_bytes.c_str(), hash_state_bytes.size()); + void* data_iterator = NULL; + + return secure_hash_->Deserialize(&data_iterator, &hash_state); +} + +bool BaseFile::IsEmptyHash(const std::string& hash) { return (hash.size() == kSha256HashLen && 0 == memcmp(hash.data(), kEmptySha256Hash, sizeof(kSha256HashLen))); } diff --git a/content/browser/download/base_file.h b/content/browser/download/base_file.h index 08a0a98..702bdbc 100644 --- a/content/browser/download/base_file.h +++ b/content/browser/download/base_file.h @@ -34,12 +34,13 @@ class CONTENT_EXPORT BaseFile { const GURL& source_url, const GURL& referrer_url, int64 received_bytes, + bool calculate_hash, + const std::string& hash_state, const linked_ptr<net::FileStream>& file_stream); virtual ~BaseFile(); - // If calculate_hash is true, sha256 hash will be calculated. // Returns net::OK on success, or a network error code on failure. - net::Error Initialize(bool calculate_hash); + net::Error Initialize(); // Write a new chunk of data to the file. // Returns net::OK on success (all bytes written to the file), @@ -69,14 +70,17 @@ class CONTENT_EXPORT BaseFile { bool in_progress() const { return file_stream_ != NULL; } int64 bytes_so_far() const { return bytes_so_far_; } - // Set |hash| with sha256 digest for the file. + // Fills |hash| with the hash digest for the file. // Returns true if digest is successfully calculated. - virtual bool GetSha256Hash(std::string* hash); + virtual bool GetHash(std::string* hash); + + // Returns the current (intermediate) state of the hash as a byte string. + virtual std::string GetHashState(); // Returns true if the given hash is considered empty. An empty hash is // a string of size kSha256HashLen that contains only zeros (initial value // for the hash). - static bool IsEmptySha256Hash(const std::string& hash); + static bool IsEmptyHash(const std::string& hash); virtual std::string DebugString() const; @@ -91,11 +95,14 @@ class CONTENT_EXPORT BaseFile { private: friend class BaseFileTest; - FRIEND_TEST_ALL_PREFIXES(BaseFileTest, IsEmptySha256Hash); + FRIEND_TEST_ALL_PREFIXES(BaseFileTest, IsEmptyHash); // Split out from CurrentSpeed to enable testing. int64 CurrentSpeedAtTime(base::TimeTicks current_time) const; + // Resets the current state of the hash to the contents of |hash_state_bytes|. + virtual bool SetHashState(const std::string& hash_state_bytes); + static const size_t kSha256HashLen = 32; static const unsigned char kEmptySha256Hash[kSha256HashLen]; @@ -117,10 +124,10 @@ class CONTENT_EXPORT BaseFile { // RAII handle to keep the system from sleeping while we're downloading. PowerSaveBlocker power_save_blocker_; - // Indicates if sha256 hash should be calculated for the file. + // Indicates if hash should be calculated for the file. bool calculate_hash_; - // Used to calculate sha256 hash for the file when calculate_hash_ + // Used to calculate hash for the file when calculate_hash_ // is set. scoped_ptr<crypto::SecureHash> secure_hash_; diff --git a/content/browser/download/base_file_unittest.cc b/content/browser/download/base_file_unittest.cc index 030a635..911af194 100644 --- a/content/browser/download/base_file_unittest.cc +++ b/content/browser/download/base_file_unittest.cc @@ -11,6 +11,7 @@ #include "base/string_number_conversions.h" #include "base/test/test_file_util.h" #include "content/browser/browser_thread_impl.h" +#include "crypto/secure_hash.h" #include "net/base/file_stream.h" #include "net/base/mock_file_stream.h" #include "net/base/net_errors.h" @@ -37,6 +38,9 @@ const base::TimeDelta kElapsedTimeDelta = base::TimeDelta::FromSeconds( class BaseFileTest : public testing::Test { public: + static const size_t kSha256HashLen = 32; + static const unsigned char kEmptySha256Hash[kSha256HashLen]; + BaseFileTest() : expect_file_survives_(false), expect_in_progress_(true), @@ -45,9 +49,10 @@ class BaseFileTest : public testing::Test { } virtual void SetUp() { + ResetHash(); ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); base_file_.reset( - new BaseFile(FilePath(), GURL(), GURL(), 0, file_stream_)); + new BaseFile(FilePath(), GURL(), GURL(), 0, false, "", file_stream_)); } virtual void TearDown() { @@ -73,6 +78,28 @@ class BaseFileTest : public testing::Test { EXPECT_EQ(expect_file_survives_, file_util::PathExists(full_path)); } + void ResetHash() { + secure_hash_.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256)); + memcpy(sha256_hash_, kEmptySha256Hash, kSha256HashLen); + } + + void UpdateHash(const char* data, size_t length) { + secure_hash_->Update(data, length); + } + + std::string GetFinalHash() { + std::string hash; + secure_hash_->Finish(sha256_hash_, kSha256HashLen); + hash.assign(reinterpret_cast<const char*>(sha256_hash_), + sizeof(sha256_hash_)); + return hash; + } + + void MakeFileWithHash() { + base_file_.reset( + new BaseFile(FilePath(), GURL(), GURL(), 0, true, "", file_stream_)); + } + bool OpenMockFileStream() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); @@ -121,9 +148,9 @@ class BaseFileTest : public testing::Test { static FilePath CreateTestFile() { FilePath file_name; linked_ptr<net::FileStream> dummy_file_stream; - BaseFile file(FilePath(), GURL(), GURL(), 0, dummy_file_stream); + BaseFile file(FilePath(), GURL(), GURL(), 0, false, "", dummy_file_stream); - EXPECT_EQ(net::OK, file.Initialize(false)); + EXPECT_EQ(net::OK, file.Initialize()); file_name = file.full_path(); EXPECT_NE(FilePath::StringType(), file_name.value()); @@ -139,8 +166,9 @@ class BaseFileTest : public testing::Test { static void CreateFileWithName(const FilePath& file_name) { EXPECT_NE(FilePath::StringType(), file_name.value()); linked_ptr<net::FileStream> dummy_file_stream; - BaseFile duplicate_file(file_name, GURL(), GURL(), 0, dummy_file_stream); - EXPECT_EQ(net::OK, duplicate_file.Initialize(false)); + BaseFile duplicate_file( + file_name, GURL(), GURL(), 0, false, "", dummy_file_stream); + EXPECT_EQ(net::OK, duplicate_file.Initialize()); // Write something into it. duplicate_file.AppendDataToFile(kTestData4, kTestDataLength4); // Detach the file so it isn't deleted on destruction of |duplicate_file|. @@ -173,6 +201,11 @@ class BaseFileTest : public testing::Test { // Expect the file to be in progress. bool expect_in_progress_; + // Hash calculator. + scoped_ptr<crypto::SecureHash> secure_hash_; + + unsigned char sha256_hash_[kSha256HashLen]; + private: // Keep track of what data should be saved to the disk file. std::string expected_data_; @@ -183,6 +216,9 @@ class BaseFileTest : public testing::Test { BrowserThreadImpl file_thread_; }; +// This will initialize the entire array to zero. +const unsigned char BaseFileTest::kEmptySha256Hash[] = { 0 }; + // Test the most basic scenario: just create the object and do a sanity check // on all its accessors. This is actually a case that rarely happens // in production, where we would at least Initialize it. @@ -192,7 +228,7 @@ TEST_F(BaseFileTest, CreateDestroy) { // Cancel the download explicitly. TEST_F(BaseFileTest, Cancel) { - ASSERT_EQ(net::OK, base_file_->Initialize(false)); + ASSERT_EQ(net::OK, base_file_->Initialize()); EXPECT_TRUE(file_util::PathExists(base_file_->full_path())); base_file_->Cancel(); EXPECT_FALSE(file_util::PathExists(base_file_->full_path())); @@ -202,7 +238,7 @@ TEST_F(BaseFileTest, Cancel) { // Write data to the file and detach it, so it doesn't get deleted // automatically when base_file_ is destructed. TEST_F(BaseFileTest, WriteAndDetach) { - ASSERT_EQ(net::OK, base_file_->Initialize(false)); + ASSERT_EQ(net::OK, base_file_->Initialize()); ASSERT_EQ(net::OK, AppendDataToFile(kTestData1)); base_file_->Finish(); base_file_->Detach(); @@ -211,14 +247,23 @@ TEST_F(BaseFileTest, WriteAndDetach) { // Write data to the file and detach it, and calculate its sha256 hash. TEST_F(BaseFileTest, WriteWithHashAndDetach) { - ASSERT_EQ(net::OK, base_file_->Initialize(true)); + // Calculate the final hash. + ResetHash(); + UpdateHash(kTestData1, kTestDataLength1); + std::string expected_hash = GetFinalHash(); + std::string expected_hash_hex = + base::HexEncode(expected_hash.data(), expected_hash.size()); + + MakeFileWithHash(); + ASSERT_EQ(net::OK, base_file_->Initialize()); ASSERT_EQ(net::OK, AppendDataToFile(kTestData1)); base_file_->Finish(); std::string hash; - base_file_->GetSha256Hash(&hash); + base_file_->GetHash(&hash); EXPECT_EQ("0B2D3F3F7943AD64B860DF94D05CB56A8A97C6EC5768B5B70B930C5AA7FA9ADE", - base::HexEncode(hash.data(), hash.size())); + expected_hash_hex); + EXPECT_EQ(expected_hash_hex, base::HexEncode(hash.data(), hash.size())); base_file_->Detach(); expect_file_survives_ = true; @@ -226,7 +271,7 @@ TEST_F(BaseFileTest, WriteWithHashAndDetach) { // Rename the file after writing to it, then detach. TEST_F(BaseFileTest, WriteThenRenameAndDetach) { - ASSERT_EQ(net::OK, base_file_->Initialize(false)); + ASSERT_EQ(net::OK, base_file_->Initialize()); FilePath initial_path(base_file_->full_path()); EXPECT_TRUE(file_util::PathExists(initial_path)); @@ -246,54 +291,118 @@ TEST_F(BaseFileTest, WriteThenRenameAndDetach) { // Write data to the file once. TEST_F(BaseFileTest, SingleWrite) { - ASSERT_EQ(net::OK, base_file_->Initialize(false)); + ASSERT_EQ(net::OK, base_file_->Initialize()); ASSERT_EQ(net::OK, AppendDataToFile(kTestData1)); base_file_->Finish(); } // Write data to the file multiple times. TEST_F(BaseFileTest, MultipleWrites) { - ASSERT_EQ(net::OK, base_file_->Initialize(false)); + ASSERT_EQ(net::OK, base_file_->Initialize()); ASSERT_EQ(net::OK, AppendDataToFile(kTestData1)); ASSERT_EQ(net::OK, AppendDataToFile(kTestData2)); ASSERT_EQ(net::OK, AppendDataToFile(kTestData3)); std::string hash; - EXPECT_FALSE(base_file_->GetSha256Hash(&hash)); + EXPECT_FALSE(base_file_->GetHash(&hash)); base_file_->Finish(); } // Write data to the file once and calculate its sha256 hash. TEST_F(BaseFileTest, SingleWriteWithHash) { - ASSERT_EQ(net::OK, base_file_->Initialize(true)); + // Calculate the final hash. + ResetHash(); + UpdateHash(kTestData1, kTestDataLength1); + std::string expected_hash = GetFinalHash(); + std::string expected_hash_hex = + base::HexEncode(expected_hash.data(), expected_hash.size()); + + MakeFileWithHash(); + ASSERT_EQ(net::OK, base_file_->Initialize()); + // Can get partial hash states before Finish() is called. + EXPECT_STRNE(std::string().c_str(), base_file_->GetHashState().c_str()); ASSERT_EQ(net::OK, AppendDataToFile(kTestData1)); + EXPECT_STRNE(std::string().c_str(), base_file_->GetHashState().c_str()); base_file_->Finish(); std::string hash; - base_file_->GetSha256Hash(&hash); - EXPECT_EQ("0B2D3F3F7943AD64B860DF94D05CB56A8A97C6EC5768B5B70B930C5AA7FA9ADE", - base::HexEncode(hash.data(), hash.size())); + base_file_->GetHash(&hash); + EXPECT_EQ(expected_hash_hex, base::HexEncode(hash.data(), hash.size())); } // Write data to the file multiple times and calculate its sha256 hash. TEST_F(BaseFileTest, MultipleWritesWithHash) { - std::string hash; + // Calculate the final hash. + ResetHash(); + UpdateHash(kTestData1, kTestDataLength1); + UpdateHash(kTestData2, kTestDataLength2); + UpdateHash(kTestData3, kTestDataLength3); + std::string expected_hash = GetFinalHash(); + std::string expected_hash_hex = + base::HexEncode(expected_hash.data(), expected_hash.size()); - ASSERT_EQ(net::OK, base_file_->Initialize(true)); + std::string hash; + MakeFileWithHash(); + ASSERT_EQ(net::OK, base_file_->Initialize()); ASSERT_EQ(net::OK, AppendDataToFile(kTestData1)); ASSERT_EQ(net::OK, AppendDataToFile(kTestData2)); ASSERT_EQ(net::OK, AppendDataToFile(kTestData3)); - // no hash before Finish() is called either. - EXPECT_FALSE(base_file_->GetSha256Hash(&hash)); + // No hash before Finish() is called. + EXPECT_FALSE(base_file_->GetHash(&hash)); base_file_->Finish(); - EXPECT_TRUE(base_file_->GetSha256Hash(&hash)); + EXPECT_TRUE(base_file_->GetHash(&hash)); EXPECT_EQ("CBF68BF10F8003DB86B31343AFAC8C7175BD03FB5FC905650F8C80AF087443A8", - base::HexEncode(hash.data(), hash.size())); + expected_hash_hex); + EXPECT_EQ(expected_hash_hex, base::HexEncode(hash.data(), hash.size())); +} + +// Write data to the file multiple times, interrupt it, and continue using +// another file. Calculate the resulting combined sha256 hash. +TEST_F(BaseFileTest, MultipleWritesInterruptedWithHash) { + // Calculate the final hash. + ResetHash(); + UpdateHash(kTestData1, kTestDataLength1); + UpdateHash(kTestData2, kTestDataLength2); + UpdateHash(kTestData3, kTestDataLength3); + std::string expected_hash = GetFinalHash(); + std::string expected_hash_hex = + base::HexEncode(expected_hash.data(), expected_hash.size()); + + MakeFileWithHash(); + ASSERT_EQ(net::OK, base_file_->Initialize()); + // Write some data + ASSERT_EQ(net::OK, AppendDataToFile(kTestData1)); + ASSERT_EQ(net::OK, AppendDataToFile(kTestData2)); + // Get the hash state and file name. + std::string hash_state; + hash_state = base_file_->GetHashState(); + // Finish the file. + base_file_->Finish(); + + // Create another file + linked_ptr<net::FileStream> second_stream; + BaseFile second_file(FilePath(), + GURL(), + GURL(), + base_file_->bytes_so_far(), + true, + hash_state, + second_stream); + ASSERT_EQ(net::OK, second_file.Initialize()); + std::string data(kTestData3); + EXPECT_EQ(net::OK, second_file.AppendDataToFile(data.data(), data.size())); + second_file.Finish(); + + std::string hash; + EXPECT_TRUE(second_file.GetHash(&hash)); + // This will fail until getting the hash state is supported in SecureHash. + EXPECT_STREQ(expected_hash_hex.c_str(), + base::HexEncode(hash.data(), hash.size()).c_str()); } // Rename the file after all writes to it. TEST_F(BaseFileTest, WriteThenRename) { - ASSERT_EQ(net::OK, base_file_->Initialize(false)); + ASSERT_EQ(net::OK, base_file_->Initialize()); FilePath initial_path(base_file_->full_path()); EXPECT_TRUE(file_util::PathExists(initial_path)); @@ -311,7 +420,7 @@ TEST_F(BaseFileTest, WriteThenRename) { // Rename the file while the download is still in progress. TEST_F(BaseFileTest, RenameWhileInProgress) { - ASSERT_EQ(net::OK, base_file_->Initialize(false)); + ASSERT_EQ(net::OK, base_file_->Initialize()); FilePath initial_path(base_file_->full_path()); EXPECT_TRUE(file_util::PathExists(initial_path)); @@ -334,14 +443,19 @@ TEST_F(BaseFileTest, RenameWhileInProgress) { TEST_F(BaseFileTest, MultipleWritesWithError) { ASSERT_TRUE(OpenMockFileStream()); base_file_.reset(new BaseFile(mock_file_stream_->get_path(), - GURL(), GURL(), 0, mock_file_stream_)); - EXPECT_EQ(net::OK, base_file_->Initialize(false)); + GURL(), + GURL(), + 0, + false, + "", + mock_file_stream_)); + EXPECT_EQ(net::OK, base_file_->Initialize()); ASSERT_EQ(net::OK, AppendDataToFile(kTestData1)); ASSERT_EQ(net::OK, AppendDataToFile(kTestData2)); ForceError(net::ERR_ACCESS_DENIED); ASSERT_NE(net::OK, AppendDataToFile(kTestData3)); std::string hash; - EXPECT_FALSE(base_file_->GetSha256Hash(&hash)); + EXPECT_FALSE(base_file_->GetHash(&hash)); base_file_->Finish(); } @@ -355,7 +469,7 @@ TEST_F(BaseFileTest, UninitializedFile) { // Overwrite base_file_ with another file with the same name and // non-zero contents, and make sure the last file to close 'wins'. TEST_F(BaseFileTest, DuplicateBaseFile) { - EXPECT_EQ(net::OK, base_file_->Initialize(false)); + EXPECT_EQ(net::OK, base_file_->Initialize()); // Create another |BaseFile| referring to the file that |base_file_| owns. CreateFileWithName(base_file_->full_path()); @@ -372,11 +486,15 @@ TEST_F(BaseFileTest, AppendToBaseFile) { set_expected_data(kTestData4); // Use the file we've just created. - base_file_.reset( - new BaseFile(existing_file_name, GURL(), GURL(), kTestDataLength4, - file_stream_)); + base_file_.reset(new BaseFile(existing_file_name, + GURL(), + GURL(), + kTestDataLength4, + false, + "", + file_stream_)); - EXPECT_EQ(net::OK, base_file_->Initialize(false)); + EXPECT_EQ(net::OK, base_file_->Initialize()); const FilePath file_name = base_file_->full_path(); EXPECT_NE(FilePath::StringType(), file_name.value()); @@ -398,12 +516,17 @@ TEST_F(BaseFileTest, ReadonlyBaseFile) { EXPECT_TRUE(file_util::MakeFileUnwritable(readonly_file_name)); // Try to overwrite it. - base_file_.reset( - new BaseFile(readonly_file_name, GURL(), GURL(), 0, file_stream_)); + base_file_.reset(new BaseFile(readonly_file_name, + GURL(), + GURL(), + 0, + false, + "", + file_stream_)); expect_in_progress_ = false; - int init_error = base_file_->Initialize(false); + int init_error = base_file_->Initialize(); DVLOG(1) << " init_error = " << init_error; EXPECT_NE(net::OK, init_error); @@ -418,17 +541,17 @@ TEST_F(BaseFileTest, ReadonlyBaseFile) { expect_file_survives_ = true; } -TEST_F(BaseFileTest, IsEmptySha256Hash) { +TEST_F(BaseFileTest, IsEmptyHash) { std::string empty(BaseFile::kSha256HashLen, '\x00'); - EXPECT_TRUE(BaseFile::IsEmptySha256Hash(empty)); + EXPECT_TRUE(BaseFile::IsEmptyHash(empty)); std::string not_empty(BaseFile::kSha256HashLen, '\x01'); - EXPECT_FALSE(BaseFile::IsEmptySha256Hash(not_empty)); - EXPECT_FALSE(BaseFile::IsEmptySha256Hash("")); + EXPECT_FALSE(BaseFile::IsEmptyHash(not_empty)); + EXPECT_FALSE(BaseFile::IsEmptyHash("")); } // Test that calculating speed after no writes. TEST_F(BaseFileTest, SpeedWithoutWrite) { - ASSERT_EQ(net::OK, base_file_->Initialize(false)); + ASSERT_EQ(net::OK, base_file_->Initialize()); base::TimeTicks current = StartTick() + kElapsedTimeDelta; ASSERT_EQ(0, CurrentSpeedAtTime(current)); base_file_->Finish(); @@ -436,7 +559,7 @@ TEST_F(BaseFileTest, SpeedWithoutWrite) { // Test that calculating speed after a single write. TEST_F(BaseFileTest, SpeedAfterSingleWrite) { - ASSERT_EQ(net::OK, base_file_->Initialize(false)); + ASSERT_EQ(net::OK, base_file_->Initialize()); ASSERT_EQ(net::OK, AppendDataToFile(kTestData1)); base::TimeTicks current = StartTick() + kElapsedTimeDelta; int64 expected_speed = kTestDataLength1 / kElapsedTimeSeconds; @@ -446,7 +569,7 @@ TEST_F(BaseFileTest, SpeedAfterSingleWrite) { // Test that calculating speed after a multiple writes. TEST_F(BaseFileTest, SpeedAfterMultipleWrite) { - ASSERT_EQ(net::OK, base_file_->Initialize(false)); + ASSERT_EQ(net::OK, base_file_->Initialize()); ASSERT_EQ(net::OK, AppendDataToFile(kTestData1)); ASSERT_EQ(net::OK, AppendDataToFile(kTestData2)); ASSERT_EQ(net::OK, AppendDataToFile(kTestData3)); @@ -460,7 +583,7 @@ TEST_F(BaseFileTest, SpeedAfterMultipleWrite) { // Test that calculating speed after no delay - should not divide by 0. TEST_F(BaseFileTest, SpeedAfterNoElapsedTime) { - ASSERT_EQ(net::OK, base_file_->Initialize(false)); + ASSERT_EQ(net::OK, base_file_->Initialize()); ASSERT_EQ(net::OK, AppendDataToFile(kTestData1)); ASSERT_EQ(0, CurrentSpeedAtTime(StartTick())); base_file_->Finish(); diff --git a/content/browser/download/download_create_info.h b/content/browser/download/download_create_info.h index 0369877..90f9160 100644 --- a/content/browser/download/download_create_info.h +++ b/content/browser/download/download_create_info.h @@ -88,9 +88,17 @@ struct CONTENT_EXPORT DownloadCreateInfo { // which may look at the file extension and first few bytes of the file. std::string original_mime_type; + // For continuing a download, the modification time of the file. + // Storing as a string for exact match to server format on + // "If-Unmodified-Since" comparison. + std::string last_modified; + + // For continuing a download, the ETAG of the file. + std::string etag; + // True if we should display the 'save as...' UI and prompt the user // for the download location. - // False if the UI should be supressed and the download performed to the + // False if the UI should be suppressed and the download performed to the // default location. bool prompt_user_for_save_location; diff --git a/content/browser/download/download_file_impl.cc b/content/browser/download/download_file_impl.cc index f065746..4cff9e8 100644 --- a/content/browser/download/download_file_impl.cc +++ b/content/browser/download/download_file_impl.cc @@ -86,11 +86,14 @@ int DownloadFile::GetUniquePathNumberWithSuffix( DownloadFileImpl::DownloadFileImpl( const DownloadCreateInfo* info, DownloadRequestHandleInterface* request_handle, - content::DownloadManager* download_manager) + content::DownloadManager* download_manager, + bool calculate_hash) : file_(info->save_info.file_path, info->url(), info->referrer_url, info->received_bytes, + calculate_hash, + info->save_info.hash_state, info->save_info.file_stream), id_(info->download_id), request_handle_(request_handle), @@ -103,8 +106,8 @@ DownloadFileImpl::~DownloadFileImpl() { } // BaseFile delegated functions. -net::Error DownloadFileImpl::Initialize(bool calculate_hash) { - return file_.Initialize(calculate_hash); +net::Error DownloadFileImpl::Initialize() { + return file_.Initialize(); } net::Error DownloadFileImpl::AppendDataToFile(const char* data, @@ -148,8 +151,12 @@ int64 DownloadFileImpl::CurrentSpeed() const { return file_.CurrentSpeed(); } -bool DownloadFileImpl::GetSha256Hash(std::string* hash) { - return file_.GetSha256Hash(hash); +bool DownloadFileImpl::GetHash(std::string* hash) { + return file_.GetHash(hash); +} + +std::string DownloadFileImpl::GetHashState() { + return file_.GetHashState(); } // DownloadFileInterface implementation. diff --git a/content/browser/download/download_file_impl.h b/content/browser/download/download_file_impl.h index 3133135..69a7c5e 100644 --- a/content/browser/download/download_file_impl.h +++ b/content/browser/download/download_file_impl.h @@ -27,11 +27,12 @@ class CONTENT_EXPORT DownloadFileImpl : virtual public content::DownloadFile { // Takes ownership of the object pointed to by |request_handle|. DownloadFileImpl(const DownloadCreateInfo* info, DownloadRequestHandleInterface* request_handle, - content::DownloadManager* download_manager); + content::DownloadManager* download_manager, + bool calculate_hash); virtual ~DownloadFileImpl(); // DownloadFile functions. - virtual net::Error Initialize(bool calculate_hash) OVERRIDE; + 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; @@ -43,7 +44,8 @@ class CONTENT_EXPORT DownloadFileImpl : virtual public content::DownloadFile { virtual bool InProgress() const OVERRIDE; virtual int64 BytesSoFar() const OVERRIDE; virtual int64 CurrentSpeed() const OVERRIDE; - virtual bool GetSha256Hash(std::string* hash) OVERRIDE; + virtual bool GetHash(std::string* hash) OVERRIDE; + virtual std::string GetHashState() OVERRIDE; virtual void CancelDownloadRequest() OVERRIDE; virtual int Id() const OVERRIDE; virtual content::DownloadManager* GetDownloadManager() OVERRIDE; diff --git a/content/browser/download/download_file_manager.cc b/content/browser/download/download_file_manager.cc index 1fdf9e4..45fea51 100644 --- a/content/browser/download/download_file_manager.cc +++ b/content/browser/download/download_file_manager.cc @@ -44,16 +44,18 @@ class DownloadFileFactoryImpl virtual content::DownloadFile* CreateFile( DownloadCreateInfo* info, const DownloadRequestHandle& request_handle, - content::DownloadManager* download_manager) OVERRIDE; + content::DownloadManager* download_manager, + bool calculate_hash) OVERRIDE; }; DownloadFile* DownloadFileFactoryImpl::CreateFile( DownloadCreateInfo* info, const DownloadRequestHandle& request_handle, - content::DownloadManager* download_manager) { + content::DownloadManager* download_manager, + bool calculate_hash) { return new DownloadFileImpl(info, new DownloadRequestHandle(request_handle), - download_manager); + download_manager, calculate_hash); } } // namespace @@ -93,8 +95,8 @@ void DownloadFileManager::CreateDownloadFile( scoped_ptr<DownloadCreateInfo> infop(info); scoped_ptr<DownloadFile> download_file(download_file_factory_->CreateFile( - info, request_handle, download_manager)); - if (net::OK != download_file->Initialize(get_hash)) { + info, request_handle, download_manager, get_hash)); + if (net::OK != download_file->Initialize()) { request_handle.CancelRequest(); return; } @@ -142,9 +144,12 @@ void DownloadFileManager::UpdateInProgressDownloads() { content::DownloadManager* manager = download_file->GetDownloadManager(); if (manager) { BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&content::DownloadManager::UpdateDownload, manager, - global_id.local(), download_file->BytesSoFar(), - download_file->CurrentSpeed())); + base::Bind(&content::DownloadManager::UpdateDownload, + manager, + global_id.local(), + download_file->BytesSoFar(), + download_file->CurrentSpeed(), + download_file->GetHashState())); } } } @@ -197,6 +202,8 @@ void DownloadFileManager::UpdateDownload( 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. @@ -207,9 +214,13 @@ void DownloadFileManager::UpdateDownload( BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&content::DownloadManager::OnDownloadInterrupted, - download_manager, global_id.local(), bytes_downloaded, + download_manager, + global_id.local(), + bytes_downloaded, + hash_state, ConvertNetErrorToInterruptReason( - write_result, DOWNLOAD_INTERRUPT_FROM_DISK))); + write_result, + DOWNLOAD_INTERRUPT_FROM_DISK))); } } } @@ -238,11 +249,13 @@ void DownloadFileManager::OnResponseCompleted( return; } - std::string hash; - if (!download_file->GetSha256Hash(&hash) || BaseFile::IsEmptySha256Hash(hash)) - hash.clear(); - if (reason == 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(&content::DownloadManager::OnResponseCompleted, @@ -252,8 +265,11 @@ void DownloadFileManager::OnResponseCompleted( BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&content::DownloadManager::OnDownloadInterrupted, - download_manager, global_id.local(), - download_file->BytesSoFar(), reason)); + 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. @@ -433,10 +449,13 @@ void DownloadFileManager::CancelDownloadOnRename( BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&content::DownloadManager::OnDownloadInterrupted, - download_manager, global_id.local(), + download_manager, + global_id.local(), download_file->BytesSoFar(), + download_file->GetHashState(), ConvertNetErrorToInterruptReason( - rename_error, DOWNLOAD_INTERRUPT_FROM_DISK))); + rename_error, + DOWNLOAD_INTERRUPT_FROM_DISK))); } void DownloadFileManager::EraseDownload(DownloadId global_id) { diff --git a/content/browser/download/download_file_manager.h b/content/browser/download/download_file_manager.h index 989c896..fdce6af 100644 --- a/content/browser/download/download_file_manager.h +++ b/content/browser/download/download_file_manager.h @@ -77,7 +77,8 @@ class CONTENT_EXPORT DownloadFileManager virtual content::DownloadFile* CreateFile( DownloadCreateInfo* info, const DownloadRequestHandle& request_handle, - content::DownloadManager* download_manager) = 0; + content::DownloadManager* download_manager, + bool calculate_hash) = 0; }; // Takes ownership of the factory. diff --git a/content/browser/download/download_file_unittest.cc b/content/browser/download/download_file_unittest.cc index e037f56..4bc6ec354 100644 --- a/content/browser/download/download_file_unittest.cc +++ b/content/browser/download/download_file_unittest.cc @@ -68,14 +68,16 @@ class DownloadFileTest : public testing::Test { ui_thread_.message_loop()->RunAllPending(); } - virtual void CreateDownloadFile(scoped_ptr<DownloadFile>* file, int offset) { + virtual void CreateDownloadFile(scoped_ptr<DownloadFile>* file, + int offset, + bool calculate_hash) { 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_)); + download_manager_, calculate_hash)); } virtual void DestroyDownloadFile(scoped_ptr<DownloadFile>* file, int offset) { @@ -141,8 +143,8 @@ 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); - ASSERT_EQ(net::OK, download_file_->Initialize(true)); + CreateDownloadFile(&download_file_, 0, true); + ASSERT_EQ(net::OK, download_file_->Initialize()); FilePath initial_path(download_file_->FullPath()); EXPECT_TRUE(file_util::PathExists(initial_path)); FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1")); @@ -185,7 +187,7 @@ TEST_F(DownloadFileTest, RenameFileFinal) { // Should not be able to get the hash until the file is closed. std::string hash; - EXPECT_FALSE(download_file_->GetSha256Hash(&hash)); + EXPECT_FALSE(download_file_->GetHash(&hash)); download_file_->Finish(); @@ -199,7 +201,7 @@ TEST_F(DownloadFileTest, RenameFileFinal) { EXPECT_TRUE(file_util::PathExists(path_4)); // Check the hash. - EXPECT_TRUE(download_file_->GetSha256Hash(&hash)); + EXPECT_TRUE(download_file_->GetHash(&hash)); EXPECT_EQ(kDataHash, base::HexEncode(hash.data(), hash.size())); DestroyDownloadFile(&download_file_, 0); diff --git a/content/browser/download/download_item_impl.cc b/content/browser/download/download_item_impl.cc index 437209f..505fea3 100644 --- a/content/browser/download/download_item_impl.cc +++ b/content/browser/download/download_item_impl.cc @@ -167,6 +167,7 @@ DownloadItemImpl::DownloadItemImpl(Delegate* delegate, total_bytes_(info.total_bytes), received_bytes_(info.received_bytes), bytes_per_sec_(0), + last_reason_(DOWNLOAD_INTERRUPT_REASON_NONE), start_tick_(base::TimeTicks()), state_(static_cast<DownloadState>(info.state)), start_time_(info.start_time), @@ -367,10 +368,26 @@ void DownloadItemImpl::DangerousDownloadValidated() { delegate_->MaybeCompleteDownload(this); } -void DownloadItemImpl::UpdateSize(int64 bytes_so_far) { +void DownloadItemImpl::ProgressComplete(int64 bytes_so_far, + const std::string& final_hash) { // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + hash_ = final_hash; + hash_state_ = ""; + + received_bytes_ = bytes_so_far; + + // If we've received more data than we were expecting (bad server info?), + // revert to 'unknown size mode'. + if (received_bytes_ > total_bytes_) + total_bytes_ = 0; +} + +void DownloadItemImpl::UpdateProgress(int64 bytes_so_far, + const std::string& hash_state) { + hash_state_ = hash_state; + received_bytes_ = bytes_so_far; // If we've received more data than we were expecting (bad server info?), @@ -382,7 +399,9 @@ void DownloadItemImpl::UpdateSize(int64 bytes_so_far) { // Updates from the download thread may have been posted while this download // was being cancelled in the UI thread, so we'll accept them unless we're // complete. -void DownloadItemImpl::UpdateProgress(int64 bytes_so_far, int64 bytes_per_sec) { +void DownloadItemImpl::UpdateProgress(int64 bytes_so_far, + int64 bytes_per_sec, + const std::string& hash_state) { // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -391,7 +410,7 @@ void DownloadItemImpl::UpdateProgress(int64 bytes_so_far, int64 bytes_per_sec) { return; } bytes_per_sec_ = bytes_per_sec; - UpdateSize(bytes_so_far); + UpdateProgress(bytes_so_far, hash_state); UpdateObservers(); } @@ -439,8 +458,7 @@ void DownloadItemImpl::OnAllDataSaved( DCHECK(!all_data_saved_); all_data_saved_ = true; - UpdateSize(size); - hash_ = final_hash; + ProgressComplete(size, final_hash); } void DownloadItemImpl::OnDownloadedFileRemoved() { @@ -506,7 +524,9 @@ void DownloadItemImpl::UpdateTarget() { state_info_.target_name = full_path_.BaseName(); } -void DownloadItemImpl::Interrupted(int64 size, InterruptReason reason) { +void DownloadItemImpl::Interrupted(int64 size, + const std::string& hash_state, + InterruptReason reason) { // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -514,7 +534,7 @@ void DownloadItemImpl::Interrupted(int64 size, InterruptReason reason) { return; last_reason_ = reason; - UpdateSize(size); + UpdateProgress(size, hash_state); download_stats::RecordDownloadInterrupted(reason, received_bytes_, total_bytes_); @@ -794,9 +814,8 @@ void DownloadItemImpl::Init(bool active) { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); UpdateTarget(); - if (active) { + if (active) download_stats::RecordDownloadCount(download_stats::START_COUNT); - } VLOG(20) << __FUNCTION__ << "() " << DebugString(true); } @@ -857,6 +876,8 @@ std::string DownloadItemImpl::DebugString(bool verbose) const { " is_paused = %c" " is_otr = %c" " safety_state = %s" + " last_modified = '%s'" + " etag = '%s'" " url_chain = \n\t\"%s\"\n\t" " target_name = \"%" PRFilePath "\"" " full_path = \"%" PRFilePath "\"", @@ -866,6 +887,8 @@ std::string DownloadItemImpl::DebugString(bool verbose) const { IsPaused() ? 'T' : 'F', IsOtr() ? 'T' : 'F', DebugSafetyStateString(GetSafetyState()), + GetLastModifiedTime().c_str(), + GetETag().c_str(), url_list.c_str(), state_info_.target_name.value().c_str(), GetFullPath().value().c_str()); @@ -915,6 +938,9 @@ void DownloadItemImpl::SetTotalBytes(int64 total_bytes) { } const std::string& DownloadItemImpl::GetHash() const { return hash_; } int64 DownloadItemImpl::GetReceivedBytes() const { return received_bytes_; } +const std::string& DownloadItemImpl::GetHashState() const { + return hash_state_; +} int32 DownloadItemImpl::GetId() const { return download_id_.local(); } DownloadId DownloadItemImpl::GetGlobalId() const { return download_id_; } base::Time DownloadItemImpl::GetStartTime() const { return start_time_; } @@ -948,6 +974,10 @@ const FilePath& DownloadItemImpl::GetSuggestedPath() const { bool DownloadItemImpl::IsTemporary() const { return is_temporary_; } void DownloadItemImpl::SetOpened(bool opened) { opened_ = opened; } bool DownloadItemImpl::GetOpened() const { return opened_; } +const std::string& DownloadItemImpl::GetLastModifiedTime() const { + return last_modified_time_; +} +const std::string& DownloadItemImpl::GetETag() const { return etag_; } InterruptReason DownloadItemImpl::GetLastReason() const { return last_reason_; } diff --git a/content/browser/download/download_item_impl.h b/content/browser/download/download_item_impl.h index a84faa1..dca9c0f 100644 --- a/content/browser/download/download_item_impl.h +++ b/content/browser/download/download_item_impl.h @@ -111,7 +111,9 @@ class CONTENT_EXPORT DownloadItemImpl : public content::DownloadItem { virtual void OpenDownload() OVERRIDE; virtual void ShowDownloadInShell() OVERRIDE; virtual void DangerousDownloadValidated() OVERRIDE; - virtual void UpdateProgress(int64 bytes_so_far, int64 bytes_per_sec) OVERRIDE; + virtual void UpdateProgress(int64 bytes_so_far, + int64 bytes_per_sec, + const std::string& hash_state) OVERRIDE; virtual void Cancel(bool user_cancel) OVERRIDE; virtual void MarkAsComplete() OVERRIDE; virtual void DelayedDownloadOpened() OVERRIDE; @@ -119,7 +121,9 @@ class CONTENT_EXPORT DownloadItemImpl : public content::DownloadItem { int64 size, const std::string& final_hash) OVERRIDE; virtual void OnDownloadedFileRemoved() OVERRIDE; virtual void MaybeCompleteDownload() OVERRIDE; - virtual void Interrupted(int64 size, InterruptReason reason) OVERRIDE; + virtual void Interrupted(int64 size, + const std::string& hash_state, + InterruptReason reason) OVERRIDE; virtual void Delete(DeleteReason reason) OVERRIDE; virtual void Remove() OVERRIDE; virtual bool TimeRemaining(base::TimeDelta* remaining) const OVERRIDE; @@ -155,6 +159,7 @@ class CONTENT_EXPORT DownloadItemImpl : public content::DownloadItem { virtual void SetTotalBytes(int64 total_bytes) OVERRIDE; virtual const std::string& GetHash() const OVERRIDE; virtual int64 GetReceivedBytes() const OVERRIDE; + virtual const std::string& GetHashState() const OVERRIDE; virtual int32 GetId() const OVERRIDE; virtual DownloadId GetGlobalId() const OVERRIDE; virtual base::Time GetStartTime() const OVERRIDE; @@ -179,6 +184,8 @@ class CONTENT_EXPORT DownloadItemImpl : public content::DownloadItem { virtual bool IsTemporary() const OVERRIDE; virtual void SetOpened(bool opened) OVERRIDE; virtual bool GetOpened() const OVERRIDE; + virtual const std::string& GetLastModifiedTime() const OVERRIDE; + virtual const std::string& GetETag() const OVERRIDE; virtual InterruptReason GetLastReason() const OVERRIDE; virtual DownloadPersistentStoreInfo GetPersistentStoreInfo() const OVERRIDE; virtual DownloadStateInfo GetStateInfo() const OVERRIDE; @@ -199,8 +206,15 @@ class CONTENT_EXPORT DownloadItemImpl : public content::DownloadItem { // downloads and false for downloads from the history. void Init(bool active); - // Internal helper for maintaining consistent received and total sizes. - void UpdateSize(int64 size); + // Internal helper for maintaining consistent received and total sizes, and + // hash state. + void UpdateProgress(int64 bytes_so_far, const std::string& hash_state); + + // Internal helper for maintaining consistent received and total sizes, and + // setting the final hash. + // Should only be called from |OnAllDataSaved|. + void ProgressComplete(int64 bytes_so_far, + const std::string& final_hash); // Called when the entire download operation (including renaming etc) // is completed. @@ -265,10 +279,10 @@ class CONTENT_EXPORT DownloadItemImpl : public content::DownloadItem { // DownloadCreateInfo::remote_address. std::string remote_address_; - // Total bytes expected + // Total bytes expected. int64 total_bytes_; - // Current received bytes + // Current received bytes. int64 received_bytes_; // Current speed. Calculated by the DownloadFile. @@ -279,31 +293,41 @@ class CONTENT_EXPORT DownloadItemImpl : public content::DownloadItem { // (ChromeDownloadManagerDelegate::GenerateFileHash() returned false). std::string hash_; + // A blob containing the state of the hash algorithm. Only valid while the + // download is in progress. + std::string hash_state_; + + // Server's time stamp for the file. + std::string last_modified_time_; + + // Server's ETAG for the file. + std::string etag_; + // Last reason. InterruptReason last_reason_; // Start time for recording statistics. base::TimeTicks start_tick_; - // The current state of this download + // The current state of this download. DownloadState state_; - // The views of this item in the download shelf and download tab + // The views of this item in the download shelf and download tab. ObserverList<Observer> observers_; - // Time the download was started + // Time the download was started. base::Time start_time_; - // Time the download completed + // Time the download completed. base::Time end_time_; - // Our persistent store handle + // Our persistent store handle. int64 db_handle_; // Our delegate. Delegate* delegate_; - // In progress downloads may be paused by the user, we note it here + // In progress downloads may be paused by the user, we note it here. bool is_paused_; // A flag for indicating if the download should be opened at completion. diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc index c73046b..cb25d5f 100644 --- a/content/browser/download/download_manager_impl.cc +++ b/content/browser/download/download_manager_impl.cc @@ -294,8 +294,7 @@ void DownloadManagerImpl::OnFileRemovalDetected(int64 db_handle) { } } -void DownloadManagerImpl::RestartDownload( - int32 download_id) { +void DownloadManagerImpl::RestartDownload(int32 download_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DownloadItem* download = GetActiveDownloadItem(download_id); @@ -422,14 +421,16 @@ void DownloadManagerImpl::ContinueDownloadWithPath( delegate_->AddItemToPersistentStore(download); } -void DownloadManagerImpl::UpdateDownload(int32 download_id, int64 bytes_so_far, - int64 bytes_per_sec) { +void DownloadManagerImpl::UpdateDownload(int32 download_id, + int64 bytes_so_far, + int64 bytes_per_sec, + std::string hash_state) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DownloadMap::iterator it = active_downloads_.find(download_id); if (it != active_downloads_.end()) { DownloadItem* download = it->second; if (download->IsInProgress()) { - download->UpdateProgress(bytes_so_far, bytes_per_sec); + download->UpdateProgress(bytes_so_far, bytes_per_sec, hash_state); UpdateDownloadProgress(); // Reflect size updates. delegate_->UpdateItemInPersistentStore(download); } @@ -622,6 +623,7 @@ void DownloadManagerImpl::DownloadCancelled(DownloadItem* download) { void DownloadManagerImpl::OnDownloadInterrupted(int32 download_id, int64 size, + std::string hash_state, InterruptReason reason) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -636,7 +638,7 @@ void DownloadManagerImpl::OnDownloadInterrupted(int32 download_id, << " download = " << download->DebugString(true); RemoveFromActiveList(download); - download->Interrupted(size, reason); + download->Interrupted(size, hash_state, reason); download->OffThreadCancel(file_manager_); } @@ -769,6 +771,7 @@ void DownloadManagerImpl::DownloadUrlToFile(const GURL& url, DCHECK(tab_contents); ResourceDispatcherHost* resource_dispatcher_host = content::GetContentClient()->browser()->GetResourceDispatcherHost(); + // We send a pointer to content::ResourceContext, instead of the usual // reference, so that a copy of the object isn't made. // base::Bind can't handle 7 args, so we use URLParams and RenderParams. diff --git a/content/browser/download/download_manager_impl.h b/content/browser/download/download_manager_impl.h index 9f8a7b5..6966fa3 100644 --- a/content/browser/download/download_manager_impl.h +++ b/content/browser/download/download_manager_impl.h @@ -44,12 +44,16 @@ class CONTENT_EXPORT DownloadManagerImpl DownloadVector* result) OVERRIDE; virtual bool Init(content::BrowserContext* browser_context) OVERRIDE; virtual void StartDownload(int32 id) OVERRIDE; - virtual void UpdateDownload(int32 download_id, int64 bytes_so_far, - int64 bytes_per_sec) OVERRIDE; + virtual void UpdateDownload(int32 download_id, + int64 bytes_so_far, + int64 bytes_per_sec, + std::string hash_state) OVERRIDE; virtual void OnResponseCompleted(int32 download_id, int64 size, const std::string& hash) OVERRIDE; virtual void CancelDownload(int32 download_id) OVERRIDE; - virtual void OnDownloadInterrupted(int32 download_id, int64 size, + virtual void OnDownloadInterrupted(int32 download_id, + int64 size, + std::string hash_state, InterruptReason reason) OVERRIDE; virtual void OnDownloadRenamedToFinalName(int download_id, const FilePath& full_path, diff --git a/content/browser/download/download_persistent_store_info.h b/content/browser/download/download_persistent_store_info.h index 082e4ef..429d64a 100644 --- a/content/browser/download/download_persistent_store_info.h +++ b/content/browser/download/download_persistent_store_info.h @@ -30,7 +30,6 @@ class DownloadItem; // the history, all fields except |referrer_url| are set by the DownloadDatabase // and read by the DownloadItem. struct CONTENT_EXPORT DownloadPersistentStoreInfo { - // TODO(ahendrickson) -- Reduce the number of constructors. DownloadPersistentStoreInfo(); DownloadPersistentStoreInfo(const FilePath& path, const GURL& url, diff --git a/content/browser/download/download_request_handle.h b/content/browser/download/download_request_handle.h index 9b797b7..a81c2845 100644 --- a/content/browser/download/download_request_handle.h +++ b/content/browser/download/download_request_handle.h @@ -32,11 +32,11 @@ class CONTENT_EXPORT DownloadRequestHandleInterface { virtual TabContents* GetTabContents() const = 0; virtual content::DownloadManager* GetDownloadManager() const = 0; - // Pause or resume the matching URL request. + // Pauses or resumes the matching URL request. virtual void PauseRequest() const = 0; virtual void ResumeRequest() const = 0; - // Cancel the request. + // Cancels the request. virtual void CancelRequest() const = 0; // Describe the object. diff --git a/content/browser/download/download_resource_handler.cc b/content/browser/download/download_resource_handler.cc index b9604bc..531f209 100644 --- a/content/browser/download/download_resource_handler.cc +++ b/content/browser/download/download_resource_handler.cc @@ -105,7 +105,7 @@ bool DownloadResourceHandler::OnResponseStarted( info->url_chain = request_->url_chain(); info->referrer_url = GURL(request_->referrer()); info->start_time = base::Time::Now(); - info->received_bytes = 0; + info->received_bytes = save_info_.offset; info->total_bytes = content_length_; info->state = DownloadItem::IN_PROGRESS; info->download_id = download_id_; @@ -118,8 +118,16 @@ bool DownloadResourceHandler::OnResponseStarted( DownloadRequestHandle request_handle(rdh_, global_id_.child_id, render_view_id_, global_id_.request_id); - // TODO(ahendrickson) -- Get the last modified time and etag, so we can - // resume downloading. + // Get the last modified time and etag. + const net::HttpResponseHeaders* headers = request_->response_headers(); + if (headers) { + std::string last_modified_hdr; + std::string etag; + if (headers->EnumerateHeader(NULL, "Last-Modified", &last_modified_hdr)) + info->last_modified = last_modified_hdr; + if (headers->EnumerateHeader(NULL, "ETag", &etag)) + info->etag = etag; + } CallStartedCB(net::OK); diff --git a/content/browser/download/download_resource_handler.h b/content/browser/download/download_resource_handler.h index f3cad2f..85f669c 100644 --- a/content/browser/download/download_resource_handler.h +++ b/content/browser/download/download_resource_handler.h @@ -15,7 +15,7 @@ #include "content/browser/download/download_types.h" #include "content/browser/renderer_host/global_request_id.h" #include "content/browser/renderer_host/resource_handler.h" -#include "content/public/browser/download_file.h" +#include "net/base/net_errors.h" class DownloadFileManager; class ResourceDispatcherHost; diff --git a/content/browser/download/download_types.cc b/content/browser/download/download_types.cc index 007ca56..34aceb3 100644 --- a/content/browser/download/download_types.cc +++ b/content/browser/download/download_types.cc @@ -4,13 +4,16 @@ #include "content/browser/download/download_types.h" -DownloadSaveInfo::DownloadSaveInfo() { +DownloadSaveInfo::DownloadSaveInfo() + : offset(0) { } DownloadSaveInfo::DownloadSaveInfo(const DownloadSaveInfo& info) : file_path(info.file_path), file_stream(info.file_stream), - suggested_name(info.suggested_name) { + suggested_name(info.suggested_name), + offset(info.offset), + hash_state(info.hash_state) { } DownloadSaveInfo::~DownloadSaveInfo() { @@ -20,5 +23,7 @@ DownloadSaveInfo& DownloadSaveInfo::operator=(const DownloadSaveInfo& info) { file_path = info.file_path; file_stream = info.file_stream; suggested_name = info.suggested_name; + offset = info.offset; + suggested_name = info.suggested_name; return *this; } diff --git a/content/browser/download/download_types.h b/content/browser/download/download_types.h index f0153a5..e302ed5 100644 --- a/content/browser/download/download_types.h +++ b/content/browser/download/download_types.h @@ -12,15 +12,28 @@ #include "net/base/file_stream.h" // Holds the information about how to save a download file. +// In the case of download continuation, |file_path| is set to the current file +// name, |offset| is set to the point where we left off, and |hash_state| will +// hold the state of the hash algorithm where we left off. struct CONTENT_EXPORT DownloadSaveInfo { DownloadSaveInfo(); DownloadSaveInfo(const DownloadSaveInfo& info); ~DownloadSaveInfo(); DownloadSaveInfo& operator=(const DownloadSaveInfo& info); + // This is usually the tentative final name, but not during resumption + // where it will be the intermediate file name. FilePath file_path; + linked_ptr<net::FileStream> file_stream; + string16 suggested_name; + + // The file offset at which to start the download. May be 0. + int64 offset; + + // The state of the hash at the start of the download. May be empty. + std::string hash_state; }; #endif // CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_TYPES_H_ diff --git a/content/browser/download/mock_download_file.cc b/content/browser/download/mock_download_file.cc index 7042f00..885def1 100644 --- a/content/browser/download/mock_download_file.cc +++ b/content/browser/download/mock_download_file.cc @@ -46,7 +46,7 @@ MockDownloadFile::MockDownloadFile( MockDownloadFile::~MockDownloadFile() { } -net::Error MockDownloadFile::Initialize(bool calculate_hash) { +net::Error MockDownloadFile::Initialize() { in_progress_ = true; if (recorder_) recorder_->Record(StatisticsRecorder::STAT_INITIALIZE); @@ -109,10 +109,14 @@ int64 MockDownloadFile::CurrentSpeed() const { return 0; } -bool MockDownloadFile::GetSha256Hash(std::string* hash) { +bool MockDownloadFile::GetHash(std::string* hash) { return false; } +std::string MockDownloadFile::GetHashState() { + return ""; +} + // DownloadFileInterface implementation. void MockDownloadFile::CancelDownloadRequest() { } diff --git a/content/browser/download/mock_download_file.h b/content/browser/download/mock_download_file.h index f89643c..4a2a5ef 100644 --- a/content/browser/download/mock_download_file.h +++ b/content/browser/download/mock_download_file.h @@ -62,7 +62,7 @@ class MockDownloadFile : virtual public content::DownloadFile { virtual ~MockDownloadFile(); // DownloadFile functions. - virtual net::Error Initialize(bool calculate_hash) OVERRIDE; + 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; @@ -74,7 +74,8 @@ class MockDownloadFile : virtual public content::DownloadFile { virtual bool InProgress() const OVERRIDE; virtual int64 BytesSoFar() const OVERRIDE; virtual int64 CurrentSpeed() const OVERRIDE; - virtual bool GetSha256Hash(std::string* hash) OVERRIDE; + virtual bool GetHash(std::string* hash) OVERRIDE; + virtual std::string GetHashState() OVERRIDE; virtual void CancelDownloadRequest() OVERRIDE; virtual int Id() const OVERRIDE; virtual content::DownloadManager* GetDownloadManager() OVERRIDE; diff --git a/content/browser/download/mock_download_item.h b/content/browser/download/mock_download_item.h index a46b7eb..2ffb07b 100644 --- a/content/browser/download/mock_download_item.h +++ b/content/browser/download/mock_download_item.h @@ -27,14 +27,14 @@ class MockDownloadItem : public content::DownloadItem { MOCK_METHOD0(OpenDownload, void()); MOCK_METHOD0(ShowDownloadInShell, void()); MOCK_METHOD0(DangerousDownloadValidated, void()); - MOCK_METHOD2(UpdateProgress, void(int64, int64)); + MOCK_METHOD3(UpdateProgress, void(int64, int64, const std::string&)); MOCK_METHOD1(Cancel, void(bool)); MOCK_METHOD0(MarkAsComplete, void()); MOCK_METHOD0(DelayedDownloadOpened, void()); MOCK_METHOD2(OnAllDataSaved, void(int64, const std::string&)); MOCK_METHOD0(OnDownloadedFileRemoved, void()); MOCK_METHOD0(MaybeCompleteDownload, void()); - MOCK_METHOD2(Interrupted, void(int64, InterruptReason)); + MOCK_METHOD3(Interrupted, void(int64, const std::string&, InterruptReason)); MOCK_METHOD1(Delete, void(DeleteReason)); MOCK_METHOD0(Remove, void()); MOCK_CONST_METHOD1(TimeRemaining, bool(base::TimeDelta*)); @@ -69,6 +69,7 @@ class MockDownloadItem : public content::DownloadItem { MOCK_CONST_METHOD0(GetRemoteAddress, std::string()); MOCK_CONST_METHOD0(GetTotalBytes, int64()); MOCK_CONST_METHOD0(GetReceivedBytes, int64()); + MOCK_CONST_METHOD0(GetHashState, const std::string&()); MOCK_CONST_METHOD0(GetHash, const std::string&()); MOCK_CONST_METHOD0(GetId, int32()); MOCK_CONST_METHOD0(GetGlobalId, DownloadId()); @@ -95,6 +96,8 @@ class MockDownloadItem : public content::DownloadItem { MOCK_CONST_METHOD0(IsTemporary, bool()); MOCK_METHOD1(SetOpened, void(bool)); MOCK_CONST_METHOD0(GetOpened, bool()); + MOCK_CONST_METHOD0(GetLastModifiedTime, const std::string&()); + MOCK_CONST_METHOD0(GetETag, const std::string&()); MOCK_CONST_METHOD0(GetLastReason, InterruptReason()); MOCK_CONST_METHOD0(GetPersistentStoreInfo, DownloadPersistentStoreInfo()); MOCK_CONST_METHOD0(GetStateInfo, DownloadStateInfo()); diff --git a/content/browser/download/mock_download_manager.cc b/content/browser/download/mock_download_manager.cc index 58113b7..b5f7142 100644 --- a/content/browser/download/mock_download_manager.cc +++ b/content/browser/download/mock_download_manager.cc @@ -52,8 +52,10 @@ bool MockDownloadManager::Init(content::BrowserContext* browser_context) { void MockDownloadManager::StartDownload(int32 id) { } -void MockDownloadManager::UpdateDownload(int32 download_id, int64 bytes_so_far, - int64 bytes_per_sec) { +void MockDownloadManager::UpdateDownload(int32 download_id, + int64 bytes_so_far, + int64 bytes_per_sec, + std::string hash_state) { } void MockDownloadManager::OnResponseCompleted(int32 download_id, int64 size, @@ -63,7 +65,9 @@ void MockDownloadManager::OnResponseCompleted(int32 download_id, int64 size, void MockDownloadManager::CancelDownload(int32 download_id) { } -void MockDownloadManager::OnDownloadInterrupted(int32 download_id, int64 size, +void MockDownloadManager::OnDownloadInterrupted(int32 download_id, + int64 size, + std::string hash_state, InterruptReason reason) { } diff --git a/content/browser/download/mock_download_manager.h b/content/browser/download/mock_download_manager.h index ed4f6af..d7a1f2b 100644 --- a/content/browser/download/mock_download_manager.h +++ b/content/browser/download/mock_download_manager.h @@ -30,12 +30,16 @@ class MockDownloadManager : public content::DownloadManager { DownloadVector* result) OVERRIDE; virtual bool Init(content::BrowserContext* browser_context) OVERRIDE; virtual void StartDownload(int32 id) OVERRIDE; - virtual void UpdateDownload(int32 download_id, int64 bytes_so_far, - int64 bytes_per_sec) OVERRIDE; + virtual void UpdateDownload(int32 download_id, + int64 bytes_so_far, + int64 bytes_per_sec, + std::string hash_state) OVERRIDE; virtual void OnResponseCompleted(int32 download_id, int64 size, const std::string& hash) OVERRIDE; virtual void CancelDownload(int32 download_id) OVERRIDE; - virtual void OnDownloadInterrupted(int32 download_id, int64 size, + virtual void OnDownloadInterrupted(int32 download_id, + int64 size, + std::string hash_state, InterruptReason reason) OVERRIDE; virtual void OnDownloadRenamedToFinalName(int download_id, const FilePath& full_path, diff --git a/content/browser/download/save_file.cc b/content/browser/download/save_file.cc index 599ad76..d15aaa1 100644 --- a/content/browser/download/save_file.cc +++ b/content/browser/download/save_file.cc @@ -10,8 +10,14 @@ using content::BrowserThread; -SaveFile::SaveFile(const SaveFileCreateInfo* info) - : file_(FilePath(), info->url, GURL(), 0, linked_ptr<net::FileStream>()), +SaveFile::SaveFile(const SaveFileCreateInfo* info, bool calculate_hash) + : file_(FilePath(), + info->url, + GURL(), + 0, + calculate_hash, + "", + linked_ptr<net::FileStream>()), info_(info) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); @@ -23,8 +29,8 @@ SaveFile::~SaveFile() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); } -net::Error SaveFile::Initialize(bool calculate_hash) { - return file_.Initialize(calculate_hash); +net::Error SaveFile::Initialize() { + return file_.Initialize(); } net::Error SaveFile::AppendDataToFile(const char* data, size_t data_len) { @@ -63,8 +69,8 @@ int64 SaveFile::BytesSoFar() const { return file_.bytes_so_far(); } -bool SaveFile::GetSha256Hash(std::string* hash) { - return file_.GetSha256Hash(hash); +bool SaveFile::GetHash(std::string* hash) { + return file_.GetHash(hash); } std::string SaveFile::DebugString() const { diff --git a/content/browser/download/save_file.h b/content/browser/download/save_file.h index baa863d..9f3710f 100644 --- a/content/browser/download/save_file.h +++ b/content/browser/download/save_file.h @@ -21,11 +21,11 @@ // in a save session. class SaveFile { public: - explicit SaveFile(const SaveFileCreateInfo* info); + explicit SaveFile(const SaveFileCreateInfo* info, bool calculate_hash); virtual ~SaveFile(); // BaseFile delegated functions. - net::Error Initialize(bool calculate_hash); + net::Error Initialize(); net::Error AppendDataToFile(const char* data, size_t data_len); net::Error Rename(const FilePath& full_path); void Detach(); @@ -35,7 +35,7 @@ class SaveFile { FilePath FullPath() const; bool InProgress() const; int64 BytesSoFar() const; - bool GetSha256Hash(std::string* hash); + bool GetHash(std::string* hash); std::string DebugString() const; // Accessors. diff --git a/content/browser/download/save_file_manager.cc b/content/browser/download/save_file_manager.cc index 99a9f49..467df60 100644 --- a/content/browser/download/save_file_manager.cc +++ b/content/browser/download/save_file_manager.cc @@ -213,10 +213,11 @@ void SaveFileManager::SendCancelRequest(int save_id) { void SaveFileManager::StartSave(SaveFileCreateInfo* info) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); DCHECK(info); - SaveFile* save_file = new SaveFile(info); + // No need to calculate hash. + SaveFile* save_file = new SaveFile(info, false); // TODO(phajdan.jr): We should check the return value and handle errors here. - save_file->Initialize(false); // No need to calculate hash. + save_file->Initialize(); DCHECK(!LookupSaveFile(info->save_id)); save_file_map_[info->save_id] = save_file; diff --git a/content/browser/download/save_package.cc b/content/browser/download/save_package.cc index b1f3516..e84ef66 100644 --- a/content/browser/download/save_package.cc +++ b/content/browser/download/save_package.cc @@ -705,7 +705,7 @@ void SavePackage::SaveFinished(int32 save_id, int64 size, bool is_success) { // Inform the DownloadItem to update UI. // We use the received bytes as number of saved files. if (download_) - download_->UpdateProgress(completed_count(), CurrentSpeed()); + download_->UpdateProgress(completed_count(), CurrentSpeed(), ""); if (save_item->save_source() == SaveFileCreateInfo::SAVE_FILE_FROM_DOM && save_item->url() == page_url_ && !save_item->received_bytes()) { @@ -747,7 +747,7 @@ void SavePackage::SaveFailed(const GURL& save_url) { // Inform the DownloadItem to update UI. // We use the received bytes as number of saved files. if (download_) - download_->UpdateProgress(completed_count(), CurrentSpeed()); + download_->UpdateProgress(completed_count(), CurrentSpeed(), ""); if (save_type_ == SAVE_AS_ONLY_HTML || save_item->save_source() == SaveFileCreateInfo::SAVE_FILE_FROM_DOM) { diff --git a/content/browser/renderer_host/resource_dispatcher_host_unittest.cc b/content/browser/renderer_host/resource_dispatcher_host_unittest.cc index 9da45d5..8a45b66 100644 --- a/content/browser/renderer_host/resource_dispatcher_host_unittest.cc +++ b/content/browser/renderer_host/resource_dispatcher_host_unittest.cc @@ -34,6 +34,10 @@ #include "testing/gtest/include/gtest/gtest.h" #include "webkit/appcache/appcache_interfaces.h" +namespace content { +class DownloadManager; +} // namespace content + using content::BrowserThread; using content::BrowserThreadImpl; using content::ChildProcessHostImpl; diff --git a/content/public/browser/download_file.h b/content/public/browser/download_file.h index f55d9d3..4a84282 100644 --- a/content/public/browser/download_file.h +++ b/content/public/browser/download_file.h @@ -28,7 +28,7 @@ class CONTENT_EXPORT DownloadFile { // If calculate_hash is true, sha256 hash will be calculated. // Returns net::OK on success, or a network error code on failure. - virtual net::Error Initialize(bool calculate_hash) = 0; + 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), @@ -58,7 +58,10 @@ class CONTENT_EXPORT DownloadFile { // Set |hash| with sha256 digest for the file. // Returns true if digest is successfully calculated. - virtual bool GetSha256Hash(std::string* hash) = 0; + virtual bool GetHash(std::string* hash) = 0; + + // Returns the current (intermediate) state of the hash as a byte string. + virtual std::string GetHashState() = 0; // Cancels the download request associated with this file. virtual void CancelDownloadRequest() = 0; diff --git a/content/public/browser/download_item.h b/content/public/browser/download_item.h index ea6d87b..0d194842 100644 --- a/content/public/browser/download_item.h +++ b/content/public/browser/download_item.h @@ -139,7 +139,11 @@ class CONTENT_EXPORT DownloadItem { // Called periodically from the download thread, or from the UI thread // for saving packages. - virtual void UpdateProgress(int64 bytes_so_far, int64 bytes_per_sec) = 0; + // |bytes_so_far| is the number of bytes received so far. + // |hash_state| is the current hash state. + virtual void UpdateProgress(int64 bytes_so_far, + int64 bytes_per_sec, + const std::string& hash_state) = 0; // Cancel the download operation. We need to distinguish between cancels at // exit (DownloadManager destructor) from user interface initiated cancels @@ -174,8 +178,11 @@ class CONTENT_EXPORT DownloadItem { // Download operation had an error. // |size| is the amount of data received at interruption. + // |hash_state| is the current hash state at interruption. // |reason| is the download interrupt reason code that the operation received. - virtual void Interrupted(int64 size, InterruptReason reason) = 0; + virtual void Interrupted(int64 size, + const std::string& hash_state, + InterruptReason reason) = 0; // Deletes the file from disk and removes the download from the views and // history. |user| should be true if this is the result of the user clicking @@ -261,6 +268,7 @@ class CONTENT_EXPORT DownloadItem { virtual int64 GetTotalBytes() const = 0; virtual void SetTotalBytes(int64 total_bytes) = 0; virtual int64 GetReceivedBytes() const = 0; + virtual const std::string& GetHashState() const = 0; virtual int32 GetId() const = 0; virtual DownloadId GetGlobalId() const = 0; virtual base::Time GetStartTime() const = 0; @@ -288,6 +296,9 @@ class CONTENT_EXPORT DownloadItem { virtual void SetOpened(bool opened) = 0; virtual bool GetOpened() const = 0; + virtual const std::string& GetLastModifiedTime() const = 0; + virtual const std::string& GetETag() const = 0; + virtual InterruptReason GetLastReason() const = 0; virtual DownloadPersistentStoreInfo GetPersistentStoreInfo() const = 0; virtual DownloadStateInfo GetStateInfo() const = 0; diff --git a/content/public/browser/download_manager.h b/content/public/browser/download_manager.h index a5e31fc..c123cc0 100644 --- a/content/public/browser/download_manager.h +++ b/content/public/browser/download_manager.h @@ -105,8 +105,10 @@ class CONTENT_EXPORT DownloadManager // Notifications sent from the download thread to the UI thread virtual void StartDownload(int32 id) = 0; - virtual void UpdateDownload(int32 download_id, int64 bytes_so_far, - int64 bytes_per_sec) = 0; + virtual void UpdateDownload(int32 download_id, + int64 bytes_so_far, + int64 bytes_per_sec, + std::string hash_state) = 0; // |download_id| is the ID of the download. // |size| is the number of bytes that have been downloaded. @@ -122,8 +124,12 @@ class CONTENT_EXPORT DownloadManager // Called when there is an error in the download. // |download_id| is the ID of the download. // |size| is the number of bytes that are currently downloaded. + // |hash_state| is the current state of the hash of the data that has been + // downloaded. // |reason| is a download interrupt reason code. - virtual void OnDownloadInterrupted(int32 download_id, int64 size, + virtual void OnDownloadInterrupted(int32 download_id, + int64 size, + std::string hash_state, InterruptReason reason) = 0; // Called when the download is renamed to its final name. |