OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |