| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/save_package.h" | |
| 6 | |
| 7 #include "base/file_util.h" | |
| 8 #include "base/logging.h" | |
| 9 #include "base/message_loop.h" | |
| 10 #include "base/path_service.h" | |
| 11 #include "base/string_util.h" | |
| 12 #include "base/task.h" | |
| 13 #include "base/thread.h" | |
| 14 #include "base/win_util.h" | |
| 15 #include "chrome/browser/browser_process.h" | |
| 16 #include "chrome/browser/download/download_manager.h" | |
| 17 #include "chrome/browser/profile.h" | |
| 18 #include "chrome/browser/render_process_host.h" | |
| 19 #include "chrome/browser/render_view_host.h" | |
| 20 #include "chrome/browser/render_view_host_delegate.h" | |
| 21 #include "chrome/browser/resource_dispatcher_host.h" | |
| 22 #include "chrome/browser/save_file.h" | |
| 23 #include "chrome/browser/save_file_manager.h" | |
| 24 #include "chrome/browser/save_page_model.h" | |
| 25 #include "chrome/browser/tab_util.h" | |
| 26 #include "chrome/browser/web_contents.h" | |
| 27 #include "chrome/browser/views/download_shelf_view.h" | |
| 28 #include "chrome/common/chrome_paths.h" | |
| 29 #include "chrome/common/l10n_util.h" | |
| 30 #include "chrome/common/pref_names.h" | |
| 31 #include "chrome/common/pref_service.h" | |
| 32 #include "chrome/common/stl_util-inl.h" | |
| 33 #include "chrome/common/win_util.h" | |
| 34 #include "net/base/net_util.h" | |
| 35 #include "net/url_request/url_request_context.h" | |
| 36 #include "webkit/glue/dom_serializer_delegate.h" | |
| 37 | |
| 38 #include "generated_resources.h" | |
| 39 | |
| 40 // Default name which will be used when we can not get proper name from | |
| 41 // resource URL. | |
| 42 static const wchar_t kDefaultSaveName[] = L"saved_resource"; | |
| 43 | |
| 44 // Maximum number of file ordinal number. I think it's big enough for resolving | |
| 45 // name-conflict files which has same base file name. | |
| 46 static const int32 kMaxFileOrdinalNumber = 9999; | |
| 47 | |
| 48 // Maximum length for file path. Since Windows have MAX_PATH limitation for | |
| 49 // file path, we need to make sure length of file path of every saved file | |
| 50 // is less than MAX_PATH | |
| 51 static const uint32 kMaxFilePathLength = MAX_PATH - 1; | |
| 52 | |
| 53 // Maximum length for file ordinal number part. Since we only support the | |
| 54 // maximum 9999 for ordinal number, which means maximum file ordinal number part | |
| 55 // should be "(9998)", so the value is 6. | |
| 56 static const uint32 kMaxFileOrdinalNumberPartLength = 6; | |
| 57 | |
| 58 SavePackage::SavePackage(WebContents* web_content, | |
| 59 SavePackageType save_type, | |
| 60 const std::wstring& file_full_path, | |
| 61 const std::wstring& directory_full_path) | |
| 62 : web_contents_(web_content), | |
| 63 save_type_(save_type), | |
| 64 saved_main_file_path_(file_full_path), | |
| 65 saved_main_directory_path_(directory_full_path), | |
| 66 all_save_items_count_(0), | |
| 67 disk_error_occurred_(false), | |
| 68 user_canceled_(false), | |
| 69 download_(NULL), | |
| 70 finished_(false), | |
| 71 wait_state_(INITIALIZE) { | |
| 72 DCHECK(web_content); | |
| 73 const GURL& current_page_url = web_contents_->GetURL(); | |
| 74 DCHECK(current_page_url.is_valid()); | |
| 75 page_url_ = UTF8ToWide(current_page_url.spec()); | |
| 76 DCHECK(save_type_ == SAVE_AS_ONLY_HTML || | |
| 77 save_type_ == SAVE_AS_COMPLETE_HTML); | |
| 78 DCHECK(!saved_main_file_path_.empty() && | |
| 79 saved_main_file_path_.length() <= kMaxFilePathLength); | |
| 80 DCHECK(!saved_main_directory_path_.empty() && | |
| 81 saved_main_directory_path_.length() < kMaxFilePathLength); | |
| 82 } | |
| 83 | |
| 84 // This is for testing use. Set |finished_| as true because we don't want | |
| 85 // method Cancel to be be called in destructor in test mode. | |
| 86 SavePackage::SavePackage(const wchar_t* file_full_path, | |
| 87 const wchar_t* directory_full_path) | |
| 88 : all_save_items_count_(0), | |
| 89 saved_main_file_path_(file_full_path), | |
| 90 saved_main_directory_path_(directory_full_path), | |
| 91 finished_(true), | |
| 92 download_(NULL), | |
| 93 user_canceled_(false), | |
| 94 disk_error_occurred_(false) { | |
| 95 DCHECK(!saved_main_file_path_.empty() && | |
| 96 saved_main_file_path_.length() <= kMaxFilePathLength); | |
| 97 DCHECK(!saved_main_directory_path_.empty() && | |
| 98 saved_main_directory_path_.length() < kMaxFilePathLength); | |
| 99 } | |
| 100 | |
| 101 SavePackage::~SavePackage() { | |
| 102 // Stop receiving saving job's updates | |
| 103 if (!finished_ && !canceled()) { | |
| 104 // Unexpected quit. | |
| 105 Cancel(true); | |
| 106 } | |
| 107 | |
| 108 DCHECK(all_save_items_count_ == (waiting_item_queue_.size() + | |
| 109 completed_count() + | |
| 110 in_process_count())); | |
| 111 // Free all SaveItems. | |
| 112 while (!waiting_item_queue_.empty()) { | |
| 113 // We still have some items which are waiting for start to save. | |
| 114 SaveItem* save_item = waiting_item_queue_.front(); | |
| 115 waiting_item_queue_.pop(); | |
| 116 delete save_item; | |
| 117 } | |
| 118 | |
| 119 STLDeleteValues(&saved_success_items_); | |
| 120 STLDeleteValues(&in_progress_items_); | |
| 121 STLDeleteValues(&saved_failed_items_); | |
| 122 | |
| 123 if (download_) { | |
| 124 // We call this to remove the view from the shelf. It will invoke | |
| 125 // DownloadManager::RemoveDownload, but since the fake DownloadItem is not | |
| 126 // owned by DownloadManager, it will do nothing to our fake item. | |
| 127 download_->Remove(); | |
| 128 delete download_; | |
| 129 download_ = NULL; | |
| 130 } | |
| 131 file_manager_ = NULL; | |
| 132 } | |
| 133 | |
| 134 // Cancel all in progress request, might be called by user or internal error. | |
| 135 void SavePackage::Cancel(bool user_action) { | |
| 136 if (!canceled()) { | |
| 137 if (user_action) | |
| 138 user_canceled_ = true; | |
| 139 else | |
| 140 disk_error_occurred_ = true; | |
| 141 Stop(); | |
| 142 } | |
| 143 } | |
| 144 | |
| 145 // Initialize the SavePackage. | |
| 146 bool SavePackage::Init() { | |
| 147 // Set proper running state. | |
| 148 if (wait_state_ != INITIALIZE) | |
| 149 return false; | |
| 150 | |
| 151 wait_state_ = START_PROCESS; | |
| 152 | |
| 153 // Initialize the request context and resource dispatcher. | |
| 154 Profile* profile = web_contents_->profile(); | |
| 155 if (!profile) { | |
| 156 NOTREACHED(); | |
| 157 return false; | |
| 158 } | |
| 159 | |
| 160 request_context_ = profile->GetRequestContext(); | |
| 161 | |
| 162 ResourceDispatcherHost* rdh = g_browser_process->resource_dispatcher_host(); | |
| 163 if (!rdh) { | |
| 164 NOTREACHED(); | |
| 165 return false; | |
| 166 } | |
| 167 | |
| 168 file_manager_ = rdh->save_file_manager(); | |
| 169 if (!file_manager_) { | |
| 170 NOTREACHED(); | |
| 171 return false; | |
| 172 } | |
| 173 | |
| 174 // Create the fake DownloadItem and display the view. | |
| 175 download_ = new DownloadItem(1, saved_main_file_path_, | |
| 176 page_url_, Time::Now(), 0, -1, -1); | |
| 177 download_->set_manager(web_contents_->profile()->GetDownloadManager()); | |
| 178 DownloadShelfView* shelf = web_contents_->GetDownloadShelfView(); | |
| 179 shelf->AddDownloadView(new DownloadItemView( | |
| 180 download_, shelf, new SavePageModel(this, download_))); | |
| 181 web_contents_->SetDownloadShelfVisible(true); | |
| 182 | |
| 183 // Check save type and process the save page job. | |
| 184 if (save_type_ == SAVE_AS_COMPLETE_HTML) { | |
| 185 // Get directory | |
| 186 DCHECK(!saved_main_directory_path_.empty()); | |
| 187 GetAllSavableResourceLinksForCurrentPage(); | |
| 188 } else { | |
| 189 wait_state_ = NET_FILES; | |
| 190 GURL u(page_url_); | |
| 191 SaveFileCreateInfo::SaveFileSource save_source = u.SchemeIsFile() ? | |
| 192 SaveFileCreateInfo::SAVE_FILE_FROM_FILE : | |
| 193 SaveFileCreateInfo::SAVE_FILE_FROM_NET; | |
| 194 SaveItem* save_item = new SaveItem(page_url_, | |
| 195 L"", | |
| 196 this, | |
| 197 save_source); | |
| 198 // Add this item to waiting list. | |
| 199 waiting_item_queue_.push(save_item); | |
| 200 all_save_items_count_ = 1; | |
| 201 download_->set_total_bytes(1); | |
| 202 | |
| 203 DoSavingProcess(); | |
| 204 } | |
| 205 | |
| 206 return true; | |
| 207 } | |
| 208 | |
| 209 // Generate name for saving resource. | |
| 210 bool SavePackage::GenerateFilename(const std::string& disposition, | |
| 211 const std::wstring& url, | |
| 212 bool need_html_ext, | |
| 213 std::wstring* generated_name) { | |
| 214 std::wstring file_name = | |
| 215 net::GetSuggestedFilename(GURL(url), disposition, kDefaultSaveName); | |
| 216 | |
| 217 DCHECK(!file_name.empty()); | |
| 218 // Check whether we have same name before. | |
| 219 std::wstring::size_type last_dot = file_name.rfind(L'.'); | |
| 220 std::wstring pure_file_name, file_name_ext; | |
| 221 if (last_dot == std::wstring::npos) { | |
| 222 pure_file_name = file_name; | |
| 223 } else { | |
| 224 pure_file_name = std::wstring(file_name, 0, last_dot); | |
| 225 file_name_ext = std::wstring(file_name, last_dot); | |
| 226 } | |
| 227 // If it is HTML resource, use ".htm" as its extension name. | |
| 228 if (need_html_ext) | |
| 229 file_name_ext = L".htm"; | |
| 230 if (file_name_ext == L".") | |
| 231 file_name_ext.clear(); | |
| 232 | |
| 233 // Get safe pure file name. | |
| 234 if (!GetSafePureFileName(saved_main_directory_path_, file_name_ext, | |
| 235 kMaxFilePathLength, &pure_file_name)) | |
| 236 return false; | |
| 237 | |
| 238 file_name = pure_file_name + file_name_ext; | |
| 239 | |
| 240 // Check whether we already have same name. | |
| 241 if (file_name_set_.find(file_name) == file_name_set_.end()) { | |
| 242 file_name_set_.insert(file_name); | |
| 243 } else { | |
| 244 // Found same name, increase the ordinal number for the file name. | |
| 245 std::wstring base_file_name, file_ordinal_number; | |
| 246 | |
| 247 if (!GetBaseFileNameAndFileOrdinalNumber(pure_file_name, &base_file_name, | |
| 248 &file_ordinal_number)) | |
| 249 base_file_name = pure_file_name; | |
| 250 | |
| 251 // We need to make sure the length of base file name plus maximum ordinal | |
| 252 // number path will be less than or equal to kMaxFilePathLength. | |
| 253 if (!GetSafePureFileName(saved_main_directory_path_, file_name_ext, | |
| 254 kMaxFilePathLength - kMaxFileOrdinalNumberPartLength, &base_file_name)) | |
| 255 return false; | |
| 256 | |
| 257 // Prepare the new ordinal number. | |
| 258 uint32 ordinal_number; | |
| 259 FileNameCountMap::iterator it = file_name_count_map_.find(base_file_name); | |
| 260 if (it == file_name_count_map_.end()) { | |
| 261 // First base-name-conflict resolving, use 1 as initial ordinal number. | |
| 262 file_name_count_map_[base_file_name] = 1; | |
| 263 ordinal_number = 1; | |
| 264 } else { | |
| 265 // We have met same base-name conflict, use latest ordinal number. | |
| 266 ordinal_number = it->second; | |
| 267 } | |
| 268 | |
| 269 if (ordinal_number > (kMaxFileOrdinalNumber - 1)) { | |
| 270 // Use a random file from temporary file. | |
| 271 file_util::CreateTemporaryFileName(&file_name); | |
| 272 file_name = file_util::GetFilenameFromPath(file_name); | |
| 273 // Get safe pure file name. | |
| 274 if (!GetSafePureFileName(saved_main_directory_path_, std::wstring(), | |
| 275 kMaxFilePathLength, &file_name)) | |
| 276 return false; | |
| 277 } else { | |
| 278 uint32 i; | |
| 279 for (i = ordinal_number; i < kMaxFileOrdinalNumber; ++i) { | |
| 280 std::wstring new_name = | |
| 281 StringPrintf(L"%ls(%d)", base_file_name.c_str(), i) + file_name_ext; | |
| 282 if (file_name_set_.find(new_name) == file_name_set_.end()) { | |
| 283 // Resolved name conflict. | |
| 284 file_name = new_name; | |
| 285 file_name_count_map_[base_file_name] = ++i; | |
| 286 break; | |
| 287 } | |
| 288 } | |
| 289 } | |
| 290 | |
| 291 file_name_set_.insert(file_name); | |
| 292 } | |
| 293 | |
| 294 DCHECK(!file_name.empty()); | |
| 295 generated_name->assign(file_name); | |
| 296 | |
| 297 return true; | |
| 298 } | |
| 299 | |
| 300 // We have received a message from SaveFileManager about a new saving job. We | |
| 301 // create a SaveItem and store it in our in_progress list. | |
| 302 void SavePackage::StartSave(const SaveFileCreateInfo* info) { | |
| 303 DCHECK(info && !info->url.empty()); | |
| 304 | |
| 305 SaveUrlItemMap::iterator it = in_progress_items_.find(info->url); | |
| 306 if (it == in_progress_items_.end()) { | |
| 307 // If not found, we must have cancel action. | |
| 308 DCHECK(canceled()); | |
| 309 return; | |
| 310 } | |
| 311 SaveItem* save_item = it->second; | |
| 312 | |
| 313 DCHECK(!saved_main_file_path_.empty()); | |
| 314 | |
| 315 save_item->SetSaveId(info->save_id); | |
| 316 save_item->SetTotalBytes(info->total_bytes); | |
| 317 | |
| 318 // Determine the proper path for a saving job, by choosing either the default | |
| 319 // save directory, or prompting the user. | |
| 320 DCHECK(!save_item->has_final_name()); | |
| 321 if (info->url != page_url_) { | |
| 322 std::wstring generated_name; | |
| 323 // For HTML resource file, make sure it will have .htm as extension name, | |
| 324 // otherwise, when you open the saved page in Chrome again, download | |
| 325 // file manager will treat it as downloadable resource, and download it | |
| 326 // instead of opening it as HTML. | |
| 327 bool need_html_ext = | |
| 328 info->save_source == SaveFileCreateInfo::SAVE_FILE_FROM_DOM; | |
| 329 if (!GenerateFilename(info->content_disposition, | |
| 330 info->url, | |
| 331 need_html_ext, | |
| 332 &generated_name)) { | |
| 333 // We can not generate file name for this SaveItem, so we cancel the | |
| 334 // saving page job if the save source is from serialized DOM data. | |
| 335 // Otherwise, it means this SaveItem is sub-resource type, we treat it | |
| 336 // as an error happened on saving. We can ignore this type error for | |
| 337 // sub-resource links which will be resolved as absolute links instead | |
| 338 // of local links in final saved contents. | |
| 339 if (info->save_source == SaveFileCreateInfo::SAVE_FILE_FROM_DOM) | |
| 340 Cancel(true); | |
| 341 else | |
| 342 SaveFinished(save_item->save_id(), 0, false); | |
| 343 return; | |
| 344 } | |
| 345 | |
| 346 // When saving page as only-HTML, we only have a SaveItem whose url | |
| 347 // must be page_url_. | |
| 348 DCHECK(save_type_ == SAVE_AS_COMPLETE_HTML); | |
| 349 DCHECK(!saved_main_directory_path_.empty()); | |
| 350 | |
| 351 // Now we get final name retrieved from GenerateFilename, we will use it | |
| 352 // rename the SaveItem. | |
| 353 std::wstring final_name = saved_main_directory_path_; | |
| 354 file_util::AppendToPath(&final_name, generated_name); | |
| 355 save_item->Rename(final_name); | |
| 356 } else { | |
| 357 // It is the main HTML file, use the name chosen by the user. | |
| 358 save_item->Rename(saved_main_file_path_); | |
| 359 } | |
| 360 | |
| 361 // If the save source is from file system, inform SaveFileManager to copy | |
| 362 // corresponding file to the file path which this SaveItem specifies. | |
| 363 if (info->save_source == SaveFileCreateInfo::SAVE_FILE_FROM_FILE) { | |
| 364 file_manager_->GetSaveLoop()->PostTask(FROM_HERE, | |
| 365 NewRunnableMethod(file_manager_, | |
| 366 &SaveFileManager::SaveLocalFile, | |
| 367 save_item->url(), | |
| 368 save_item->save_id(), | |
| 369 GetTabId())); | |
| 370 return; | |
| 371 } | |
| 372 | |
| 373 // Check whether we begin to require serialized HTML data. | |
| 374 if (save_type_ == SAVE_AS_COMPLETE_HTML && wait_state_ == HTML_DATA) { | |
| 375 // Inform backend to serialize the all frames' DOM and send serialized | |
| 376 // HTML data back. | |
| 377 GetSerializedHtmlDataForCurrentPageWithLocalLinks(); | |
| 378 } | |
| 379 } | |
| 380 | |
| 381 // Look up SaveItem by save id from in progress map. | |
| 382 SaveItem* SavePackage::LookupItemInProcessBySaveId(int32 save_id) { | |
| 383 if (in_process_count()) { | |
| 384 SaveUrlItemMap::iterator it = in_progress_items_.begin(); | |
| 385 for (; it != in_progress_items_.end(); ++it) { | |
| 386 SaveItem* save_item = it->second; | |
| 387 DCHECK(save_item->state() == SaveItem::IN_PROGRESS); | |
| 388 if (save_item->save_id() == save_id) | |
| 389 return save_item; | |
| 390 } | |
| 391 } | |
| 392 return NULL; | |
| 393 } | |
| 394 | |
| 395 // Remove SaveItem from in progress map and put it to saved map. | |
| 396 void SavePackage::PutInProgressItemToSavedMap(SaveItem* save_item) { | |
| 397 SaveUrlItemMap::iterator it = in_progress_items_.find( | |
| 398 save_item->url()); | |
| 399 DCHECK(it != in_progress_items_.end()); | |
| 400 DCHECK(save_item == it->second); | |
| 401 in_progress_items_.erase(it); | |
| 402 | |
| 403 if (save_item->success()) { | |
| 404 // Add it to saved_success_items_. | |
| 405 DCHECK(saved_success_items_.find(save_item->save_id()) == | |
| 406 saved_success_items_.end()); | |
| 407 saved_success_items_[save_item->save_id()] = save_item; | |
| 408 } else { | |
| 409 // Add it to saved_failed_items_. | |
| 410 DCHECK(saved_failed_items_.find(save_item->url()) == | |
| 411 saved_failed_items_.end()); | |
| 412 saved_failed_items_[save_item->url()] = save_item; | |
| 413 } | |
| 414 } | |
| 415 | |
| 416 // Called for updating saving state. | |
| 417 bool SavePackage::UpdateSaveProgress(int32 save_id, | |
| 418 int64 size, | |
| 419 bool write_success) { | |
| 420 // Because we might have canceled this saving job before, | |
| 421 // so we might not find corresponding SaveItem. | |
| 422 SaveItem* save_item = LookupItemInProcessBySaveId(save_id); | |
| 423 if (!save_item) | |
| 424 return false; | |
| 425 | |
| 426 save_item->Update(size); | |
| 427 | |
| 428 // If we got disk error, cancel whole save page job. | |
| 429 if (!write_success) { | |
| 430 // Cancel job with reason of disk error. | |
| 431 Cancel(false); | |
| 432 } | |
| 433 return true; | |
| 434 } | |
| 435 | |
| 436 // Stop all page saving jobs that are in progress and instruct the file thread | |
| 437 // to delete all saved files. | |
| 438 void SavePackage::Stop() { | |
| 439 // When stopping, if it still has some items in in_progress, cancel them. | |
| 440 DCHECK(canceled()); | |
| 441 if (in_process_count()) { | |
| 442 SaveUrlItemMap::iterator it = in_progress_items_.begin(); | |
| 443 for (; it != in_progress_items_.end(); ++it) { | |
| 444 SaveItem* save_item = it->second; | |
| 445 DCHECK(save_item->state() == SaveItem::IN_PROGRESS); | |
| 446 save_item->Cancel(); | |
| 447 } | |
| 448 // Remove all in progress item to saved map. For failed items, they will | |
| 449 // be put into saved_failed_items_, for successful item, they will be put | |
| 450 // into saved_success_items_. | |
| 451 while (in_process_count()) | |
| 452 PutInProgressItemToSavedMap(in_progress_items_.begin()->second); | |
| 453 } | |
| 454 | |
| 455 // This vector contains the save ids of the save files which SaveFileManager | |
| 456 // needs to remove from its save_file_map_. | |
| 457 SaveIDList save_ids; | |
| 458 for (SavedItemMap::iterator it = saved_success_items_.begin(); | |
| 459 it != saved_success_items_.end(); ++it) | |
| 460 save_ids.push_back(it->first); | |
| 461 for (SaveUrlItemMap::iterator it = saved_failed_items_.begin(); | |
| 462 it != saved_failed_items_.end(); ++it) | |
| 463 save_ids.push_back(it->second->save_id()); | |
| 464 | |
| 465 file_manager_->GetSaveLoop()->PostTask(FROM_HERE, | |
| 466 NewRunnableMethod(file_manager_, | |
| 467 &SaveFileManager::RemoveSavedFileFromFileMap, | |
| 468 save_ids)); | |
| 469 | |
| 470 finished_ = true; | |
| 471 wait_state_ = FAILED; | |
| 472 | |
| 473 // Inform the DownloadItem we have canceled whole save page job. | |
| 474 DCHECK(download_); | |
| 475 download_->Cancel(false); | |
| 476 } | |
| 477 | |
| 478 void SavePackage::CheckFinish() { | |
| 479 if (in_process_count() || finished_) | |
| 480 return; | |
| 481 | |
| 482 std::wstring dir = save_type_ == SAVE_AS_COMPLETE_HTML ? | |
| 483 saved_main_directory_path_ : | |
| 484 L""; | |
| 485 | |
| 486 // This vector contains the final names of all the successfully saved files | |
| 487 // along with their save ids. It will be passed to SaveFileManager to do the | |
| 488 // renaming job. | |
| 489 FinalNameList final_names; | |
| 490 for (SavedItemMap::iterator it = saved_success_items_.begin(); | |
| 491 it != saved_success_items_.end(); ++it) | |
| 492 final_names.push_back(std::make_pair(it->first, | |
| 493 it->second->full_path())); | |
| 494 | |
| 495 file_manager_->GetSaveLoop()->PostTask(FROM_HERE, | |
| 496 NewRunnableMethod(file_manager_, | |
| 497 &SaveFileManager::RenameAllFiles, | |
| 498 final_names, | |
| 499 dir, | |
| 500 web_contents_->process()->host_id(), | |
| 501 web_contents_->render_view_host()->routing_id())); | |
| 502 } | |
| 503 | |
| 504 // Successfully finished all items of this SavePackage. | |
| 505 void SavePackage::Finish() { | |
| 506 // User may cancel the job when we're moving files to the final directory. | |
| 507 if (canceled()) | |
| 508 return; | |
| 509 | |
| 510 wait_state_ = SUCCESSFUL; | |
| 511 finished_ = true; | |
| 512 | |
| 513 // This vector contains the save ids of the save files which SaveFileManager | |
| 514 // needs to remove from its save_file_map_. | |
| 515 SaveIDList save_ids; | |
| 516 for (SaveUrlItemMap::iterator it = saved_failed_items_.begin(); | |
| 517 it != saved_failed_items_.end(); ++it) | |
| 518 save_ids.push_back(it->second->save_id()); | |
| 519 | |
| 520 file_manager_->GetSaveLoop()->PostTask(FROM_HERE, | |
| 521 NewRunnableMethod(file_manager_, | |
| 522 &SaveFileManager::RemoveSavedFileFromFileMap, | |
| 523 save_ids)); | |
| 524 | |
| 525 DCHECK(download_); | |
| 526 download_->Finished(all_save_items_count_); | |
| 527 } | |
| 528 | |
| 529 // Called for updating end state. | |
| 530 void SavePackage::SaveFinished(int32 save_id, int64 size, bool is_success) { | |
| 531 // Because we might have canceled this saving job before, | |
| 532 // so we might not find corresponding SaveItem. Just ignore it. | |
| 533 SaveItem* save_item = LookupItemInProcessBySaveId(save_id); | |
| 534 if (!save_item) | |
| 535 return; | |
| 536 | |
| 537 // Let SaveItem set end state. | |
| 538 save_item->Finish(size, is_success); | |
| 539 // Remove the associated save id and SavePackage. | |
| 540 file_manager_->RemoveSaveFile(save_id, save_item->url(), this); | |
| 541 | |
| 542 PutInProgressItemToSavedMap(save_item); | |
| 543 | |
| 544 // Inform the DownloadItem to update UI. | |
| 545 DCHECK(download_); | |
| 546 // We use the received bytes as number of saved files. | |
| 547 download_->Update(completed_count()); | |
| 548 | |
| 549 if (save_item->save_source() == SaveFileCreateInfo::SAVE_FILE_FROM_DOM && | |
| 550 save_item->url() == page_url_ && !save_item->received_bytes()) { | |
| 551 // If size of main HTML page is 0, treat it as disk error. | |
| 552 Cancel(false); | |
| 553 return; | |
| 554 } | |
| 555 | |
| 556 if (canceled()) { | |
| 557 DCHECK(finished_); | |
| 558 return; | |
| 559 } | |
| 560 | |
| 561 // Continue processing the save page job. | |
| 562 DoSavingProcess(); | |
| 563 | |
| 564 // Check whether we can successfully finish whole job. | |
| 565 CheckFinish(); | |
| 566 } | |
| 567 | |
| 568 // Sometimes, the net io will only call SaveFileManager::SaveFinished with | |
| 569 // save id -1 when it encounters error. Since in this case, save id will be | |
| 570 // -1, so we can only use URL to find which SaveItem is associated with | |
| 571 // this error. | |
| 572 // Saving an item failed. If it's a sub-resource, ignore it. If the error comes | |
| 573 // from serializing HTML data, then cancel saving page. | |
| 574 void SavePackage::SaveFailed(const std::wstring& save_url) { | |
| 575 SaveUrlItemMap::iterator it = in_progress_items_.find(save_url); | |
| 576 if (it == in_progress_items_.end()) { | |
| 577 NOTREACHED(); // Should not exist! | |
| 578 return; | |
| 579 } | |
| 580 SaveItem* save_item = it->second; | |
| 581 | |
| 582 save_item->Finish(0, false); | |
| 583 | |
| 584 PutInProgressItemToSavedMap(save_item); | |
| 585 | |
| 586 // Inform the DownloadItem to update UI. | |
| 587 DCHECK(download_); | |
| 588 // We use the received bytes as number of saved files. | |
| 589 download_->Update(completed_count()); | |
| 590 | |
| 591 if (save_type_ == SAVE_AS_ONLY_HTML || | |
| 592 save_item->save_source() == SaveFileCreateInfo::SAVE_FILE_FROM_DOM) { | |
| 593 // We got error when saving page. Treat it as disk error. | |
| 594 Cancel(true); | |
| 595 } | |
| 596 | |
| 597 if (canceled()) { | |
| 598 DCHECK(finished_); | |
| 599 return; | |
| 600 } | |
| 601 | |
| 602 // Continue processing the save page job. | |
| 603 DoSavingProcess(); | |
| 604 | |
| 605 CheckFinish(); | |
| 606 } | |
| 607 | |
| 608 void SavePackage::SaveCanceled(SaveItem* save_item) { | |
| 609 // Call the RemoveSaveFile in UI thread. | |
| 610 file_manager_->RemoveSaveFile(save_item->save_id(), | |
| 611 save_item->url(), | |
| 612 this); | |
| 613 if (save_item->save_id() != -1) | |
| 614 file_manager_->GetSaveLoop()->PostTask(FROM_HERE, | |
| 615 NewRunnableMethod(file_manager_, | |
| 616 &SaveFileManager::CancelSave, | |
| 617 save_item->save_id())); | |
| 618 } | |
| 619 | |
| 620 // Initiate a saving job of a specific URL. We send the request to | |
| 621 // SaveFileManager, which will dispatch it to different approach according to | |
| 622 // the save source. Parameter process_all_remaining_items indicates whether | |
| 623 // we need to save all remaining items. | |
| 624 void SavePackage::SaveNextFile(bool process_all_remaining_items) { | |
| 625 DCHECK(web_contents_); | |
| 626 DCHECK(waiting_item_queue_.size()); | |
| 627 | |
| 628 do { | |
| 629 // Pop SaveItem from waiting list. | |
| 630 SaveItem* save_item = waiting_item_queue_.front(); | |
| 631 waiting_item_queue_.pop(); | |
| 632 | |
| 633 // Add the item to in_progress_items_. | |
| 634 SaveUrlItemMap::iterator it = in_progress_items_.find( | |
| 635 save_item->url()); | |
| 636 DCHECK(it == in_progress_items_.end()); | |
| 637 in_progress_items_[save_item->url()] = save_item; | |
| 638 save_item->Start(); | |
| 639 file_manager_->SaveURL(save_item->url(), | |
| 640 save_item->referrer(), | |
| 641 web_contents_->process()->host_id(), | |
| 642 web_contents_->render_view_host()->routing_id(), | |
| 643 save_item->save_source(), | |
| 644 save_item->full_path(), | |
| 645 request_context_.get(), | |
| 646 this); | |
| 647 } while (process_all_remaining_items && waiting_item_queue_.size()); | |
| 648 } | |
| 649 | |
| 650 | |
| 651 // Open download page in windows explorer on file thread, to avoid blocking the | |
| 652 // user interface. | |
| 653 void SavePackage::ShowDownloadInShell() { | |
| 654 DCHECK(file_manager_); | |
| 655 DCHECK(finished_ && !canceled() && !saved_main_file_path_.empty()); | |
| 656 file_manager_->GetSaveLoop()->PostTask(FROM_HERE, | |
| 657 NewRunnableMethod(file_manager_, | |
| 658 &SaveFileManager::OnShowSavedFileInShell, | |
| 659 saved_main_file_path_)); | |
| 660 } | |
| 661 | |
| 662 // Calculate the percentage of whole save page job. | |
| 663 int SavePackage::PercentComplete() { | |
| 664 if (!all_save_items_count_) | |
| 665 return 0; | |
| 666 else if (!in_process_count()) | |
| 667 return 100; | |
| 668 else | |
| 669 return completed_count() / all_save_items_count_; | |
| 670 } | |
| 671 | |
| 672 // Continue processing the save page job after one SaveItem has been | |
| 673 // finished. | |
| 674 void SavePackage::DoSavingProcess() { | |
| 675 if (save_type_ == SAVE_AS_COMPLETE_HTML) { | |
| 676 // We guarantee that images and JavaScripts must be downloaded first. | |
| 677 // So when finishing all those sub-resources, we will know which | |
| 678 // sub-resource's link can be replaced with local file path, which | |
| 679 // sub-resource's link need to be replaced with absolute URL which | |
| 680 // point to its internet address because it got error when saving its data. | |
| 681 SaveItem* save_item = NULL; | |
| 682 // Start a new SaveItem job if we still have job in waiting queue. | |
| 683 if (waiting_item_queue_.size()) { | |
| 684 DCHECK(wait_state_ == NET_FILES); | |
| 685 save_item = waiting_item_queue_.front(); | |
| 686 if (save_item->save_source() != SaveFileCreateInfo::SAVE_FILE_FROM_DOM) { | |
| 687 SaveNextFile(false); | |
| 688 } else if (!in_process_count()) { | |
| 689 // If there is no in-process SaveItem, it means all sub-resources | |
| 690 // have been processed. Now we need to start serializing HTML DOM | |
| 691 // for the current page to get the generated HTML data. | |
| 692 wait_state_ = HTML_DATA; | |
| 693 // All non-HTML resources have been finished, start all remaining | |
| 694 // HTML files. | |
| 695 SaveNextFile(true); | |
| 696 } | |
| 697 } else if (in_process_count()) { | |
| 698 // Continue asking for HTML data. | |
| 699 DCHECK(wait_state_ == HTML_DATA); | |
| 700 } | |
| 701 } else { | |
| 702 // Save as HTML only. | |
| 703 DCHECK(wait_state_ == NET_FILES); | |
| 704 DCHECK(save_type_ == SAVE_AS_ONLY_HTML); | |
| 705 if (waiting_item_queue_.size()) { | |
| 706 DCHECK(all_save_items_count_ == waiting_item_queue_.size()); | |
| 707 SaveNextFile(false); | |
| 708 } | |
| 709 } | |
| 710 } | |
| 711 | |
| 712 int SavePackage::GetTabId() { | |
| 713 DCHECK(web_contents_); | |
| 714 return web_contents_->process()->host_id(); | |
| 715 } | |
| 716 | |
| 717 // After finishing all SaveItems which need to get data from net. | |
| 718 // We collect all URLs which have local storage and send the | |
| 719 // map:(originalURL:currentLocalPath) to render process (backend). | |
| 720 // Then render process will serialize DOM and send data to us. | |
| 721 void SavePackage::GetSerializedHtmlDataForCurrentPageWithLocalLinks() { | |
| 722 if (wait_state_ != HTML_DATA) | |
| 723 return; | |
| 724 std::vector<std::wstring> saved_links; | |
| 725 std::vector<std::wstring> saved_file_paths; | |
| 726 int successful_started_items_count = 0; | |
| 727 | |
| 728 // Collect all saved items which have local storage. | |
| 729 // First collect the status of all the resource files and check whether they | |
| 730 // have created local files although they have not been completely saved. | |
| 731 // If yes, the file can be saved. Otherwise, there is a disk error, so we | |
| 732 // need to cancel the page saving job. | |
| 733 for (SaveUrlItemMap::iterator it = in_progress_items_.begin(); | |
| 734 it != in_progress_items_.end(); ++it) { | |
| 735 DCHECK(it->second->save_source() == | |
| 736 SaveFileCreateInfo::SAVE_FILE_FROM_DOM); | |
| 737 if (it->second->has_final_name()) | |
| 738 successful_started_items_count++; | |
| 739 saved_links.push_back(it->second->url()); | |
| 740 saved_file_paths.push_back(it->second->file_name()); | |
| 741 } | |
| 742 | |
| 743 // If not all file of HTML resource have been started, then wait. | |
| 744 if (successful_started_items_count != in_process_count()) | |
| 745 return; | |
| 746 | |
| 747 // Collect all saved success items. | |
| 748 for (SavedItemMap::iterator it = saved_success_items_.begin(); | |
| 749 it != saved_success_items_.end(); ++it) { | |
| 750 DCHECK(it->second->has_final_name()); | |
| 751 saved_links.push_back(it->second->url()); | |
| 752 saved_file_paths.push_back(it->second->file_name()); | |
| 753 } | |
| 754 | |
| 755 // Get the relative directory name. | |
| 756 std::wstring::size_type last_slash = saved_main_directory_path_.rfind(L'\\'); | |
| 757 DCHECK(last_slash != std::wstring::npos); | |
| 758 std::wstring relative_dir_name = std::wstring(saved_main_directory_path_, | |
| 759 last_slash + 1); | |
| 760 | |
| 761 relative_dir_name = std::wstring(L"./") + relative_dir_name + L"/"; | |
| 762 | |
| 763 web_contents_->GetSerializedHtmlDataForCurrentPageWithLocalLinks( | |
| 764 saved_links, saved_file_paths, relative_dir_name); | |
| 765 } | |
| 766 | |
| 767 // Process the serialized HTML content data of a specified web page | |
| 768 // retrieved from render process. | |
| 769 void SavePackage::ProcessSerializedHtmlData(const GURL& frame_url, | |
| 770 const std::string& data, | |
| 771 int32 status) { | |
| 772 webkit_glue::DomSerializerDelegate::PageSavingSerializationStatus flag = | |
| 773 static_cast<webkit_glue::DomSerializerDelegate::PageSavingSerializationSta
tus> | |
| 774 (status); | |
| 775 // Check current state. | |
| 776 if (wait_state_ != HTML_DATA) | |
| 777 return; | |
| 778 | |
| 779 int tab_id = GetTabId(); | |
| 780 // If the all frames are finished saving, we need to close the | |
| 781 // remaining SaveItems. | |
| 782 if (flag == webkit_glue::DomSerializerDelegate::ALL_FRAMES_ARE_FINISHED) { | |
| 783 for (SaveUrlItemMap::iterator it = in_progress_items_.begin(); | |
| 784 it != in_progress_items_.end(); ++it) { | |
| 785 file_manager_->GetSaveLoop()->PostTask(FROM_HERE, | |
| 786 NewRunnableMethod(file_manager_, | |
| 787 &SaveFileManager::SaveFinished, | |
| 788 it->second->save_id(), | |
| 789 it->second->url(), | |
| 790 tab_id, | |
| 791 true)); | |
| 792 } | |
| 793 return; | |
| 794 } | |
| 795 | |
| 796 std::wstring current_frame_url = UTF8ToWide(frame_url.spec()); | |
| 797 SaveUrlItemMap::iterator it = in_progress_items_.find(current_frame_url); | |
| 798 if (it == in_progress_items_.end()) | |
| 799 return; | |
| 800 SaveItem* save_item = it->second; | |
| 801 DCHECK(save_item->save_source() == SaveFileCreateInfo::SAVE_FILE_FROM_DOM); | |
| 802 | |
| 803 if (!data.empty()) { | |
| 804 // Prepare buffer for saving HTML data. | |
| 805 char* new_data = static_cast<char*>(new char[data.size()]); | |
| 806 memcpy(new_data, data.data(), data.size()); | |
| 807 | |
| 808 // Call write file functionality in file thread. | |
| 809 file_manager_->GetSaveLoop()->PostTask(FROM_HERE, | |
| 810 NewRunnableMethod(file_manager_, | |
| 811 &SaveFileManager::UpdateSaveProgress, | |
| 812 save_item->save_id(), | |
| 813 new_data, | |
| 814 static_cast<int>(data.size()))); | |
| 815 } | |
| 816 | |
| 817 // Current frame is completed saving, call finish in file thread. | |
| 818 if (flag == webkit_glue::DomSerializerDelegate::CURRENT_FRAME_IS_FINISHED) { | |
| 819 file_manager_->GetSaveLoop()->PostTask(FROM_HERE, | |
| 820 NewRunnableMethod(file_manager_, | |
| 821 &SaveFileManager::SaveFinished, | |
| 822 save_item->save_id(), | |
| 823 save_item->url(), | |
| 824 tab_id, | |
| 825 true)); | |
| 826 } | |
| 827 } | |
| 828 | |
| 829 // Ask for all savable resource links from backend, include main frame and | |
| 830 // sub-frame. | |
| 831 void SavePackage::GetAllSavableResourceLinksForCurrentPage() { | |
| 832 if (wait_state_ != START_PROCESS) | |
| 833 return; | |
| 834 | |
| 835 wait_state_ = RESOURCES_LIST; | |
| 836 GURL main_page_url(page_url_); | |
| 837 web_contents_->GetAllSavableResourceLinksForCurrentPage(main_page_url); | |
| 838 } | |
| 839 | |
| 840 // Give backend the lists which contain all resource links that have local | |
| 841 // storage, after which, render process will serialize DOM for generating | |
| 842 // HTML data. | |
| 843 void SavePackage::ProcessCurrentPageAllSavableResourceLinks( | |
| 844 const std::vector<GURL>& resources_list, | |
| 845 const std::vector<GURL>& referrers_list, | |
| 846 const std::vector<GURL>& frames_list) { | |
| 847 if (wait_state_ != RESOURCES_LIST) | |
| 848 return; | |
| 849 | |
| 850 DCHECK(resources_list.size() == referrers_list.size()); | |
| 851 all_save_items_count_ = static_cast<int>(resources_list.size()) + | |
| 852 static_cast<int>(frames_list.size()); | |
| 853 | |
| 854 // We use total bytes as the total number of files we want to save. | |
| 855 download_->set_total_bytes(all_save_items_count_); | |
| 856 | |
| 857 if (all_save_items_count_) { | |
| 858 // Put all sub-resources to wait list. | |
| 859 for (int i = 0; i < static_cast<int>(resources_list.size()); ++i) { | |
| 860 const GURL& u = resources_list[i]; | |
| 861 DCHECK(u.is_valid()); | |
| 862 SaveFileCreateInfo::SaveFileSource save_source = u.SchemeIsFile() ? | |
| 863 SaveFileCreateInfo::SAVE_FILE_FROM_FILE : | |
| 864 SaveFileCreateInfo::SAVE_FILE_FROM_NET; | |
| 865 SaveItem* save_item = new SaveItem(UTF8ToWide(u.spec()), | |
| 866 UTF8ToWide(referrers_list[i].spec()), this, save_source); | |
| 867 waiting_item_queue_.push(save_item); | |
| 868 } | |
| 869 // Put all HTML resources to wait list. | |
| 870 for (int i = 0; i < static_cast<int>(frames_list.size()); ++i) { | |
| 871 const GURL& u = frames_list[i]; | |
| 872 DCHECK(u.is_valid()); | |
| 873 SaveItem* save_item = new SaveItem(UTF8ToWide(u.spec()), L"", | |
| 874 this, SaveFileCreateInfo::SAVE_FILE_FROM_DOM); | |
| 875 waiting_item_queue_.push(save_item); | |
| 876 } | |
| 877 wait_state_ = NET_FILES; | |
| 878 DoSavingProcess(); | |
| 879 } else { | |
| 880 // No resource files need to be saved, treat it as user cancel. | |
| 881 Cancel(true); | |
| 882 } | |
| 883 } | |
| 884 | |
| 885 std::wstring SavePackage::GetSuggestNameForSaveAs(PrefService* prefs, | |
| 886 const std::wstring& name) { | |
| 887 // Check whether the preference has the preferred directory for saving file. | |
| 888 // If not, initialize it with default directory. | |
| 889 if (!prefs->IsPrefRegistered(prefs::kSaveFileDefaultDirectory)) { | |
| 890 std::wstring default_save_path; | |
| 891 PathService::Get(chrome::DIR_USER_DOCUMENTS, &default_save_path); | |
| 892 file_util::AppendToPath(&default_save_path, | |
| 893 l10n_util::GetString(IDS_DOWNLOAD_DIRECTORY)); | |
| 894 prefs->RegisterStringPref(prefs::kSaveFileDefaultDirectory, | |
| 895 default_save_path); | |
| 896 } | |
| 897 | |
| 898 // Get the directory from preference. | |
| 899 StringPrefMember save_file_path; | |
| 900 save_file_path.Init(prefs::kSaveFileDefaultDirectory, prefs, NULL); | |
| 901 DCHECK(!(*save_file_path).empty()); | |
| 902 | |
| 903 // Ask user for getting final saving name. | |
| 904 std::wstring suggest_name, file_name; | |
| 905 | |
| 906 file_name = name; | |
| 907 file_util::ReplaceIllegalCharacters(&file_name, L' '); | |
| 908 suggest_name = *save_file_path; | |
| 909 file_util::AppendToPath(&suggest_name, file_name); | |
| 910 | |
| 911 return suggest_name; | |
| 912 } | |
| 913 | |
| 914 // Static. | |
| 915 bool SavePackage::GetSaveInfo(const std::wstring& suggest_name, | |
| 916 HWND container_hwnd, | |
| 917 SavePackageParam* param) { | |
| 918 // Use "Web Page, Complete" option as default choice of saving page. | |
| 919 unsigned index = 2; | |
| 920 | |
| 921 // If the conetnts can not be saved as complete-HTML, do not show the | |
| 922 // file filters. | |
| 923 if (CanSaveAsComplete(param->current_tab_mime_type)) { | |
| 924 // Create filter string. | |
| 925 std::wstring filter = l10n_util::GetString(IDS_SAVE_PAGE_FILTER); | |
| 926 filter.resize(filter.size() + 2); | |
| 927 filter[filter.size() - 1] = L'\0'; | |
| 928 filter[filter.size() - 2] = L'\0'; | |
| 929 | |
| 930 if (!win_util::SaveFileAsWithFilter(container_hwnd, | |
| 931 suggest_name, filter.c_str(), L"htm", | |
| 932 &index, ¶m->saved_main_file_path)) | |
| 933 return false; | |
| 934 } else { | |
| 935 if (!win_util::SaveFileAs(container_hwnd, suggest_name, | |
| 936 ¶m->saved_main_file_path)) | |
| 937 return false; | |
| 938 // Set save-as type to only-HTML if the contents of current tab can not be | |
| 939 // saved as complete-HTML. | |
| 940 index = 1; | |
| 941 } | |
| 942 // The option index is not zero-based. | |
| 943 DCHECK(index > 0 && index < 3); | |
| 944 param->dir = file_util::GetDirectoryFromPath(param->saved_main_file_path); | |
| 945 | |
| 946 StringPrefMember save_file_path; | |
| 947 save_file_path.Init(prefs::kSaveFileDefaultDirectory, param->prefs, NULL); | |
| 948 // If user change the default saving directory, we will remember it just | |
| 949 // like IE and FireFox. | |
| 950 if (save_file_path.GetValue() != param->dir) | |
| 951 save_file_path.SetValue(param->dir); | |
| 952 | |
| 953 param->save_type = (index == 1) ? SavePackage::SAVE_AS_ONLY_HTML : | |
| 954 SavePackage::SAVE_AS_COMPLETE_HTML; | |
| 955 | |
| 956 if (param->save_type == SavePackage::SAVE_AS_COMPLETE_HTML) { | |
| 957 // Make new directory for saving complete file. | |
| 958 std::wstring file_name = | |
| 959 file_util::GetFilenameFromPath(param->saved_main_file_path); | |
| 960 std::wstring::size_type last_dot = file_name.rfind(L'.'); | |
| 961 std::wstring pure_file_name; | |
| 962 if (last_dot == std::wstring::npos) | |
| 963 pure_file_name = file_name; | |
| 964 else | |
| 965 pure_file_name = std::wstring(file_name, 0, last_dot); | |
| 966 pure_file_name += L"_files"; | |
| 967 file_util::AppendToPath(¶m->dir, pure_file_name); | |
| 968 } | |
| 969 | |
| 970 return true; | |
| 971 } | |
| 972 | |
| 973 // Static. | |
| 974 bool SavePackage::GetBaseFileNameAndFileOrdinalNumber( | |
| 975 const std::wstring& file_name, | |
| 976 std::wstring* base_file_name, | |
| 977 std::wstring* file_ordinal_number) { | |
| 978 if (file_name.empty() || !base_file_name || !file_ordinal_number) | |
| 979 return false; | |
| 980 | |
| 981 // Find dot position. | |
| 982 std::wstring::size_type dot_position = file_name.rfind(L"."); | |
| 983 // Find position of right parenthesis. | |
| 984 std::wstring::size_type parenthesis_right; | |
| 985 if (std::wstring::npos == dot_position) | |
| 986 parenthesis_right = file_name.rfind(L')'); | |
| 987 else | |
| 988 parenthesis_right = dot_position - 1; | |
| 989 // The latest character of pure file name is not ")", return false. | |
| 990 if (std::wstring::npos == parenthesis_right) | |
| 991 return false; | |
| 992 if (file_name.at(parenthesis_right) != L')') | |
| 993 return false; | |
| 994 // Find position of left parenthesis. | |
| 995 std::wstring::size_type parenthesis_left = file_name.rfind(L'('); | |
| 996 if (std::wstring::npos == parenthesis_left) | |
| 997 return false; | |
| 998 | |
| 999 if (parenthesis_right <= parenthesis_left) | |
| 1000 return false; | |
| 1001 // Check whether content between left parenthesis and right parenthesis is | |
| 1002 // numeric or not. | |
| 1003 std::wstring ordinal_number(file_name, parenthesis_left + 1, | |
| 1004 parenthesis_right - parenthesis_left - 1); | |
| 1005 for (std::wstring::const_iterator cit = ordinal_number.begin(); | |
| 1006 cit != ordinal_number.end(); ++cit) | |
| 1007 if (!IsAsciiDigit(*cit)) | |
| 1008 return false; | |
| 1009 | |
| 1010 *base_file_name = std::wstring(file_name, 0, parenthesis_left); | |
| 1011 *file_ordinal_number = ordinal_number; | |
| 1012 return true; | |
| 1013 } | |
| 1014 | |
| 1015 // Static | |
| 1016 bool SavePackage::IsSavableURL(const GURL& url) { | |
| 1017 return url.SchemeIs("http") || url.SchemeIs("https") || | |
| 1018 url.SchemeIs("file") || url.SchemeIs("ftp"); | |
| 1019 } | |
| 1020 | |
| 1021 // Static | |
| 1022 bool SavePackage::IsSavableContents(const std::string& contents_mime_type) { | |
| 1023 // WebKit creates Document object when MIME type is application/xhtml+xml, | |
| 1024 // so we also support this MIME type. | |
| 1025 return contents_mime_type == "text/html" || | |
| 1026 contents_mime_type == "text/xml" || | |
| 1027 contents_mime_type == "application/xhtml+xml" || | |
| 1028 contents_mime_type == "text/plain"; | |
| 1029 } | |
| 1030 | |
| 1031 // Static | |
| 1032 bool SavePackage::CanSaveAsComplete(const std::string& contents_mime_type) { | |
| 1033 return contents_mime_type == "text/html"; | |
| 1034 } | |
| 1035 | |
| 1036 // Static | |
| 1037 bool SavePackage::GetSafePureFileName(const std::wstring& dir_path, | |
| 1038 const std::wstring& file_name_ext, | |
| 1039 uint32 max_file_path_len, | |
| 1040 std::wstring* pure_file_name) { | |
| 1041 DCHECK(!pure_file_name->empty()); | |
| 1042 std::wstring final_name = dir_path; | |
| 1043 file_util::AppendToPath(&final_name, *pure_file_name); | |
| 1044 // Get total length of dir path, including ending "\". | |
| 1045 const std::wstring::size_type dir_path_length = | |
| 1046 final_name.length() - pure_file_name->length(); | |
| 1047 // Get available length for putting dir path and pure file name. | |
| 1048 const std::wstring::size_type available_length = | |
| 1049 static_cast<std::wstring::size_type>(max_file_path_len) - | |
| 1050 file_name_ext.length(); | |
| 1051 | |
| 1052 if (final_name.length() <= available_length) | |
| 1053 return true; | |
| 1054 | |
| 1055 if (available_length > dir_path_length) { | |
| 1056 *pure_file_name = | |
| 1057 pure_file_name->substr(0, available_length - dir_path_length); | |
| 1058 return true; | |
| 1059 } else { | |
| 1060 pure_file_name->clear(); | |
| 1061 return false; | |
| 1062 } | |
| 1063 } | |
| 1064 | |
| OLD | NEW |