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 60feca041a6491796bd906c67325edb11d6016e3..dc4ef122c219fa8b43ed5f04dfbd767d836eb906 100644 |
--- a/content/browser/download/mhtml_generation_manager.cc |
+++ b/content/browser/download/mhtml_generation_manager.cc |
@@ -44,11 +44,15 @@ class MHTMLGenerationManager::Job : public RenderProcessHostObserver { |
const GenerateMHTMLCallback& callback() const { return callback_; } |
+ // Indicates whether we expect a message from the |sender| at this time. |
+ // We expect only one message per frame - therefore calling this method |
+ // will always clear |frame_tree_node_id_of_busy_frame_|. |
+ bool IsMessageFromFrameExpected(RenderFrameHostImpl* sender); |
+ |
// Handler for FrameHostMsg_SerializeAsMHTMLResponse (a notification from the |
// renderer that the MHTML generation for previous frame has finished). |
// Returns |true| upon success; |false| otherwise. |
bool OnSerializeAsMHTMLResponse( |
- RenderFrameHostImpl* sender, |
const std::set<std::string>& digests_of_uris_of_serialized_resources); |
// Sends IPC to the renderer, asking for MHTML generation of the next frame. |
@@ -75,6 +79,8 @@ class MHTMLGenerationManager::Job : public RenderProcessHostObserver { |
int exit_code) override; |
void RenderProcessHostDestroyed(RenderProcessHost* host) override; |
+ void MarkAsFinished(); |
+ |
private: |
static int64_t CloseFileOnFileThread(base::File file); |
void AddFrame(RenderFrameHost* render_frame_host); |
@@ -119,6 +125,11 @@ class MHTMLGenerationManager::Job : public RenderProcessHostObserver { |
// The callback to call once generation is complete. |
const GenerateMHTMLCallback callback_; |
+ // Whether the job is finished (set to true only for the short duration of |
+ // time between MHTMLGenerationManager::JobFinished is called and the job is |
+ // destroyed by MHTMLGenerationManager::OnFileClosed). |
+ bool is_finished_; |
+ |
// RAII helper for registering this Job as a RenderProcessHost observer. |
ScopedObserver<RenderProcessHost, MHTMLGenerationManager::Job> |
observed_renderer_process_host_; |
@@ -136,6 +147,7 @@ MHTMLGenerationManager::Job::Job(int job_id, |
mhtml_boundary_marker_(net::GenerateMimeMultipartBoundary()), |
salt_(base::GenerateGUID()), |
callback_(callback), |
+ is_finished_(false), |
observed_renderer_process_host_(this) { |
DCHECK_CURRENTLY_ON(BrowserThread::UI); |
web_contents->ForEachFrame(base::Bind( |
@@ -222,6 +234,15 @@ void MHTMLGenerationManager::Job::RenderProcessExited( |
MHTMLGenerationManager::GetInstance()->RenderProcessExited(this); |
} |
+void MHTMLGenerationManager::Job::MarkAsFinished() { |
+ DCHECK(!is_finished_); |
+ is_finished_ = true; |
+ |
+ // Stopping RenderProcessExited notifications is needed to avoid calling |
+ // JobFinished twice. See also https://crbug.com/612098. |
+ observed_renderer_process_host_.RemoveAll(); |
+} |
+ |
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(); |
@@ -255,18 +276,21 @@ void MHTMLGenerationManager::Job::CloseFile( |
callback); |
} |
-bool MHTMLGenerationManager::Job::OnSerializeAsMHTMLResponse( |
- RenderFrameHostImpl* sender, |
- const std::set<std::string>& digests_of_uris_of_serialized_resources) { |
- // Sanitize renderer input / reject unexpected messages. |
+bool MHTMLGenerationManager::Job::IsMessageFromFrameExpected( |
+ RenderFrameHostImpl* sender) { |
int sender_id = sender->frame_tree_node()->frame_tree_node_id(); |
- if (sender_id != frame_tree_node_id_of_busy_frame_) { |
- ReceivedBadMessage(sender->GetProcess(), |
- bad_message::DWNLD_INVALID_SERIALIZE_AS_MHTML_RESPONSE); |
- return false; // Report failure. |
- } |
+ if (sender_id != frame_tree_node_id_of_busy_frame_) |
+ return false; |
+ |
+ // We only expect one message per frame - let's make sure subsequent messages |
+ // from the same |sender| will be rejected. |
frame_tree_node_id_of_busy_frame_ = FrameTreeNode::kFrameTreeNodeInvalidId; |
+ return true; |
+} |
+ |
+bool MHTMLGenerationManager::Job::OnSerializeAsMHTMLResponse( |
+ const std::set<std::string>& digests_of_uris_of_serialized_resources) { |
// Renderer should be deduping resources with the same uris. |
DCHECK_EQ(0u, base::STLSetIntersection<std::set<std::string>>( |
digests_of_already_serialized_uris_, |
@@ -276,7 +300,7 @@ bool MHTMLGenerationManager::Job::OnSerializeAsMHTMLResponse( |
digests_of_uris_of_serialized_resources.end()); |
if (pending_frame_tree_node_ids_.empty()) |
- return true; // Report success. |
+ return true; // Report success - all frames have been processed. |
return SendToNextRenderFrame(); |
} |
@@ -323,7 +347,8 @@ void MHTMLGenerationManager::OnSerializeAsMHTMLResponse( |
DCHECK_CURRENTLY_ON(BrowserThread::UI); |
Job* job = FindJob(job_id); |
- if (!job) { |
+ if (!job || !job->IsMessageFromFrameExpected(sender)) { |
+ NOTREACHED(); |
ReceivedBadMessage(sender->GetProcess(), |
bad_message::DWNLD_INVALID_SERIALIZE_AS_MHTML_RESPONSE); |
return; |
@@ -335,7 +360,7 @@ void MHTMLGenerationManager::OnSerializeAsMHTMLResponse( |
} |
if (!job->OnSerializeAsMHTMLResponse( |
- sender, digests_of_uris_of_serialized_resources)) { |
+ digests_of_uris_of_serialized_resources)) { |
JobFinished(job, JobStatus::FAILURE); |
return; |
} |
@@ -388,6 +413,7 @@ void MHTMLGenerationManager::JobFinished(Job* job, JobStatus job_status) { |
DCHECK_CURRENTLY_ON(BrowserThread::UI); |
DCHECK(job); |
+ job->MarkAsFinished(); |
job->CloseFile( |
base::Bind(&MHTMLGenerationManager::OnFileClosed, |
base::Unretained(this), // Safe b/c |this| is a singleton. |