This source file includes following definitions.
- StripOrdinalNumber
- CanSaveAsComplete
- GetWebContents
- GetDownloadManager
- PauseRequest
- ResumeRequest
- CancelRequest
- DebugString
- wrote_to_failed_file_
- wrote_to_failed_file_
- wrote_to_failed_file_
- GetUrlToBeSaved
- Cancel
- InternalInit
- Init
- InitWithDownloadItem
- OnMHTMLGenerated
- GetMaxPathLengthForDirectory
- GetSafePureFileName
- GenerateFileName
- StartSave
- LookupItemInProcessBySaveId
- PutInProgressItemToSavedMap
- UpdateSaveProgress
- Stop
- CheckFinish
- Finish
- SaveFinished
- SaveFailed
- SaveCanceled
- SaveNextFile
- PercentComplete
- CurrentSpeed
- DoSavingProcess
- OnMessageReceived
- GetSerializedHtmlDataForCurrentPageWithLocalLinks
- OnReceivedSerializedHtmlData
- GetAllSavableResourceLinksForCurrentPage
- OnReceivedSavableResourceLinksForCurrentPage
- GetSuggestedNameForSaveAs
- EnsureHtmlExtension
- EnsureMimeExtension
- ExtensionForMimeType
- web_contents
- GetSaveInfo
- CreateDirectoryOnFileThread
- ContinueGetSaveInfo
- OnPathPicked
- StopObservation
- OnDownloadDestroyed
- FinalizeDownloadEntry
#include "content/browser/download/save_package.h"
#include <algorithm>
#include "base/bind.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/i18n/file_util_icu.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/stl_util.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread.h"
#include "content/browser/download/download_item_impl.h"
#include "content/browser/download/download_manager_impl.h"
#include "content/browser/download/download_stats.h"
#include "content/browser/download/save_file.h"
#include "content/browser/download/save_file_manager.h"
#include "content/browser/download/save_item.h"
#include "content/browser/loader/resource_dispatcher_host_impl.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_view_host_delegate.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/download_manager_delegate.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/resource_context.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/url_constants.h"
#include "net/base/io_buffer.h"
#include "net/base/mime_util.h"
#include "net/base/net_util.h"
#include "net/url_request/url_request_context.h"
#include "third_party/WebKit/public/web/WebPageSerializerClient.h"
using base::Time;
using blink::WebPageSerializerClient;
namespace content {
namespace {
int g_save_package_id = 0;
const char kDefaultSaveName[] = "saved_resource";
const int32 kMaxFileOrdinalNumber = 9999;
#if defined(OS_WIN)
const uint32 kMaxFilePathLength = MAX_PATH - 1;
#elif defined(OS_POSIX)
const uint32 kMaxFilePathLength = PATH_MAX - 1;
#endif
const uint32 kMaxFileOrdinalNumberPartLength = 6;
base::FilePath::StringType StripOrdinalNumber(
const base::FilePath::StringType& pure_file_name) {
base::FilePath::StringType::size_type r_paren_index =
pure_file_name.rfind(FILE_PATH_LITERAL(')'));
base::FilePath::StringType::size_type l_paren_index =
pure_file_name.rfind(FILE_PATH_LITERAL('('));
if (l_paren_index >= r_paren_index)
return pure_file_name;
for (base::FilePath::StringType::size_type i = l_paren_index + 1;
i != r_paren_index; ++i) {
if (!IsAsciiDigit(pure_file_name[i]))
return pure_file_name;
}
return pure_file_name.substr(0, l_paren_index);
}
bool CanSaveAsComplete(const std::string& contents_mime_type) {
return contents_mime_type == "text/html" ||
contents_mime_type == "application/xhtml+xml";
}
class SavePackageRequestHandle : public DownloadRequestHandleInterface {
public:
SavePackageRequestHandle(base::WeakPtr<SavePackage> save_package)
: save_package_(save_package) {}
virtual WebContents* GetWebContents() const OVERRIDE {
return save_package_.get() ? save_package_->web_contents() : NULL;
}
virtual DownloadManager* GetDownloadManager() const OVERRIDE {
return NULL;
}
virtual void PauseRequest() const OVERRIDE {}
virtual void ResumeRequest() const OVERRIDE {}
virtual void CancelRequest() const OVERRIDE {}
virtual std::string DebugString() const OVERRIDE {
return "SavePackage DownloadRequestHandle";
}
private:
base::WeakPtr<SavePackage> save_package_;
};
}
const base::FilePath::CharType SavePackage::kDefaultHtmlExtension[] =
#if defined(OS_WIN)
FILE_PATH_LITERAL("htm");
#else
FILE_PATH_LITERAL("html");
#endif
SavePackage::SavePackage(WebContents* web_contents,
SavePageType save_type,
const base::FilePath& file_full_path,
const base::FilePath& directory_full_path)
: WebContentsObserver(web_contents),
file_manager_(NULL),
download_manager_(NULL),
download_(NULL),
page_url_(GetUrlToBeSaved()),
saved_main_file_path_(file_full_path),
saved_main_directory_path_(directory_full_path),
title_(web_contents->GetTitle()),
start_tick_(base::TimeTicks::Now()),
finished_(false),
mhtml_finishing_(false),
user_canceled_(false),
disk_error_occurred_(false),
save_type_(save_type),
all_save_items_count_(0),
file_name_set_(&base::FilePath::CompareLessIgnoreCase),
wait_state_(INITIALIZE),
contents_id_(web_contents->GetRenderProcessHost()->GetID()),
unique_id_(g_save_package_id++),
wrote_to_completed_file_(false),
wrote_to_failed_file_(false) {
DCHECK(page_url_.is_valid());
DCHECK((save_type_ == SAVE_PAGE_TYPE_AS_ONLY_HTML) ||
(save_type_ == SAVE_PAGE_TYPE_AS_MHTML) ||
(save_type_ == SAVE_PAGE_TYPE_AS_COMPLETE_HTML));
DCHECK(!saved_main_file_path_.empty() &&
saved_main_file_path_.value().length() <= kMaxFilePathLength);
DCHECK(!saved_main_directory_path_.empty() &&
saved_main_directory_path_.value().length() < kMaxFilePathLength);
InternalInit();
}
SavePackage::SavePackage(WebContents* web_contents)
: WebContentsObserver(web_contents),
file_manager_(NULL),
download_manager_(NULL),
download_(NULL),
page_url_(GetUrlToBeSaved()),
title_(web_contents->GetTitle()),
start_tick_(base::TimeTicks::Now()),
finished_(false),
mhtml_finishing_(false),
user_canceled_(false),
disk_error_occurred_(false),
save_type_(SAVE_PAGE_TYPE_UNKNOWN),
all_save_items_count_(0),
file_name_set_(&base::FilePath::CompareLessIgnoreCase),
wait_state_(INITIALIZE),
contents_id_(web_contents->GetRenderProcessHost()->GetID()),
unique_id_(g_save_package_id++),
wrote_to_completed_file_(false),
wrote_to_failed_file_(false) {
DCHECK(page_url_.is_valid());
InternalInit();
}
SavePackage::SavePackage(WebContents* web_contents,
const base::FilePath& file_full_path,
const base::FilePath& directory_full_path)
: WebContentsObserver(web_contents),
file_manager_(NULL),
download_manager_(NULL),
download_(NULL),
saved_main_file_path_(file_full_path),
saved_main_directory_path_(directory_full_path),
start_tick_(base::TimeTicks::Now()),
finished_(true),
mhtml_finishing_(false),
user_canceled_(false),
disk_error_occurred_(false),
save_type_(SAVE_PAGE_TYPE_UNKNOWN),
all_save_items_count_(0),
file_name_set_(&base::FilePath::CompareLessIgnoreCase),
wait_state_(INITIALIZE),
contents_id_(0),
unique_id_(g_save_package_id++),
wrote_to_completed_file_(false),
wrote_to_failed_file_(false) {
}
SavePackage::~SavePackage() {
if (!finished_ && !canceled()) {
Cancel(true);
}
CHECK(!download_);
DCHECK(all_save_items_count_ == (waiting_item_queue_.size() +
completed_count() +
in_process_count()));
while (!waiting_item_queue_.empty()) {
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_);
file_manager_ = NULL;
}
GURL SavePackage::GetUrlToBeSaved() {
NavigationEntry* active_entry =
web_contents()->GetController().GetActiveEntry();
return active_entry->GetURL();
}
void SavePackage::Cancel(bool user_action) {
if (!canceled()) {
if (user_action)
user_canceled_ = true;
else
disk_error_occurred_ = true;
Stop();
}
RecordSavePackageEvent(SAVE_PACKAGE_CANCELLED);
}
void SavePackage::InternalInit() {
ResourceDispatcherHostImpl* rdh = ResourceDispatcherHostImpl::Get();
if (!rdh) {
NOTREACHED();
return;
}
file_manager_ = rdh->save_file_manager();
DCHECK(file_manager_);
download_manager_ = static_cast<DownloadManagerImpl*>(
BrowserContext::GetDownloadManager(
web_contents()->GetBrowserContext()));
DCHECK(download_manager_);
RecordSavePackageEvent(SAVE_PACKAGE_STARTED);
}
bool SavePackage::Init(
const SavePackageDownloadCreatedCallback& download_created_callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (wait_state_ != INITIALIZE)
return false;
wait_state_ = START_PROCESS;
BrowserContext* browser_context = web_contents()->GetBrowserContext();
if (!browser_context) {
NOTREACHED();
return false;
}
scoped_ptr<DownloadRequestHandleInterface> request_handle(
new SavePackageRequestHandle(AsWeakPtr()));
download_manager_->CreateSavePackageDownloadItem(
saved_main_file_path_,
page_url_,
((save_type_ == SAVE_PAGE_TYPE_AS_MHTML) ?
"multipart/related" : "text/html"),
request_handle.Pass(),
base::Bind(&SavePackage::InitWithDownloadItem, AsWeakPtr(),
download_created_callback));
return true;
}
void SavePackage::InitWithDownloadItem(
const SavePackageDownloadCreatedCallback& download_created_callback,
DownloadItemImpl* item) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(item);
download_ = item;
download_->AddObserver(this);
if (!download_created_callback.is_null())
download_created_callback.Run(download_);
if (save_type_ == SAVE_PAGE_TYPE_AS_COMPLETE_HTML) {
DCHECK(!saved_main_directory_path_.empty());
GetAllSavableResourceLinksForCurrentPage();
} else if (save_type_ == SAVE_PAGE_TYPE_AS_MHTML) {
web_contents()->GenerateMHTML(saved_main_file_path_, base::Bind(
&SavePackage::OnMHTMLGenerated, this));
} else {
DCHECK_EQ(SAVE_PAGE_TYPE_AS_ONLY_HTML, save_type_) << save_type_;
wait_state_ = NET_FILES;
SaveFileCreateInfo::SaveFileSource save_source = page_url_.SchemeIsFile() ?
SaveFileCreateInfo::SAVE_FILE_FROM_FILE :
SaveFileCreateInfo::SAVE_FILE_FROM_NET;
SaveItem* save_item = new SaveItem(page_url_,
Referrer(),
this,
save_source);
waiting_item_queue_.push(save_item);
all_save_items_count_ = 1;
download_->SetTotalBytes(1);
DoSavingProcess();
}
}
void SavePackage::OnMHTMLGenerated(int64 size) {
if (size <= 0) {
Cancel(false);
return;
}
wrote_to_completed_file_ = true;
if (download_->GetState() == DownloadItem::IN_PROGRESS) {
download_->SetTotalBytes(size);
download_->DestinationUpdate(size, 0, std::string());
download_->OnAllDataSaved(DownloadItem::kEmptyFileHash);
}
if (!download_manager_->GetDelegate()) {
Finish();
return;
}
if (download_manager_->GetDelegate()->ShouldCompleteDownload(
download_, base::Bind(&SavePackage::Finish, this))) {
Finish();
}
}
uint32 SavePackage::GetMaxPathLengthForDirectory(
const base::FilePath& base_dir) {
#if defined(OS_POSIX)
return std::min(kMaxFilePathLength,
static_cast<uint32>(base_dir.value().length()) +
NAME_MAX + 1);
#else
return kMaxFilePathLength;
#endif
}
bool SavePackage::GetSafePureFileName(
const base::FilePath& dir_path,
const base::FilePath::StringType& file_name_ext,
uint32 max_file_path_len,
base::FilePath::StringType* pure_file_name) {
DCHECK(!pure_file_name->empty());
int available_length = static_cast<int>(max_file_path_len -
dir_path.value().length() -
file_name_ext.length());
if (!dir_path.EndsWithSeparator())
--available_length;
if (static_cast<int>(pure_file_name->length()) <= available_length)
return true;
if (available_length > 0) {
*pure_file_name = pure_file_name->substr(0, available_length);
return true;
}
pure_file_name->clear();
return false;
}
bool SavePackage::GenerateFileName(const std::string& disposition,
const GURL& url,
bool need_html_ext,
base::FilePath::StringType* generated_name) {
base::FilePath file_path = net::GenerateFileName(url,
disposition,
std::string(),
std::string(),
std::string(),
kDefaultSaveName);
DCHECK(!file_path.empty());
base::FilePath::StringType pure_file_name =
file_path.RemoveExtension().BaseName().value();
base::FilePath::StringType file_name_ext = file_path.Extension();
if (need_html_ext) {
file_name_ext = FILE_PATH_LITERAL(".");
file_name_ext.append(kDefaultHtmlExtension);
}
uint32 max_path = GetMaxPathLengthForDirectory(saved_main_directory_path_);
if (!GetSafePureFileName(saved_main_directory_path_, file_name_ext,
max_path, &pure_file_name))
return false;
base::FilePath::StringType file_name = pure_file_name + file_name_ext;
FileNameSet::const_iterator iter = file_name_set_.find(file_name);
if (iter == file_name_set_.end()) {
file_name_set_.insert(file_name);
} else {
pure_file_name =
base::FilePath(*iter).RemoveExtension().BaseName().value();
base::FilePath::StringType base_file_name =
StripOrdinalNumber(pure_file_name);
if (!GetSafePureFileName(saved_main_directory_path_, file_name_ext,
max_path - kMaxFileOrdinalNumberPartLength, &base_file_name))
return false;
uint32 ordinal_number;
FileNameCountMap::iterator it = file_name_count_map_.find(base_file_name);
if (it == file_name_count_map_.end()) {
file_name_count_map_[base_file_name] = 1;
ordinal_number = 1;
} else {
ordinal_number = it->second;
}
if (ordinal_number > (kMaxFileOrdinalNumber - 1)) {
base::FilePath temp_file;
base::CreateTemporaryFile(&temp_file);
file_name = temp_file.RemoveExtension().BaseName().value();
if (!GetSafePureFileName(saved_main_directory_path_,
base::FilePath::StringType(),
max_path, &file_name))
return false;
} else {
for (int i = ordinal_number; i < kMaxFileOrdinalNumber; ++i) {
base::FilePath::StringType new_name = base_file_name +
base::StringPrintf(FILE_PATH_LITERAL("(%d)"), i) + file_name_ext;
if (file_name_set_.find(new_name) == file_name_set_.end()) {
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;
}
void SavePackage::StartSave(const SaveFileCreateInfo* info) {
DCHECK(info && !info->url.is_empty());
SaveUrlItemMap::iterator it = in_progress_items_.find(info->url.spec());
if (it == in_progress_items_.end()) {
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);
DCHECK(!save_item->has_final_name());
if (info->url != page_url_) {
base::FilePath::StringType generated_name;
bool need_html_ext =
info->save_source == SaveFileCreateInfo::SAVE_FILE_FROM_DOM;
if (!GenerateFileName(info->content_disposition,
GURL(info->url),
need_html_ext,
&generated_name)) {
if (info->save_source == SaveFileCreateInfo::SAVE_FILE_FROM_DOM)
Cancel(true);
else
SaveFinished(save_item->save_id(), 0, false);
return;
}
DCHECK(save_type_ == SAVE_PAGE_TYPE_AS_COMPLETE_HTML);
DCHECK(!saved_main_directory_path_.empty());
base::FilePath final_name =
saved_main_directory_path_.Append(generated_name);
save_item->Rename(final_name);
} else {
save_item->Rename(saved_main_file_path_);
}
if (info->save_source == SaveFileCreateInfo::SAVE_FILE_FROM_FILE) {
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
base::Bind(&SaveFileManager::SaveLocalFile,
file_manager_,
save_item->url(),
save_item->save_id(),
contents_id()));
return;
}
if (save_type_ == SAVE_PAGE_TYPE_AS_COMPLETE_HTML &&
wait_state_ == HTML_DATA) {
GetSerializedHtmlDataForCurrentPageWithLocalLinks();
}
}
SaveItem* SavePackage::LookupItemInProcessBySaveId(int32 save_id) {
if (in_process_count()) {
for (SaveUrlItemMap::iterator it = in_progress_items_.begin();
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;
}
void SavePackage::PutInProgressItemToSavedMap(SaveItem* save_item) {
SaveUrlItemMap::iterator it = in_progress_items_.find(
save_item->url().spec());
DCHECK(it != in_progress_items_.end());
DCHECK(save_item == it->second);
in_progress_items_.erase(it);
if (save_item->success()) {
DCHECK(saved_success_items_.find(save_item->save_id()) ==
saved_success_items_.end());
saved_success_items_[save_item->save_id()] = save_item;
} else {
DCHECK(saved_failed_items_.find(save_item->url().spec()) ==
saved_failed_items_.end());
saved_failed_items_[save_item->url().spec()] = save_item;
}
}
bool SavePackage::UpdateSaveProgress(int32 save_id,
int64 size,
bool write_success) {
SaveItem* save_item = LookupItemInProcessBySaveId(save_id);
if (!save_item)
return false;
save_item->Update(size);
if (!write_success) {
Cancel(false);
}
return true;
}
void SavePackage::Stop() {
if (wait_state_ == INITIALIZE)
return;
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();
}
while (in_process_count())
PutInProgressItemToSavedMap(in_progress_items_.begin()->second);
}
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());
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
base::Bind(&SaveFileManager::RemoveSavedFileFromFileMap,
file_manager_,
save_ids));
finished_ = true;
wait_state_ = FAILED;
if (download_) {
download_->Cancel(false);
FinalizeDownloadEntry();
}
}
void SavePackage::CheckFinish() {
if (in_process_count() || finished_)
return;
base::FilePath dir = (save_type_ == SAVE_PAGE_TYPE_AS_COMPLETE_HTML &&
saved_success_items_.size() > 1) ?
saved_main_directory_path_ : base::FilePath();
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()));
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
base::Bind(&SaveFileManager::RenameAllFiles,
file_manager_,
final_names,
dir,
web_contents()->GetRenderProcessHost()->GetID(),
web_contents()->GetRenderViewHost()->GetRoutingID(),
id()));
}
void SavePackage::Finish() {
if (canceled())
return;
wait_state_ = SUCCESSFUL;
finished_ = true;
RecordSavePackageEvent(SAVE_PACKAGE_FINISHED);
if (wrote_to_completed_file_) {
RecordSavePackageEvent(SAVE_PACKAGE_WRITE_TO_COMPLETED);
}
if (wrote_to_failed_file_) {
RecordSavePackageEvent(SAVE_PACKAGE_WRITE_TO_FAILED);
}
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());
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
base::Bind(&SaveFileManager::RemoveSavedFileFromFileMap,
file_manager_,
save_ids));
if (download_) {
if (download_->GetState() == DownloadItem::IN_PROGRESS) {
if (save_type_ != SAVE_PAGE_TYPE_AS_MHTML) {
download_->DestinationUpdate(
all_save_items_count_, CurrentSpeed(), std::string());
download_->OnAllDataSaved(DownloadItem::kEmptyFileHash);
}
download_->MarkAsComplete();
}
FinalizeDownloadEntry();
}
}
void SavePackage::SaveFinished(int32 save_id, int64 size, bool is_success) {
SaveItem* save_item = LookupItemInProcessBySaveId(save_id);
if (!save_item)
return;
save_item->Finish(size, is_success);
file_manager_->RemoveSaveFile(save_id, save_item->url(), this);
PutInProgressItemToSavedMap(save_item);
if (download_ && (download_->GetState() == DownloadItem::IN_PROGRESS)) {
download_->DestinationUpdate(
completed_count(), CurrentSpeed(), std::string());
}
if (save_item->save_source() == SaveFileCreateInfo::SAVE_FILE_FROM_DOM &&
save_item->url() == page_url_ && !save_item->received_bytes()) {
Cancel(false);
return;
}
if (canceled()) {
DCHECK(finished_);
return;
}
DoSavingProcess();
CheckFinish();
}
void SavePackage::SaveFailed(const GURL& save_url) {
SaveUrlItemMap::iterator it = in_progress_items_.find(save_url.spec());
if (it == in_progress_items_.end()) {
NOTREACHED();
return;
}
SaveItem* save_item = it->second;
save_item->Finish(0, false);
PutInProgressItemToSavedMap(save_item);
if (download_ && (download_->GetState() == DownloadItem::IN_PROGRESS)) {
download_->DestinationUpdate(
completed_count(), CurrentSpeed(), std::string());
}
if ((save_type_ == SAVE_PAGE_TYPE_AS_ONLY_HTML) ||
(save_type_ == SAVE_PAGE_TYPE_AS_MHTML) ||
(save_item->save_source() == SaveFileCreateInfo::SAVE_FILE_FROM_DOM)) {
Cancel(true);
}
if (canceled()) {
DCHECK(finished_);
return;
}
DoSavingProcess();
CheckFinish();
}
void SavePackage::SaveCanceled(SaveItem* save_item) {
file_manager_->RemoveSaveFile(save_item->save_id(),
save_item->url(),
this);
if (save_item->save_id() != -1)
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
base::Bind(&SaveFileManager::CancelSave,
file_manager_,
save_item->save_id()));
}
void SavePackage::SaveNextFile(bool process_all_remaining_items) {
DCHECK(web_contents());
DCHECK(waiting_item_queue_.size());
do {
SaveItem* save_item = waiting_item_queue_.front();
waiting_item_queue_.pop();
SaveUrlItemMap::iterator it = in_progress_items_.find(
save_item->url().spec());
DCHECK(it == in_progress_items_.end());
in_progress_items_[save_item->url().spec()] = save_item;
save_item->Start();
file_manager_->SaveURL(save_item->url(),
save_item->referrer(),
web_contents()->GetRenderProcessHost()->GetID(),
routing_id(),
save_item->save_source(),
save_item->full_path(),
web_contents()->
GetBrowserContext()->GetResourceContext(),
this);
} while (process_all_remaining_items && waiting_item_queue_.size());
}
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_;
}
int64 SavePackage::CurrentSpeed() const {
base::TimeDelta diff = base::TimeTicks::Now() - start_tick_;
int64 diff_ms = diff.InMilliseconds();
return diff_ms == 0 ? 0 : completed_count() * 1000 / diff_ms;
}
void SavePackage::DoSavingProcess() {
if (save_type_ == SAVE_PAGE_TYPE_AS_COMPLETE_HTML) {
if (waiting_item_queue_.size()) {
DCHECK(wait_state_ == NET_FILES);
SaveItem* save_item = waiting_item_queue_.front();
if (save_item->save_source() != SaveFileCreateInfo::SAVE_FILE_FROM_DOM) {
SaveNextFile(false);
} else if (!in_process_count()) {
wait_state_ = HTML_DATA;
SaveNextFile(true);
}
} else if (in_process_count()) {
DCHECK(wait_state_ == HTML_DATA);
}
} else {
DCHECK(wait_state_ == NET_FILES);
DCHECK((save_type_ == SAVE_PAGE_TYPE_AS_ONLY_HTML) ||
(save_type_ == SAVE_PAGE_TYPE_AS_MHTML));
if (waiting_item_queue_.size()) {
DCHECK(all_save_items_count_ == waiting_item_queue_.size());
SaveNextFile(false);
}
}
}
bool SavePackage::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(SavePackage, message)
IPC_MESSAGE_HANDLER(ViewHostMsg_SendCurrentPageAllSavableResourceLinks,
OnReceivedSavableResourceLinksForCurrentPage)
IPC_MESSAGE_HANDLER(ViewHostMsg_SendSerializedHtmlData,
OnReceivedSerializedHtmlData)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void SavePackage::GetSerializedHtmlDataForCurrentPageWithLocalLinks() {
if (wait_state_ != HTML_DATA)
return;
std::vector<GURL> saved_links;
std::vector<base::FilePath> saved_file_paths;
int successful_started_items_count = 0;
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 (successful_started_items_count != in_process_count())
return;
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());
}
base::FilePath relative_dir_name = saved_main_directory_path_.BaseName();
Send(new ViewMsg_GetSerializedHtmlDataForCurrentPageWithLocalLinks(
routing_id(), saved_links, saved_file_paths, relative_dir_name));
}
void SavePackage::OnReceivedSerializedHtmlData(const GURL& frame_url,
const std::string& data,
int32 status) {
WebPageSerializerClient::PageSerializationStatus flag =
static_cast<WebPageSerializerClient::PageSerializationStatus>(status);
if (wait_state_ != HTML_DATA)
return;
int id = contents_id();
if (flag == WebPageSerializerClient::AllFramesAreFinished) {
for (SaveUrlItemMap::iterator it = in_progress_items_.begin();
it != in_progress_items_.end(); ++it) {
VLOG(20) << " " << __FUNCTION__ << "()"
<< " save_id = " << it->second->save_id()
<< " url = \"" << it->second->url().spec() << "\"";
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
base::Bind(&SaveFileManager::SaveFinished,
file_manager_,
it->second->save_id(),
it->second->url(),
id,
true));
}
return;
}
SaveUrlItemMap::iterator it = in_progress_items_.find(frame_url.spec());
if (it == in_progress_items_.end()) {
for (SavedItemMap::iterator saved_it = saved_success_items_.begin();
saved_it != saved_success_items_.end(); ++saved_it) {
if (saved_it->second->url() == frame_url) {
wrote_to_completed_file_ = true;
break;
}
}
it = saved_failed_items_.find(frame_url.spec());
if (it != saved_failed_items_.end())
wrote_to_failed_file_ = true;
return;
}
SaveItem* save_item = it->second;
DCHECK(save_item->save_source() == SaveFileCreateInfo::SAVE_FILE_FROM_DOM);
if (!data.empty()) {
scoped_refptr<net::IOBuffer> new_data(new net::IOBuffer(data.size()));
memcpy(new_data->data(), data.data(), data.size());
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
base::Bind(&SaveFileManager::UpdateSaveProgress,
file_manager_,
save_item->save_id(),
new_data,
static_cast<int>(data.size())));
}
if (flag == WebPageSerializerClient::CurrentFrameIsFinished) {
VLOG(20) << " " << __FUNCTION__ << "()"
<< " save_id = " << save_item->save_id()
<< " url = \"" << save_item->url().spec() << "\"";
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
base::Bind(&SaveFileManager::SaveFinished,
file_manager_,
save_item->save_id(),
save_item->url(),
id,
true));
}
}
void SavePackage::GetAllSavableResourceLinksForCurrentPage() {
if (wait_state_ != START_PROCESS)
return;
wait_state_ = RESOURCES_LIST;
Send(new ViewMsg_GetAllSavableResourceLinksForCurrentPage(routing_id(),
page_url_));
}
void SavePackage::OnReceivedSavableResourceLinksForCurrentPage(
const std::vector<GURL>& resources_list,
const std::vector<Referrer>& referrers_list,
const std::vector<GURL>& frames_list) {
if (wait_state_ != RESOURCES_LIST)
return;
if (resources_list.size() != referrers_list.size())
return;
all_save_items_count_ = static_cast<int>(resources_list.size()) +
static_cast<int>(frames_list.size());
if (download_ && (download_->GetState() == DownloadItem::IN_PROGRESS))
download_->SetTotalBytes(all_save_items_count_);
if (all_save_items_count_) {
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(u, referrers_list[i],
this, save_source);
waiting_item_queue_.push(save_item);
}
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(
u, Referrer(), this, SaveFileCreateInfo::SAVE_FILE_FROM_DOM);
waiting_item_queue_.push(save_item);
}
wait_state_ = NET_FILES;
DoSavingProcess();
} else {
Cancel(true);
}
}
base::FilePath SavePackage::GetSuggestedNameForSaveAs(
bool can_save_as_complete,
const std::string& contents_mime_type,
const std::string& accept_langs) {
base::FilePath name_with_proper_ext = base::FilePath::FromUTF16Unsafe(title_);
if (title_ == net::FormatUrl(page_url_, accept_langs)) {
std::string url_path;
if (!page_url_.SchemeIs(kDataScheme)) {
std::vector<std::string> url_parts;
base::SplitString(page_url_.path(), '/', &url_parts);
if (!url_parts.empty()) {
for (int i = static_cast<int>(url_parts.size()) - 1; i >= 0; --i) {
url_path = url_parts[i];
if (!url_path.empty())
break;
}
}
if (url_path.empty())
url_path = page_url_.host();
} else {
url_path = "dataurl";
}
name_with_proper_ext = base::FilePath::FromUTF8Unsafe(url_path);
}
name_with_proper_ext = EnsureMimeExtension(name_with_proper_ext,
contents_mime_type);
if (can_save_as_complete)
name_with_proper_ext = EnsureHtmlExtension(name_with_proper_ext);
base::FilePath::StringType file_name = name_with_proper_ext.value();
file_util::ReplaceIllegalCharactersInPath(&file_name, ' ');
return base::FilePath(file_name);
}
base::FilePath SavePackage::EnsureHtmlExtension(const base::FilePath& name) {
base::FilePath::StringType ext = name.Extension();
if (!ext.empty())
ext.erase(ext.begin());
std::string mime_type;
if (!net::GetMimeTypeFromExtension(ext, &mime_type) ||
!CanSaveAsComplete(mime_type)) {
return base::FilePath(name.value() + FILE_PATH_LITERAL(".") +
kDefaultHtmlExtension);
}
return name;
}
base::FilePath SavePackage::EnsureMimeExtension(const base::FilePath& name,
const std::string& contents_mime_type) {
base::FilePath::StringType ext = name.Extension().length() ?
name.Extension().substr(1) : name.Extension();
base::FilePath::StringType suggested_extension =
ExtensionForMimeType(contents_mime_type);
std::string mime_type;
if (!suggested_extension.empty() &&
!net::GetMimeTypeFromExtension(ext, &mime_type)) {
return base::FilePath(name.value() + FILE_PATH_LITERAL(".") +
suggested_extension);
}
return name;
}
const base::FilePath::CharType* SavePackage::ExtensionForMimeType(
const std::string& contents_mime_type) {
static const struct {
const base::FilePath::CharType *mime_type;
const base::FilePath::CharType *suggested_extension;
} extensions[] = {
{ FILE_PATH_LITERAL("text/html"), kDefaultHtmlExtension },
{ FILE_PATH_LITERAL("text/xml"), FILE_PATH_LITERAL("xml") },
{ FILE_PATH_LITERAL("application/xhtml+xml"), FILE_PATH_LITERAL("xhtml") },
{ FILE_PATH_LITERAL("text/plain"), FILE_PATH_LITERAL("txt") },
{ FILE_PATH_LITERAL("text/css"), FILE_PATH_LITERAL("css") },
};
#if defined(OS_POSIX)
base::FilePath::StringType mime_type(contents_mime_type);
#elif defined(OS_WIN)
base::FilePath::StringType mime_type(base::UTF8ToWide(contents_mime_type));
#endif
for (uint32 i = 0; i < ARRAYSIZE_UNSAFE(extensions); ++i) {
if (mime_type == extensions[i].mime_type)
return extensions[i].suggested_extension;
}
return FILE_PATH_LITERAL("");
}
WebContents* SavePackage::web_contents() const {
return WebContentsObserver::web_contents();
}
void SavePackage::GetSaveInfo() {
base::FilePath website_save_dir, download_save_dir;
bool skip_dir_check = false;
DCHECK(download_manager_);
if (download_manager_->GetDelegate()) {
download_manager_->GetDelegate()->GetSaveDir(
web_contents()->GetBrowserContext(), &website_save_dir,
&download_save_dir, &skip_dir_check);
}
std::string mime_type = web_contents()->GetContentsMimeType();
std::string accept_languages =
GetContentClient()->browser()->GetAcceptLangs(
web_contents()->GetBrowserContext());
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
base::Bind(&SavePackage::CreateDirectoryOnFileThread, this,
website_save_dir, download_save_dir, skip_dir_check,
mime_type, accept_languages));
}
void SavePackage::CreateDirectoryOnFileThread(
const base::FilePath& website_save_dir,
const base::FilePath& download_save_dir,
bool skip_dir_check,
const std::string& mime_type,
const std::string& accept_langs) {
base::FilePath save_dir;
if (!skip_dir_check && !base::DirectoryExists(website_save_dir)) {
if (!base::DirectoryExists(download_save_dir)) {
bool res = base::CreateDirectory(download_save_dir);
DCHECK(res);
}
save_dir = download_save_dir;
} else {
save_dir = website_save_dir;
}
bool can_save_as_complete = CanSaveAsComplete(mime_type);
base::FilePath suggested_filename = GetSuggestedNameForSaveAs(
can_save_as_complete, mime_type, accept_langs);
base::FilePath::StringType pure_file_name =
suggested_filename.RemoveExtension().BaseName().value();
base::FilePath::StringType file_name_ext = suggested_filename.Extension();
uint32 max_path = GetMaxPathLengthForDirectory(save_dir);
if (GetSafePureFileName(save_dir, file_name_ext, max_path, &pure_file_name)) {
save_dir = save_dir.Append(pure_file_name + file_name_ext);
} else {
save_dir = save_dir.Append(suggested_filename);
}
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&SavePackage::ContinueGetSaveInfo, this, save_dir,
can_save_as_complete));
}
void SavePackage::ContinueGetSaveInfo(const base::FilePath& suggested_path,
bool can_save_as_complete) {
if (!web_contents() || !download_manager_->GetDelegate())
return;
base::FilePath::StringType default_extension;
if (can_save_as_complete)
default_extension = kDefaultHtmlExtension;
download_manager_->GetDelegate()->ChooseSavePath(
web_contents(),
suggested_path,
default_extension,
can_save_as_complete,
base::Bind(&SavePackage::OnPathPicked, AsWeakPtr()));
}
void SavePackage::OnPathPicked(
const base::FilePath& final_name,
SavePageType type,
const SavePackageDownloadCreatedCallback& download_created_callback) {
DCHECK((type == SAVE_PAGE_TYPE_AS_ONLY_HTML) ||
(type == SAVE_PAGE_TYPE_AS_MHTML) ||
(type == SAVE_PAGE_TYPE_AS_COMPLETE_HTML)) << type;
saved_main_file_path_ = final_name;
net::GenerateSafeFileName(web_contents()->GetContentsMimeType(), false,
&saved_main_file_path_);
saved_main_directory_path_ = saved_main_file_path_.DirName();
save_type_ = type;
if (save_type_ == SAVE_PAGE_TYPE_AS_COMPLETE_HTML) {
saved_main_directory_path_ = saved_main_directory_path_.Append(
saved_main_file_path_.RemoveExtension().BaseName().value() +
FILE_PATH_LITERAL("_files"));
}
Init(download_created_callback);
}
void SavePackage::StopObservation() {
DCHECK(download_);
DCHECK(download_manager_);
download_->RemoveObserver(this);
download_ = NULL;
download_manager_ = NULL;
}
void SavePackage::OnDownloadDestroyed(DownloadItem* download) {
StopObservation();
}
void SavePackage::FinalizeDownloadEntry() {
DCHECK(download_);
DCHECK(download_manager_);
download_manager_->OnSavePackageSuccessfullyFinished(download_);
StopObservation();
}
}