OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/download/base_file.h" | 5 #include "content/browser/download/base_file.h" |
6 | 6 |
7 #include <utility> | 7 #include <utility> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/files/file.h" | 10 #include "base/files/file.h" |
11 #include "base/files/file_util.h" | 11 #include "base/files/file_util.h" |
12 #include "base/format_macros.h" | 12 #include "base/format_macros.h" |
13 #include "base/logging.h" | 13 #include "base/logging.h" |
14 #include "base/pickle.h" | 14 #include "base/pickle.h" |
15 #include "base/strings/stringprintf.h" | 15 #include "base/strings/stringprintf.h" |
16 #include "base/threading/thread_restrictions.h" | 16 #include "base/threading/thread_restrictions.h" |
17 #include "build/build_config.h" | 17 #include "build/build_config.h" |
18 #include "content/browser/download/download_interrupt_reasons_impl.h" | 18 #include "content/browser/download/download_interrupt_reasons_impl.h" |
19 #include "content/browser/download/download_net_log_parameters.h" | 19 #include "content/browser/download/download_net_log_parameters.h" |
20 #include "content/browser/download/download_stats.h" | 20 #include "content/browser/download/download_stats.h" |
21 #include "content/public/browser/browser_thread.h" | 21 #include "content/public/browser/browser_thread.h" |
22 #include "content/public/browser/content_browser_client.h" | 22 #include "content/public/browser/content_browser_client.h" |
23 #include "crypto/secure_hash.h" | 23 #include "crypto/secure_hash.h" |
24 #include "net/base/net_errors.h" | 24 #include "net/base/net_errors.h" |
25 | 25 |
26 namespace content { | 26 namespace content { |
27 | 27 |
28 // This will initialize the entire array to zero. | 28 BaseFile::BaseFile(const net::BoundNetLog& bound_net_log) |
29 const unsigned char BaseFile::kEmptySha256Hash[] = { 0 }; | 29 : bound_net_log_(bound_net_log) {} |
30 | |
31 BaseFile::BaseFile(const base::FilePath& full_path, | |
32 const GURL& source_url, | |
33 const GURL& referrer_url, | |
34 int64_t received_bytes, | |
35 bool calculate_hash, | |
36 const std::string& hash_state_bytes, | |
37 base::File file, | |
38 const net::BoundNetLog& bound_net_log) | |
39 : full_path_(full_path), | |
40 source_url_(source_url), | |
41 referrer_url_(referrer_url), | |
42 file_(std::move(file)), | |
43 bytes_so_far_(received_bytes), | |
44 start_tick_(base::TimeTicks::Now()), | |
45 calculate_hash_(calculate_hash), | |
46 detached_(false), | |
47 bound_net_log_(bound_net_log) { | |
48 memcpy(sha256_hash_, kEmptySha256Hash, crypto::kSHA256Length); | |
49 if (calculate_hash_) { | |
50 secure_hash_.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256)); | |
51 if ((bytes_so_far_ > 0) && // Not starting at the beginning. | |
52 (!IsEmptyHash(hash_state_bytes))) { | |
53 base::Pickle hash_state(hash_state_bytes.c_str(), | |
54 hash_state_bytes.size()); | |
55 base::PickleIterator data_iterator(hash_state); | |
56 secure_hash_->Deserialize(&data_iterator); | |
57 } | |
58 } | |
59 } | |
60 | 30 |
61 BaseFile::~BaseFile() { | 31 BaseFile::~BaseFile() { |
62 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 32 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
63 if (detached_) | 33 if (detached_) |
64 Close(); | 34 Close(); |
65 else | 35 else |
66 Cancel(); // Will delete the file. | 36 Cancel(); // Will delete the file. |
67 } | 37 } |
68 | 38 |
69 DownloadInterruptReason BaseFile::Initialize( | 39 DownloadInterruptReason BaseFile::Initialize( |
70 const base::FilePath& default_directory) { | 40 const base::FilePath& full_path, |
41 const base::FilePath& default_directory, | |
42 base::File file, | |
43 int64_t bytes_so_far, | |
44 const std::string& hash_so_far, | |
45 scoped_ptr<crypto::SecureHash> hash_state) { | |
71 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 46 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
72 DCHECK(!detached_); | 47 DCHECK(!detached_); |
73 | 48 |
74 if (full_path_.empty()) { | 49 if (full_path.empty()) { |
75 base::FilePath initial_directory(default_directory); | 50 base::FilePath initial_directory(default_directory); |
76 base::FilePath temp_file; | 51 base::FilePath temp_file; |
77 if (initial_directory.empty()) { | 52 if (initial_directory.empty()) { |
78 initial_directory = | 53 initial_directory = |
79 GetContentClient()->browser()->GetDefaultDownloadDirectory(); | 54 GetContentClient()->browser()->GetDefaultDownloadDirectory(); |
80 } | 55 } |
81 // |initial_directory| can still be empty if ContentBrowserClient returned | 56 // |initial_directory| can still be empty if ContentBrowserClient returned |
82 // an empty path for the downloads directory. | 57 // an empty path for the downloads directory. |
83 if ((initial_directory.empty() || | 58 if ((initial_directory.empty() || |
84 !base::CreateTemporaryFileInDir(initial_directory, &temp_file)) && | 59 !base::CreateTemporaryFileInDir(initial_directory, &temp_file)) && |
85 !base::CreateTemporaryFile(&temp_file)) { | 60 !base::CreateTemporaryFile(&temp_file)) { |
86 return LogInterruptReason("Unable to create", 0, | 61 return LogInterruptReason("Unable to create", 0, |
87 DOWNLOAD_INTERRUPT_REASON_FILE_FAILED); | 62 DOWNLOAD_INTERRUPT_REASON_FILE_FAILED); |
88 } | 63 } |
89 full_path_ = temp_file; | 64 full_path_ = temp_file; |
65 } else { | |
66 full_path_ = full_path; | |
90 } | 67 } |
91 | 68 |
92 return Open(); | 69 bytes_so_far_ = bytes_so_far; |
70 secure_hash_ = std::move(hash_state); | |
71 file_ = std::move(file); | |
72 | |
73 return Open(hash_so_far); | |
93 } | 74 } |
94 | 75 |
95 DownloadInterruptReason BaseFile::AppendDataToFile(const char* data, | 76 DownloadInterruptReason BaseFile::AppendDataToFile(const char* data, |
96 size_t data_len) { | 77 size_t data_len) { |
97 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 78 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
98 DCHECK(!detached_); | 79 DCHECK(!detached_); |
99 | 80 |
100 // NOTE(benwells): The above DCHECK won't be present in release builds, | 81 // NOTE(benwells): The above DCHECK won't be present in release builds, |
101 // so we log any occurences to see how common this error is in the wild. | 82 // so we log any occurences to see how common this error is in the wild. |
102 if (detached_) | 83 if (detached_) |
(...skipping 24 matching lines...) Expand all Loading... | |
127 size_t write_size = static_cast<size_t>(write_result); | 108 size_t write_size = static_cast<size_t>(write_result); |
128 DCHECK_LE(write_size, len); | 109 DCHECK_LE(write_size, len); |
129 len -= write_size; | 110 len -= write_size; |
130 current_data += write_size; | 111 current_data += write_size; |
131 bytes_so_far_ += write_size; | 112 bytes_so_far_ += write_size; |
132 } | 113 } |
133 | 114 |
134 RecordDownloadWriteSize(data_len); | 115 RecordDownloadWriteSize(data_len); |
135 RecordDownloadWriteLoopCount(write_count); | 116 RecordDownloadWriteLoopCount(write_count); |
136 | 117 |
137 if (calculate_hash_) | 118 if (secure_hash_) |
138 secure_hash_->Update(data, data_len); | 119 secure_hash_->Update(data, data_len); |
139 | 120 |
140 return DOWNLOAD_INTERRUPT_REASON_NONE; | 121 return DOWNLOAD_INTERRUPT_REASON_NONE; |
141 } | 122 } |
142 | 123 |
143 DownloadInterruptReason BaseFile::Rename(const base::FilePath& new_path) { | 124 DownloadInterruptReason BaseFile::Rename(const base::FilePath& new_path) { |
144 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 125 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
145 DownloadInterruptReason rename_result = DOWNLOAD_INTERRUPT_REASON_NONE; | 126 DownloadInterruptReason rename_result = DOWNLOAD_INTERRUPT_REASON_NONE; |
146 | 127 |
147 // If the new path is same as the old one, there is no need to perform the | 128 // If the new path is same as the old one, there is no need to perform the |
(...skipping 15 matching lines...) Expand all Loading... | |
163 // permissions / security descriptors that makes sense in the new directory. | 144 // permissions / security descriptors that makes sense in the new directory. |
164 rename_result = MoveFileAndAdjustPermissions(new_path); | 145 rename_result = MoveFileAndAdjustPermissions(new_path); |
165 | 146 |
166 if (rename_result == DOWNLOAD_INTERRUPT_REASON_NONE) | 147 if (rename_result == DOWNLOAD_INTERRUPT_REASON_NONE) |
167 full_path_ = new_path; | 148 full_path_ = new_path; |
168 | 149 |
169 // Re-open the file if we were still using it regardless of the interrupt | 150 // Re-open the file if we were still using it regardless of the interrupt |
170 // reason. | 151 // reason. |
171 DownloadInterruptReason open_result = DOWNLOAD_INTERRUPT_REASON_NONE; | 152 DownloadInterruptReason open_result = DOWNLOAD_INTERRUPT_REASON_NONE; |
172 if (was_in_progress) | 153 if (was_in_progress) |
173 open_result = Open(); | 154 open_result = Open(std::string()); |
174 | 155 |
175 bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_FILE_RENAMED); | 156 bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_FILE_RENAMED); |
176 return rename_result == DOWNLOAD_INTERRUPT_REASON_NONE ? open_result | 157 return rename_result == DOWNLOAD_INTERRUPT_REASON_NONE ? open_result |
177 : rename_result; | 158 : rename_result; |
178 } | 159 } |
179 | 160 |
180 void BaseFile::Detach() { | 161 void BaseFile::Detach() { |
181 detached_ = true; | 162 detached_ = true; |
182 bound_net_log_.AddEvent(net::NetLog::TYPE_DOWNLOAD_FILE_DETACHED); | 163 bound_net_log_.AddEvent(net::NetLog::TYPE_DOWNLOAD_FILE_DETACHED); |
183 } | 164 } |
184 | 165 |
185 void BaseFile::Cancel() { | 166 void BaseFile::Cancel() { |
186 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 167 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
187 DCHECK(!detached_); | 168 DCHECK(!detached_); |
188 | 169 |
189 bound_net_log_.AddEvent(net::NetLog::TYPE_CANCELLED); | 170 bound_net_log_.AddEvent(net::NetLog::TYPE_CANCELLED); |
190 | 171 |
191 Close(); | 172 Close(); |
192 | 173 |
193 if (!full_path_.empty()) { | 174 if (!full_path_.empty()) { |
194 bound_net_log_.AddEvent(net::NetLog::TYPE_DOWNLOAD_FILE_DELETED); | 175 bound_net_log_.AddEvent(net::NetLog::TYPE_DOWNLOAD_FILE_DELETED); |
195 base::DeleteFile(full_path_, false); | 176 base::DeleteFile(full_path_, false); |
196 } | 177 } |
197 | 178 |
198 Detach(); | 179 Detach(); |
199 } | 180 } |
200 | 181 |
201 void BaseFile::Finish() { | 182 scoped_ptr<crypto::SecureHash> BaseFile::Finish() { |
202 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | |
203 | |
204 if (calculate_hash_) | |
205 secure_hash_->Finish(sha256_hash_, crypto::kSHA256Length); | |
206 Close(); | |
207 } | |
208 | |
209 void BaseFile::FinishWithError() { | |
210 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 183 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
211 Close(); | 184 Close(); |
212 } | 185 return std::move(secure_hash_); |
213 | |
214 void BaseFile::SetClientGuid(const std::string& guid) { | |
215 client_guid_ = guid; | |
216 } | 186 } |
217 | 187 |
218 // OS_WIN, OS_MACOSX and OS_LINUX have specialized implementations. | 188 // OS_WIN, OS_MACOSX and OS_LINUX have specialized implementations. |
219 #if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_LINUX) | 189 #if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_LINUX) |
220 DownloadInterruptReason BaseFile::AnnotateWithSourceInformation() { | 190 DownloadInterruptReason BaseFile::AnnotateWithSourceInformation( |
191 const std::string& client_guid, | |
192 const GURL& source_url, | |
193 const GURL& referrer_url) { | |
221 return DOWNLOAD_INTERRUPT_REASON_NONE; | 194 return DOWNLOAD_INTERRUPT_REASON_NONE; |
222 } | 195 } |
223 #endif | 196 #endif |
224 | 197 |
225 bool BaseFile::GetHash(std::string* hash) { | 198 std::string BaseFile::DebugString() const { |
226 DCHECK(!detached_); | 199 return base::StringPrintf( |
227 hash->assign(reinterpret_cast<const char*>(sha256_hash_), | 200 "{ " |
228 sizeof(sha256_hash_)); | 201 " full_path_ = \"%" PRFilePath |
229 return (calculate_hash_ && !in_progress()); | 202 "\"" |
203 " bytes_so_far_ = %" PRId64 " detached_ = %c }", | |
204 full_path_.value().c_str(), bytes_so_far_, detached_ ? 'T' : 'F'); | |
230 } | 205 } |
231 | 206 |
232 std::string BaseFile::GetHashState() { | 207 DownloadInterruptReason BaseFile::CalculatePartialHash( |
233 if (!calculate_hash_) | 208 const std::string& hash_to_expect) { |
234 return std::string(); | 209 secure_hash_.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256)); |
235 | 210 |
236 base::Pickle hash_state; | 211 if (bytes_so_far_ == 0) |
237 if (!secure_hash_->Serialize(&hash_state)) | 212 return DOWNLOAD_INTERRUPT_REASON_NONE; |
238 return std::string(); | |
239 | 213 |
240 return std::string(reinterpret_cast<const char*>(hash_state.data()), | 214 if (file_.Seek(base::File::FROM_BEGIN, 0) != 0) |
241 hash_state.size()); | 215 return LogSystemError("Seek partial file", |
216 logging::GetLastSystemErrorCode()); | |
217 | |
218 const size_t kMinBufferSize = secure_hash_->GetHashLength(); | |
219 const size_t kMaxBufferSize = 1024 * 512; | |
220 | |
221 // The size of the buffer is: | |
222 // - at least kMinBufferSize so that we can use it to hold the hash as well. | |
223 // - at most kMaxBufferSize so that there's a reasonable bound. | |
224 // - not larger than |bytes_so_far_| unless bytes_so_far_ is less than the | |
225 // hash size. | |
226 std::vector<char> buffer(std::max( | |
227 kMinBufferSize, std::min<size_t>(kMaxBufferSize, bytes_so_far_))); | |
228 | |
229 int64_t current_position = 0; | |
230 while (current_position < bytes_so_far_) { | |
231 int length = file_.ReadAtCurrentPos(&buffer.front(), buffer.size()); | |
232 if (length == -1) { | |
233 return LogInterruptReason("Reading partial file", | |
234 logging::GetLastSystemErrorCode(), | |
235 DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT); | |
236 } | |
237 | |
238 if (length == 0) | |
239 break; | |
240 | |
241 secure_hash_->Update(&buffer.front(), length); | |
242 current_position += length; | |
243 } | |
244 | |
245 if (current_position != bytes_so_far_) { | |
246 return LogInterruptReason("Verifying prefix hash", 0, | |
247 DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT); | |
248 } | |
249 | |
250 if (!hash_to_expect.empty()) { | |
251 DCHECK_EQ(secure_hash_->GetHashLength(), hash_to_expect.size()); | |
252 DCHECK(buffer.size() >= secure_hash_->GetHashLength()); | |
253 scoped_ptr<crypto::SecureHash> partial_hash(secure_hash_->Clone()); | |
254 partial_hash->Finish(&buffer.front(), buffer.size()); | |
255 | |
256 if (memcmp(&buffer.front(), hash_to_expect.c_str(), | |
257 partial_hash->GetHashLength())) { | |
258 return LogInterruptReason("Verifying prefix hash", 0, | |
259 DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH); | |
260 } | |
261 } | |
262 | |
263 return DOWNLOAD_INTERRUPT_REASON_NONE; | |
242 } | 264 } |
243 | 265 |
244 // static | 266 DownloadInterruptReason BaseFile::Open(const std::string& hash_so_far) { |
245 bool BaseFile::IsEmptyHash(const std::string& hash) { | |
246 return (hash.size() == crypto::kSHA256Length && | |
247 0 == memcmp(hash.data(), kEmptySha256Hash, crypto::kSHA256Length)); | |
248 } | |
249 | |
250 std::string BaseFile::DebugString() const { | |
251 return base::StringPrintf("{ source_url_ = \"%s\"" | |
252 " full_path_ = \"%" PRFilePath "\"" | |
253 " bytes_so_far_ = %" PRId64 | |
254 " detached_ = %c }", | |
255 source_url_.spec().c_str(), | |
256 full_path_.value().c_str(), | |
257 bytes_so_far_, | |
258 detached_ ? 'T' : 'F'); | |
259 } | |
260 | |
261 DownloadInterruptReason BaseFile::Open() { | |
262 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 267 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
263 DCHECK(!detached_); | 268 DCHECK(!detached_); |
264 DCHECK(!full_path_.empty()); | 269 DCHECK(!full_path_.empty()); |
265 | 270 |
271 // Create a new file if it is not provided. | |
272 if (!file_.IsValid()) { | |
273 file_.Initialize(full_path_, base::File::FLAG_OPEN_ALWAYS | | |
274 base::File::FLAG_WRITE | | |
275 base::File::FLAG_READ); | |
276 if (!file_.IsValid()) { | |
277 return LogNetError("Open/Initialize File", | |
278 net::FileErrorToNetError(file_.error_details())); | |
279 } | |
280 } | |
281 | |
266 bound_net_log_.BeginEvent( | 282 bound_net_log_.BeginEvent( |
267 net::NetLog::TYPE_DOWNLOAD_FILE_OPENED, | 283 net::NetLog::TYPE_DOWNLOAD_FILE_OPENED, |
268 base::Bind(&FileOpenedNetLogCallback, &full_path_, bytes_so_far_)); | 284 base::Bind(&FileOpenedNetLogCallback, &full_path_, bytes_so_far_)); |
269 | 285 |
270 // Create a new file if it is not provided. | 286 if (!secure_hash_) { |
svaldez
2016/03/09 19:27:33
|| secure_hash_.empty()? (Might not be necessaru)
asanka
2016/03/10 16:48:08
secure_hash_ is a scoped_ptr<crypto::SecureHash>.
| |
271 if (!file_.IsValid()) { | 287 DownloadInterruptReason reason = CalculatePartialHash(hash_so_far); |
272 file_.Initialize( | 288 if (reason != DOWNLOAD_INTERRUPT_REASON_NONE) { |
273 full_path_, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE); | 289 ClearFile(); |
274 if (!file_.IsValid()) { | 290 return reason; |
275 return LogNetError("Open", | |
276 net::FileErrorToNetError(file_.error_details())); | |
277 } | 291 } |
278 } | 292 } |
279 | 293 |
280 // We may be re-opening the file after rename. Always make sure we're | |
281 // writing at the end of the file. | |
282 int64_t file_size = file_.Seek(base::File::FROM_END, 0); | 294 int64_t file_size = file_.Seek(base::File::FROM_END, 0); |
283 if (file_size < 0) { | 295 if (file_size < 0) { |
284 logging::SystemErrorCode error = logging::GetLastSystemErrorCode(); | 296 logging::SystemErrorCode error = logging::GetLastSystemErrorCode(); |
285 ClearFile(); | 297 ClearFile(); |
286 return LogSystemError("Seek", error); | 298 return LogSystemError("Seeking to end", error); |
287 } else if (file_size > bytes_so_far_) { | 299 } else if (file_size > bytes_so_far_) { |
288 // The file is larger than we expected. | 300 // The file is larger than we expected. |
289 // This is OK, as long as we don't use the extra. | 301 // This is OK, as long as we don't use the extra. |
290 // Truncate the file. | 302 // Truncate the file. |
291 if (!file_.SetLength(bytes_so_far_) || | 303 if (!file_.SetLength(bytes_so_far_) || |
292 file_.Seek(base::File::FROM_BEGIN, bytes_so_far_) != bytes_so_far_) { | 304 file_.Seek(base::File::FROM_BEGIN, bytes_so_far_) != bytes_so_far_) { |
293 logging::SystemErrorCode error = logging::GetLastSystemErrorCode(); | 305 logging::SystemErrorCode error = logging::GetLastSystemErrorCode(); |
294 ClearFile(); | 306 ClearFile(); |
295 return LogSystemError("Truncate", error); | 307 return LogSystemError("Truncating to last known offset", error); |
296 } | 308 } |
297 } else if (file_size < bytes_so_far_) { | 309 } else if (file_size < bytes_so_far_) { |
298 // The file is shorter than we expected. Our hashes won't be valid. | 310 // The file is shorter than we expected. Our hashes won't be valid. |
299 ClearFile(); | 311 ClearFile(); |
300 return LogInterruptReason("Unable to seek to last written point", 0, | 312 return LogInterruptReason("Unable to seek to last written point", 0, |
301 DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT); | 313 DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT); |
302 } | 314 } |
303 | 315 |
304 return DOWNLOAD_INTERRUPT_REASON_NONE; | 316 return DOWNLOAD_INTERRUPT_REASON_NONE; |
305 } | 317 } |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
340 base::File::Error file_error = base::File::OSErrorToFileError(os_error); | 352 base::File::Error file_error = base::File::OSErrorToFileError(os_error); |
341 return LogInterruptReason( | 353 return LogInterruptReason( |
342 operation, os_error, | 354 operation, os_error, |
343 ConvertFileErrorToInterruptReason(file_error)); | 355 ConvertFileErrorToInterruptReason(file_error)); |
344 } | 356 } |
345 | 357 |
346 DownloadInterruptReason BaseFile::LogInterruptReason( | 358 DownloadInterruptReason BaseFile::LogInterruptReason( |
347 const char* operation, | 359 const char* operation, |
348 int os_error, | 360 int os_error, |
349 DownloadInterruptReason reason) { | 361 DownloadInterruptReason reason) { |
362 DVLOG(1) << __FUNCTION__ << "() operation:" << operation | |
363 << " os_error:" << os_error | |
364 << " reason:" << DownloadInterruptReasonToString(reason); | |
350 bound_net_log_.AddEvent( | 365 bound_net_log_.AddEvent( |
351 net::NetLog::TYPE_DOWNLOAD_FILE_ERROR, | 366 net::NetLog::TYPE_DOWNLOAD_FILE_ERROR, |
352 base::Bind(&FileInterruptedNetLogCallback, operation, os_error, reason)); | 367 base::Bind(&FileInterruptedNetLogCallback, operation, os_error, reason)); |
353 return reason; | 368 return reason; |
354 } | 369 } |
355 | 370 |
356 } // namespace content | 371 } // namespace content |
OLD | NEW |