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

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

Issue 10069014: Save Page As MHTML (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: -osall, extract_actions.py Created 8 years, 7 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 #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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698