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 <stdint.h> | 5 #include <stdint.h> |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/callback.h" | 8 #include "base/callback.h" |
9 #include "base/files/file_path.h" | 9 #include "base/files/file_path.h" |
10 #include "base/files/file_util.h" | 10 #include "base/files/file_util.h" |
11 #include "base/files/scoped_temp_dir.h" | 11 #include "base/files/scoped_temp_dir.h" |
12 #include "base/macros.h" | 12 #include "base/macros.h" |
13 #include "base/run_loop.h" | 13 #include "base/run_loop.h" |
14 #include "base/strings/utf_string_conversions.h" | 14 #include "base/strings/utf_string_conversions.h" |
| 15 #include "content/browser/renderer_host/render_process_host_impl.h" |
| 16 #include "content/common/frame_messages.h" |
| 17 #include "content/public/browser/render_process_host.h" |
15 #include "content/public/browser/web_contents.h" | 18 #include "content/public/browser/web_contents.h" |
16 #include "content/public/common/mhtml_generation_params.h" | 19 #include "content/public/common/mhtml_generation_params.h" |
17 #include "content/public/test/browser_test_utils.h" | 20 #include "content/public/test/browser_test_utils.h" |
18 #include "content/public/test/content_browser_test.h" | 21 #include "content/public/test/content_browser_test.h" |
19 #include "content/public/test/content_browser_test_utils.h" | 22 #include "content/public/test/content_browser_test_utils.h" |
20 #include "content/public/test/test_utils.h" | 23 #include "content/public/test/test_utils.h" |
21 #include "content/shell/browser/shell.h" | 24 #include "content/shell/browser/shell.h" |
22 #include "net/base/filename_util.h" | 25 #include "net/base/filename_util.h" |
23 #include "net/dns/mock_host_resolver.h" | 26 #include "net/dns/mock_host_resolver.h" |
24 #include "net/test/embedded_test_server/embedded_test_server.h" | 27 #include "net/test/embedded_test_server/embedded_test_server.h" |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
95 ASSERT_TRUE(embedded_test_server()->Start()); | 98 ASSERT_TRUE(embedded_test_server()->Start()); |
96 ContentBrowserTest::SetUp(); | 99 ContentBrowserTest::SetUp(); |
97 } | 100 } |
98 | 101 |
99 void GenerateMHTML(const base::FilePath& path, const GURL& url) { | 102 void GenerateMHTML(const base::FilePath& path, const GURL& url) { |
100 GenerateMHTML(MHTMLGenerationParams(path), url); | 103 GenerateMHTML(MHTMLGenerationParams(path), url); |
101 } | 104 } |
102 | 105 |
103 void GenerateMHTML(const MHTMLGenerationParams& params, const GURL& url) { | 106 void GenerateMHTML(const MHTMLGenerationParams& params, const GURL& url) { |
104 NavigateToURL(shell(), url); | 107 NavigateToURL(shell(), url); |
| 108 GenerateMHTMLForCurrentPage(params); |
| 109 } |
105 | 110 |
| 111 void GenerateMHTMLForCurrentPage(const MHTMLGenerationParams& params) { |
106 base::RunLoop run_loop; | 112 base::RunLoop run_loop; |
107 | 113 |
108 shell()->web_contents()->GenerateMHTML( | 114 shell()->web_contents()->GenerateMHTML( |
109 params, base::Bind(&MHTMLGenerationTest::MHTMLGenerated, this, | 115 params, base::Bind(&MHTMLGenerationTest::MHTMLGenerated, this, |
110 run_loop.QuitClosure())); | 116 run_loop.QuitClosure())); |
111 | 117 |
112 // Block until the MHTML is generated. | 118 // Block until the MHTML is generated. |
113 run_loop.Run(); | 119 run_loop.Run(); |
114 | 120 |
115 EXPECT_TRUE(has_mhtml_callback_run()); | 121 EXPECT_TRUE(has_mhtml_callback_run()); |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
213 // Make sure the actual generated file has some contents. | 219 // Make sure the actual generated file has some contents. |
214 EXPECT_GT(file_size(), 0); // Verify the size reported by the callback. | 220 EXPECT_GT(file_size(), 0); // Verify the size reported by the callback. |
215 EXPECT_GT(ReadFileSizeFromDisk(path), 100); // Verify the actual file size. | 221 EXPECT_GT(ReadFileSizeFromDisk(path), 100); // Verify the actual file size. |
216 | 222 |
217 std::string mhtml; | 223 std::string mhtml; |
218 ASSERT_TRUE(base::ReadFileToString(path, &mhtml)); | 224 ASSERT_TRUE(base::ReadFileToString(path, &mhtml)); |
219 EXPECT_THAT(mhtml, | 225 EXPECT_THAT(mhtml, |
220 HasSubstr("Content-Transfer-Encoding: quoted-printable")); | 226 HasSubstr("Content-Transfer-Encoding: quoted-printable")); |
221 } | 227 } |
222 | 228 |
| 229 class GenerateMHTMLAndExitRendererMessageFilter : public BrowserMessageFilter { |
| 230 public: |
| 231 GenerateMHTMLAndExitRendererMessageFilter( |
| 232 RenderProcessHostImpl* render_process_host) |
| 233 : BrowserMessageFilter(FrameMsgStart), |
| 234 render_process_host_(render_process_host) {} |
| 235 |
| 236 protected: |
| 237 ~GenerateMHTMLAndExitRendererMessageFilter() override {} |
| 238 |
| 239 private: |
| 240 bool OnMessageReceived(const IPC::Message& message) override { |
| 241 if (message.type() == FrameHostMsg_SerializeAsMHTMLResponse::ID) { |
| 242 // After |return false| below, this IPC message will be handled by the |
| 243 // product code as illustrated below. (1), (2), (3) depict points in time |
| 244 // when product code runs on UI and FILE threads. (X), (Y), (Z) depict |
| 245 // when we want test-injected tasks to run - for the repro, (Z) has to |
| 246 // happen between (1) and (3). (Y?) and (Z?) depict when test tasks can |
| 247 // theoretically happen and ruin the repro. |
| 248 // |
| 249 // IO thread UI thread FILE thread |
| 250 // --------- --------- ----------- |
| 251 // | | | |
| 252 // WE ARE HERE | | |
| 253 // | | | |
| 254 // after |return false| | | |
| 255 // +--------------->+ | |
| 256 // | | | |
| 257 // | (X) | |
| 258 // | | | |
| 259 // | | (Y?) |
| 260 // | (Z?) | |
| 261 // | | | |
| 262 // (1) | MHTMLGenerationManager | |
| 263 // | ::OnSerializeAsMHTMLResponse | |
| 264 // | +-------------------->+ |
| 265 // | | | |
| 266 // | | (Y) |
| 267 // | | | |
| 268 // (2) | | MHTMLGenerationManager::Job |
| 269 // | | ::CloseFileOnFileThread |
| 270 // | | | |
| 271 // | (Z) | |
| 272 // | test needs to inject | |
| 273 // | fast renderer shutdown | |
| 274 // | HERE - between (1) and (3) | |
| 275 // | | | |
| 276 // | | | |
| 277 // | +<--------------------+ |
| 278 // | | | |
| 279 // (3) | MHTMLGenerationManager | |
| 280 // | ::OnFileClosed | |
| 281 // | | | |
| 282 // |
| 283 // We hope that (Z) happens between (1) and (3) by doing the following: |
| 284 // - From here post TaskX to UI thread. (X) is guaranteed to happen |
| 285 // before timepoint (1) (because posting of (1) happens after |
| 286 // |return false| / before we post TaskX below). |
| 287 // - From (X) post TaskY to FILE thread. Because this posting is done |
| 288 // before (1), we can guarantee that (Y) will happen before (2). |
| 289 // - From (Y) post TaskZ to UI thread. Because this posting is done |
| 290 // before (2), we can guarantee that (Z) will happen before (3). |
| 291 // - We cannot really guarantee that (Y) and (Z) happen *after* (1) - i.e. |
| 292 // execution at (Y?) and (Z?) instead is possible. In practice, |
| 293 // bouncing off of UI and FILE thread does mean (Z) happens after (1). |
| 294 BrowserThread::PostTask( |
| 295 BrowserThread::UI, FROM_HERE, base::Bind( |
| 296 &GenerateMHTMLAndExitRendererMessageFilter::TaskX, |
| 297 base::Unretained(this))); |
| 298 } |
| 299 |
| 300 return false; |
| 301 }; |
| 302 |
| 303 void TaskX() { |
| 304 BrowserThread::PostTask( |
| 305 BrowserThread::FILE, FROM_HERE, base::Bind( |
| 306 &GenerateMHTMLAndExitRendererMessageFilter::TaskY, |
| 307 base::Unretained(this))); |
| 308 } |
| 309 |
| 310 void TaskY() { |
| 311 BrowserThread::PostTask( |
| 312 BrowserThread::UI, FROM_HERE, base::Bind( |
| 313 &GenerateMHTMLAndExitRendererMessageFilter::TaskZ, |
| 314 base::Unretained(this))); |
| 315 } |
| 316 |
| 317 void TaskZ() { |
| 318 render_process_host_->FastShutdownIfPossible(); |
| 319 } |
| 320 |
| 321 RenderProcessHostImpl* render_process_host_; |
| 322 |
| 323 DISALLOW_COPY_AND_ASSIGN(GenerateMHTMLAndExitRendererMessageFilter); |
| 324 }; |
| 325 |
| 326 // Regression test for the crash/race from https://crbug.com/612098. |
| 327 IN_PROC_BROWSER_TEST_F(MHTMLGenerationTest, GenerateMHTMLAndExitRenderer) { |
| 328 NavigateToURL(shell(), embedded_test_server()->GetURL("/simple_page.html")); |
| 329 |
| 330 RenderProcessHostImpl* render_process_host = |
| 331 static_cast<RenderProcessHostImpl*>( |
| 332 shell()->web_contents()->GetRenderProcessHost()); |
| 333 scoped_refptr<BrowserMessageFilter> filter = |
| 334 new GenerateMHTMLAndExitRendererMessageFilter(render_process_host); |
| 335 render_process_host->AddFilter(filter.get()); |
| 336 |
| 337 base::FilePath path(temp_dir_.path()); |
| 338 path = path.Append(FILE_PATH_LITERAL("test.mht")); |
| 339 GenerateMHTMLForCurrentPage(MHTMLGenerationParams(path)); |
| 340 |
| 341 EXPECT_GT(ReadFileSizeFromDisk(path), 100); // Verify the actual file size. |
| 342 } |
| 343 |
223 IN_PROC_BROWSER_TEST_F(MHTMLGenerationTest, InvalidPath) { | 344 IN_PROC_BROWSER_TEST_F(MHTMLGenerationTest, InvalidPath) { |
224 base::FilePath path(FILE_PATH_LITERAL("/invalid/file/path")); | 345 base::FilePath path(FILE_PATH_LITERAL("/invalid/file/path")); |
225 | 346 |
226 GenerateMHTML(path, embedded_test_server()->GetURL( | 347 GenerateMHTML(path, embedded_test_server()->GetURL( |
227 "/download/local-about-blank-subframes.html")); | 348 "/download/local-about-blank-subframes.html")); |
228 ASSERT_FALSE(HasFailure()); // No failures with the invocation itself? | 349 ASSERT_FALSE(HasFailure()); // No failures with the invocation itself? |
229 | 350 |
230 EXPECT_EQ(file_size(), -1); // Expecting that the callback reported failure. | 351 EXPECT_EQ(file_size(), -1); // Expecting that the callback reported failure. |
231 } | 352 } |
232 | 353 |
(...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
470 | 591 |
471 // Make sure that URLs of both frames are present | 592 // Make sure that URLs of both frames are present |
472 // (note that these are single-line regexes). | 593 // (note that these are single-line regexes). |
473 EXPECT_THAT( | 594 EXPECT_THAT( |
474 mhtml, | 595 mhtml, |
475 ContainsRegex("Content-Location:.*/frame_tree/page_with_one_frame.html")); | 596 ContainsRegex("Content-Location:.*/frame_tree/page_with_one_frame.html")); |
476 EXPECT_THAT(mhtml, ContainsRegex("Content-Location:.*/title1.html")); | 597 EXPECT_THAT(mhtml, ContainsRegex("Content-Location:.*/title1.html")); |
477 } | 598 } |
478 | 599 |
479 } // namespace content | 600 } // namespace content |
OLD | NEW |