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

Unified Diff: content/browser/download/mhtml_generation_manager.cc

Issue 1386873003: OOPIFs: Transitioning MHTML generation from view-oriented to frame-oriented. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@mhtml-generation-mgr-cleanup
Patch Set: Rebasing... Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/browser/download/mhtml_generation_manager.cc
diff --git a/content/browser/download/mhtml_generation_manager.cc b/content/browser/download/mhtml_generation_manager.cc
index bd432eefd4df03d74039f513002993c852d3f87b..e011f42dae39635230366e9daa7947ffb863d6d8 100644
--- a/content/browser/download/mhtml_generation_manager.cc
+++ b/content/browser/download/mhtml_generation_manager.cc
@@ -4,17 +4,22 @@
#include "content/browser/download/mhtml_generation_manager.h"
+#include <map>
+#include <queue>
+#include <sstream>
+
#include "base/bind.h"
#include "base/files/file.h"
+#include "base/guid.h"
#include "base/scoped_observer.h"
#include "base/stl_util.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/frame_host/frame_tree_node.h"
+#include "content/common/frame_messages.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_process_host_observer.h"
#include "content/public/browser/web_contents.h"
-#include "content/common/view_messages.h"
namespace content {
@@ -29,9 +34,18 @@ class MHTMLGenerationManager::Job : public RenderProcessHostObserver {
GenerateMHTMLCallback callback() const { return callback_; }
- // Sends IPC to the renderer, asking for MHTML generation.
+ // Sends IPC to the renderer, asking for MHTML generation of the next frame.
+ //
// Returns true if the message was sent successfully; false otherwise.
- bool SendToRenderView();
+ //
+ // See FrameMsg_SerializeAsMHTML IPC message for description of
+ // |mhtml_boundary_marker| parameter.
+ bool SendToNextRenderFrame(const std::string& mhtml_boundary_marker);
+
+ // Indicates if more calls to SendToNextRenderFrame are needed.
+ bool GotMoreFramesToProcess() {
+ return !pending_frame_tree_node_ids_.empty();
+ }
// Close the file on the file thread and respond back on the UI thread with
// file size.
@@ -45,6 +59,12 @@ class MHTMLGenerationManager::Job : public RenderProcessHostObserver {
private:
static int64 CloseFileOnFileThread(base::File file);
+ void AddFrame(RenderFrameHost* render_frame_host);
+
+ // Translates |frame_tree_node_to_content_id_| into
+ // a |site_instance|-specific, routing-id-based map.
+ std::map<int, std::string> CreateFrameRoutingIdToContentId(
+ SiteInstance* site_instance);
// Id used to map renderer responses to jobs.
// See also MHTMLGenerationManager::id_to_job_ map.
@@ -53,9 +73,12 @@ class MHTMLGenerationManager::Job : public RenderProcessHostObserver {
// The handle to the file the MHTML is saved to for the browser process.
base::File browser_file_;
- // The IDs mapping to a specific contents.
- int process_id_;
- int routing_id_;
+ // The IDs of frames we still need to process.
+ std::queue<int> pending_frame_tree_node_ids_;
+
+ // Map from frames into content ids (see WebPageSerializer::generateMHTMLParts
+ // for more details about what "content ids" are and how they are used).
+ std::map<int, std::string> frame_tree_node_to_content_id_;
// The callback to call once generation is complete.
GenerateMHTMLCallback callback_;
@@ -71,33 +94,69 @@ MHTMLGenerationManager::Job::Job(int job_id,
WebContents* web_contents,
GenerateMHTMLCallback callback)
: job_id_(job_id),
- process_id_(web_contents->GetRenderProcessHost()->GetID()),
- routing_id_(web_contents->GetRenderViewHost()->GetRoutingID()),
callback_(callback),
observed_renderer_process_host_(this) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ web_contents->ForEachFrame(base::Bind(
+ &MHTMLGenerationManager::Job::AddFrame,
+ base::Unretained(this))); // Safe because ForEachFrame is synchronus.
ncarter (slow) 2015/12/04 22:54:47 "synchronus" -> "synchronous"
Łukasz Anforowicz 2015/12/05 00:18:39 Done.
+
+ // Main frame needs to be processed first.
+ DCHECK(!pending_frame_tree_node_ids_.empty());
+ DCHECK(FrameTreeNode::GloballyFindByID(pending_frame_tree_node_ids_.front())
+ ->parent() == nullptr);
}
MHTMLGenerationManager::Job::~Job() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
-bool MHTMLGenerationManager::Job::SendToRenderView() {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
+std::map<int, std::string>
+MHTMLGenerationManager::Job::CreateFrameRoutingIdToContentId(
+ SiteInstance* site_instance) {
+ std::map<int, std::string> result;
+ for (const auto& it : frame_tree_node_to_content_id_) {
+ int ftn_id = it.first;
+ const std::string& content_id = it.second;
+
+ FrameTreeNode* ftn = FrameTreeNode::GloballyFindByID(ftn_id);
+ if (!ftn)
+ continue;
+
+ int routing_id =
+ ftn->render_manager()->GetRoutingIdForSiteInstance(site_instance);
+ if (routing_id == MSG_ROUTING_NONE)
+ continue;
+
+ result[routing_id] = content_id;
+ }
+ return result;
+}
+
+bool MHTMLGenerationManager::Job::SendToNextRenderFrame(
+ const std::string& mhtml_boundary_marker) {
DCHECK(browser_file_.IsValid());
+ DCHECK_LT(0u, pending_frame_tree_node_ids_.size());
+
+ int frame_tree_node_id = pending_frame_tree_node_ids_.front();
+ pending_frame_tree_node_ids_.pop();
+ bool is_last_frame = pending_frame_tree_node_ids_.empty();
- RenderViewHost* rvh = RenderViewHost::FromID(process_id_, routing_id_);
- if (!rvh) { // The contents went away.
+ FrameTreeNode* ftn = FrameTreeNode::GloballyFindByID(frame_tree_node_id);
+ if (!ftn) // The contents went away.
return false;
- }
+ RenderFrameHost* rfh = ftn->current_frame_host();
- observed_renderer_process_host_.Add(rvh->GetMainFrame()->GetProcess());
+ // Get notified if the target of the IPC message dies between responding.
+ observed_renderer_process_host_.RemoveAll();
+ observed_renderer_process_host_.Add(rfh->GetProcess());
IPC::PlatformFileForTransit renderer_file = IPC::GetFileHandleForProcess(
- browser_file_.GetPlatformFile(), rvh->GetProcess()->GetHandle(),
+ browser_file_.GetPlatformFile(), rfh->GetProcess()->GetHandle(),
false); // |close_source_handle|.
- rvh->Send(
- new ViewMsg_SavePageAsMHTML(rvh->GetRoutingID(), job_id_, renderer_file));
+ rfh->Send(new FrameMsg_SerializeAsMHTML(
+ rfh->GetRoutingID(), job_id_, renderer_file, mhtml_boundary_marker,
+ CreateFrameRoutingIdToContentId(rfh->GetSiteInstance()), is_last_frame));
return true;
}
@@ -109,6 +168,18 @@ void MHTMLGenerationManager::Job::RenderProcessExited(
MHTMLGenerationManager::GetInstance()->RenderProcessExited(this);
}
+void MHTMLGenerationManager::Job::AddFrame(RenderFrameHost* render_frame_host) {
+ auto* rfhi = static_cast<RenderFrameHostImpl*>(render_frame_host);
+ int frame_tree_node_id = rfhi->frame_tree_node()->frame_tree_node_id();
+ pending_frame_tree_node_ids_.push(frame_tree_node_id);
+
+ std::ostringstream content_id_builder;
+ content_id_builder << "<frame" << frame_tree_node_id << "@save"
+ << base::GenerateGUID() << ".mhtml.blink>";
+ std::string content_id = content_id_builder.str();
+ frame_tree_node_to_content_id_[frame_tree_node_id] = content_id;
+}
+
void MHTMLGenerationManager::Job::RenderProcessHostDestroyed(
RenderProcessHost* host) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -167,19 +238,42 @@ void MHTMLGenerationManager::SaveMHTML(WebContents* web_contents,
void MHTMLGenerationManager::OnSavedPageAsMHTML(
int job_id,
- bool mhtml_generation_in_renderer_succeeded) {
+ bool mhtml_generation_in_renderer_succeeded,
+ const std::string& mhtml_boundary_marker) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- JobStatus job_status = mhtml_generation_in_renderer_succeeded
- ? JobStatus::SUCCESS
- : JobStatus::FAILURE;
- JobFinished(job_id, job_status);
+
+ if (!mhtml_generation_in_renderer_succeeded) {
+ JobFinished(job_id, JobStatus::FAILURE);
+ return;
+ }
+
+ Job* job = FindJob(job_id);
+ if (!job)
+ return;
+
+ if (job->GotMoreFramesToProcess()) {
+ if (!job->SendToNextRenderFrame(mhtml_boundary_marker)) {
+ JobFinished(job_id, JobStatus::FAILURE);
+ }
+ return;
+ }
+
+ JobFinished(job_id, JobStatus::SUCCESS);
}
// static
base::File MHTMLGenerationManager::CreateFile(const base::FilePath& file_path) {
DCHECK_CURRENTLY_ON(BrowserThread::FILE);
- base::File browser_file(
- file_path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+
+ // SECURITY NOTE: A file descriptor to the file created below will be passed
+ // to multiple renderer processes which (in out-of-process iframes mode) can
+ // act on behalf of separate web principals. Therefore it is important to
+ // only allow writing to the file and forbid reading from the file (as this
+ // would allow reading content generated by other renderers / other web
+ // principals).
+ uint32 file_flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE;
+
+ base::File browser_file(file_path, file_flags);
if (!browser_file.IsValid()) {
LOG(ERROR) << "Failed to create file to save MHTML at: " <<
file_path.value();
@@ -203,7 +297,9 @@ void MHTMLGenerationManager::OnFileAvailable(int job_id,
job->set_browser_file(browser_file.Pass());
- if (!job->SendToRenderView()) {
+ // We don't yet have an mhtml boundary marker when serializing the 1st frame.
+ std::string initial_mhtml_boundary_marker = "";
+ if (!job->SendToNextRenderFrame(initial_mhtml_boundary_marker)) {
JobFinished(job_id, JobStatus::FAILURE);
}
}

Powered by Google App Engine
This is Rietveld 408576698