Chromium Code Reviews| 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/save_package.h" | 5 #include "content/browser/download/save_package.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/file_path.h" | 10 #include "base/file_path.h" |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 134 user_canceled_(false), | 134 user_canceled_(false), |
| 135 disk_error_occurred_(false), | 135 disk_error_occurred_(false), |
| 136 save_type_(save_type), | 136 save_type_(save_type), |
| 137 all_save_items_count_(0), | 137 all_save_items_count_(0), |
| 138 wait_state_(INITIALIZE), | 138 wait_state_(INITIALIZE), |
| 139 contents_id_(web_contents->GetRenderProcessHost()->GetID()), | 139 contents_id_(web_contents->GetRenderProcessHost()->GetID()), |
| 140 unique_id_(g_save_package_id++), | 140 unique_id_(g_save_package_id++), |
| 141 wrote_to_completed_file_(false), | 141 wrote_to_completed_file_(false), |
| 142 wrote_to_failed_file_(false) { | 142 wrote_to_failed_file_(false) { |
| 143 DCHECK(page_url_.is_valid()); | 143 DCHECK(page_url_.is_valid()); |
| 144 DCHECK(save_type_ == content::SAVE_PAGE_TYPE_AS_ONLY_HTML || | 144 DCHECK((save_type_ == content::SAVE_PAGE_TYPE_AS_ONLY_HTML) || |
| 145 save_type_ == content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML); | 145 (save_type_ == content::SAVE_PAGE_TYPE_AS_MHTML) || |
| 146 (save_type_ == content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML)); | |
| 146 DCHECK(!saved_main_file_path_.empty() && | 147 DCHECK(!saved_main_file_path_.empty() && |
| 147 saved_main_file_path_.value().length() <= kMaxFilePathLength); | 148 saved_main_file_path_.value().length() <= kMaxFilePathLength); |
| 148 DCHECK(!saved_main_directory_path_.empty() && | 149 DCHECK(!saved_main_directory_path_.empty() && |
| 149 saved_main_directory_path_.value().length() < kMaxFilePathLength); | 150 saved_main_directory_path_.value().length() < kMaxFilePathLength); |
| 150 InternalInit(); | 151 InternalInit(); |
| 151 } | 152 } |
| 152 | 153 |
| 153 SavePackage::SavePackage(WebContents* web_contents) | 154 SavePackage::SavePackage(WebContents* web_contents) |
| 154 : content::WebContentsObserver(web_contents), | 155 : content::WebContentsObserver(web_contents), |
| 155 file_manager_(NULL), | 156 file_manager_(NULL), |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 258 | 259 |
| 259 file_manager_ = rdh->save_file_manager(); | 260 file_manager_ = rdh->save_file_manager(); |
| 260 DCHECK(file_manager_); | 261 DCHECK(file_manager_); |
| 261 | 262 |
| 262 download_manager_ = web_contents()->GetBrowserContext()->GetDownloadManager(); | 263 download_manager_ = web_contents()->GetBrowserContext()->GetDownloadManager(); |
| 263 DCHECK(download_manager_); | 264 DCHECK(download_manager_); |
| 264 | 265 |
| 265 download_stats::RecordSavePackageEvent(download_stats::SAVE_PACKAGE_STARTED); | 266 download_stats::RecordSavePackageEvent(download_stats::SAVE_PACKAGE_STARTED); |
| 266 } | 267 } |
| 267 | 268 |
| 268 bool SavePackage::Init() { | 269 bool SavePackage::Init( |
| 270 const content::SaveFileDownloadCreatedCallback& download_created_callback) { | |
| 269 // Set proper running state. | 271 // Set proper running state. |
| 270 if (wait_state_ != INITIALIZE) | 272 if (wait_state_ != INITIALIZE) |
| 271 return false; | 273 return false; |
| 272 | 274 |
| 273 wait_state_ = START_PROCESS; | 275 wait_state_ = START_PROCESS; |
| 274 | 276 |
| 275 // Initialize the request context and resource dispatcher. | 277 // Initialize the request context and resource dispatcher. |
| 276 content::BrowserContext* browser_context = | 278 content::BrowserContext* browser_context = |
| 277 web_contents()->GetBrowserContext(); | 279 web_contents()->GetBrowserContext(); |
| 278 if (!browser_context) { | 280 if (!browser_context) { |
| 279 NOTREACHED(); | 281 NOTREACHED(); |
| 280 return false; | 282 return false; |
| 281 } | 283 } |
| 282 | 284 |
| 283 // The download manager keeps ownership but adds us as an observer. | 285 // The download manager keeps ownership but adds us as an observer. |
| 284 download_ = download_manager_->CreateSavePackageDownloadItem( | 286 download_ = download_manager_->CreateSavePackageDownloadItem( |
| 285 saved_main_file_path_, page_url_, | 287 saved_main_file_path_, |
| 286 browser_context->IsOffTheRecord(), this); | 288 page_url_, |
| 289 browser_context->IsOffTheRecord(), | |
| 290 ((save_type_ == content::SAVE_PAGE_TYPE_AS_MHTML) ? | |
| 291 "multipart/related" : "text/html"), | |
| 292 this); | |
| 293 if (!download_created_callback.is_null()) | |
| 294 download_created_callback.Run(download_); | |
| 287 | 295 |
| 288 // Check save type and process the save page job. | 296 // Check save type and process the save page job. |
| 289 if (save_type_ == content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML) { | 297 if (save_type_ == content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML) { |
| 290 // Get directory | 298 // Get directory |
| 291 DCHECK(!saved_main_directory_path_.empty()); | 299 DCHECK(!saved_main_directory_path_.empty()); |
| 292 GetAllSavableResourceLinksForCurrentPage(); | 300 GetAllSavableResourceLinksForCurrentPage(); |
| 301 } else if (save_type_ == content::SAVE_PAGE_TYPE_AS_MHTML) { | |
| 302 web_contents()->GenerateMHTML(saved_main_file_path_, base::Bind( | |
| 303 &SavePackage::OnMHTMLGenerated, this)); | |
| 293 } else { | 304 } else { |
| 294 wait_state_ = NET_FILES; | 305 wait_state_ = NET_FILES; |
| 295 SaveFileCreateInfo::SaveFileSource save_source = page_url_.SchemeIsFile() ? | 306 SaveFileCreateInfo::SaveFileSource save_source = page_url_.SchemeIsFile() ? |
| 296 SaveFileCreateInfo::SAVE_FILE_FROM_FILE : | 307 SaveFileCreateInfo::SAVE_FILE_FROM_FILE : |
| 297 SaveFileCreateInfo::SAVE_FILE_FROM_NET; | 308 SaveFileCreateInfo::SAVE_FILE_FROM_NET; |
| 298 SaveItem* save_item = new SaveItem(page_url_, | 309 SaveItem* save_item = new SaveItem(page_url_, |
| 299 GURL(), | 310 GURL(), |
| 300 this, | 311 this, |
| 301 save_source); | 312 save_source); |
| 302 // Add this item to waiting list. | 313 // Add this item to waiting list. |
| 303 waiting_item_queue_.push(save_item); | 314 waiting_item_queue_.push(save_item); |
| 304 all_save_items_count_ = 1; | 315 all_save_items_count_ = 1; |
| 305 download_->SetTotalBytes(1); | 316 download_->SetTotalBytes(1); |
| 306 | 317 |
| 307 DoSavingProcess(); | 318 DoSavingProcess(); |
| 308 } | 319 } |
| 309 | 320 |
| 310 return true; | 321 return true; |
| 311 } | 322 } |
| 312 | 323 |
| 324 void SavePackage::OnMHTMLGenerated(const FilePath& path, int64 size) { | |
| 325 if (size <= 0) { | |
| 326 Cancel(false); | |
| 327 return; | |
| 328 } | |
| 329 wrote_to_completed_file_ = true; | |
| 330 download_->SetTotalBytes(size); | |
| 331 download_->UpdateProgress(size, size, DownloadItem::kEmptyFileHash); | |
| 332 // Must call OnAllDataSaved here in order for | |
| 333 // GDataDownloadObserver::ShouldUpload() to return true. | |
| 334 // ShouldCompleteDownload() may depend on the gdata uploader to finish. | |
| 335 download_->OnAllDataSaved(size, DownloadItem::kEmptyFileHash); | |
| 336 // GDataDownloadObserver is waiting for the upload to complete. When that | |
| 337 // happens, it will call download_->MaybeCompleteDownload(), which will call | |
| 338 // through our OnDownloadUpdated() allowing us to Finish(). | |
| 339 // OnDownloadUpdated() may have been called in OnAllDataSaved(), so |this| may | |
| 340 // be deleted at this point. | |
| 341 } | |
| 342 | |
| 313 // On POSIX, the length of |pure_file_name| + |file_name_ext| is further | 343 // On POSIX, the length of |pure_file_name| + |file_name_ext| is further |
| 314 // restricted by NAME_MAX. The maximum allowed path looks like: | 344 // restricted by NAME_MAX. The maximum allowed path looks like: |
| 315 // '/path/to/save_dir' + '/' + NAME_MAX. | 345 // '/path/to/save_dir' + '/' + NAME_MAX. |
| 316 uint32 SavePackage::GetMaxPathLengthForDirectory(const FilePath& base_dir) { | 346 uint32 SavePackage::GetMaxPathLengthForDirectory(const FilePath& base_dir) { |
| 317 #if defined(OS_POSIX) | 347 #if defined(OS_POSIX) |
| 318 return std::min(kMaxFilePathLength, | 348 return std::min(kMaxFilePathLength, |
| 319 static_cast<uint32>(base_dir.value().length()) + | 349 static_cast<uint32>(base_dir.value().length()) + |
| 320 NAME_MAX + 1); | 350 NAME_MAX + 1); |
| 321 #else | 351 #else |
| 322 return kMaxFilePathLength; | 352 return kMaxFilePathLength; |
| (...skipping 375 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 698 it != saved_failed_items_.end(); ++it) | 728 it != saved_failed_items_.end(); ++it) |
| 699 save_ids.push_back(it->second->save_id()); | 729 save_ids.push_back(it->second->save_id()); |
| 700 | 730 |
| 701 BrowserThread::PostTask( | 731 BrowserThread::PostTask( |
| 702 BrowserThread::FILE, FROM_HERE, | 732 BrowserThread::FILE, FROM_HERE, |
| 703 base::Bind(&SaveFileManager::RemoveSavedFileFromFileMap, | 733 base::Bind(&SaveFileManager::RemoveSavedFileFromFileMap, |
| 704 file_manager_, | 734 file_manager_, |
| 705 save_ids)); | 735 save_ids)); |
| 706 | 736 |
| 707 if (download_) { | 737 if (download_) { |
| 708 download_->OnAllDataSaved(all_save_items_count_, | 738 if (save_type_ != content::SAVE_PAGE_TYPE_AS_MHTML) |
| 709 DownloadItem::kEmptyFileHash); | 739 download_->OnAllDataSaved(all_save_items_count_, |
| 740 DownloadItem::kEmptyFileHash); | |
| 710 download_->MarkAsComplete(); | 741 download_->MarkAsComplete(); |
| 711 FinalizeDownloadEntry(); | 742 FinalizeDownloadEntry(); |
| 712 } | 743 } |
| 713 } | 744 } |
| 714 | 745 |
| 715 // Called for updating end state. | 746 // Called for updating end state. |
| 716 void SavePackage::SaveFinished(int32 save_id, int64 size, bool is_success) { | 747 void SavePackage::SaveFinished(int32 save_id, int64 size, bool is_success) { |
| 717 // Because we might have canceled this saving job before, | 748 // Because we might have canceled this saving job before, |
| 718 // so we might not find corresponding SaveItem. Just ignore it. | 749 // so we might not find corresponding SaveItem. Just ignore it. |
| 719 SaveItem* save_item = LookupItemInProcessBySaveId(save_id); | 750 SaveItem* save_item = LookupItemInProcessBySaveId(save_id); |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 767 | 798 |
| 768 save_item->Finish(0, false); | 799 save_item->Finish(0, false); |
| 769 | 800 |
| 770 PutInProgressItemToSavedMap(save_item); | 801 PutInProgressItemToSavedMap(save_item); |
| 771 | 802 |
| 772 // Inform the DownloadItem to update UI. | 803 // Inform the DownloadItem to update UI. |
| 773 // We use the received bytes as number of saved files. | 804 // We use the received bytes as number of saved files. |
| 774 if (download_) | 805 if (download_) |
| 775 download_->UpdateProgress(completed_count(), CurrentSpeed(), ""); | 806 download_->UpdateProgress(completed_count(), CurrentSpeed(), ""); |
| 776 | 807 |
| 777 if (save_type_ == content::SAVE_PAGE_TYPE_AS_ONLY_HTML || | 808 if ((save_type_ == content::SAVE_PAGE_TYPE_AS_ONLY_HTML) || |
| 778 save_item->save_source() == SaveFileCreateInfo::SAVE_FILE_FROM_DOM) { | 809 (save_type_ == content::SAVE_PAGE_TYPE_AS_MHTML) || |
| 810 (save_item->save_source() == SaveFileCreateInfo::SAVE_FILE_FROM_DOM)) { | |
| 779 // We got error when saving page. Treat it as disk error. | 811 // We got error when saving page. Treat it as disk error. |
| 780 Cancel(true); | 812 Cancel(true); |
| 781 } | 813 } |
| 782 | 814 |
| 783 if (canceled()) { | 815 if (canceled()) { |
| 784 DCHECK(finished_); | 816 DCHECK(finished_); |
| 785 return; | 817 return; |
| 786 } | 818 } |
| 787 | 819 |
| 788 // Continue processing the save page job. | 820 // Continue processing the save page job. |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 874 wait_state_ = HTML_DATA; | 906 wait_state_ = HTML_DATA; |
| 875 // All non-HTML resources have been finished, start all remaining | 907 // All non-HTML resources have been finished, start all remaining |
| 876 // HTML files. | 908 // HTML files. |
| 877 SaveNextFile(true); | 909 SaveNextFile(true); |
| 878 } | 910 } |
| 879 } else if (in_process_count()) { | 911 } else if (in_process_count()) { |
| 880 // Continue asking for HTML data. | 912 // Continue asking for HTML data. |
| 881 DCHECK(wait_state_ == HTML_DATA); | 913 DCHECK(wait_state_ == HTML_DATA); |
| 882 } | 914 } |
| 883 } else { | 915 } else { |
| 884 // Save as HTML only. | 916 // Save as HTML only or MHTML. |
| 885 DCHECK(wait_state_ == NET_FILES); | 917 DCHECK(wait_state_ == NET_FILES); |
| 886 DCHECK(save_type_ == content::SAVE_PAGE_TYPE_AS_ONLY_HTML); | 918 DCHECK((save_type_ == content::SAVE_PAGE_TYPE_AS_ONLY_HTML) || |
| 919 (save_type_ == content::SAVE_PAGE_TYPE_AS_MHTML)); | |
| 887 if (waiting_item_queue_.size()) { | 920 if (waiting_item_queue_.size()) { |
| 888 DCHECK(all_save_items_count_ == waiting_item_queue_.size()); | 921 DCHECK(all_save_items_count_ == waiting_item_queue_.size()); |
| 889 SaveNextFile(false); | 922 SaveNextFile(false); |
| 890 } | 923 } |
| 891 } | 924 } |
| 892 } | 925 } |
| 893 | 926 |
| 894 bool SavePackage::OnMessageReceived(const IPC::Message& message) { | 927 bool SavePackage::OnMessageReceived(const IPC::Message& message) { |
| 895 bool handled = true; | 928 bool handled = true; |
| 896 IPC_BEGIN_MESSAGE_MAP(SavePackage, message) | 929 IPC_BEGIN_MESSAGE_MAP(SavePackage, message) |
| (...skipping 363 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1260 // The WebContents which owns this SavePackage may have disappeared during | 1293 // The WebContents which owns this SavePackage may have disappeared during |
| 1261 // the UI->FILE->UI thread hop of | 1294 // the UI->FILE->UI thread hop of |
| 1262 // GetSaveInfo->CreateDirectoryOnFileThread->ContinueGetSaveInfo. | 1295 // GetSaveInfo->CreateDirectoryOnFileThread->ContinueGetSaveInfo. |
| 1263 if (!web_contents()) | 1296 if (!web_contents()) |
| 1264 return; | 1297 return; |
| 1265 | 1298 |
| 1266 FilePath::StringType default_extension; | 1299 FilePath::StringType default_extension; |
| 1267 if (can_save_as_complete) | 1300 if (can_save_as_complete) |
| 1268 default_extension = kDefaultHtmlExtension; | 1301 default_extension = kDefaultHtmlExtension; |
| 1269 | 1302 |
| 1270 // On ChromeOS, OnPathPicked is not invoked; SavePackageFilePickerChromeOS | |
| 1271 // handles the the save. | |
| 1272 download_manager_->delegate()->ChooseSavePath( | 1303 download_manager_->delegate()->ChooseSavePath( |
| 1273 web_contents(), suggested_path, default_extension, can_save_as_complete, | 1304 web_contents(), |
| 1305 suggested_path, | |
| 1306 default_extension, | |
| 1307 can_save_as_complete, | |
| 1274 base::Bind(&SavePackage::OnPathPicked, AsWeakPtr())); | 1308 base::Bind(&SavePackage::OnPathPicked, AsWeakPtr())); |
| 1275 } | 1309 } |
| 1276 | 1310 |
| 1277 void SavePackage::OnPathPicked(const FilePath& final_name, | 1311 void SavePackage::OnPathPicked( |
| 1278 content::SavePageType type) { | 1312 const FilePath& final_name, |
| 1313 content::SavePageType type, | |
| 1314 const content::SaveFileDownloadCreatedCallback& download_created_callback) { | |
| 1279 // Ensure the filename is safe. | 1315 // Ensure the filename is safe. |
| 1280 saved_main_file_path_ = final_name; | 1316 saved_main_file_path_ = final_name; |
| 1281 // TODO(asanka): This call may block on IO and shouldn't be made | 1317 // TODO(asanka): This call may block on IO and shouldn't be made |
| 1282 // from the UI thread. See http://crbug.com/61827. | 1318 // from the UI thread. See http://crbug.com/61827. |
| 1283 net::GenerateSafeFileName(web_contents()->GetContentsMimeType(), false, | 1319 net::GenerateSafeFileName(web_contents()->GetContentsMimeType(), false, |
| 1284 &saved_main_file_path_); | 1320 &saved_main_file_path_); |
| 1285 | 1321 |
| 1286 saved_main_directory_path_ = saved_main_file_path_.DirName(); | 1322 saved_main_directory_path_ = saved_main_file_path_.DirName(); |
| 1287 save_type_ = type; | 1323 save_type_ = type; |
| 1288 if (save_type_ == content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML) { | 1324 if (save_type_ == content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML) { |
| 1289 // Make new directory for saving complete file. | 1325 // Make new directory for saving complete file. |
| 1290 saved_main_directory_path_ = saved_main_directory_path_.Append( | 1326 saved_main_directory_path_ = saved_main_directory_path_.Append( |
| 1291 saved_main_file_path_.RemoveExtension().BaseName().value() + | 1327 saved_main_file_path_.RemoveExtension().BaseName().value() + |
| 1292 FILE_PATH_LITERAL("_files")); | 1328 FILE_PATH_LITERAL("_files")); |
| 1293 } | 1329 } |
| 1294 | 1330 |
| 1295 Init(); | 1331 Init(download_created_callback); |
| 1296 } | 1332 } |
| 1297 | 1333 |
| 1298 void SavePackage::StopObservation() { | 1334 void SavePackage::StopObservation() { |
| 1299 DCHECK(download_); | 1335 DCHECK(download_); |
| 1300 DCHECK(download_manager_); | 1336 DCHECK(download_manager_); |
| 1301 | 1337 |
| 1302 download_->RemoveObserver(this); | 1338 download_->RemoveObserver(this); |
| 1303 download_ = NULL; | 1339 download_ = NULL; |
| 1304 download_manager_ = NULL; | 1340 download_manager_ = NULL; |
| 1305 } | 1341 } |
| 1306 | 1342 |
| 1307 void SavePackage::OnDownloadUpdated(DownloadItem* download) { | 1343 void SavePackage::OnDownloadUpdated(DownloadItem* download) { |
| 1308 DCHECK(download_); | 1344 DCHECK(download_); |
| 1309 DCHECK(download_ == download); | 1345 DCHECK(download_ == download); |
| 1310 DCHECK(download_manager_); | 1346 DCHECK(download_manager_); |
| 1311 | 1347 |
| 1312 // Check for removal. | 1348 // Check for removal. |
| 1313 if (download->GetState() == DownloadItem::REMOVING) | 1349 if (download_->GetState() == DownloadItem::REMOVING) { |
| 1314 StopObservation(); | 1350 StopObservation(); |
| 1351 } | |
| 1352 | |
| 1353 // MHTML saves may need to wait for GData to finish uploading. | |
| 1354 if ((save_type_ == content::SAVE_PAGE_TYPE_AS_MHTML) && | |
| 1355 download_->AllDataSaved() && | |
| 1356 !download_->IsComplete() && | |
| 1357 download_manager_->delegate()->ShouldCompleteDownload(download_)) { | |
| 1358 Finish(); | |
|
asanka
2012/04/27 16:00:55
Note that this does a state transition on the Down
benjhayden
2012/04/27 17:28:52
Done.
| |
| 1359 } | |
| 1315 } | 1360 } |
| 1316 | 1361 |
| 1317 void SavePackage::FinalizeDownloadEntry() { | 1362 void SavePackage::FinalizeDownloadEntry() { |
| 1318 DCHECK(download_); | 1363 DCHECK(download_); |
| 1319 DCHECK(download_manager_); | 1364 DCHECK(download_manager_); |
| 1320 | 1365 |
| 1321 download_manager_->SavePageDownloadFinished(download_); | 1366 download_manager_->SavePageDownloadFinished(download_); |
| 1322 StopObservation(); | 1367 StopObservation(); |
| 1323 } | 1368 } |
| OLD | NEW |