| Index: content/browser/download/save_package.cc
|
| diff --git a/content/browser/download/save_package.cc b/content/browser/download/save_package.cc
|
| index 96871950bac431327eb405ebbbcbef4711ef8d96..2ffa24f1cdad2b53b0df1abdd97da9acb472a2b1 100644
|
| --- a/content/browser/download/save_package.cc
|
| +++ b/content/browser/download/save_package.cc
|
| @@ -25,12 +25,14 @@
|
| #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/frame_host/frame_tree.h"
|
| +#include "content/browser/frame_host/frame_tree_node.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/browser/web_contents/web_contents_impl.h"
|
| #include "content/common/frame_messages.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"
|
| @@ -131,6 +133,11 @@ class SavePackageRequestHandle : public DownloadRequestHandleInterface {
|
| base::WeakPtr<SavePackage> save_package_;
|
| };
|
|
|
| +int GetFrameTreeNodeId(RenderFrameHost* render_frame_host) {
|
| + auto rfhi = static_cast<RenderFrameHostImpl*>(render_frame_host);
|
| + return rfhi->frame_tree_node()->frame_tree_node_id();
|
| +}
|
| +
|
| } // namespace
|
|
|
| const base::FilePath::CharType SavePackage::kDefaultHtmlExtension[] =
|
| @@ -349,10 +356,9 @@ void SavePackage::InitWithDownloadItem(
|
| 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);
|
| + SaveItem* save_item = new SaveItem(page_url_, this, save_source);
|
| + save_item->set_frame_tree_node_id(
|
| + GetFrameTreeNodeId(web_contents()->GetMainFrame()));
|
| // Add this item to waiting list.
|
| waiting_item_queue_.push(save_item);
|
| all_save_items_count_ = 1;
|
| @@ -1011,8 +1017,14 @@ bool SavePackage::OnMessageReceived(const IPC::Message& message,
|
| OnSavableResourceLinksResponse)
|
| IPC_MESSAGE_HANDLER(FrameHostMsg_SavableResourceLinksError,
|
| OnSavableResourceLinksError)
|
| - IPC_MESSAGE_HANDLER(FrameHostMsg_SerializedHtmlWithLocalLinksResponse,
|
| - OnSerializedHtmlWithLocalLinksResponse)
|
| + IPC_MESSAGE_HANDLER(FrameHostMsg_SerializedHtmlFragment,
|
| + OnSerializedHtmlFragment)
|
| + IPC_MESSAGE_HANDLER(FrameHostMsg_SerializedLocalPathForSubFrame,
|
| + OnSerializedLocalPathForSubFrame)
|
| + IPC_MESSAGE_HANDLER(FrameHostMsg_SerializedLocalPathForSavableResource,
|
| + OnSerializedLocalPathForSavableResource)
|
| + IPC_MESSAGE_HANDLER(FrameHostMsg_SerializedEndOfFrame,
|
| + OnSerializedEndOfFrame)
|
| IPC_MESSAGE_UNHANDLED(handled = false)
|
| IPC_END_MESSAGE_MAP()
|
| return handled;
|
| @@ -1025,103 +1037,50 @@ bool SavePackage::OnMessageReceived(const IPC::Message& message,
|
| void SavePackage::GetSerializedHtmlWithLocalLinks() {
|
| if (wait_state_ != HTML_DATA)
|
| return;
|
| - std::vector<GURL> saved_links;
|
| - std::vector<base::FilePath> 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.
|
| + // Count all saved items which have local storage.
|
| 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.
|
| - base::FilePath relative_dir_name = saved_main_directory_path_.BaseName();
|
| -
|
| // Ask all frames for their serialized data.
|
| DCHECK_EQ(0, number_of_frames_pending_response_);
|
| web_contents()->ForEachFrame(base::Bind(
|
| &SavePackage::GetSerializedHtmlWithLocalLinksForFrame,
|
| - base::Unretained(this), // Safe, because ForEachFrame is synchronous.
|
| - saved_links, saved_file_paths, relative_dir_name));
|
| + base::Unretained(this))); // Safe, because ForEachFrame is synchronous.
|
| DCHECK_LT(0, number_of_frames_pending_response_);
|
| }
|
|
|
| void SavePackage::GetSerializedHtmlWithLocalLinksForFrame(
|
| - const std::vector<GURL>& saved_links,
|
| - const std::vector<base::FilePath>& saved_file_paths,
|
| - const base::FilePath& relative_dir_name,
|
| RenderFrameHost* target) {
|
| number_of_frames_pending_response_++;
|
| - target->Send(new FrameMsg_GetSerializedHtmlWithLocalLinks(
|
| - target->GetRoutingID(), saved_links, saved_file_paths,
|
| - relative_dir_name));
|
| + target->Send(
|
| + new FrameMsg_GetSerializedHtmlWithLocalLinks(target->GetRoutingID()));
|
| }
|
|
|
| -// Process the serialized HTML content data of a specified frame
|
| -// retrieved from the renderer process.
|
| -void SavePackage::OnSerializedHtmlWithLocalLinksResponse(
|
| - RenderFrameHost* sender,
|
| - const GURL& frame_url,
|
| - const std::string& data,
|
| - int32 status) {
|
| - WebPageSerializerClient::PageSerializationStatus flag =
|
| - static_cast<WebPageSerializerClient::PageSerializationStatus>(status);
|
| -
|
| - // When calling WebPageSerializer::serialize in non-recursive mode, the
|
| - // AllFramesAreFinished is redundant - it is sent by each frame right after
|
| - // CurrentFrameIsFinished. Therefore we ignore AllFramesAreFinished and
|
| - // instead track pending frames in |number_of_frames_pending_response_|.
|
| - if (flag == WebPageSerializerClient::AllFramesAreFinished)
|
| - return;
|
| -
|
| - // Check current state.
|
| +void SavePackage::OnSerializedHtmlFragment(RenderFrameHost* sender,
|
| + const std::string& data) {
|
| if (wait_state_ != HTML_DATA)
|
| return;
|
|
|
| - int id = contents_id();
|
| -
|
| - 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;
|
| -
|
| + int frame_tree_node_id = GetFrameTreeNodeId(sender);
|
| + SaveItem* save_item =
|
| + FindInProgressSaveItemByFrameTreeNodeID(frame_tree_node_id);
|
| + if (!save_item) {
|
| + LogWriteToAlreadyCompletedOrFailedSaveItem(frame_tree_node_id);
|
| 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.
|
| scoped_refptr<net::IOBuffer> new_data(new net::IOBuffer(data.size()));
|
| @@ -1130,46 +1089,141 @@ void SavePackage::OnSerializedHtmlWithLocalLinksResponse(
|
| // Call write file functionality in file thread.
|
| BrowserThread::PostTask(
|
| BrowserThread::FILE, FROM_HERE,
|
| - base::Bind(&SaveFileManager::UpdateSaveProgress,
|
| - file_manager_,
|
| - save_item->save_id(),
|
| - new_data,
|
| + base::Bind(&SaveFileManager::UpdateSaveProgress, file_manager_,
|
| + save_item->save_id(), new_data,
|
| static_cast<int>(data.size())));
|
| }
|
| +}
|
|
|
| - // Current frame is completed saving, call finish in file thread.
|
| - if (flag == WebPageSerializerClient::CurrentFrameIsFinished) {
|
| - DVLOG(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));
|
| - number_of_frames_pending_response_--;
|
| - DCHECK_LE(0, number_of_frames_pending_response_);
|
| +void SavePackage::OnSerializedLocalPathForSubFrame(
|
| + RenderFrameHost* sender,
|
| + int render_frame_or_proxy_routing_id) {
|
| + if (wait_state_ != HTML_DATA)
|
| + return;
|
| +
|
| + FrameTreeNode* frame_tree_node =
|
| + static_cast<WebContentsImpl*>(web_contents())
|
| + ->GetFrameTree()
|
| + ->FindByRoutingID(sender->GetProcess()->GetID(),
|
| + render_frame_or_proxy_routing_id);
|
| + if (!frame_tree_node)
|
| + return;
|
| +
|
| + SerializeLocalPathForSaveItem(sender,
|
| + FindInProgressSaveItemByFrameTreeNodeID(
|
| + frame_tree_node->frame_tree_node_id()),
|
| + frame_tree_node->current_url());
|
| +}
|
| +
|
| +void SavePackage::OnSerializedLocalPathForSavableResource(
|
| + RenderFrameHost* sender,
|
| + const GURL& savable_resource) {
|
| + if (wait_state_ != HTML_DATA)
|
| + return;
|
| + if (!savable_resource.is_valid())
|
| + return;
|
| +
|
| + // Find SaveItem by URL.
|
| + auto it =
|
| + std::find_if(in_progress_items_.begin(), in_progress_items_.end(),
|
| + [&savable_resource](const SaveUrlItemMap::value_type& v) {
|
| + DCHECK(v.second->has_final_name());
|
| + return v.second->url() == savable_resource;
|
| + });
|
| + SaveItem* save_item = (it == in_progress_items_.end()) ? nullptr : it->second;
|
| +
|
| + SerializeLocalPathForSaveItem(sender, save_item, savable_resource);
|
| +}
|
| +
|
| +void SavePackage::OnSerializedEndOfFrame(RenderFrameHost* sender) {
|
| + if (wait_state_ != HTML_DATA)
|
| + return;
|
| +
|
| + int frame_tree_node_id = GetFrameTreeNodeId(sender);
|
| + SaveItem* save_item =
|
| + FindInProgressSaveItemByFrameTreeNodeID(frame_tree_node_id);
|
| + if (!save_item) {
|
| + LogWriteToAlreadyCompletedOrFailedSaveItem(frame_tree_node_id);
|
| + return;
|
| }
|
|
|
| - // If all frames are finished saving, we need to close the remaining
|
| - // SaveItems.
|
| + int id = contents_id();
|
| +
|
| + // Current frame is completed saving, call finish in file thread.
|
| + DVLOG(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));
|
| + number_of_frames_pending_response_--;
|
| + DCHECK_LE(0, number_of_frames_pending_response_);
|
| +
|
| + // If the all frames are finished saving, we need to close the
|
| + // remaining SaveItems.
|
| if (number_of_frames_pending_response_ == 0) {
|
| - for (SaveUrlItemMap::iterator it = in_progress_items_.begin();
|
| - it != in_progress_items_.end(); ++it) {
|
| + for (const auto pair : in_progress_items_) {
|
| DVLOG(20) << " " << __FUNCTION__ << "()"
|
| - << " save_id = " << it->second->save_id() << " url = \""
|
| - << it->second->url().spec() << "\"";
|
| + << " save_id = " << pair.second->save_id() << " url = \""
|
| + << pair.second->url().spec() << "\"";
|
| BrowserThread::PostTask(
|
| BrowserThread::FILE, FROM_HERE,
|
| base::Bind(&SaveFileManager::SaveFinished, file_manager_,
|
| - it->second->save_id(), it->second->url(), id, true));
|
| + pair.second->save_id(), pair.second->url(), id, true));
|
| }
|
| }
|
| }
|
|
|
| +void SavePackage::SerializeLocalPathForSaveItem(RenderFrameHost* sender,
|
| + SaveItem* save_item,
|
| + const GURL& fallback_url) {
|
| + std::string attr_value;
|
| + if (save_item) {
|
| + base::FilePath relative_path(FILE_PATH_LITERAL("."));
|
| + relative_path = relative_path.Append(saved_main_directory_path_.BaseName());
|
| + relative_path = relative_path.Append(save_item->file_name());
|
| + attr_value = relative_path.AsUTF8Unsafe();
|
| + } else {
|
| + attr_value = fallback_url.spec();
|
| + }
|
| +
|
| + // TODO(lukasza): Escape attr_value (" + &).
|
| + OnSerializedHtmlFragment(sender, attr_value);
|
| +}
|
| +
|
| +SaveItem* SavePackage::FindInProgressSaveItemByFrameTreeNodeID(
|
| + int frame_tree_node_id) {
|
| + auto it = std::find_if(
|
| + in_progress_items_.begin(), in_progress_items_.end(),
|
| + [frame_tree_node_id](const SaveUrlItemMap::value_type& v) {
|
| + DCHECK(v.second->has_final_name());
|
| + return v.second->frame_tree_node_id() == frame_tree_node_id;
|
| + });
|
| +
|
| + return (it == in_progress_items_.end()) ? nullptr : it->second;
|
| +}
|
| +
|
| +void SavePackage::LogWriteToAlreadyCompletedOrFailedSaveItem(
|
| + int frame_tree_node_id) {
|
| + auto already_completed_it = std::find_if(
|
| + saved_success_items_.begin(), saved_success_items_.end(),
|
| + [frame_tree_node_id](const SavedItemMap::value_type& v) {
|
| + return v.second->frame_tree_node_id() == frame_tree_node_id;
|
| + });
|
| + if (already_completed_it != saved_success_items_.end())
|
| + wrote_to_completed_file_ = true;
|
| +
|
| + auto failed_it = std::find_if(
|
| + saved_failed_items_.begin(), saved_failed_items_.end(),
|
| + [frame_tree_node_id](const SaveUrlItemMap::value_type& v) {
|
| + DCHECK(v.second->has_final_name());
|
| + return v.second->frame_tree_node_id() == frame_tree_node_id;
|
| + });
|
| + if (failed_it != saved_failed_items_.end())
|
| + wrote_to_failed_file_ = true;
|
| +}
|
| +
|
| // Ask for all savable resource links from backend, include main frame and
|
| // sub-frame.
|
| void SavePackage::GetSavableResourceLinks() {
|
| @@ -1206,20 +1260,26 @@ void SavePackage::OnSavableResourceLinksResponse(
|
| const GURL& u = resources_list[i];
|
| if (!u.is_valid())
|
| continue;
|
| - if (unique_urls_to_save_.count(u))
|
| - continue;
|
| - unique_urls_to_save_.insert(u);
|
|
|
| - 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);
|
| + SaveItem*& save_item = url_to_save_item_[u];
|
| + if (save_item == nullptr) {
|
| + SaveFileCreateInfo::SaveFileSource save_source =
|
| + u.SchemeIsFile() ? SaveFileCreateInfo::SAVE_FILE_FROM_FILE
|
| + : SaveFileCreateInfo::SAVE_FILE_FROM_NET;
|
| + save_item = new SaveItem(u, this, save_source);
|
| + waiting_item_queue_.push(save_item);
|
| + }
|
| + save_item->set_referrer(referrers_list[i]);
|
| }
|
|
|
| - // Store savable frame_url for later processing.
|
| - if (frame_url.is_valid())
|
| - frame_urls_to_save_.push_back(frame_url);
|
| + // Add the frame to wait list.
|
| + SaveItem*& save_item = url_to_save_item_[frame_url];
|
| + if (save_item == nullptr) {
|
| + save_item =
|
| + new SaveItem(frame_url, this, SaveFileCreateInfo::SAVE_FILE_FROM_DOM);
|
| + waiting_item_queue_.push(save_item);
|
| + }
|
| + save_item->set_frame_tree_node_id(GetFrameTreeNodeId(sender));
|
|
|
| CompleteSavableResourceLinksResponse();
|
| }
|
| @@ -1234,20 +1294,6 @@ void SavePackage::CompleteSavableResourceLinksResponse() {
|
| if (number_of_frames_pending_response_ != 0)
|
| return; // Need to wait for more responses from RenderFrames.
|
|
|
| - // Add frame urls to the waiting_item_queue_. This is done *after* processing
|
| - // all savable resource links (i.e. in OnSavableResourceLinksResponse), to
|
| - // prefer their referrers in cases where the frame url has already been
|
| - // covered by savable resource links.
|
| - for (auto& frame_url : frame_urls_to_save_) {
|
| - DCHECK(frame_url.is_valid());
|
| - if (0 == unique_urls_to_save_.count(frame_url)) {
|
| - unique_urls_to_save_.insert(frame_url);
|
| - SaveItem* save_item = new SaveItem(
|
| - frame_url, Referrer(), this, SaveFileCreateInfo::SAVE_FILE_FROM_DOM);
|
| - waiting_item_queue_.push(save_item);
|
| - }
|
| - }
|
| -
|
| all_save_items_count_ = static_cast<int>(waiting_item_queue_.size());
|
|
|
| // We use total bytes as the total number of files we want to save.
|
|
|