Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(443)

Side by Side Diff: content/browser/download/download_item_impl.cc

Issue 10912173: Replace the DownloadFileManager with direct ownership of DownloadFileImpl (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Sync'd to LKGR (r162700) Created 8 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 // File method ordering: Methods in this file are in the same order as 5 // File method ordering: Methods in this file are in the same order as
6 // in download_item_impl.h, with the following exception: The public 6 // in download_item_impl.h, with the following exception: The public
7 // interfaces DelayedDownloadOpened, OnDownloadTargetDetermined, 7 // interfaces Start, DelayedDownloadOpened, MaybeCompleteDownload, and
8 // MaybeCompleteDownload, and OnDownloadCompleting are placed in 8 // OnDownloadCompleting are placed in chronological order with the other
9 // chronological order with the other (private) routines that together 9 // (private) routines that together define a DownloadItem's state transitions
10 // define a DownloadItem's state transitions as the download 10 // as the download progresses. See "Download progression cascade" later in
11 // progresses. See "Download progression cascade" later in this file. 11 // this file.
12 12
13 // A regular DownloadItem (created for a download in this session of the 13 // A regular DownloadItem (created for a download in this session of the
14 // browser) normally goes through the following states: 14 // browser) normally goes through the following states:
15 // * Created (when download starts) 15 // * Created (when download starts)
16 // * Destination filename determined 16 // * Destination filename determined
17 // * Entered into the history database. 17 // * Entered into the history database.
18 // * Made visible in the download shelf. 18 // * Made visible in the download shelf.
19 // * All the data is saved. Note that the actual data download occurs 19 // * All the data is saved. Note that the actual data download occurs
20 // in parallel with the above steps, but until those steps are 20 // in parallel with the above steps, but until those steps are
21 // complete, the state of the data save will be ignored. 21 // complete, the state of the data save will be ignored.
22 // * Download file is renamed to its final name, and possibly 22 // * Download file is renamed to its final name, and possibly
23 // auto-opened. 23 // auto-opened.
24 24
25 #include "content/browser/download/download_item_impl.h" 25 #include "content/browser/download/download_item_impl.h"
26 26
27 #include <vector> 27 #include <vector>
28 28
29 #include "base/basictypes.h" 29 #include "base/basictypes.h"
30 #include "base/bind.h" 30 #include "base/bind.h"
31 #include "base/file_util.h" 31 #include "base/file_util.h"
32 #include "base/format_macros.h" 32 #include "base/format_macros.h"
33 #include "base/logging.h" 33 #include "base/logging.h"
34 #include "base/metrics/histogram.h" 34 #include "base/metrics/histogram.h"
35 #include "base/stl_util.h" 35 #include "base/stl_util.h"
36 #include "base/stringprintf.h" 36 #include "base/stringprintf.h"
37 #include "base/utf_string_conversions.h" 37 #include "base/utf_string_conversions.h"
38 #include "content/browser/download/download_create_info.h" 38 #include "content/browser/download/download_create_info.h"
39 #include "content/browser/download/download_file.h" 39 #include "content/browser/download/download_file.h"
40 #include "content/browser/download/download_file_manager.h"
41 #include "content/browser/download/download_interrupt_reasons_impl.h" 40 #include "content/browser/download/download_interrupt_reasons_impl.h"
42 #include "content/browser/download/download_item_impl_delegate.h" 41 #include "content/browser/download/download_item_impl_delegate.h"
43 #include "content/browser/download/download_request_handle.h" 42 #include "content/browser/download/download_request_handle.h"
44 #include "content/browser/download/download_stats.h" 43 #include "content/browser/download/download_stats.h"
45 #include "content/browser/web_contents/web_contents_impl.h" 44 #include "content/browser/web_contents/web_contents_impl.h"
46 #include "content/public/browser/browser_thread.h" 45 #include "content/public/browser/browser_thread.h"
47 #include "content/public/browser/content_browser_client.h" 46 #include "content/public/browser/content_browser_client.h"
48 #include "content/public/browser/download_persistent_store_info.h" 47 #include "content/public/browser/download_persistent_store_info.h"
49 #include "net/base/net_util.h" 48 #include "net/base/net_util.h"
50 49
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
97 return NULL; 96 return NULL;
98 } 97 }
99 virtual void PauseRequest() const OVERRIDE {} 98 virtual void PauseRequest() const OVERRIDE {}
100 virtual void ResumeRequest() const OVERRIDE {} 99 virtual void ResumeRequest() const OVERRIDE {}
101 virtual void CancelRequest() const OVERRIDE {} 100 virtual void CancelRequest() const OVERRIDE {}
102 virtual std::string DebugString() const OVERRIDE { 101 virtual std::string DebugString() const OVERRIDE {
103 return "Null DownloadRequestHandle"; 102 return "Null DownloadRequestHandle";
104 } 103 }
105 }; 104 };
106 105
106 // Wrapper around DownloadFile::Detach and DownloadFile::Cancel that
107 // takes ownership of the DownloadFile and hence implicitly destroys it
108 // at the end of the function.
109 static void DownloadFileDetach(
110 scoped_ptr<DownloadFile> download_file, base::Closure callback) {
111 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
112 download_file->Detach(callback);
113 }
114
115 static void DownloadFileCancel(scoped_ptr<DownloadFile> download_file) {
116 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
117 download_file->Cancel();
118 }
119
107 } // namespace 120 } // namespace
108 121
109 namespace content { 122 namespace content {
110 123
111 // Our download table ID starts at 1, so we use 0 to represent a download that 124 // Our download table ID starts at 1, so we use 0 to represent a download that
112 // has started, but has not yet had its data persisted in the table. We use fake 125 // has started, but has not yet had its data persisted in the table. We use fake
113 // database handles in incognito mode starting at -1 and progressively getting 126 // database handles in incognito mode starting at -1 and progressively getting
114 // more negative. 127 // more negative.
115 // static 128 // static
116 const int DownloadItem::kUninitializedHandle = 0; 129 const int DownloadItem::kUninitializedHandle = 0;
117 130
118 const char DownloadItem::kEmptyFileHash[] = ""; 131 const char DownloadItem::kEmptyFileHash[] = "";
119 132
120 } 133 }
121 134
122 // Our download table ID starts at 1, so we use 0 to represent a download that 135 // Our download table ID starts at 1, so we use 0 to represent a download that
123 // has started, but has not yet had its data persisted in the table. We use fake 136 // has started, but has not yet had its data persisted in the table. We use fake
124 // database handles in incognito mode starting at -1 and progressively getting 137 // database handles in incognito mode starting at -1 and progressively getting
125 // more negative. 138 // more negative.
126 139
127 // Constructor for reading from the history service. 140 // Constructor for reading from the history service.
128 DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate, 141 DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate,
129 DownloadId download_id, 142 DownloadId download_id,
130 const DownloadPersistentStoreInfo& info, 143 const DownloadPersistentStoreInfo& info,
131 const net::BoundNetLog& bound_net_log) 144 const net::BoundNetLog& bound_net_log)
132 : download_id_(download_id), 145 : is_save_package_download_(false),
146 download_id_(download_id),
133 current_path_(info.path), 147 current_path_(info.path),
134 target_path_(info.path), 148 target_path_(info.path),
135 target_disposition_(TARGET_DISPOSITION_OVERWRITE), 149 target_disposition_(TARGET_DISPOSITION_OVERWRITE),
136 url_chain_(1, info.url), 150 url_chain_(1, info.url),
137 referrer_url_(info.referrer_url), 151 referrer_url_(info.referrer_url),
138 transition_type_(content::PAGE_TRANSITION_LINK), 152 transition_type_(content::PAGE_TRANSITION_LINK),
139 has_user_gesture_(false), 153 has_user_gesture_(false),
140 total_bytes_(info.total_bytes), 154 total_bytes_(info.total_bytes),
141 received_bytes_(info.received_bytes), 155 received_bytes_(info.received_bytes),
142 bytes_per_sec_(0), 156 bytes_per_sec_(0),
(...skipping 26 matching lines...) Expand all
169 Init(false /* not actively downloading */, 183 Init(false /* not actively downloading */,
170 download_net_logs::SRC_HISTORY_IMPORT); 184 download_net_logs::SRC_HISTORY_IMPORT);
171 } 185 }
172 186
173 // Constructing for a regular download: 187 // Constructing for a regular download:
174 DownloadItemImpl::DownloadItemImpl( 188 DownloadItemImpl::DownloadItemImpl(
175 DownloadItemImplDelegate* delegate, 189 DownloadItemImplDelegate* delegate,
176 const DownloadCreateInfo& info, 190 const DownloadCreateInfo& info,
177 scoped_ptr<DownloadRequestHandleInterface> request_handle, 191 scoped_ptr<DownloadRequestHandleInterface> request_handle,
178 const net::BoundNetLog& bound_net_log) 192 const net::BoundNetLog& bound_net_log)
179 : request_handle_(request_handle.Pass()), 193 : is_save_package_download_(false),
194 request_handle_(request_handle.Pass()),
180 download_id_(info.download_id), 195 download_id_(info.download_id),
181 target_disposition_( 196 target_disposition_(
182 (info.prompt_user_for_save_location) ? 197 (info.prompt_user_for_save_location) ?
183 TARGET_DISPOSITION_PROMPT : TARGET_DISPOSITION_OVERWRITE), 198 TARGET_DISPOSITION_PROMPT : TARGET_DISPOSITION_OVERWRITE),
184 url_chain_(info.url_chain), 199 url_chain_(info.url_chain),
185 referrer_url_(info.referrer_url), 200 referrer_url_(info.referrer_url),
186 suggested_filename_(UTF16ToUTF8(info.save_info->suggested_name)), 201 suggested_filename_(UTF16ToUTF8(info.save_info->suggested_name)),
187 forced_file_path_(info.save_info->file_path), 202 forced_file_path_(info.save_info->file_path),
188 transition_type_(info.transition_type), 203 transition_type_(info.transition_type),
189 has_user_gesture_(info.has_user_gesture), 204 has_user_gesture_(info.has_user_gesture),
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
229 bound_net_log_.source().ToEventParametersCallback()); 244 bound_net_log_.source().ToEventParametersCallback());
230 } 245 }
231 246
232 // Constructing for the "Save Page As..." feature: 247 // Constructing for the "Save Page As..." feature:
233 DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate, 248 DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate,
234 const FilePath& path, 249 const FilePath& path,
235 const GURL& url, 250 const GURL& url,
236 DownloadId download_id, 251 DownloadId download_id,
237 const std::string& mime_type, 252 const std::string& mime_type,
238 const net::BoundNetLog& bound_net_log) 253 const net::BoundNetLog& bound_net_log)
239 : request_handle_(new NullDownloadRequestHandle()), 254 : is_save_package_download_(true),
255 request_handle_(new NullDownloadRequestHandle()),
240 download_id_(download_id), 256 download_id_(download_id),
241 current_path_(path), 257 current_path_(path),
242 target_path_(path), 258 target_path_(path),
243 target_disposition_(TARGET_DISPOSITION_OVERWRITE), 259 target_disposition_(TARGET_DISPOSITION_OVERWRITE),
244 url_chain_(1, url), 260 url_chain_(1, url),
245 referrer_url_(GURL()), 261 referrer_url_(GURL()),
246 transition_type_(content::PAGE_TRANSITION_LINK), 262 transition_type_(content::PAGE_TRANSITION_LINK),
247 has_user_gesture_(false), 263 has_user_gesture_(false),
248 mime_type_(mime_type), 264 mime_type_(mime_type),
249 original_mime_type_(mime_type), 265 original_mime_type_(mime_type),
(...skipping 20 matching lines...) Expand all
270 delegate_delayed_complete_(false), 286 delegate_delayed_complete_(false),
271 bound_net_log_(bound_net_log), 287 bound_net_log_(bound_net_log),
272 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { 288 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
273 delegate_->Attach(); 289 delegate_->Attach();
274 Init(true /* actively downloading */, 290 Init(true /* actively downloading */,
275 download_net_logs::SRC_SAVE_PAGE_AS); 291 download_net_logs::SRC_SAVE_PAGE_AS);
276 } 292 }
277 293
278 DownloadItemImpl::~DownloadItemImpl() { 294 DownloadItemImpl::~DownloadItemImpl() {
279 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
296
297 // Should always have been nuked before now, at worst in
298 // DownloadManager shutdown.
299 DCHECK(!download_file_.get());
300
280 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadDestroyed(this)); 301 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadDestroyed(this));
281 delegate_->AssertStateConsistent(this); 302 delegate_->AssertStateConsistent(this);
282 delegate_->Detach(); 303 delegate_->Detach();
283 } 304 }
284 305
285 void DownloadItemImpl::AddObserver(Observer* observer) { 306 void DownloadItemImpl::AddObserver(Observer* observer) {
286 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 307 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
287 308
288 observers_.AddObserver(observer); 309 observers_.AddObserver(observer);
289 } 310 }
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
321 342
322 UpdateObservers(); 343 UpdateObservers();
323 344
324 MaybeCompleteDownload(); 345 MaybeCompleteDownload();
325 } 346 }
326 347
327 void DownloadItemImpl::TogglePause() { 348 void DownloadItemImpl::TogglePause() {
328 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 349 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
329 350
330 DCHECK(state_ == IN_PROGRESS_INTERNAL || state_ == COMPLETING_INTERNAL); 351 DCHECK(state_ == IN_PROGRESS_INTERNAL || state_ == COMPLETING_INTERNAL);
352
353 // Ignore pauses when we've passed the commit point.
354 if (state_ == COMPLETING_INTERNAL)
355 return;
356
331 if (is_paused_) 357 if (is_paused_)
332 request_handle_->ResumeRequest(); 358 request_handle_->ResumeRequest();
333 else 359 else
334 request_handle_->PauseRequest(); 360 request_handle_->PauseRequest();
335 is_paused_ = !is_paused_; 361 is_paused_ = !is_paused_;
336 UpdateObservers(); 362 UpdateObservers();
337 } 363 }
338 364
339 void DownloadItemImpl::Cancel(bool user_cancel) { 365 void DownloadItemImpl::Cancel(bool user_cancel) {
340 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 366 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
341 367
342 last_reason_ = user_cancel ? 368 last_reason_ = user_cancel ?
343 content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED : 369 content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED :
344 content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN; 370 content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN;
345 371
346 VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true); 372 VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true);
347 if (state_ != IN_PROGRESS_INTERNAL) { 373 if (state_ != IN_PROGRESS_INTERNAL) {
348 // Small downloads might be complete before this method has 374 // Small downloads might be complete before this method has
349 // a chance to run. 375 // a chance to run.
350 return; 376 return;
351 } 377 }
352 378
353 download_stats::RecordDownloadCount(download_stats::CANCELLED_COUNT); 379 download_stats::RecordDownloadCount(download_stats::CANCELLED_COUNT);
354 380
355 TransitionTo(CANCELLED_INTERNAL); 381 TransitionTo(CANCELLED_INTERNAL);
382
383 CancelDownloadFile();
384
385 // Cancel the originating URL request.
386 request_handle_->CancelRequest();
387
356 delegate_->DownloadStopped(this); 388 delegate_->DownloadStopped(this);
357 } 389 }
358 390
359 void DownloadItemImpl::Delete(DeleteReason reason) { 391 void DownloadItemImpl::Delete(DeleteReason reason) {
360 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 392 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
361 393
362 switch (reason) { 394 switch (reason) {
363 case DELETE_DUE_TO_USER_DISCARD: 395 case DELETE_DUE_TO_USER_DISCARD:
364 UMA_HISTOGRAM_ENUMERATION( 396 UMA_HISTOGRAM_ENUMERATION(
365 "Download.UserDiscard", GetDangerType(), 397 "Download.UserDiscard", GetDangerType(),
366 content::DOWNLOAD_DANGER_TYPE_MAX); 398 content::DOWNLOAD_DANGER_TYPE_MAX);
367 break; 399 break;
368 case DELETE_DUE_TO_BROWSER_SHUTDOWN: 400 case DELETE_DUE_TO_BROWSER_SHUTDOWN:
369 UMA_HISTOGRAM_ENUMERATION( 401 UMA_HISTOGRAM_ENUMERATION(
370 "Download.Discard", GetDangerType(), 402 "Download.Discard", GetDangerType(),
371 content::DOWNLOAD_DANGER_TYPE_MAX); 403 content::DOWNLOAD_DANGER_TYPE_MAX);
372 break; 404 break;
373 default: 405 default:
374 NOTREACHED(); 406 NOTREACHED();
375 } 407 }
376 408
377 // TODO(asanka): Avoid deleting a file that is still owned by DownloadFile. 409 // Delete the file if it exists and is not owned by a DownloadFile object.
378 if (!current_path_.empty()) 410 // (In the latter case the DownloadFile object will delete it on cancel.)
411 if (!current_path_.empty() && download_file_.get() == NULL) {
379 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 412 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
380 base::Bind(&DeleteDownloadedFile, current_path_)); 413 base::Bind(&DeleteDownloadedFile, current_path_));
414 }
381 Remove(); 415 Remove();
382 // We have now been deleted. 416 // We have now been deleted.
383 } 417 }
384 418
385 void DownloadItemImpl::Remove() { 419 void DownloadItemImpl::Remove() {
386 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 420 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
387 421
388 delegate_->AssertStateConsistent(this); 422 delegate_->AssertStateConsistent(this);
389 Cancel(true); 423 Cancel(true);
390 delegate_->AssertStateConsistent(this); 424 delegate_->AssertStateConsistent(this);
(...skipping 367 matching lines...) Expand 10 before | Expand all | Expand 10 after
758 " db_handle = %" PRId64 792 " db_handle = %" PRId64
759 " total = %" PRId64 793 " total = %" PRId64
760 " received = %" PRId64 794 " received = %" PRId64
761 " reason = %s" 795 " reason = %s"
762 " paused = %c" 796 " paused = %c"
763 " safety = %s" 797 " safety = %s"
764 " last_modified = '%s'" 798 " last_modified = '%s'"
765 " etag = '%s'" 799 " etag = '%s'"
766 " url_chain = \n\t\"%s\"\n\t" 800 " url_chain = \n\t\"%s\"\n\t"
767 " full_path = \"%" PRFilePath "\"" 801 " full_path = \"%" PRFilePath "\""
768 " target_path = \"%" PRFilePath "\"", 802 " target_path = \"%" PRFilePath "\""
803 " has download file = %s",
769 GetDbHandle(), 804 GetDbHandle(),
770 GetTotalBytes(), 805 GetTotalBytes(),
771 GetReceivedBytes(), 806 GetReceivedBytes(),
772 InterruptReasonDebugString(last_reason_).c_str(), 807 InterruptReasonDebugString(last_reason_).c_str(),
773 IsPaused() ? 'T' : 'F', 808 IsPaused() ? 'T' : 'F',
774 DebugSafetyStateString(GetSafetyState()), 809 DebugSafetyStateString(GetSafetyState()),
775 GetLastModifiedTime().c_str(), 810 GetLastModifiedTime().c_str(),
776 GetETag().c_str(), 811 GetETag().c_str(),
777 url_list.c_str(), 812 url_list.c_str(),
778 GetFullPath().value().c_str(), 813 GetFullPath().value().c_str(),
779 GetTargetFilePath().value().c_str()); 814 GetTargetFilePath().value().c_str(),
815 download_file_.get() ? "true" : "false");
780 } else { 816 } else {
781 description += base::StringPrintf(" url = \"%s\"", url_list.c_str()); 817 description += base::StringPrintf(" url = \"%s\"", url_list.c_str());
782 } 818 }
783 819
784 description += " }"; 820 description += " }";
785 821
786 return description; 822 return description;
787 } 823 }
788 824
789 void DownloadItemImpl::MockDownloadOpenForTesting() { 825 void DownloadItemImpl::MockDownloadOpenForTesting() {
790 open_enabled_ = false; 826 open_enabled_ = false;
791 } 827 }
792 828
793 void DownloadItemImpl::NotifyRemoved() { 829 void DownloadItemImpl::NotifyRemoved() {
794 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadRemoved(this)); 830 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadRemoved(this));
795 } 831 }
796 832
797 void DownloadItemImpl::OnDownloadedFileRemoved() { 833 void DownloadItemImpl::OnDownloadedFileRemoved() {
798 file_externally_removed_ = true; 834 file_externally_removed_ = true;
799 UpdateObservers(); 835 UpdateObservers();
800 } 836 }
801 837
802 void DownloadItemImpl::OffThreadCancel() {
803 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
804 request_handle_->CancelRequest();
805
806 BrowserThread::PostTask(
807 BrowserThread::FILE, FROM_HERE,
808 base::Bind(&DownloadFileManager::CancelDownload,
809 delegate_->GetDownloadFileManager(), download_id_));
810 }
811
812 // An error occurred somewhere. 838 // An error occurred somewhere.
813 void DownloadItemImpl::Interrupt(content::DownloadInterruptReason reason) { 839 void DownloadItemImpl::Interrupt(content::DownloadInterruptReason reason) {
814 // Somewhat counter-intuitively, it is possible for us to receive an 840 // Somewhat counter-intuitively, it is possible for us to receive an
815 // interrupt after we've already been interrupted. The generation of 841 // interrupt after we've already been interrupted. The generation of
816 // interrupts from the file thread Renames and the generation of 842 // interrupts from the file thread Renames and the generation of
817 // interrupts from disk writes go through two different mechanisms (driven 843 // interrupts from disk writes go through two different mechanisms (driven
818 // by rename requests from UI thread and by write requests from IO thread, 844 // by rename requests from UI thread and by write requests from IO thread,
819 // respectively), and since we choose not to keep state on the File thread, 845 // respectively), and since we choose not to keep state on the File thread,
820 // this is the place where the races collide. It's also possible for 846 // this is the place where the races collide. It's also possible for
821 // interrupts to race with cancels. 847 // interrupts to race with cancels.
822 848
823 // Whatever happens, the first one to hit the UI thread wins. 849 // Whatever happens, the first one to hit the UI thread wins.
824 if (state_ != IN_PROGRESS_INTERNAL) 850 if (state_ != IN_PROGRESS_INTERNAL)
825 return; 851 return;
826 852
827 last_reason_ = reason; 853 last_reason_ = reason;
828 TransitionTo(INTERRUPTED_INTERNAL); 854 TransitionTo(INTERRUPTED_INTERNAL);
855
856 CancelDownloadFile();
857
858 // Cancel the originating URL request.
859 request_handle_->CancelRequest();
860
829 download_stats::RecordDownloadInterrupted( 861 download_stats::RecordDownloadInterrupted(
830 reason, received_bytes_, total_bytes_); 862 reason, received_bytes_, total_bytes_);
831 delegate_->DownloadStopped(this); 863 delegate_->DownloadStopped(this);
832 } 864 }
833 865
866 base::WeakPtr<content::DownloadDestinationObserver>
867 DownloadItemImpl::DestinationObserverAsWeakPtr() {
868 return weak_ptr_factory_.GetWeakPtr();
869 }
870
871 bool DownloadItemImpl::IsSavePackageDownload() const {
872 return is_save_package_download_;
873 }
874
834 void DownloadItemImpl::SetTotalBytes(int64 total_bytes) { 875 void DownloadItemImpl::SetTotalBytes(int64 total_bytes) {
835 total_bytes_ = total_bytes; 876 total_bytes_ = total_bytes;
836 } 877 }
837 878
838 // Updates from the download thread may have been posted while this download 879 // Updates from the download thread may have been posted while this download
839 // was being cancelled in the UI thread, so we'll accept them unless we're 880 // was being cancelled in the UI thread, so we'll accept them unless we're
840 // complete. 881 // complete.
841 void DownloadItemImpl::UpdateProgress(int64 bytes_so_far, 882 void DownloadItemImpl::UpdateProgress(int64 bytes_so_far,
842 int64 bytes_per_sec, 883 int64 bytes_per_sec,
843 const std::string& hash_state) { 884 const std::string& hash_state) {
(...skipping 22 matching lines...) Expand all
866 907
867 if (bound_net_log_.IsLoggingAllEvents()) { 908 if (bound_net_log_.IsLoggingAllEvents()) {
868 bound_net_log_.AddEvent( 909 bound_net_log_.AddEvent(
869 net::NetLog::TYPE_DOWNLOAD_ITEM_UPDATED, 910 net::NetLog::TYPE_DOWNLOAD_ITEM_UPDATED,
870 net::NetLog::Int64Callback("bytes_so_far", received_bytes_)); 911 net::NetLog::Int64Callback("bytes_so_far", received_bytes_));
871 } 912 }
872 913
873 UpdateObservers(); 914 UpdateObservers();
874 } 915 }
875 916
876 void DownloadItemImpl::OnAllDataSaved( 917 void DownloadItemImpl::OnAllDataSaved(const std::string& final_hash) {
877 int64 size, const std::string& final_hash) {
878 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 918 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
879 919
920 DCHECK_EQ(IN_PROGRESS_INTERNAL, state_);
880 DCHECK(!all_data_saved_); 921 DCHECK(!all_data_saved_);
881 all_data_saved_ = true; 922 all_data_saved_ = true;
882 ProgressComplete(size, final_hash); 923
924 // Store final hash and null out intermediate serialized hash state.
925 hash_ = final_hash;
926 hash_state_ = "";
927
883 UpdateObservers(); 928 UpdateObservers();
884 } 929 }
885 930
886 void DownloadItemImpl::MarkAsComplete() { 931 void DownloadItemImpl::MarkAsComplete() {
887 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 932 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
888 933
889 DCHECK(all_data_saved_); 934 DCHECK(all_data_saved_);
890 end_time_ = base::Time::Now(); 935 end_time_ = base::Time::Now();
891 TransitionTo(COMPLETE_INTERNAL); 936 TransitionTo(COMPLETE_INTERNAL);
892 } 937 }
893 938
894 void DownloadItemImpl::SetIsPersisted() { 939 void DownloadItemImpl::SetIsPersisted() {
895 is_persisted_ = true; 940 is_persisted_ = true;
896 UpdateObservers(); 941 UpdateObservers();
897 } 942 }
898 943
899 void DownloadItemImpl::SetDbHandle(int64 handle) { 944 void DownloadItemImpl::SetDbHandle(int64 handle) {
900 db_handle_ = handle; 945 db_handle_ = handle;
901 946
902 bound_net_log_.AddEvent( 947 bound_net_log_.AddEvent(
903 net::NetLog::TYPE_DOWNLOAD_ITEM_IN_HISTORY, 948 net::NetLog::TYPE_DOWNLOAD_ITEM_IN_HISTORY,
904 net::NetLog::Int64Callback("db_handle", db_handle_)); 949 net::NetLog::Int64Callback("db_handle", db_handle_));
905 } 950 }
906 951
952 void DownloadItemImpl::DestinationUpdate(int64 bytes_so_far,
953 int64 bytes_per_sec,
954 const std::string& hash_state) {
955 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
956
957 if (!IsInProgress()) {
958 // Ignore if we're no longer in-progress. This can happen if we race a
959 // Cancel on the UI thread with an update on the FILE thread.
960 //
961 // TODO(rdsmith): Arguably we should let this go through, as this means
962 // the download really did get further than we know before it was
963 // cancelled. But the gain isn't very large, and the code is more
964 // fragile if it has to support in progress updates in a non-in-progress
965 // state. This issue should be readdressed when we revamp performance
966 // reporting.
967 return;
968 }
969 bytes_per_sec_ = bytes_per_sec;
970 hash_state_ = hash_state;
971 received_bytes_ = bytes_so_far;
972
973 // If we've received more data than we were expecting (bad server info?),
974 // revert to 'unknown size mode'.
975 if (received_bytes_ > total_bytes_)
976 total_bytes_ = 0;
977
978 if (bound_net_log_.IsLoggingAllEvents()) {
979 bound_net_log_.AddEvent(
980 net::NetLog::TYPE_DOWNLOAD_ITEM_UPDATED,
981 net::NetLog::Int64Callback("bytes_so_far", received_bytes_));
982 }
983
984 UpdateObservers();
985 }
986
987 void DownloadItemImpl::DestinationError(
988 content::DownloadInterruptReason reason) {
989 // The DestinationError and Interrupt routines are being kept separate
990 // to allow for a future merging of the Cancel and Interrupt routines..
991 Interrupt(reason);
992 }
993
994 void DownloadItemImpl::DestinationCompleted(const std::string& final_hash) {
995 if (!IsInProgress())
996 return;
997 OnAllDataSaved(final_hash);
998 MaybeCompleteDownload();
999 }
1000
907 // **** Download progression cascade 1001 // **** Download progression cascade
908 1002
909 void DownloadItemImpl::Init(bool active, 1003 void DownloadItemImpl::Init(bool active,
910 download_net_logs::DownloadType download_type) { 1004 download_net_logs::DownloadType download_type) {
911 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1005 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
912 1006
913 if (active) 1007 if (active)
914 download_stats::RecordDownloadCount(download_stats::START_COUNT); 1008 download_stats::RecordDownloadCount(download_stats::START_COUNT);
915 1009
916 if (target_path_.empty()) 1010 if (target_path_.empty())
(...skipping 23 matching lines...) Expand all
940 bound_net_log_.AddEvent( 1034 bound_net_log_.AddEvent(
941 net::NetLog::TYPE_DOWNLOAD_ITEM_IN_HISTORY, 1035 net::NetLog::TYPE_DOWNLOAD_ITEM_IN_HISTORY,
942 net::NetLog::Int64Callback("db_handle", db_handle_)); 1036 net::NetLog::Int64Callback("db_handle", db_handle_));
943 1037
944 bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE); 1038 bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE);
945 } 1039 }
946 1040
947 VLOG(20) << __FUNCTION__ << "() " << DebugString(true); 1041 VLOG(20) << __FUNCTION__ << "() " << DebugString(true);
948 } 1042 }
949 1043
950 // Called by DownloadManagerImpl when the download target path has been 1044 // We're starting the download.
1045 void DownloadItemImpl::Start(scoped_ptr<content::DownloadFile> file) {
1046 DCHECK(!download_file_.get());
1047 DCHECK(file.get());
1048 download_file_ = file.Pass();
1049
1050 BrowserThread::PostTask(
1051 BrowserThread::FILE, FROM_HERE,
1052 base::Bind(&DownloadFile::Initialize,
1053 // Safe because we control download file lifetime.
1054 base::Unretained(download_file_.get()),
1055 base::Bind(&DownloadItemImpl::OnDownloadFileInitialized,
1056 weak_ptr_factory_.GetWeakPtr())));
1057 }
1058
1059 void DownloadItemImpl::OnDownloadFileInitialized(
1060 content::DownloadInterruptReason result) {
1061 if (result != content::DOWNLOAD_INTERRUPT_REASON_NONE) {
1062 Interrupt(result);
1063 // TODO(rdsmith): It makes no sense to continue along the
1064 // regular download path after we've gotten an error. But it's
1065 // the way the code has historically worked, and this allows us
1066 // to get the download persisted and observers of the download manager
1067 // notified, so tests work. When we execute all side effects of cancel
1068 // (including queue removal) immedately rather than waiting for
1069 // persistence we should replace this comment with a "return;".
1070 }
1071
1072 delegate_->DetermineDownloadTarget(
1073 this, base::Bind(&DownloadItemImpl::OnDownloadTargetDetermined,
1074 weak_ptr_factory_.GetWeakPtr()));
1075 }
1076
1077 // Called by delegate_ when the download target path has been
951 // determined. 1078 // determined.
952 void DownloadItemImpl::OnDownloadTargetDetermined( 1079 void DownloadItemImpl::OnDownloadTargetDetermined(
953 const FilePath& target_path, 1080 const FilePath& target_path,
954 TargetDisposition disposition, 1081 TargetDisposition disposition,
955 content::DownloadDangerType danger_type, 1082 content::DownloadDangerType danger_type,
956 const FilePath& intermediate_path) { 1083 const FilePath& intermediate_path) {
957 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1084 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
958 1085
959 // If the |target_path| is empty, then we consider this download to be 1086 // If the |target_path| is empty, then we consider this download to be
960 // canceled. 1087 // canceled.
(...skipping 20 matching lines...) Expand all
981 // whatever was recorded for consistency. 1108 // whatever was recorded for consistency.
982 OnDownloadRenamedToIntermediateName(last_reason_, FilePath()); 1109 OnDownloadRenamedToIntermediateName(last_reason_, FilePath());
983 return; 1110 return;
984 } 1111 }
985 1112
986 // Rename to intermediate name. 1113 // Rename to intermediate name.
987 // TODO(asanka): Skip this rename if AllDataSaved() is true. This avoids a 1114 // TODO(asanka): Skip this rename if AllDataSaved() is true. This avoids a
988 // spurious rename when we can just rename to the final 1115 // spurious rename when we can just rename to the final
989 // filename. Unnecessary renames may cause bugs like 1116 // filename. Unnecessary renames may cause bugs like
990 // http://crbug.com/74187. 1117 // http://crbug.com/74187.
991 DownloadFileManager::RenameCompletionCallback callback = 1118 DCHECK(!is_save_package_download_);
1119 CHECK(download_file_.get());
1120 DownloadFile::RenameCompletionCallback callback =
992 base::Bind(&DownloadItemImpl::OnDownloadRenamedToIntermediateName, 1121 base::Bind(&DownloadItemImpl::OnDownloadRenamedToIntermediateName,
993 weak_ptr_factory_.GetWeakPtr()); 1122 weak_ptr_factory_.GetWeakPtr());
994 BrowserThread::PostTask( 1123 BrowserThread::PostTask(
995 BrowserThread::FILE, FROM_HERE, 1124 BrowserThread::FILE, FROM_HERE,
996 base::Bind(&DownloadFileManager::RenameDownloadFile, 1125 base::Bind(&DownloadFile::Rename,
997 delegate_->GetDownloadFileManager(), GetGlobalId(), 1126 // Safe because we control download file lifetime.
1127 base::Unretained(download_file_.get()),
998 intermediate_path, false, callback)); 1128 intermediate_path, false, callback));
999 } 1129 }
1000 1130
1001 void DownloadItemImpl::OnDownloadRenamedToIntermediateName( 1131 void DownloadItemImpl::OnDownloadRenamedToIntermediateName(
1002 content::DownloadInterruptReason reason, 1132 content::DownloadInterruptReason reason,
1003 const FilePath& full_path) { 1133 const FilePath& full_path) {
1004 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1134 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1005 if (content::DOWNLOAD_INTERRUPT_REASON_NONE != reason) { 1135 if (content::DOWNLOAD_INTERRUPT_REASON_NONE != reason) {
1006 Interrupt(reason); 1136 Interrupt(reason);
1007 } else { 1137 } else {
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
1060 void DownloadItemImpl::ReadyForDownloadCompletionDone() { 1190 void DownloadItemImpl::ReadyForDownloadCompletionDone() {
1061 if (state_ != IN_PROGRESS_INTERNAL) 1191 if (state_ != IN_PROGRESS_INTERNAL)
1062 return; 1192 return;
1063 1193
1064 VLOG(20) << __FUNCTION__ << "()" 1194 VLOG(20) << __FUNCTION__ << "()"
1065 << " needs rename = " << NeedsRename() 1195 << " needs rename = " << NeedsRename()
1066 << " " << DebugString(true); 1196 << " " << DebugString(true);
1067 DCHECK(!GetTargetFilePath().empty()); 1197 DCHECK(!GetTargetFilePath().empty());
1068 DCHECK_NE(DANGEROUS, GetSafetyState()); 1198 DCHECK_NE(DANGEROUS, GetSafetyState());
1069 1199
1200 // TODO(rdsmith/benjhayden): Remove as part of SavePackage integration.
1201 if (is_save_package_download_) {
1202 // Avoid doing anything on the file thread; there's nothing we control
1203 // there.
1204 OnDownloadFileReleased();
1205 return;
1206 }
1207
1208 CHECK(download_file_.get());
1070 if (NeedsRename()) { 1209 if (NeedsRename()) {
1071 DownloadFileManager::RenameCompletionCallback callback = 1210 content::DownloadFile::RenameCompletionCallback callback =
1072 base::Bind(&DownloadItemImpl::OnDownloadRenamedToFinalName, 1211 base::Bind(&DownloadItemImpl::OnDownloadRenamedToFinalName,
1073 weak_ptr_factory_.GetWeakPtr()); 1212 weak_ptr_factory_.GetWeakPtr());
1074 BrowserThread::PostTask( 1213 BrowserThread::PostTask(
1075 BrowserThread::FILE, FROM_HERE, 1214 BrowserThread::FILE, FROM_HERE,
1076 base::Bind(&DownloadFileManager::RenameDownloadFile, 1215 base::Bind(&DownloadFile::Rename,
1077 delegate_->GetDownloadFileManager(), GetGlobalId(), 1216 base::Unretained(download_file_.get()),
1078 GetTargetFilePath(), true, callback)); 1217 GetTargetFilePath(), true, callback));
1079 } else { 1218 } else {
1080 // Complete the download and release the DownloadFile. 1219 ReleaseDownloadFile();
1081 BrowserThread::PostTask(
1082 BrowserThread::FILE, FROM_HERE,
1083 base::Bind(&DownloadFileManager::CompleteDownload,
1084 delegate_->GetDownloadFileManager(), GetGlobalId(),
1085 base::Bind(&DownloadItemImpl::OnDownloadFileReleased,
1086 weak_ptr_factory_.GetWeakPtr())));
1087 TransitionTo(COMPLETING_INTERNAL);
1088 } 1220 }
1089 } 1221 }
1090 1222
1091 void DownloadItemImpl::OnDownloadRenamedToFinalName( 1223 void DownloadItemImpl::OnDownloadRenamedToFinalName(
1092 content::DownloadInterruptReason reason, 1224 content::DownloadInterruptReason reason,
1093 const FilePath& full_path) { 1225 const FilePath& full_path) {
1094 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1095 1227
1096 // If a cancel or interrupt hit, we'll cancel the DownloadFile, which 1228 // If a cancel or interrupt hit, we'll cancel the DownloadFile, which
1097 // will result in deleting the file on the file thread. So we don't 1229 // will result in deleting the file on the file thread. So we don't
(...skipping 11 matching lines...) Expand all
1109 Interrupt(reason); 1241 Interrupt(reason);
1110 return; 1242 return;
1111 } 1243 }
1112 1244
1113 // full_path is now the current and target file path. 1245 // full_path is now the current and target file path.
1114 DCHECK(!full_path.empty()); 1246 DCHECK(!full_path.empty());
1115 target_path_ = full_path; 1247 target_path_ = full_path;
1116 SetFullPath(full_path); 1248 SetFullPath(full_path);
1117 delegate_->DownloadRenamedToFinalName(this); 1249 delegate_->DownloadRenamedToFinalName(this);
1118 1250
1251 ReleaseDownloadFile();
1252 }
1253
1254 void DownloadItemImpl::ReleaseDownloadFile() {
1119 // Complete the download and release the DownloadFile. 1255 // Complete the download and release the DownloadFile.
1256 DCHECK(!is_save_package_download_);
1257 CHECK(download_file_.get());
1120 BrowserThread::PostTask( 1258 BrowserThread::PostTask(
1121 BrowserThread::FILE, FROM_HERE, 1259 BrowserThread::FILE, FROM_HERE,
1122 base::Bind(&DownloadFileManager::CompleteDownload, 1260 base::Bind(&DownloadFileDetach, base::Passed(download_file_.Pass()),
1123 delegate_->GetDownloadFileManager(), GetGlobalId(),
1124 base::Bind(&DownloadItemImpl::OnDownloadFileReleased, 1261 base::Bind(&DownloadItemImpl::OnDownloadFileReleased,
1125 weak_ptr_factory_.GetWeakPtr()))); 1262 weak_ptr_factory_.GetWeakPtr())));
1263
1264 // We're not completely done with the download item yet, but at this
1265 // point we're committed to complete the download. Cancels (or Interrupts,
1266 // though it's not clear how they could happen) after this point will be
1267 // ignored.
1126 TransitionTo(COMPLETING_INTERNAL); 1268 TransitionTo(COMPLETING_INTERNAL);
1127 } 1269 }
1128 1270
1129 void DownloadItemImpl::OnDownloadFileReleased() { 1271 void DownloadItemImpl::OnDownloadFileReleased() {
1130 if (delegate_->ShouldOpenDownload(this)) 1272 if (delegate_->ShouldOpenDownload(this))
1131 Completed(); 1273 Completed();
1132 else 1274 else
1133 delegate_delayed_complete_ = true; 1275 delegate_delayed_complete_ = true;
1134 } 1276 }
1135 1277
(...skipping 24 matching lines...) Expand all
1160 if (!IsTemporary()) 1302 if (!IsTemporary())
1161 OpenDownload(); 1303 OpenDownload();
1162 1304
1163 auto_opened_ = true; 1305 auto_opened_ = true;
1164 UpdateObservers(); 1306 UpdateObservers();
1165 } 1307 }
1166 } 1308 }
1167 1309
1168 // **** End of Download progression cascade 1310 // **** End of Download progression cascade
1169 1311
1312 void DownloadItemImpl::CancelDownloadFile() {
1313 // TODO(rdsmith/benjhayden): Remove condition as part of
1314 // SavePackage integration.
1315 if (!is_save_package_download_) {
1316 CHECK(download_file_.get());
1317 BrowserThread::PostTask(
1318 BrowserThread::FILE, FROM_HERE,
1319 // Will be deleted at end of task execution.
1320 base::Bind(&DownloadFileCancel, base::Passed(download_file_.Pass())));
1321 }
1322 }
1323
1170 bool DownloadItemImpl::IsDownloadReadyForCompletion() { 1324 bool DownloadItemImpl::IsDownloadReadyForCompletion() {
1171 // If we don't have all the data, the download is not ready for 1325 // If we don't have all the data, the download is not ready for
1172 // completion. 1326 // completion.
1173 if (!AllDataSaved()) 1327 if (!AllDataSaved())
1174 return false; 1328 return false;
1175 1329
1176 // If the download is dangerous, but not yet validated, it's not ready for 1330 // If the download is dangerous, but not yet validated, it's not ready for
1177 // completion. 1331 // completion.
1178 if (GetSafetyState() == DownloadItem::DANGEROUS) 1332 if (GetSafetyState() == DownloadItem::DANGEROUS)
1179 return false; 1333 return false;
(...skipping 10 matching lines...) Expand all
1190 return false; 1344 return false;
1191 1345
1192 return true; 1346 return true;
1193 } 1347 }
1194 1348
1195 bool DownloadItemImpl::NeedsRename() const { 1349 bool DownloadItemImpl::NeedsRename() const {
1196 DCHECK(target_path_.DirName() == current_path_.DirName()); 1350 DCHECK(target_path_.DirName() == current_path_.DirName());
1197 return target_path_ != current_path_; 1351 return target_path_ != current_path_;
1198 } 1352 }
1199 1353
1200 void DownloadItemImpl::ProgressComplete(int64 bytes_so_far,
1201 const std::string& final_hash) {
1202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1203
1204 hash_ = final_hash;
1205 hash_state_ = "";
1206
1207 received_bytes_ = bytes_so_far;
1208
1209 // If we've received more data than we were expecting (bad server info?),
1210 // revert to 'unknown size mode'.
1211 if (received_bytes_ > total_bytes_)
1212 total_bytes_ = 0;
1213 }
1214
1215 void DownloadItemImpl::TransitionTo(DownloadInternalState new_state) { 1354 void DownloadItemImpl::TransitionTo(DownloadInternalState new_state) {
1216 if (state_ == new_state) 1355 if (state_ == new_state)
1217 return; 1356 return;
1218 1357
1219 DownloadInternalState old_state = state_; 1358 DownloadInternalState old_state = state_;
1220 state_ = new_state; 1359 state_ = new_state;
1221 1360
1222 switch (state_) { 1361 switch (state_) {
1223 case COMPLETING_INTERNAL: 1362 case COMPLETING_INTERNAL:
1224 bound_net_log_.AddEvent( 1363 bound_net_log_.AddEvent(
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
1341 return "COMPLETE"; 1480 return "COMPLETE";
1342 case CANCELLED_INTERNAL: 1481 case CANCELLED_INTERNAL:
1343 return "CANCELLED"; 1482 return "CANCELLED";
1344 case INTERRUPTED_INTERNAL: 1483 case INTERRUPTED_INTERNAL:
1345 return "INTERRUPTED"; 1484 return "INTERRUPTED";
1346 default: 1485 default:
1347 NOTREACHED() << "Unknown download state " << state; 1486 NOTREACHED() << "Unknown download state " << state;
1348 return "unknown"; 1487 return "unknown";
1349 }; 1488 };
1350 } 1489 }
OLDNEW
« no previous file with comments | « content/browser/download/download_item_impl.h ('k') | content/browser/download/download_item_impl_delegate.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698