| 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 |