| Index: chrome/browser/save_package.cc
|
| ===================================================================
|
| --- chrome/browser/save_package.cc (revision 2173)
|
| +++ chrome/browser/save_package.cc (working copy)
|
| @@ -1,1064 +0,0 @@
|
| -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "chrome/browser/save_package.h"
|
| -
|
| -#include "base/file_util.h"
|
| -#include "base/logging.h"
|
| -#include "base/message_loop.h"
|
| -#include "base/path_service.h"
|
| -#include "base/string_util.h"
|
| -#include "base/task.h"
|
| -#include "base/thread.h"
|
| -#include "base/win_util.h"
|
| -#include "chrome/browser/browser_process.h"
|
| -#include "chrome/browser/download/download_manager.h"
|
| -#include "chrome/browser/profile.h"
|
| -#include "chrome/browser/render_process_host.h"
|
| -#include "chrome/browser/render_view_host.h"
|
| -#include "chrome/browser/render_view_host_delegate.h"
|
| -#include "chrome/browser/resource_dispatcher_host.h"
|
| -#include "chrome/browser/save_file.h"
|
| -#include "chrome/browser/save_file_manager.h"
|
| -#include "chrome/browser/save_page_model.h"
|
| -#include "chrome/browser/tab_util.h"
|
| -#include "chrome/browser/web_contents.h"
|
| -#include "chrome/browser/views/download_shelf_view.h"
|
| -#include "chrome/common/chrome_paths.h"
|
| -#include "chrome/common/l10n_util.h"
|
| -#include "chrome/common/pref_names.h"
|
| -#include "chrome/common/pref_service.h"
|
| -#include "chrome/common/stl_util-inl.h"
|
| -#include "chrome/common/win_util.h"
|
| -#include "net/base/net_util.h"
|
| -#include "net/url_request/url_request_context.h"
|
| -#include "webkit/glue/dom_serializer_delegate.h"
|
| -
|
| -#include "generated_resources.h"
|
| -
|
| -// Default name which will be used when we can not get proper name from
|
| -// resource URL.
|
| -static const wchar_t kDefaultSaveName[] = L"saved_resource";
|
| -
|
| -// Maximum number of file ordinal number. I think it's big enough for resolving
|
| -// name-conflict files which has same base file name.
|
| -static const int32 kMaxFileOrdinalNumber = 9999;
|
| -
|
| -// Maximum length for file path. Since Windows have MAX_PATH limitation for
|
| -// file path, we need to make sure length of file path of every saved file
|
| -// is less than MAX_PATH
|
| -static const uint32 kMaxFilePathLength = MAX_PATH - 1;
|
| -
|
| -// Maximum length for file ordinal number part. Since we only support the
|
| -// maximum 9999 for ordinal number, which means maximum file ordinal number part
|
| -// should be "(9998)", so the value is 6.
|
| -static const uint32 kMaxFileOrdinalNumberPartLength = 6;
|
| -
|
| -SavePackage::SavePackage(WebContents* web_content,
|
| - SavePackageType save_type,
|
| - const std::wstring& file_full_path,
|
| - const std::wstring& directory_full_path)
|
| - : web_contents_(web_content),
|
| - save_type_(save_type),
|
| - saved_main_file_path_(file_full_path),
|
| - saved_main_directory_path_(directory_full_path),
|
| - all_save_items_count_(0),
|
| - disk_error_occurred_(false),
|
| - user_canceled_(false),
|
| - download_(NULL),
|
| - finished_(false),
|
| - wait_state_(INITIALIZE) {
|
| - DCHECK(web_content);
|
| - const GURL& current_page_url = web_contents_->GetURL();
|
| - DCHECK(current_page_url.is_valid());
|
| - page_url_ = UTF8ToWide(current_page_url.spec());
|
| - DCHECK(save_type_ == SAVE_AS_ONLY_HTML ||
|
| - save_type_ == SAVE_AS_COMPLETE_HTML);
|
| - DCHECK(!saved_main_file_path_.empty() &&
|
| - saved_main_file_path_.length() <= kMaxFilePathLength);
|
| - DCHECK(!saved_main_directory_path_.empty() &&
|
| - saved_main_directory_path_.length() < kMaxFilePathLength);
|
| -}
|
| -
|
| -// This is for testing use. Set |finished_| as true because we don't want
|
| -// method Cancel to be be called in destructor in test mode.
|
| -SavePackage::SavePackage(const wchar_t* file_full_path,
|
| - const wchar_t* directory_full_path)
|
| - : all_save_items_count_(0),
|
| - saved_main_file_path_(file_full_path),
|
| - saved_main_directory_path_(directory_full_path),
|
| - finished_(true),
|
| - download_(NULL),
|
| - user_canceled_(false),
|
| - disk_error_occurred_(false) {
|
| - DCHECK(!saved_main_file_path_.empty() &&
|
| - saved_main_file_path_.length() <= kMaxFilePathLength);
|
| - DCHECK(!saved_main_directory_path_.empty() &&
|
| - saved_main_directory_path_.length() < kMaxFilePathLength);
|
| -}
|
| -
|
| -SavePackage::~SavePackage() {
|
| - // Stop receiving saving job's updates
|
| - if (!finished_ && !canceled()) {
|
| - // Unexpected quit.
|
| - Cancel(true);
|
| - }
|
| -
|
| - DCHECK(all_save_items_count_ == (waiting_item_queue_.size() +
|
| - completed_count() +
|
| - in_process_count()));
|
| - // Free all SaveItems.
|
| - while (!waiting_item_queue_.empty()) {
|
| - // We still have some items which are waiting for start to save.
|
| - SaveItem* save_item = waiting_item_queue_.front();
|
| - waiting_item_queue_.pop();
|
| - delete save_item;
|
| - }
|
| -
|
| - STLDeleteValues(&saved_success_items_);
|
| - STLDeleteValues(&in_progress_items_);
|
| - STLDeleteValues(&saved_failed_items_);
|
| -
|
| - if (download_) {
|
| - // We call this to remove the view from the shelf. It will invoke
|
| - // DownloadManager::RemoveDownload, but since the fake DownloadItem is not
|
| - // owned by DownloadManager, it will do nothing to our fake item.
|
| - download_->Remove();
|
| - delete download_;
|
| - download_ = NULL;
|
| - }
|
| - file_manager_ = NULL;
|
| -}
|
| -
|
| -// Cancel all in progress request, might be called by user or internal error.
|
| -void SavePackage::Cancel(bool user_action) {
|
| - if (!canceled()) {
|
| - if (user_action)
|
| - user_canceled_ = true;
|
| - else
|
| - disk_error_occurred_ = true;
|
| - Stop();
|
| - }
|
| -}
|
| -
|
| -// Initialize the SavePackage.
|
| -bool SavePackage::Init() {
|
| - // Set proper running state.
|
| - if (wait_state_ != INITIALIZE)
|
| - return false;
|
| -
|
| - wait_state_ = START_PROCESS;
|
| -
|
| - // Initialize the request context and resource dispatcher.
|
| - Profile* profile = web_contents_->profile();
|
| - if (!profile) {
|
| - NOTREACHED();
|
| - return false;
|
| - }
|
| -
|
| - request_context_ = profile->GetRequestContext();
|
| -
|
| - ResourceDispatcherHost* rdh = g_browser_process->resource_dispatcher_host();
|
| - if (!rdh) {
|
| - NOTREACHED();
|
| - return false;
|
| - }
|
| -
|
| - file_manager_ = rdh->save_file_manager();
|
| - if (!file_manager_) {
|
| - NOTREACHED();
|
| - return false;
|
| - }
|
| -
|
| - // Create the fake DownloadItem and display the view.
|
| - download_ = new DownloadItem(1, saved_main_file_path_,
|
| - page_url_, Time::Now(), 0, -1, -1);
|
| - download_->set_manager(web_contents_->profile()->GetDownloadManager());
|
| - DownloadShelfView* shelf = web_contents_->GetDownloadShelfView();
|
| - shelf->AddDownloadView(new DownloadItemView(
|
| - download_, shelf, new SavePageModel(this, download_)));
|
| - web_contents_->SetDownloadShelfVisible(true);
|
| -
|
| - // Check save type and process the save page job.
|
| - if (save_type_ == SAVE_AS_COMPLETE_HTML) {
|
| - // Get directory
|
| - DCHECK(!saved_main_directory_path_.empty());
|
| - GetAllSavableResourceLinksForCurrentPage();
|
| - } else {
|
| - wait_state_ = NET_FILES;
|
| - GURL u(page_url_);
|
| - SaveFileCreateInfo::SaveFileSource save_source = u.SchemeIsFile() ?
|
| - SaveFileCreateInfo::SAVE_FILE_FROM_FILE :
|
| - SaveFileCreateInfo::SAVE_FILE_FROM_NET;
|
| - SaveItem* save_item = new SaveItem(page_url_,
|
| - L"",
|
| - this,
|
| - save_source);
|
| - // Add this item to waiting list.
|
| - waiting_item_queue_.push(save_item);
|
| - all_save_items_count_ = 1;
|
| - download_->set_total_bytes(1);
|
| -
|
| - DoSavingProcess();
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -// Generate name for saving resource.
|
| -bool SavePackage::GenerateFilename(const std::string& disposition,
|
| - const std::wstring& url,
|
| - bool need_html_ext,
|
| - std::wstring* generated_name) {
|
| - std::wstring file_name =
|
| - net::GetSuggestedFilename(GURL(url), disposition, kDefaultSaveName);
|
| -
|
| - DCHECK(!file_name.empty());
|
| - // Check whether we have same name before.
|
| - std::wstring::size_type last_dot = file_name.rfind(L'.');
|
| - std::wstring pure_file_name, file_name_ext;
|
| - if (last_dot == std::wstring::npos) {
|
| - pure_file_name = file_name;
|
| - } else {
|
| - pure_file_name = std::wstring(file_name, 0, last_dot);
|
| - file_name_ext = std::wstring(file_name, last_dot);
|
| - }
|
| - // If it is HTML resource, use ".htm" as its extension name.
|
| - if (need_html_ext)
|
| - file_name_ext = L".htm";
|
| - if (file_name_ext == L".")
|
| - file_name_ext.clear();
|
| -
|
| - // Get safe pure file name.
|
| - if (!GetSafePureFileName(saved_main_directory_path_, file_name_ext,
|
| - kMaxFilePathLength, &pure_file_name))
|
| - return false;
|
| -
|
| - file_name = pure_file_name + file_name_ext;
|
| -
|
| - // Check whether we already have same name.
|
| - if (file_name_set_.find(file_name) == file_name_set_.end()) {
|
| - file_name_set_.insert(file_name);
|
| - } else {
|
| - // Found same name, increase the ordinal number for the file name.
|
| - std::wstring base_file_name, file_ordinal_number;
|
| -
|
| - if (!GetBaseFileNameAndFileOrdinalNumber(pure_file_name, &base_file_name,
|
| - &file_ordinal_number))
|
| - base_file_name = pure_file_name;
|
| -
|
| - // We need to make sure the length of base file name plus maximum ordinal
|
| - // number path will be less than or equal to kMaxFilePathLength.
|
| - if (!GetSafePureFileName(saved_main_directory_path_, file_name_ext,
|
| - kMaxFilePathLength - kMaxFileOrdinalNumberPartLength, &base_file_name))
|
| - return false;
|
| -
|
| - // Prepare the new ordinal number.
|
| - uint32 ordinal_number;
|
| - FileNameCountMap::iterator it = file_name_count_map_.find(base_file_name);
|
| - if (it == file_name_count_map_.end()) {
|
| - // First base-name-conflict resolving, use 1 as initial ordinal number.
|
| - file_name_count_map_[base_file_name] = 1;
|
| - ordinal_number = 1;
|
| - } else {
|
| - // We have met same base-name conflict, use latest ordinal number.
|
| - ordinal_number = it->second;
|
| - }
|
| -
|
| - if (ordinal_number > (kMaxFileOrdinalNumber - 1)) {
|
| - // Use a random file from temporary file.
|
| - file_util::CreateTemporaryFileName(&file_name);
|
| - file_name = file_util::GetFilenameFromPath(file_name);
|
| - // Get safe pure file name.
|
| - if (!GetSafePureFileName(saved_main_directory_path_, std::wstring(),
|
| - kMaxFilePathLength, &file_name))
|
| - return false;
|
| - } else {
|
| - uint32 i;
|
| - for (i = ordinal_number; i < kMaxFileOrdinalNumber; ++i) {
|
| - std::wstring new_name =
|
| - StringPrintf(L"%ls(%d)", base_file_name.c_str(), i) + file_name_ext;
|
| - if (file_name_set_.find(new_name) == file_name_set_.end()) {
|
| - // Resolved name conflict.
|
| - file_name = new_name;
|
| - file_name_count_map_[base_file_name] = ++i;
|
| - break;
|
| - }
|
| - }
|
| - }
|
| -
|
| - file_name_set_.insert(file_name);
|
| - }
|
| -
|
| - DCHECK(!file_name.empty());
|
| - generated_name->assign(file_name);
|
| -
|
| - return true;
|
| -}
|
| -
|
| -// We have received a message from SaveFileManager about a new saving job. We
|
| -// create a SaveItem and store it in our in_progress list.
|
| -void SavePackage::StartSave(const SaveFileCreateInfo* info) {
|
| - DCHECK(info && !info->url.empty());
|
| -
|
| - SaveUrlItemMap::iterator it = in_progress_items_.find(info->url);
|
| - if (it == in_progress_items_.end()) {
|
| - // If not found, we must have cancel action.
|
| - DCHECK(canceled());
|
| - return;
|
| - }
|
| - SaveItem* save_item = it->second;
|
| -
|
| - DCHECK(!saved_main_file_path_.empty());
|
| -
|
| - save_item->SetSaveId(info->save_id);
|
| - save_item->SetTotalBytes(info->total_bytes);
|
| -
|
| - // Determine the proper path for a saving job, by choosing either the default
|
| - // save directory, or prompting the user.
|
| - DCHECK(!save_item->has_final_name());
|
| - if (info->url != page_url_) {
|
| - std::wstring generated_name;
|
| - // For HTML resource file, make sure it will have .htm as extension name,
|
| - // otherwise, when you open the saved page in Chrome again, download
|
| - // file manager will treat it as downloadable resource, and download it
|
| - // instead of opening it as HTML.
|
| - bool need_html_ext =
|
| - info->save_source == SaveFileCreateInfo::SAVE_FILE_FROM_DOM;
|
| - if (!GenerateFilename(info->content_disposition,
|
| - info->url,
|
| - need_html_ext,
|
| - &generated_name)) {
|
| - // We can not generate file name for this SaveItem, so we cancel the
|
| - // saving page job if the save source is from serialized DOM data.
|
| - // Otherwise, it means this SaveItem is sub-resource type, we treat it
|
| - // as an error happened on saving. We can ignore this type error for
|
| - // sub-resource links which will be resolved as absolute links instead
|
| - // of local links in final saved contents.
|
| - if (info->save_source == SaveFileCreateInfo::SAVE_FILE_FROM_DOM)
|
| - Cancel(true);
|
| - else
|
| - SaveFinished(save_item->save_id(), 0, false);
|
| - return;
|
| - }
|
| -
|
| - // When saving page as only-HTML, we only have a SaveItem whose url
|
| - // must be page_url_.
|
| - DCHECK(save_type_ == SAVE_AS_COMPLETE_HTML);
|
| - DCHECK(!saved_main_directory_path_.empty());
|
| -
|
| - // Now we get final name retrieved from GenerateFilename, we will use it
|
| - // rename the SaveItem.
|
| - std::wstring final_name = saved_main_directory_path_;
|
| - file_util::AppendToPath(&final_name, generated_name);
|
| - save_item->Rename(final_name);
|
| - } else {
|
| - // It is the main HTML file, use the name chosen by the user.
|
| - save_item->Rename(saved_main_file_path_);
|
| - }
|
| -
|
| - // If the save source is from file system, inform SaveFileManager to copy
|
| - // corresponding file to the file path which this SaveItem specifies.
|
| - if (info->save_source == SaveFileCreateInfo::SAVE_FILE_FROM_FILE) {
|
| - file_manager_->GetSaveLoop()->PostTask(FROM_HERE,
|
| - NewRunnableMethod(file_manager_,
|
| - &SaveFileManager::SaveLocalFile,
|
| - save_item->url(),
|
| - save_item->save_id(),
|
| - GetTabId()));
|
| - return;
|
| - }
|
| -
|
| - // Check whether we begin to require serialized HTML data.
|
| - if (save_type_ == SAVE_AS_COMPLETE_HTML && wait_state_ == HTML_DATA) {
|
| - // Inform backend to serialize the all frames' DOM and send serialized
|
| - // HTML data back.
|
| - GetSerializedHtmlDataForCurrentPageWithLocalLinks();
|
| - }
|
| -}
|
| -
|
| -// Look up SaveItem by save id from in progress map.
|
| -SaveItem* SavePackage::LookupItemInProcessBySaveId(int32 save_id) {
|
| - if (in_process_count()) {
|
| - SaveUrlItemMap::iterator it = in_progress_items_.begin();
|
| - for (; it != in_progress_items_.end(); ++it) {
|
| - SaveItem* save_item = it->second;
|
| - DCHECK(save_item->state() == SaveItem::IN_PROGRESS);
|
| - if (save_item->save_id() == save_id)
|
| - return save_item;
|
| - }
|
| - }
|
| - return NULL;
|
| -}
|
| -
|
| -// Remove SaveItem from in progress map and put it to saved map.
|
| -void SavePackage::PutInProgressItemToSavedMap(SaveItem* save_item) {
|
| - SaveUrlItemMap::iterator it = in_progress_items_.find(
|
| - save_item->url());
|
| - DCHECK(it != in_progress_items_.end());
|
| - DCHECK(save_item == it->second);
|
| - in_progress_items_.erase(it);
|
| -
|
| - if (save_item->success()) {
|
| - // Add it to saved_success_items_.
|
| - DCHECK(saved_success_items_.find(save_item->save_id()) ==
|
| - saved_success_items_.end());
|
| - saved_success_items_[save_item->save_id()] = save_item;
|
| - } else {
|
| - // Add it to saved_failed_items_.
|
| - DCHECK(saved_failed_items_.find(save_item->url()) ==
|
| - saved_failed_items_.end());
|
| - saved_failed_items_[save_item->url()] = save_item;
|
| - }
|
| -}
|
| -
|
| -// Called for updating saving state.
|
| -bool SavePackage::UpdateSaveProgress(int32 save_id,
|
| - int64 size,
|
| - bool write_success) {
|
| - // Because we might have canceled this saving job before,
|
| - // so we might not find corresponding SaveItem.
|
| - SaveItem* save_item = LookupItemInProcessBySaveId(save_id);
|
| - if (!save_item)
|
| - return false;
|
| -
|
| - save_item->Update(size);
|
| -
|
| - // If we got disk error, cancel whole save page job.
|
| - if (!write_success) {
|
| - // Cancel job with reason of disk error.
|
| - Cancel(false);
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -// Stop all page saving jobs that are in progress and instruct the file thread
|
| -// to delete all saved files.
|
| -void SavePackage::Stop() {
|
| - // When stopping, if it still has some items in in_progress, cancel them.
|
| - DCHECK(canceled());
|
| - if (in_process_count()) {
|
| - SaveUrlItemMap::iterator it = in_progress_items_.begin();
|
| - for (; it != in_progress_items_.end(); ++it) {
|
| - SaveItem* save_item = it->second;
|
| - DCHECK(save_item->state() == SaveItem::IN_PROGRESS);
|
| - save_item->Cancel();
|
| - }
|
| - // Remove all in progress item to saved map. For failed items, they will
|
| - // be put into saved_failed_items_, for successful item, they will be put
|
| - // into saved_success_items_.
|
| - while (in_process_count())
|
| - PutInProgressItemToSavedMap(in_progress_items_.begin()->second);
|
| - }
|
| -
|
| - // This vector contains the save ids of the save files which SaveFileManager
|
| - // needs to remove from its save_file_map_.
|
| - SaveIDList save_ids;
|
| - for (SavedItemMap::iterator it = saved_success_items_.begin();
|
| - it != saved_success_items_.end(); ++it)
|
| - save_ids.push_back(it->first);
|
| - for (SaveUrlItemMap::iterator it = saved_failed_items_.begin();
|
| - it != saved_failed_items_.end(); ++it)
|
| - save_ids.push_back(it->second->save_id());
|
| -
|
| - file_manager_->GetSaveLoop()->PostTask(FROM_HERE,
|
| - NewRunnableMethod(file_manager_,
|
| - &SaveFileManager::RemoveSavedFileFromFileMap,
|
| - save_ids));
|
| -
|
| - finished_ = true;
|
| - wait_state_ = FAILED;
|
| -
|
| - // Inform the DownloadItem we have canceled whole save page job.
|
| - DCHECK(download_);
|
| - download_->Cancel(false);
|
| -}
|
| -
|
| -void SavePackage::CheckFinish() {
|
| - if (in_process_count() || finished_)
|
| - return;
|
| -
|
| - std::wstring dir = save_type_ == SAVE_AS_COMPLETE_HTML ?
|
| - saved_main_directory_path_ :
|
| - L"";
|
| -
|
| - // This vector contains the final names of all the successfully saved files
|
| - // along with their save ids. It will be passed to SaveFileManager to do the
|
| - // renaming job.
|
| - FinalNameList final_names;
|
| - for (SavedItemMap::iterator it = saved_success_items_.begin();
|
| - it != saved_success_items_.end(); ++it)
|
| - final_names.push_back(std::make_pair(it->first,
|
| - it->second->full_path()));
|
| -
|
| - file_manager_->GetSaveLoop()->PostTask(FROM_HERE,
|
| - NewRunnableMethod(file_manager_,
|
| - &SaveFileManager::RenameAllFiles,
|
| - final_names,
|
| - dir,
|
| - web_contents_->process()->host_id(),
|
| - web_contents_->render_view_host()->routing_id()));
|
| -}
|
| -
|
| -// Successfully finished all items of this SavePackage.
|
| -void SavePackage::Finish() {
|
| - // User may cancel the job when we're moving files to the final directory.
|
| - if (canceled())
|
| - return;
|
| -
|
| - wait_state_ = SUCCESSFUL;
|
| - finished_ = true;
|
| -
|
| - // This vector contains the save ids of the save files which SaveFileManager
|
| - // needs to remove from its save_file_map_.
|
| - SaveIDList save_ids;
|
| - for (SaveUrlItemMap::iterator it = saved_failed_items_.begin();
|
| - it != saved_failed_items_.end(); ++it)
|
| - save_ids.push_back(it->second->save_id());
|
| -
|
| - file_manager_->GetSaveLoop()->PostTask(FROM_HERE,
|
| - NewRunnableMethod(file_manager_,
|
| - &SaveFileManager::RemoveSavedFileFromFileMap,
|
| - save_ids));
|
| -
|
| - DCHECK(download_);
|
| - download_->Finished(all_save_items_count_);
|
| -}
|
| -
|
| -// Called for updating end state.
|
| -void SavePackage::SaveFinished(int32 save_id, int64 size, bool is_success) {
|
| - // Because we might have canceled this saving job before,
|
| - // so we might not find corresponding SaveItem. Just ignore it.
|
| - SaveItem* save_item = LookupItemInProcessBySaveId(save_id);
|
| - if (!save_item)
|
| - return;
|
| -
|
| - // Let SaveItem set end state.
|
| - save_item->Finish(size, is_success);
|
| - // Remove the associated save id and SavePackage.
|
| - file_manager_->RemoveSaveFile(save_id, save_item->url(), this);
|
| -
|
| - PutInProgressItemToSavedMap(save_item);
|
| -
|
| - // Inform the DownloadItem to update UI.
|
| - DCHECK(download_);
|
| - // We use the received bytes as number of saved files.
|
| - download_->Update(completed_count());
|
| -
|
| - if (save_item->save_source() == SaveFileCreateInfo::SAVE_FILE_FROM_DOM &&
|
| - save_item->url() == page_url_ && !save_item->received_bytes()) {
|
| - // If size of main HTML page is 0, treat it as disk error.
|
| - Cancel(false);
|
| - return;
|
| - }
|
| -
|
| - if (canceled()) {
|
| - DCHECK(finished_);
|
| - return;
|
| - }
|
| -
|
| - // Continue processing the save page job.
|
| - DoSavingProcess();
|
| -
|
| - // Check whether we can successfully finish whole job.
|
| - CheckFinish();
|
| -}
|
| -
|
| -// Sometimes, the net io will only call SaveFileManager::SaveFinished with
|
| -// save id -1 when it encounters error. Since in this case, save id will be
|
| -// -1, so we can only use URL to find which SaveItem is associated with
|
| -// this error.
|
| -// Saving an item failed. If it's a sub-resource, ignore it. If the error comes
|
| -// from serializing HTML data, then cancel saving page.
|
| -void SavePackage::SaveFailed(const std::wstring& save_url) {
|
| - SaveUrlItemMap::iterator it = in_progress_items_.find(save_url);
|
| - if (it == in_progress_items_.end()) {
|
| - NOTREACHED(); // Should not exist!
|
| - return;
|
| - }
|
| - SaveItem* save_item = it->second;
|
| -
|
| - save_item->Finish(0, false);
|
| -
|
| - PutInProgressItemToSavedMap(save_item);
|
| -
|
| - // Inform the DownloadItem to update UI.
|
| - DCHECK(download_);
|
| - // We use the received bytes as number of saved files.
|
| - download_->Update(completed_count());
|
| -
|
| - if (save_type_ == SAVE_AS_ONLY_HTML ||
|
| - save_item->save_source() == SaveFileCreateInfo::SAVE_FILE_FROM_DOM) {
|
| - // We got error when saving page. Treat it as disk error.
|
| - Cancel(true);
|
| - }
|
| -
|
| - if (canceled()) {
|
| - DCHECK(finished_);
|
| - return;
|
| - }
|
| -
|
| - // Continue processing the save page job.
|
| - DoSavingProcess();
|
| -
|
| - CheckFinish();
|
| -}
|
| -
|
| -void SavePackage::SaveCanceled(SaveItem* save_item) {
|
| - // Call the RemoveSaveFile in UI thread.
|
| - file_manager_->RemoveSaveFile(save_item->save_id(),
|
| - save_item->url(),
|
| - this);
|
| - if (save_item->save_id() != -1)
|
| - file_manager_->GetSaveLoop()->PostTask(FROM_HERE,
|
| - NewRunnableMethod(file_manager_,
|
| - &SaveFileManager::CancelSave,
|
| - save_item->save_id()));
|
| -}
|
| -
|
| -// Initiate a saving job of a specific URL. We send the request to
|
| -// SaveFileManager, which will dispatch it to different approach according to
|
| -// the save source. Parameter process_all_remaining_items indicates whether
|
| -// we need to save all remaining items.
|
| -void SavePackage::SaveNextFile(bool process_all_remaining_items) {
|
| - DCHECK(web_contents_);
|
| - DCHECK(waiting_item_queue_.size());
|
| -
|
| - do {
|
| - // Pop SaveItem from waiting list.
|
| - SaveItem* save_item = waiting_item_queue_.front();
|
| - waiting_item_queue_.pop();
|
| -
|
| - // Add the item to in_progress_items_.
|
| - SaveUrlItemMap::iterator it = in_progress_items_.find(
|
| - save_item->url());
|
| - DCHECK(it == in_progress_items_.end());
|
| - in_progress_items_[save_item->url()] = save_item;
|
| - save_item->Start();
|
| - file_manager_->SaveURL(save_item->url(),
|
| - save_item->referrer(),
|
| - web_contents_->process()->host_id(),
|
| - web_contents_->render_view_host()->routing_id(),
|
| - save_item->save_source(),
|
| - save_item->full_path(),
|
| - request_context_.get(),
|
| - this);
|
| - } while (process_all_remaining_items && waiting_item_queue_.size());
|
| -}
|
| -
|
| -
|
| -// Open download page in windows explorer on file thread, to avoid blocking the
|
| -// user interface.
|
| -void SavePackage::ShowDownloadInShell() {
|
| - DCHECK(file_manager_);
|
| - DCHECK(finished_ && !canceled() && !saved_main_file_path_.empty());
|
| - file_manager_->GetSaveLoop()->PostTask(FROM_HERE,
|
| - NewRunnableMethod(file_manager_,
|
| - &SaveFileManager::OnShowSavedFileInShell,
|
| - saved_main_file_path_));
|
| -}
|
| -
|
| -// Calculate the percentage of whole save page job.
|
| -int SavePackage::PercentComplete() {
|
| - if (!all_save_items_count_)
|
| - return 0;
|
| - else if (!in_process_count())
|
| - return 100;
|
| - else
|
| - return completed_count() / all_save_items_count_;
|
| -}
|
| -
|
| -// Continue processing the save page job after one SaveItem has been
|
| -// finished.
|
| -void SavePackage::DoSavingProcess() {
|
| - if (save_type_ == SAVE_AS_COMPLETE_HTML) {
|
| - // We guarantee that images and JavaScripts must be downloaded first.
|
| - // So when finishing all those sub-resources, we will know which
|
| - // sub-resource's link can be replaced with local file path, which
|
| - // sub-resource's link need to be replaced with absolute URL which
|
| - // point to its internet address because it got error when saving its data.
|
| - SaveItem* save_item = NULL;
|
| - // Start a new SaveItem job if we still have job in waiting queue.
|
| - if (waiting_item_queue_.size()) {
|
| - DCHECK(wait_state_ == NET_FILES);
|
| - save_item = waiting_item_queue_.front();
|
| - if (save_item->save_source() != SaveFileCreateInfo::SAVE_FILE_FROM_DOM) {
|
| - SaveNextFile(false);
|
| - } else if (!in_process_count()) {
|
| - // If there is no in-process SaveItem, it means all sub-resources
|
| - // have been processed. Now we need to start serializing HTML DOM
|
| - // for the current page to get the generated HTML data.
|
| - wait_state_ = HTML_DATA;
|
| - // All non-HTML resources have been finished, start all remaining
|
| - // HTML files.
|
| - SaveNextFile(true);
|
| - }
|
| - } else if (in_process_count()) {
|
| - // Continue asking for HTML data.
|
| - DCHECK(wait_state_ == HTML_DATA);
|
| - }
|
| - } else {
|
| - // Save as HTML only.
|
| - DCHECK(wait_state_ == NET_FILES);
|
| - DCHECK(save_type_ == SAVE_AS_ONLY_HTML);
|
| - if (waiting_item_queue_.size()) {
|
| - DCHECK(all_save_items_count_ == waiting_item_queue_.size());
|
| - SaveNextFile(false);
|
| - }
|
| - }
|
| -}
|
| -
|
| -int SavePackage::GetTabId() {
|
| - DCHECK(web_contents_);
|
| - return web_contents_->process()->host_id();
|
| -}
|
| -
|
| -// After finishing all SaveItems which need to get data from net.
|
| -// We collect all URLs which have local storage and send the
|
| -// map:(originalURL:currentLocalPath) to render process (backend).
|
| -// Then render process will serialize DOM and send data to us.
|
| -void SavePackage::GetSerializedHtmlDataForCurrentPageWithLocalLinks() {
|
| - if (wait_state_ != HTML_DATA)
|
| - return;
|
| - std::vector<std::wstring> saved_links;
|
| - std::vector<std::wstring> saved_file_paths;
|
| - int successful_started_items_count = 0;
|
| -
|
| - // Collect all saved items which have local storage.
|
| - // First collect the status of all the resource files and check whether they
|
| - // have created local files although they have not been completely saved.
|
| - // If yes, the file can be saved. Otherwise, there is a disk error, so we
|
| - // need to cancel the page saving job.
|
| - for (SaveUrlItemMap::iterator it = in_progress_items_.begin();
|
| - it != in_progress_items_.end(); ++it) {
|
| - DCHECK(it->second->save_source() ==
|
| - SaveFileCreateInfo::SAVE_FILE_FROM_DOM);
|
| - if (it->second->has_final_name())
|
| - successful_started_items_count++;
|
| - saved_links.push_back(it->second->url());
|
| - saved_file_paths.push_back(it->second->file_name());
|
| - }
|
| -
|
| - // If not all file of HTML resource have been started, then wait.
|
| - if (successful_started_items_count != in_process_count())
|
| - return;
|
| -
|
| - // Collect all saved success items.
|
| - for (SavedItemMap::iterator it = saved_success_items_.begin();
|
| - it != saved_success_items_.end(); ++it) {
|
| - DCHECK(it->second->has_final_name());
|
| - saved_links.push_back(it->second->url());
|
| - saved_file_paths.push_back(it->second->file_name());
|
| - }
|
| -
|
| - // Get the relative directory name.
|
| - std::wstring::size_type last_slash = saved_main_directory_path_.rfind(L'\\');
|
| - DCHECK(last_slash != std::wstring::npos);
|
| - std::wstring relative_dir_name = std::wstring(saved_main_directory_path_,
|
| - last_slash + 1);
|
| -
|
| - relative_dir_name = std::wstring(L"./") + relative_dir_name + L"/";
|
| -
|
| - web_contents_->GetSerializedHtmlDataForCurrentPageWithLocalLinks(
|
| - saved_links, saved_file_paths, relative_dir_name);
|
| -}
|
| -
|
| -// Process the serialized HTML content data of a specified web page
|
| -// retrieved from render process.
|
| -void SavePackage::ProcessSerializedHtmlData(const GURL& frame_url,
|
| - const std::string& data,
|
| - int32 status) {
|
| - webkit_glue::DomSerializerDelegate::PageSavingSerializationStatus flag =
|
| - static_cast<webkit_glue::DomSerializerDelegate::PageSavingSerializationStatus>
|
| - (status);
|
| - // Check current state.
|
| - if (wait_state_ != HTML_DATA)
|
| - return;
|
| -
|
| - int tab_id = GetTabId();
|
| - // If the all frames are finished saving, we need to close the
|
| - // remaining SaveItems.
|
| - if (flag == webkit_glue::DomSerializerDelegate::ALL_FRAMES_ARE_FINISHED) {
|
| - for (SaveUrlItemMap::iterator it = in_progress_items_.begin();
|
| - it != in_progress_items_.end(); ++it) {
|
| - file_manager_->GetSaveLoop()->PostTask(FROM_HERE,
|
| - NewRunnableMethod(file_manager_,
|
| - &SaveFileManager::SaveFinished,
|
| - it->second->save_id(),
|
| - it->second->url(),
|
| - tab_id,
|
| - true));
|
| - }
|
| - return;
|
| - }
|
| -
|
| - std::wstring current_frame_url = UTF8ToWide(frame_url.spec());
|
| - SaveUrlItemMap::iterator it = in_progress_items_.find(current_frame_url);
|
| - if (it == in_progress_items_.end())
|
| - return;
|
| - SaveItem* save_item = it->second;
|
| - DCHECK(save_item->save_source() == SaveFileCreateInfo::SAVE_FILE_FROM_DOM);
|
| -
|
| - if (!data.empty()) {
|
| - // Prepare buffer for saving HTML data.
|
| - char* new_data = static_cast<char*>(new char[data.size()]);
|
| - memcpy(new_data, data.data(), data.size());
|
| -
|
| - // Call write file functionality in file thread.
|
| - file_manager_->GetSaveLoop()->PostTask(FROM_HERE,
|
| - NewRunnableMethod(file_manager_,
|
| - &SaveFileManager::UpdateSaveProgress,
|
| - save_item->save_id(),
|
| - new_data,
|
| - static_cast<int>(data.size())));
|
| - }
|
| -
|
| - // Current frame is completed saving, call finish in file thread.
|
| - if (flag == webkit_glue::DomSerializerDelegate::CURRENT_FRAME_IS_FINISHED) {
|
| - file_manager_->GetSaveLoop()->PostTask(FROM_HERE,
|
| - NewRunnableMethod(file_manager_,
|
| - &SaveFileManager::SaveFinished,
|
| - save_item->save_id(),
|
| - save_item->url(),
|
| - tab_id,
|
| - true));
|
| - }
|
| -}
|
| -
|
| -// Ask for all savable resource links from backend, include main frame and
|
| -// sub-frame.
|
| -void SavePackage::GetAllSavableResourceLinksForCurrentPage() {
|
| - if (wait_state_ != START_PROCESS)
|
| - return;
|
| -
|
| - wait_state_ = RESOURCES_LIST;
|
| - GURL main_page_url(page_url_);
|
| - web_contents_->GetAllSavableResourceLinksForCurrentPage(main_page_url);
|
| -}
|
| -
|
| -// Give backend the lists which contain all resource links that have local
|
| -// storage, after which, render process will serialize DOM for generating
|
| -// HTML data.
|
| -void SavePackage::ProcessCurrentPageAllSavableResourceLinks(
|
| - const std::vector<GURL>& resources_list,
|
| - const std::vector<GURL>& referrers_list,
|
| - const std::vector<GURL>& frames_list) {
|
| - if (wait_state_ != RESOURCES_LIST)
|
| - return;
|
| -
|
| - DCHECK(resources_list.size() == referrers_list.size());
|
| - all_save_items_count_ = static_cast<int>(resources_list.size()) +
|
| - static_cast<int>(frames_list.size());
|
| -
|
| - // We use total bytes as the total number of files we want to save.
|
| - download_->set_total_bytes(all_save_items_count_);
|
| -
|
| - if (all_save_items_count_) {
|
| - // Put all sub-resources to wait list.
|
| - for (int i = 0; i < static_cast<int>(resources_list.size()); ++i) {
|
| - const GURL& u = resources_list[i];
|
| - DCHECK(u.is_valid());
|
| - SaveFileCreateInfo::SaveFileSource save_source = u.SchemeIsFile() ?
|
| - SaveFileCreateInfo::SAVE_FILE_FROM_FILE :
|
| - SaveFileCreateInfo::SAVE_FILE_FROM_NET;
|
| - SaveItem* save_item = new SaveItem(UTF8ToWide(u.spec()),
|
| - UTF8ToWide(referrers_list[i].spec()), this, save_source);
|
| - waiting_item_queue_.push(save_item);
|
| - }
|
| - // Put all HTML resources to wait list.
|
| - for (int i = 0; i < static_cast<int>(frames_list.size()); ++i) {
|
| - const GURL& u = frames_list[i];
|
| - DCHECK(u.is_valid());
|
| - SaveItem* save_item = new SaveItem(UTF8ToWide(u.spec()), L"",
|
| - this, SaveFileCreateInfo::SAVE_FILE_FROM_DOM);
|
| - waiting_item_queue_.push(save_item);
|
| - }
|
| - wait_state_ = NET_FILES;
|
| - DoSavingProcess();
|
| - } else {
|
| - // No resource files need to be saved, treat it as user cancel.
|
| - Cancel(true);
|
| - }
|
| -}
|
| -
|
| -std::wstring SavePackage::GetSuggestNameForSaveAs(PrefService* prefs,
|
| - const std::wstring& name) {
|
| - // Check whether the preference has the preferred directory for saving file.
|
| - // If not, initialize it with default directory.
|
| - if (!prefs->IsPrefRegistered(prefs::kSaveFileDefaultDirectory)) {
|
| - std::wstring default_save_path;
|
| - PathService::Get(chrome::DIR_USER_DOCUMENTS, &default_save_path);
|
| - file_util::AppendToPath(&default_save_path,
|
| - l10n_util::GetString(IDS_DOWNLOAD_DIRECTORY));
|
| - prefs->RegisterStringPref(prefs::kSaveFileDefaultDirectory,
|
| - default_save_path);
|
| - }
|
| -
|
| - // Get the directory from preference.
|
| - StringPrefMember save_file_path;
|
| - save_file_path.Init(prefs::kSaveFileDefaultDirectory, prefs, NULL);
|
| - DCHECK(!(*save_file_path).empty());
|
| -
|
| - // Ask user for getting final saving name.
|
| - std::wstring suggest_name, file_name;
|
| -
|
| - file_name = name;
|
| - file_util::ReplaceIllegalCharacters(&file_name, L' ');
|
| - suggest_name = *save_file_path;
|
| - file_util::AppendToPath(&suggest_name, file_name);
|
| -
|
| - return suggest_name;
|
| -}
|
| -
|
| -// Static.
|
| -bool SavePackage::GetSaveInfo(const std::wstring& suggest_name,
|
| - HWND container_hwnd,
|
| - SavePackageParam* param) {
|
| - // Use "Web Page, Complete" option as default choice of saving page.
|
| - unsigned index = 2;
|
| -
|
| - // If the conetnts can not be saved as complete-HTML, do not show the
|
| - // file filters.
|
| - if (CanSaveAsComplete(param->current_tab_mime_type)) {
|
| - // Create filter string.
|
| - std::wstring filter = l10n_util::GetString(IDS_SAVE_PAGE_FILTER);
|
| - filter.resize(filter.size() + 2);
|
| - filter[filter.size() - 1] = L'\0';
|
| - filter[filter.size() - 2] = L'\0';
|
| -
|
| - if (!win_util::SaveFileAsWithFilter(container_hwnd,
|
| - suggest_name, filter.c_str(), L"htm",
|
| - &index, ¶m->saved_main_file_path))
|
| - return false;
|
| - } else {
|
| - if (!win_util::SaveFileAs(container_hwnd, suggest_name,
|
| - ¶m->saved_main_file_path))
|
| - return false;
|
| - // Set save-as type to only-HTML if the contents of current tab can not be
|
| - // saved as complete-HTML.
|
| - index = 1;
|
| - }
|
| - // The option index is not zero-based.
|
| - DCHECK(index > 0 && index < 3);
|
| - param->dir = file_util::GetDirectoryFromPath(param->saved_main_file_path);
|
| -
|
| - StringPrefMember save_file_path;
|
| - save_file_path.Init(prefs::kSaveFileDefaultDirectory, param->prefs, NULL);
|
| - // If user change the default saving directory, we will remember it just
|
| - // like IE and FireFox.
|
| - if (save_file_path.GetValue() != param->dir)
|
| - save_file_path.SetValue(param->dir);
|
| -
|
| - param->save_type = (index == 1) ? SavePackage::SAVE_AS_ONLY_HTML :
|
| - SavePackage::SAVE_AS_COMPLETE_HTML;
|
| -
|
| - if (param->save_type == SavePackage::SAVE_AS_COMPLETE_HTML) {
|
| - // Make new directory for saving complete file.
|
| - std::wstring file_name =
|
| - file_util::GetFilenameFromPath(param->saved_main_file_path);
|
| - std::wstring::size_type last_dot = file_name.rfind(L'.');
|
| - std::wstring pure_file_name;
|
| - if (last_dot == std::wstring::npos)
|
| - pure_file_name = file_name;
|
| - else
|
| - pure_file_name = std::wstring(file_name, 0, last_dot);
|
| - pure_file_name += L"_files";
|
| - file_util::AppendToPath(¶m->dir, pure_file_name);
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -// Static.
|
| -bool SavePackage::GetBaseFileNameAndFileOrdinalNumber(
|
| - const std::wstring& file_name,
|
| - std::wstring* base_file_name,
|
| - std::wstring* file_ordinal_number) {
|
| - if (file_name.empty() || !base_file_name || !file_ordinal_number)
|
| - return false;
|
| -
|
| - // Find dot position.
|
| - std::wstring::size_type dot_position = file_name.rfind(L".");
|
| - // Find position of right parenthesis.
|
| - std::wstring::size_type parenthesis_right;
|
| - if (std::wstring::npos == dot_position)
|
| - parenthesis_right = file_name.rfind(L')');
|
| - else
|
| - parenthesis_right = dot_position - 1;
|
| - // The latest character of pure file name is not ")", return false.
|
| - if (std::wstring::npos == parenthesis_right)
|
| - return false;
|
| - if (file_name.at(parenthesis_right) != L')')
|
| - return false;
|
| - // Find position of left parenthesis.
|
| - std::wstring::size_type parenthesis_left = file_name.rfind(L'(');
|
| - if (std::wstring::npos == parenthesis_left)
|
| - return false;
|
| -
|
| - if (parenthesis_right <= parenthesis_left)
|
| - return false;
|
| - // Check whether content between left parenthesis and right parenthesis is
|
| - // numeric or not.
|
| - std::wstring ordinal_number(file_name, parenthesis_left + 1,
|
| - parenthesis_right - parenthesis_left - 1);
|
| - for (std::wstring::const_iterator cit = ordinal_number.begin();
|
| - cit != ordinal_number.end(); ++cit)
|
| - if (!IsAsciiDigit(*cit))
|
| - return false;
|
| -
|
| - *base_file_name = std::wstring(file_name, 0, parenthesis_left);
|
| - *file_ordinal_number = ordinal_number;
|
| - return true;
|
| -}
|
| -
|
| -// Static
|
| -bool SavePackage::IsSavableURL(const GURL& url) {
|
| - return url.SchemeIs("http") || url.SchemeIs("https") ||
|
| - url.SchemeIs("file") || url.SchemeIs("ftp");
|
| -}
|
| -
|
| -// Static
|
| -bool SavePackage::IsSavableContents(const std::string& contents_mime_type) {
|
| - // WebKit creates Document object when MIME type is application/xhtml+xml,
|
| - // so we also support this MIME type.
|
| - return contents_mime_type == "text/html" ||
|
| - contents_mime_type == "text/xml" ||
|
| - contents_mime_type == "application/xhtml+xml" ||
|
| - contents_mime_type == "text/plain";
|
| -}
|
| -
|
| -// Static
|
| -bool SavePackage::CanSaveAsComplete(const std::string& contents_mime_type) {
|
| - return contents_mime_type == "text/html";
|
| -}
|
| -
|
| -// Static
|
| -bool SavePackage::GetSafePureFileName(const std::wstring& dir_path,
|
| - const std::wstring& file_name_ext,
|
| - uint32 max_file_path_len,
|
| - std::wstring* pure_file_name) {
|
| - DCHECK(!pure_file_name->empty());
|
| - std::wstring final_name = dir_path;
|
| - file_util::AppendToPath(&final_name, *pure_file_name);
|
| - // Get total length of dir path, including ending "\".
|
| - const std::wstring::size_type dir_path_length =
|
| - final_name.length() - pure_file_name->length();
|
| - // Get available length for putting dir path and pure file name.
|
| - const std::wstring::size_type available_length =
|
| - static_cast<std::wstring::size_type>(max_file_path_len) -
|
| - file_name_ext.length();
|
| -
|
| - if (final_name.length() <= available_length)
|
| - return true;
|
| -
|
| - if (available_length > dir_path_length) {
|
| - *pure_file_name =
|
| - pure_file_name->substr(0, available_length - dir_path_length);
|
| - return true;
|
| - } else {
|
| - pure_file_name->clear();
|
| - return false;
|
| - }
|
| -}
|
| -
|
|
|