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

Side by Side Diff: content/browser/download/mhtml_generation_manager.cc

Issue 2163073002: Avoid crashing if MHTMLGenerationManager::JobFinished is called twice. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Posting the injection ->UI->FILE->UI. Created 4 years, 5 months 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 unified diff | Download patch
« no previous file with comments | « content/browser/download/mhtml_generation_manager.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/download/mhtml_generation_manager.h" 5 #include "content/browser/download/mhtml_generation_manager.h"
6 6
7 #include <map> 7 #include <map>
8 #include <queue> 8 #include <queue>
9 #include <utility> 9 #include <utility>
10 10
(...skipping 26 matching lines...) Expand all
37 WebContents* web_contents, 37 WebContents* web_contents,
38 const MHTMLGenerationParams& params, 38 const MHTMLGenerationParams& params,
39 const GenerateMHTMLCallback& callback); 39 const GenerateMHTMLCallback& callback);
40 ~Job() override; 40 ~Job() override;
41 41
42 int id() const { return job_id_; } 42 int id() const { return job_id_; }
43 void set_browser_file(base::File file) { browser_file_ = std::move(file); } 43 void set_browser_file(base::File file) { browser_file_ = std::move(file); }
44 44
45 const GenerateMHTMLCallback& callback() const { return callback_; } 45 const GenerateMHTMLCallback& callback() const { return callback_; }
46 46
47 // Indicates whether we expect a message from the |sender| at this time.
48 // We expect only one message per frame - therefore calling this method
49 // will always clear |frame_tree_node_id_of_busy_frame_|.
50 bool IsMessageFromFrameExpected(RenderFrameHostImpl* sender);
51
47 // Handler for FrameHostMsg_SerializeAsMHTMLResponse (a notification from the 52 // Handler for FrameHostMsg_SerializeAsMHTMLResponse (a notification from the
48 // renderer that the MHTML generation for previous frame has finished). 53 // renderer that the MHTML generation for previous frame has finished).
49 // Returns |true| upon success; |false| otherwise. 54 // Returns |true| upon success; |false| otherwise.
50 bool OnSerializeAsMHTMLResponse( 55 bool OnSerializeAsMHTMLResponse(
51 RenderFrameHostImpl* sender,
52 const std::set<std::string>& digests_of_uris_of_serialized_resources); 56 const std::set<std::string>& digests_of_uris_of_serialized_resources);
53 57
54 // Sends IPC to the renderer, asking for MHTML generation of the next frame. 58 // Sends IPC to the renderer, asking for MHTML generation of the next frame.
55 // 59 //
56 // Returns true if the message was sent successfully; false otherwise. 60 // Returns true if the message was sent successfully; false otherwise.
57 bool SendToNextRenderFrame(); 61 bool SendToNextRenderFrame();
58 62
59 // Indicates if more calls to SendToNextRenderFrame are needed. 63 // Indicates if more calls to SendToNextRenderFrame are needed.
60 bool IsDone() const { 64 bool IsDone() const {
61 bool waiting_for_response_from_renderer = 65 bool waiting_for_response_from_renderer =
62 frame_tree_node_id_of_busy_frame_ != 66 frame_tree_node_id_of_busy_frame_ !=
63 FrameTreeNode::kFrameTreeNodeInvalidId; 67 FrameTreeNode::kFrameTreeNodeInvalidId;
64 bool no_more_requests_to_send = pending_frame_tree_node_ids_.empty(); 68 bool no_more_requests_to_send = pending_frame_tree_node_ids_.empty();
65 return !waiting_for_response_from_renderer && no_more_requests_to_send; 69 return !waiting_for_response_from_renderer && no_more_requests_to_send;
66 } 70 }
67 71
68 // Close the file on the file thread and respond back on the UI thread with 72 // Close the file on the file thread and respond back on the UI thread with
69 // file size. 73 // file size.
70 void CloseFile(base::Callback<void(int64_t file_size)> callback); 74 void CloseFile(base::Callback<void(int64_t file_size)> callback);
71 75
72 // RenderProcessHostObserver: 76 // RenderProcessHostObserver:
73 void RenderProcessExited(RenderProcessHost* host, 77 void RenderProcessExited(RenderProcessHost* host,
74 base::TerminationStatus status, 78 base::TerminationStatus status,
75 int exit_code) override; 79 int exit_code) override;
76 void RenderProcessHostDestroyed(RenderProcessHost* host) override; 80 void RenderProcessHostDestroyed(RenderProcessHost* host) override;
77 81
82 void MarkAsFinished();
83
78 private: 84 private:
79 static int64_t CloseFileOnFileThread(base::File file); 85 static int64_t CloseFileOnFileThread(base::File file);
80 void AddFrame(RenderFrameHost* render_frame_host); 86 void AddFrame(RenderFrameHost* render_frame_host);
81 87
82 // Creates a new map with values (content ids) the same as in 88 // Creates a new map with values (content ids) the same as in
83 // |frame_tree_node_to_content_id_| map, but with the keys translated from 89 // |frame_tree_node_to_content_id_| map, but with the keys translated from
84 // frame_tree_node_id into a |site_instance|-specific routing_id. 90 // frame_tree_node_id into a |site_instance|-specific routing_id.
85 std::map<int, std::string> CreateFrameRoutingIdToContentId( 91 std::map<int, std::string> CreateFrameRoutingIdToContentId(
86 SiteInstance* site_instance); 92 SiteInstance* site_instance);
87 93
(...skipping 24 matching lines...) Expand all
112 // MIME multipart boundary to use in the MHTML doc. 118 // MIME multipart boundary to use in the MHTML doc.
113 std::string mhtml_boundary_marker_; 119 std::string mhtml_boundary_marker_;
114 120
115 // Digests of URIs of already generated MHTML parts. 121 // Digests of URIs of already generated MHTML parts.
116 std::set<std::string> digests_of_already_serialized_uris_; 122 std::set<std::string> digests_of_already_serialized_uris_;
117 std::string salt_; 123 std::string salt_;
118 124
119 // The callback to call once generation is complete. 125 // The callback to call once generation is complete.
120 const GenerateMHTMLCallback callback_; 126 const GenerateMHTMLCallback callback_;
121 127
128 // Whether the job is finished (set to true only for the short duration of
129 // time between MHTMLGenerationManager::JobFinished is called and the job is
130 // destroyed by MHTMLGenerationManager::OnFileClosed).
131 bool is_finished_;
132
122 // RAII helper for registering this Job as a RenderProcessHost observer. 133 // RAII helper for registering this Job as a RenderProcessHost observer.
123 ScopedObserver<RenderProcessHost, MHTMLGenerationManager::Job> 134 ScopedObserver<RenderProcessHost, MHTMLGenerationManager::Job>
124 observed_renderer_process_host_; 135 observed_renderer_process_host_;
125 136
126 DISALLOW_COPY_AND_ASSIGN(Job); 137 DISALLOW_COPY_AND_ASSIGN(Job);
127 }; 138 };
128 139
129 MHTMLGenerationManager::Job::Job(int job_id, 140 MHTMLGenerationManager::Job::Job(int job_id,
130 WebContents* web_contents, 141 WebContents* web_contents,
131 const MHTMLGenerationParams& params, 142 const MHTMLGenerationParams& params,
132 const GenerateMHTMLCallback& callback) 143 const GenerateMHTMLCallback& callback)
133 : job_id_(job_id), 144 : job_id_(job_id),
134 params_(params), 145 params_(params),
135 frame_tree_node_id_of_busy_frame_(FrameTreeNode::kFrameTreeNodeInvalidId), 146 frame_tree_node_id_of_busy_frame_(FrameTreeNode::kFrameTreeNodeInvalidId),
136 mhtml_boundary_marker_(net::GenerateMimeMultipartBoundary()), 147 mhtml_boundary_marker_(net::GenerateMimeMultipartBoundary()),
137 salt_(base::GenerateGUID()), 148 salt_(base::GenerateGUID()),
138 callback_(callback), 149 callback_(callback),
150 is_finished_(false),
139 observed_renderer_process_host_(this) { 151 observed_renderer_process_host_(this) {
140 DCHECK_CURRENTLY_ON(BrowserThread::UI); 152 DCHECK_CURRENTLY_ON(BrowserThread::UI);
141 web_contents->ForEachFrame(base::Bind( 153 web_contents->ForEachFrame(base::Bind(
142 &MHTMLGenerationManager::Job::AddFrame, 154 &MHTMLGenerationManager::Job::AddFrame,
143 base::Unretained(this))); // Safe because ForEachFrame is synchronous. 155 base::Unretained(this))); // Safe because ForEachFrame is synchronous.
144 156
145 // Main frame needs to be processed first. 157 // Main frame needs to be processed first.
146 DCHECK(!pending_frame_tree_node_ids_.empty()); 158 DCHECK(!pending_frame_tree_node_ids_.empty());
147 DCHECK(FrameTreeNode::GloballyFindByID(pending_frame_tree_node_ids_.front()) 159 DCHECK(FrameTreeNode::GloballyFindByID(pending_frame_tree_node_ids_.front())
148 ->parent() == nullptr); 160 ->parent() == nullptr);
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
215 } 227 }
216 228
217 void MHTMLGenerationManager::Job::RenderProcessExited( 229 void MHTMLGenerationManager::Job::RenderProcessExited(
218 RenderProcessHost* host, 230 RenderProcessHost* host,
219 base::TerminationStatus status, 231 base::TerminationStatus status,
220 int exit_code) { 232 int exit_code) {
221 DCHECK_CURRENTLY_ON(BrowserThread::UI); 233 DCHECK_CURRENTLY_ON(BrowserThread::UI);
222 MHTMLGenerationManager::GetInstance()->RenderProcessExited(this); 234 MHTMLGenerationManager::GetInstance()->RenderProcessExited(this);
223 } 235 }
224 236
237 void MHTMLGenerationManager::Job::MarkAsFinished() {
238 DCHECK(!is_finished_);
239 is_finished_ = true;
240
241 // Stopping RenderProcessExited notifications is needed to avoid calling
242 // JobFinished twice. See also https://crbug.com/612098.
243 observed_renderer_process_host_.RemoveAll();
244 }
245
225 void MHTMLGenerationManager::Job::AddFrame(RenderFrameHost* render_frame_host) { 246 void MHTMLGenerationManager::Job::AddFrame(RenderFrameHost* render_frame_host) {
226 auto* rfhi = static_cast<RenderFrameHostImpl*>(render_frame_host); 247 auto* rfhi = static_cast<RenderFrameHostImpl*>(render_frame_host);
227 int frame_tree_node_id = rfhi->frame_tree_node()->frame_tree_node_id(); 248 int frame_tree_node_id = rfhi->frame_tree_node()->frame_tree_node_id();
228 pending_frame_tree_node_ids_.push(frame_tree_node_id); 249 pending_frame_tree_node_ids_.push(frame_tree_node_id);
229 250
230 std::string guid = base::GenerateGUID(); 251 std::string guid = base::GenerateGUID();
231 std::string content_id = base::StringPrintf("<frame-%d-%s@mhtml.blink>", 252 std::string content_id = base::StringPrintf("<frame-%d-%s@mhtml.blink>",
232 frame_tree_node_id, guid.c_str()); 253 frame_tree_node_id, guid.c_str());
233 frame_tree_node_to_content_id_[frame_tree_node_id] = content_id; 254 frame_tree_node_to_content_id_[frame_tree_node_id] = content_id;
234 } 255 }
(...skipping 13 matching lines...) Expand all
248 return; 269 return;
249 } 270 }
250 271
251 BrowserThread::PostTaskAndReplyWithResult( 272 BrowserThread::PostTaskAndReplyWithResult(
252 BrowserThread::FILE, FROM_HERE, 273 BrowserThread::FILE, FROM_HERE,
253 base::Bind(&MHTMLGenerationManager::Job::CloseFileOnFileThread, 274 base::Bind(&MHTMLGenerationManager::Job::CloseFileOnFileThread,
254 base::Passed(std::move(browser_file_))), 275 base::Passed(std::move(browser_file_))),
255 callback); 276 callback);
256 } 277 }
257 278
258 bool MHTMLGenerationManager::Job::OnSerializeAsMHTMLResponse( 279 bool MHTMLGenerationManager::Job::IsMessageFromFrameExpected(
259 RenderFrameHostImpl* sender, 280 RenderFrameHostImpl* sender) {
260 const std::set<std::string>& digests_of_uris_of_serialized_resources) {
261 // Sanitize renderer input / reject unexpected messages.
262 int sender_id = sender->frame_tree_node()->frame_tree_node_id(); 281 int sender_id = sender->frame_tree_node()->frame_tree_node_id();
263 if (sender_id != frame_tree_node_id_of_busy_frame_) { 282 if (sender_id != frame_tree_node_id_of_busy_frame_)
264 ReceivedBadMessage(sender->GetProcess(), 283 return false;
265 bad_message::DWNLD_INVALID_SERIALIZE_AS_MHTML_RESPONSE); 284
266 return false; // Report failure. 285 // We only expect one message per frame - let's make sure subsequent messages
267 } 286 // from the same |sender| will be rejected.
268 frame_tree_node_id_of_busy_frame_ = FrameTreeNode::kFrameTreeNodeInvalidId; 287 frame_tree_node_id_of_busy_frame_ = FrameTreeNode::kFrameTreeNodeInvalidId;
269 288
289 return true;
290 }
291
292 bool MHTMLGenerationManager::Job::OnSerializeAsMHTMLResponse(
293 const std::set<std::string>& digests_of_uris_of_serialized_resources) {
270 // Renderer should be deduping resources with the same uris. 294 // Renderer should be deduping resources with the same uris.
271 DCHECK_EQ(0u, base::STLSetIntersection<std::set<std::string>>( 295 DCHECK_EQ(0u, base::STLSetIntersection<std::set<std::string>>(
272 digests_of_already_serialized_uris_, 296 digests_of_already_serialized_uris_,
273 digests_of_uris_of_serialized_resources).size()); 297 digests_of_uris_of_serialized_resources).size());
274 digests_of_already_serialized_uris_.insert( 298 digests_of_already_serialized_uris_.insert(
275 digests_of_uris_of_serialized_resources.begin(), 299 digests_of_uris_of_serialized_resources.begin(),
276 digests_of_uris_of_serialized_resources.end()); 300 digests_of_uris_of_serialized_resources.end());
277 301
278 if (pending_frame_tree_node_ids_.empty()) 302 if (pending_frame_tree_node_ids_.empty())
279 return true; // Report success. 303 return true; // Report success - all frames have been processed.
280 304
281 return SendToNextRenderFrame(); 305 return SendToNextRenderFrame();
282 } 306 }
283 307
284 // static 308 // static
285 int64_t MHTMLGenerationManager::Job::CloseFileOnFileThread(base::File file) { 309 int64_t MHTMLGenerationManager::Job::CloseFileOnFileThread(base::File file) {
286 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 310 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
287 DCHECK(file.IsValid()); 311 DCHECK(file.IsValid());
288 int64_t file_size = file.GetLength(); 312 int64_t file_size = file.GetLength();
289 file.Close(); 313 file.Close();
(...skipping 26 matching lines...) Expand all
316 } 340 }
317 341
318 void MHTMLGenerationManager::OnSerializeAsMHTMLResponse( 342 void MHTMLGenerationManager::OnSerializeAsMHTMLResponse(
319 RenderFrameHostImpl* sender, 343 RenderFrameHostImpl* sender,
320 int job_id, 344 int job_id,
321 bool mhtml_generation_in_renderer_succeeded, 345 bool mhtml_generation_in_renderer_succeeded,
322 const std::set<std::string>& digests_of_uris_of_serialized_resources) { 346 const std::set<std::string>& digests_of_uris_of_serialized_resources) {
323 DCHECK_CURRENTLY_ON(BrowserThread::UI); 347 DCHECK_CURRENTLY_ON(BrowserThread::UI);
324 348
325 Job* job = FindJob(job_id); 349 Job* job = FindJob(job_id);
326 if (!job) { 350 if (!job || !job->IsMessageFromFrameExpected(sender)) {
351 NOTREACHED();
327 ReceivedBadMessage(sender->GetProcess(), 352 ReceivedBadMessage(sender->GetProcess(),
328 bad_message::DWNLD_INVALID_SERIALIZE_AS_MHTML_RESPONSE); 353 bad_message::DWNLD_INVALID_SERIALIZE_AS_MHTML_RESPONSE);
329 return; 354 return;
330 } 355 }
331 356
332 if (!mhtml_generation_in_renderer_succeeded) { 357 if (!mhtml_generation_in_renderer_succeeded) {
333 JobFinished(job, JobStatus::FAILURE); 358 JobFinished(job, JobStatus::FAILURE);
334 return; 359 return;
335 } 360 }
336 361
337 if (!job->OnSerializeAsMHTMLResponse( 362 if (!job->OnSerializeAsMHTMLResponse(
338 sender, digests_of_uris_of_serialized_resources)) { 363 digests_of_uris_of_serialized_resources)) {
339 JobFinished(job, JobStatus::FAILURE); 364 JobFinished(job, JobStatus::FAILURE);
340 return; 365 return;
341 } 366 }
342 367
343 if (job->IsDone()) 368 if (job->IsDone())
344 JobFinished(job, JobStatus::SUCCESS); 369 JobFinished(job, JobStatus::SUCCESS);
345 } 370 }
346 371
347 // static 372 // static
348 base::File MHTMLGenerationManager::CreateFile(const base::FilePath& file_path) { 373 base::File MHTMLGenerationManager::CreateFile(const base::FilePath& file_path) {
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
381 406
382 if (!job->SendToNextRenderFrame()) { 407 if (!job->SendToNextRenderFrame()) {
383 JobFinished(job, JobStatus::FAILURE); 408 JobFinished(job, JobStatus::FAILURE);
384 } 409 }
385 } 410 }
386 411
387 void MHTMLGenerationManager::JobFinished(Job* job, JobStatus job_status) { 412 void MHTMLGenerationManager::JobFinished(Job* job, JobStatus job_status) {
388 DCHECK_CURRENTLY_ON(BrowserThread::UI); 413 DCHECK_CURRENTLY_ON(BrowserThread::UI);
389 DCHECK(job); 414 DCHECK(job);
390 415
416 job->MarkAsFinished();
391 job->CloseFile( 417 job->CloseFile(
392 base::Bind(&MHTMLGenerationManager::OnFileClosed, 418 base::Bind(&MHTMLGenerationManager::OnFileClosed,
393 base::Unretained(this), // Safe b/c |this| is a singleton. 419 base::Unretained(this), // Safe b/c |this| is a singleton.
394 job->id(), job_status)); 420 job->id(), job_status));
395 } 421 }
396 422
397 void MHTMLGenerationManager::OnFileClosed(int job_id, 423 void MHTMLGenerationManager::OnFileClosed(int job_id,
398 JobStatus job_status, 424 JobStatus job_status,
399 int64_t file_size) { 425 int64_t file_size) {
400 DCHECK_CURRENTLY_ON(BrowserThread::UI); 426 DCHECK_CURRENTLY_ON(BrowserThread::UI);
(...skipping 25 matching lines...) Expand all
426 return iter->second; 452 return iter->second;
427 } 453 }
428 454
429 void MHTMLGenerationManager::RenderProcessExited(Job* job) { 455 void MHTMLGenerationManager::RenderProcessExited(Job* job) {
430 DCHECK_CURRENTLY_ON(BrowserThread::UI); 456 DCHECK_CURRENTLY_ON(BrowserThread::UI);
431 DCHECK(job); 457 DCHECK(job);
432 JobFinished(job, JobStatus::FAILURE); 458 JobFinished(job, JobStatus::FAILURE);
433 } 459 }
434 460
435 } // namespace content 461 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/download/mhtml_generation_manager.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698