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

Side by Side 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 unified diff | Download patch
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>
8 #include <queue>
9 #include <sstream>
10
7 #include "base/bind.h" 11 #include "base/bind.h"
8 #include "base/files/file.h" 12 #include "base/files/file.h"
13 #include "base/guid.h"
9 #include "base/scoped_observer.h" 14 #include "base/scoped_observer.h"
10 #include "base/stl_util.h" 15 #include "base/stl_util.h"
11 #include "content/browser/renderer_host/render_view_host_impl.h" 16 #include "content/browser/frame_host/frame_tree_node.h"
17 #include "content/common/frame_messages.h"
12 #include "content/public/browser/browser_thread.h" 18 #include "content/public/browser/browser_thread.h"
13 #include "content/public/browser/render_frame_host.h" 19 #include "content/public/browser/render_frame_host.h"
14 #include "content/public/browser/render_process_host.h" 20 #include "content/public/browser/render_process_host.h"
15 #include "content/public/browser/render_process_host_observer.h" 21 #include "content/public/browser/render_process_host_observer.h"
16 #include "content/public/browser/web_contents.h" 22 #include "content/public/browser/web_contents.h"
17 #include "content/common/view_messages.h"
18 23
19 namespace content { 24 namespace content {
20 25
21 // The class and all of its members live on the UI thread. Only static methods 26 // The class and all of its members live on the UI thread. Only static methods
22 // are executed on other threads. 27 // are executed on other threads.
23 class MHTMLGenerationManager::Job : public RenderProcessHostObserver { 28 class MHTMLGenerationManager::Job : public RenderProcessHostObserver {
24 public: 29 public:
25 Job(int job_id, WebContents* web_contents, GenerateMHTMLCallback callback); 30 Job(int job_id, WebContents* web_contents, GenerateMHTMLCallback callback);
26 ~Job() override; 31 ~Job() override;
27 32
28 void set_browser_file(base::File file) { browser_file_ = file.Pass(); } 33 void set_browser_file(base::File file) { browser_file_ = file.Pass(); }
29 34
30 GenerateMHTMLCallback callback() const { return callback_; } 35 GenerateMHTMLCallback callback() const { return callback_; }
31 36
32 // Sends IPC to the renderer, asking for MHTML generation. 37 // Sends IPC to the renderer, asking for MHTML generation of the next frame.
38 //
33 // Returns true if the message was sent successfully; false otherwise. 39 // Returns true if the message was sent successfully; false otherwise.
34 bool SendToRenderView(); 40 //
41 // See FrameMsg_SerializeAsMHTML IPC message for description of
42 // |mhtml_boundary_marker| parameter.
43 bool SendToNextRenderFrame(const std::string& mhtml_boundary_marker);
44
45 // Indicates if more calls to SendToNextRenderFrame are needed.
46 bool GotMoreFramesToProcess() {
47 return !pending_frame_tree_node_ids_.empty();
48 }
35 49
36 // Close the file on the file thread and respond back on the UI thread with 50 // Close the file on the file thread and respond back on the UI thread with
37 // file size. 51 // file size.
38 void CloseFile(base::Callback<void(int64 file_size)> callback); 52 void CloseFile(base::Callback<void(int64 file_size)> callback);
39 53
40 // RenderProcessHostObserver: 54 // RenderProcessHostObserver:
41 void RenderProcessExited(RenderProcessHost* host, 55 void RenderProcessExited(RenderProcessHost* host,
42 base::TerminationStatus status, 56 base::TerminationStatus status,
43 int exit_code) override; 57 int exit_code) override;
44 void RenderProcessHostDestroyed(RenderProcessHost* host) override; 58 void RenderProcessHostDestroyed(RenderProcessHost* host) override;
45 59
46 private: 60 private:
47 static int64 CloseFileOnFileThread(base::File file); 61 static int64 CloseFileOnFileThread(base::File file);
62 void AddFrame(RenderFrameHost* render_frame_host);
63
64 // Translates |frame_tree_node_to_content_id_| into
65 // a |site_instance|-specific, routing-id-based map.
66 std::map<int, std::string> CreateFrameRoutingIdToContentId(
67 SiteInstance* site_instance);
48 68
49 // Id used to map renderer responses to jobs. 69 // Id used to map renderer responses to jobs.
50 // See also MHTMLGenerationManager::id_to_job_ map. 70 // See also MHTMLGenerationManager::id_to_job_ map.
51 int job_id_; 71 int job_id_;
52 72
53 // The handle to the file the MHTML is saved to for the browser process. 73 // The handle to the file the MHTML is saved to for the browser process.
54 base::File browser_file_; 74 base::File browser_file_;
55 75
56 // The IDs mapping to a specific contents. 76 // The IDs of frames we still need to process.
57 int process_id_; 77 std::queue<int> pending_frame_tree_node_ids_;
58 int routing_id_; 78
79 // Map from frames into content ids (see WebPageSerializer::generateMHTMLParts
80 // for more details about what "content ids" are and how they are used).
81 std::map<int, std::string> frame_tree_node_to_content_id_;
59 82
60 // The callback to call once generation is complete. 83 // The callback to call once generation is complete.
61 GenerateMHTMLCallback callback_; 84 GenerateMHTMLCallback callback_;
62 85
63 // RAII helper for registering this Job as a RenderProcessHost observer. 86 // RAII helper for registering this Job as a RenderProcessHost observer.
64 ScopedObserver<RenderProcessHost, MHTMLGenerationManager::Job> 87 ScopedObserver<RenderProcessHost, MHTMLGenerationManager::Job>
65 observed_renderer_process_host_; 88 observed_renderer_process_host_;
66 89
67 DISALLOW_COPY_AND_ASSIGN(Job); 90 DISALLOW_COPY_AND_ASSIGN(Job);
68 }; 91 };
69 92
70 MHTMLGenerationManager::Job::Job(int job_id, 93 MHTMLGenerationManager::Job::Job(int job_id,
71 WebContents* web_contents, 94 WebContents* web_contents,
72 GenerateMHTMLCallback callback) 95 GenerateMHTMLCallback callback)
73 : job_id_(job_id), 96 : job_id_(job_id),
74 process_id_(web_contents->GetRenderProcessHost()->GetID()),
75 routing_id_(web_contents->GetRenderViewHost()->GetRoutingID()),
76 callback_(callback), 97 callback_(callback),
77 observed_renderer_process_host_(this) { 98 observed_renderer_process_host_(this) {
78 DCHECK_CURRENTLY_ON(BrowserThread::UI); 99 DCHECK_CURRENTLY_ON(BrowserThread::UI);
100 web_contents->ForEachFrame(base::Bind(
101 &MHTMLGenerationManager::Job::AddFrame,
102 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.
103
104 // Main frame needs to be processed first.
105 DCHECK(!pending_frame_tree_node_ids_.empty());
106 DCHECK(FrameTreeNode::GloballyFindByID(pending_frame_tree_node_ids_.front())
107 ->parent() == nullptr);
79 } 108 }
80 109
81 MHTMLGenerationManager::Job::~Job() { 110 MHTMLGenerationManager::Job::~Job() {
82 DCHECK_CURRENTLY_ON(BrowserThread::UI); 111 DCHECK_CURRENTLY_ON(BrowserThread::UI);
83 } 112 }
84 113
85 bool MHTMLGenerationManager::Job::SendToRenderView() { 114 std::map<int, std::string>
86 DCHECK_CURRENTLY_ON(BrowserThread::UI); 115 MHTMLGenerationManager::Job::CreateFrameRoutingIdToContentId(
116 SiteInstance* site_instance) {
117 std::map<int, std::string> result;
118 for (const auto& it : frame_tree_node_to_content_id_) {
119 int ftn_id = it.first;
120 const std::string& content_id = it.second;
121
122 FrameTreeNode* ftn = FrameTreeNode::GloballyFindByID(ftn_id);
123 if (!ftn)
124 continue;
125
126 int routing_id =
127 ftn->render_manager()->GetRoutingIdForSiteInstance(site_instance);
128 if (routing_id == MSG_ROUTING_NONE)
129 continue;
130
131 result[routing_id] = content_id;
132 }
133 return result;
134 }
135
136 bool MHTMLGenerationManager::Job::SendToNextRenderFrame(
137 const std::string& mhtml_boundary_marker) {
87 DCHECK(browser_file_.IsValid()); 138 DCHECK(browser_file_.IsValid());
139 DCHECK_LT(0u, pending_frame_tree_node_ids_.size());
88 140
89 RenderViewHost* rvh = RenderViewHost::FromID(process_id_, routing_id_); 141 int frame_tree_node_id = pending_frame_tree_node_ids_.front();
90 if (!rvh) { // The contents went away. 142 pending_frame_tree_node_ids_.pop();
143 bool is_last_frame = pending_frame_tree_node_ids_.empty();
144
145 FrameTreeNode* ftn = FrameTreeNode::GloballyFindByID(frame_tree_node_id);
146 if (!ftn) // The contents went away.
91 return false; 147 return false;
92 } 148 RenderFrameHost* rfh = ftn->current_frame_host();
93 149
94 observed_renderer_process_host_.Add(rvh->GetMainFrame()->GetProcess()); 150 // Get notified if the target of the IPC message dies between responding.
151 observed_renderer_process_host_.RemoveAll();
152 observed_renderer_process_host_.Add(rfh->GetProcess());
95 153
96 IPC::PlatformFileForTransit renderer_file = IPC::GetFileHandleForProcess( 154 IPC::PlatformFileForTransit renderer_file = IPC::GetFileHandleForProcess(
97 browser_file_.GetPlatformFile(), rvh->GetProcess()->GetHandle(), 155 browser_file_.GetPlatformFile(), rfh->GetProcess()->GetHandle(),
98 false); // |close_source_handle|. 156 false); // |close_source_handle|.
99 rvh->Send( 157 rfh->Send(new FrameMsg_SerializeAsMHTML(
100 new ViewMsg_SavePageAsMHTML(rvh->GetRoutingID(), job_id_, renderer_file)); 158 rfh->GetRoutingID(), job_id_, renderer_file, mhtml_boundary_marker,
159 CreateFrameRoutingIdToContentId(rfh->GetSiteInstance()), is_last_frame));
101 return true; 160 return true;
102 } 161 }
103 162
104 void MHTMLGenerationManager::Job::RenderProcessExited( 163 void MHTMLGenerationManager::Job::RenderProcessExited(
105 RenderProcessHost* host, 164 RenderProcessHost* host,
106 base::TerminationStatus status, 165 base::TerminationStatus status,
107 int exit_code) { 166 int exit_code) {
108 DCHECK_CURRENTLY_ON(BrowserThread::UI); 167 DCHECK_CURRENTLY_ON(BrowserThread::UI);
109 MHTMLGenerationManager::GetInstance()->RenderProcessExited(this); 168 MHTMLGenerationManager::GetInstance()->RenderProcessExited(this);
110 } 169 }
111 170
171 void MHTMLGenerationManager::Job::AddFrame(RenderFrameHost* render_frame_host) {
172 auto* rfhi = static_cast<RenderFrameHostImpl*>(render_frame_host);
173 int frame_tree_node_id = rfhi->frame_tree_node()->frame_tree_node_id();
174 pending_frame_tree_node_ids_.push(frame_tree_node_id);
175
176 std::ostringstream content_id_builder;
177 content_id_builder << "<frame" << frame_tree_node_id << "@save"
178 << base::GenerateGUID() << ".mhtml.blink>";
179 std::string content_id = content_id_builder.str();
180 frame_tree_node_to_content_id_[frame_tree_node_id] = content_id;
181 }
182
112 void MHTMLGenerationManager::Job::RenderProcessHostDestroyed( 183 void MHTMLGenerationManager::Job::RenderProcessHostDestroyed(
113 RenderProcessHost* host) { 184 RenderProcessHost* host) {
114 DCHECK_CURRENTLY_ON(BrowserThread::UI); 185 DCHECK_CURRENTLY_ON(BrowserThread::UI);
115 observed_renderer_process_host_.Remove(host); 186 observed_renderer_process_host_.Remove(host);
116 } 187 }
117 188
118 void MHTMLGenerationManager::Job::CloseFile( 189 void MHTMLGenerationManager::Job::CloseFile(
119 base::Callback<void(int64)> callback) { 190 base::Callback<void(int64)> callback) {
120 DCHECK_CURRENTLY_ON(BrowserThread::UI); 191 DCHECK_CURRENTLY_ON(BrowserThread::UI);
121 192
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
160 BrowserThread::PostTaskAndReplyWithResult( 231 BrowserThread::PostTaskAndReplyWithResult(
161 BrowserThread::FILE, FROM_HERE, 232 BrowserThread::FILE, FROM_HERE,
162 base::Bind(&MHTMLGenerationManager::CreateFile, file_path), 233 base::Bind(&MHTMLGenerationManager::CreateFile, file_path),
163 base::Bind(&MHTMLGenerationManager::OnFileAvailable, 234 base::Bind(&MHTMLGenerationManager::OnFileAvailable,
164 base::Unretained(this), // Safe b/c |this| is a singleton. 235 base::Unretained(this), // Safe b/c |this| is a singleton.
165 job_id)); 236 job_id));
166 } 237 }
167 238
168 void MHTMLGenerationManager::OnSavedPageAsMHTML( 239 void MHTMLGenerationManager::OnSavedPageAsMHTML(
169 int job_id, 240 int job_id,
170 bool mhtml_generation_in_renderer_succeeded) { 241 bool mhtml_generation_in_renderer_succeeded,
242 const std::string& mhtml_boundary_marker) {
171 DCHECK_CURRENTLY_ON(BrowserThread::UI); 243 DCHECK_CURRENTLY_ON(BrowserThread::UI);
172 JobStatus job_status = mhtml_generation_in_renderer_succeeded 244
173 ? JobStatus::SUCCESS 245 if (!mhtml_generation_in_renderer_succeeded) {
174 : JobStatus::FAILURE; 246 JobFinished(job_id, JobStatus::FAILURE);
175 JobFinished(job_id, job_status); 247 return;
248 }
249
250 Job* job = FindJob(job_id);
251 if (!job)
252 return;
253
254 if (job->GotMoreFramesToProcess()) {
255 if (!job->SendToNextRenderFrame(mhtml_boundary_marker)) {
256 JobFinished(job_id, JobStatus::FAILURE);
257 }
258 return;
259 }
260
261 JobFinished(job_id, JobStatus::SUCCESS);
176 } 262 }
177 263
178 // static 264 // static
179 base::File MHTMLGenerationManager::CreateFile(const base::FilePath& file_path) { 265 base::File MHTMLGenerationManager::CreateFile(const base::FilePath& file_path) {
180 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 266 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
181 base::File browser_file( 267
182 file_path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); 268 // SECURITY NOTE: A file descriptor to the file created below will be passed
269 // to multiple renderer processes which (in out-of-process iframes mode) can
270 // act on behalf of separate web principals. Therefore it is important to
271 // only allow writing to the file and forbid reading from the file (as this
272 // would allow reading content generated by other renderers / other web
273 // principals).
274 uint32 file_flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE;
275
276 base::File browser_file(file_path, file_flags);
183 if (!browser_file.IsValid()) { 277 if (!browser_file.IsValid()) {
184 LOG(ERROR) << "Failed to create file to save MHTML at: " << 278 LOG(ERROR) << "Failed to create file to save MHTML at: " <<
185 file_path.value(); 279 file_path.value();
186 } 280 }
187 return browser_file.Pass(); 281 return browser_file.Pass();
188 } 282 }
189 283
190 void MHTMLGenerationManager::OnFileAvailable(int job_id, 284 void MHTMLGenerationManager::OnFileAvailable(int job_id,
191 base::File browser_file) { 285 base::File browser_file) {
192 DCHECK_CURRENTLY_ON(BrowserThread::UI); 286 DCHECK_CURRENTLY_ON(BrowserThread::UI);
193 287
194 if (!browser_file.IsValid()) { 288 if (!browser_file.IsValid()) {
195 LOG(ERROR) << "Failed to create file"; 289 LOG(ERROR) << "Failed to create file";
196 JobFinished(job_id, JobStatus::FAILURE); 290 JobFinished(job_id, JobStatus::FAILURE);
197 return; 291 return;
198 } 292 }
199 293
200 Job* job = FindJob(job_id); 294 Job* job = FindJob(job_id);
201 if (!job) 295 if (!job)
202 return; 296 return;
203 297
204 job->set_browser_file(browser_file.Pass()); 298 job->set_browser_file(browser_file.Pass());
205 299
206 if (!job->SendToRenderView()) { 300 // We don't yet have an mhtml boundary marker when serializing the 1st frame.
301 std::string initial_mhtml_boundary_marker = "";
302 if (!job->SendToNextRenderFrame(initial_mhtml_boundary_marker)) {
207 JobFinished(job_id, JobStatus::FAILURE); 303 JobFinished(job_id, JobStatus::FAILURE);
208 } 304 }
209 } 305 }
210 306
211 void MHTMLGenerationManager::JobFinished(int job_id, JobStatus job_status) { 307 void MHTMLGenerationManager::JobFinished(int job_id, JobStatus job_status) {
212 DCHECK_CURRENTLY_ON(BrowserThread::UI); 308 DCHECK_CURRENTLY_ON(BrowserThread::UI);
213 309
214 Job* job = FindJob(job_id); 310 Job* job = FindJob(job_id);
215 if (!job) 311 if (!job)
216 return; 312 return;
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
263 ++it) { 359 ++it) {
264 if (it->second == job) { 360 if (it->second == job) {
265 JobFinished(it->first, JobStatus::FAILURE); 361 JobFinished(it->first, JobStatus::FAILURE);
266 return; 362 return;
267 } 363 }
268 } 364 }
269 NOTREACHED(); 365 NOTREACHED();
270 } 366 }
271 367
272 } // namespace content 368 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698