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

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

Issue 1977303003: Adds a feature to MHTML serialization that omits subframes and subresources marked no-store. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@no-store
Patch Set: Adds a test that compares actual visible content. Created 4 years, 7 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
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 <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 "content/public/browser/web_contents.h" 15 #include "content/public/browser/web_contents.h"
15 #include "content/public/common/mhtml_generation_params.h" 16 #include "content/public/common/mhtml_generation_params.h"
16 #include "content/public/test/browser_test_utils.h" 17 #include "content/public/test/browser_test_utils.h"
17 #include "content/public/test/content_browser_test.h" 18 #include "content/public/test/content_browser_test.h"
18 #include "content/public/test/content_browser_test_utils.h" 19 #include "content/public/test/content_browser_test_utils.h"
19 #include "content/public/test/test_utils.h" 20 #include "content/public/test/test_utils.h"
20 #include "content/shell/browser/shell.h" 21 #include "content/shell/browser/shell.h"
22 #include "net/base/filename_util.h"
21 #include "net/dns/mock_host_resolver.h" 23 #include "net/dns/mock_host_resolver.h"
22 #include "net/test/embedded_test_server/embedded_test_server.h" 24 #include "net/test/embedded_test_server/embedded_test_server.h"
23 #include "testing/gmock/include/gmock/gmock.h" 25 #include "testing/gmock/include/gmock/gmock.h"
24 #include "testing/gtest/include/gtest/gtest.h" 26 #include "testing/gtest/include/gtest/gtest.h"
27 #include "third_party/WebKit/public/web/WebFindOptions.h"
25 28
26 using testing::ContainsRegex; 29 using testing::ContainsRegex;
27 using testing::HasSubstr; 30 using testing::HasSubstr;
28 using testing::Not; 31 using testing::Not;
29 32
30 namespace content { 33 namespace content {
31 34
35 namespace {
36
37 int global_request_id = 0;
Łukasz Anforowicz 2016/05/19 17:00:13 Can this be made a static member of FindTrackingDe
dewittj 2016/05/19 18:18:35 Done.
38
39 // A dummy WebContentsDelegate which tracks whether CloseContents() has been
40 // called. It refuses the actual close but keeps track of whether the renderer
41 // requested it.
42 class FindTrackingDelegate : public WebContentsDelegate {
43 public:
44 FindTrackingDelegate(const std::string& search)
45 : search_(search), matches_(-1) {}
46
47 // Returns number of result.
48 int Wait(WebContents* web_contents) {
49 WebContentsDelegate* old_delegate = web_contents->GetDelegate();
50 web_contents->SetDelegate(this);
51
52 blink::WebFindOptions options;
53 options.matchCase = false;
54
55 web_contents->Find(global_request_id++, base::UTF8ToUTF16(search_),
56 options);
57 run_loop_.Run();
58
59 web_contents->SetDelegate(old_delegate);
60
61 return matches_;
62 }
63
64 void FindReply(WebContents* web_contents,
65 int request_id,
66 int number_of_matches,
67 const gfx::Rect& selection_rect,
68 int active_match_ordinal,
69 bool final_update) override {
70 matches_ = number_of_matches;
71 run_loop_.Quit();
72 }
73
74 private:
75 std::string search_;
76 int matches_;
77 base::RunLoop run_loop_;
78
79 DISALLOW_COPY_AND_ASSIGN(FindTrackingDelegate);
80 };
81 }
82
32 class MHTMLGenerationTest : public ContentBrowserTest { 83 class MHTMLGenerationTest : public ContentBrowserTest {
33 public: 84 public:
34 MHTMLGenerationTest() : has_mhtml_callback_run_(false), file_size_(0) {} 85 MHTMLGenerationTest() : has_mhtml_callback_run_(false), file_size_(0) {}
35 86
36 protected: 87 protected:
37 void SetUp() override { 88 void SetUp() override {
38 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 89 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
39 ASSERT_TRUE(embedded_test_server()->Start()); 90 ASSERT_TRUE(embedded_test_server()->Start());
40 ContentBrowserTest::SetUp(); 91 ContentBrowserTest::SetUp();
41 } 92 }
(...skipping 12 matching lines...) Expand all
54 run_loop.QuitClosure())); 105 run_loop.QuitClosure()));
55 106
56 // Block until the MHTML is generated. 107 // Block until the MHTML is generated.
57 run_loop.Run(); 108 run_loop.Run();
58 109
59 EXPECT_TRUE(has_mhtml_callback_run()); 110 EXPECT_TRUE(has_mhtml_callback_run());
60 } 111 }
61 112
62 int64_t ReadFileSizeFromDisk(base::FilePath path) { 113 int64_t ReadFileSizeFromDisk(base::FilePath path) {
63 int64_t file_size; 114 int64_t file_size;
64 if (!base::GetFileSize(path, &file_size)) return -1; 115 if (!base::GetFileSize(path, &file_size))
116 return -1;
65 return file_size; 117 return file_size;
66 } 118 }
67 119
120 void TestOriginalVsSavedPage(
121 const GURL& url,
122 const MHTMLGenerationParams params,
123 int expected_number_of_frames,
124 const std::vector<std::string>& expected_substrings,
125 const std::vector<std::string>& forbidden_substrings_in_saved_page,
126 bool skip_verification_of_original_page = false) {
127 // Navigate to the test page and verify if test expectations
128 // are met (this is mostly a sanity check - a failure to meet
129 // expectations would probably mean that there is a test bug
130 // (i.e. that we got called with wrong expected_foo argument).
131 NavigateToURL(shell(), url);
132 DLOG(INFO) << "Verifying test expectations for original page... : "
133 << shell()->web_contents()->GetLastCommittedURL();
134 if (!skip_verification_of_original_page) {
135 AssertExpectationsAboutCurrentTab(expected_number_of_frames,
136 expected_substrings,
137 std::vector<std::string>());
138 }
139
140 GenerateMHTML(params, url);
141 ASSERT_FALSE(HasFailure());
142
143 // Stop the test server (to make sure the locally saved page
144 // is self-contained / won't try to open original resources).
145 ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
146
147 // Open the saved page and verify if test expectations are
148 // met (i.e. if the same expectations are met for "after"
149 // [saved version of the page] as for the "before"
150 // [the original version of the page].
151 NavigateToURL(shell(), GURL(net::FilePathToFileURL(params.file_path)));
152 DLOG(INFO) << "Verifying test expectations for saved page... : "
153 << shell()->web_contents()->GetLastCommittedURL();
154 AssertExpectationsAboutCurrentTab(expected_number_of_frames,
155 expected_substrings,
156 forbidden_substrings_in_saved_page);
157 }
158
159 void AssertExpectationsAboutCurrentTab(
160 int expected_number_of_frames,
161 const std::vector<std::string>& expected_substrings,
162 const std::vector<std::string>& forbidden_substrings) {
163 int actual_number_of_frames = 0;
164 shell()->web_contents()->ForEachFrame(
165 base::Bind(&MHTMLGenerationTest::increment, base::Unretained(this),
166 &actual_number_of_frames));
Łukasz Anforowicz 2016/05/19 17:00:13 This rather ugly code was written before web_conte
dewittj 2016/05/19 18:18:35 Done.
167 EXPECT_EQ(expected_number_of_frames, actual_number_of_frames);
168
169 for (const auto& expected_substring : expected_substrings) {
170 FindTrackingDelegate delegate(expected_substring);
171 int actual_number_of_matches = delegate.Wait(shell()->web_contents());
172 EXPECT_EQ(1, actual_number_of_matches)
173 << "Verifying that \"" << expected_substring << "\" appears "
174 << "exactly once in the text of web contents of "
175 << shell()->web_contents()->GetURL().spec();
176 }
177
178 for (const auto& forbidden_substring : forbidden_substrings) {
179 FindTrackingDelegate delegate(forbidden_substring);
180 int actual_number_of_matches = delegate.Wait(shell()->web_contents());
181 EXPECT_EQ(0, actual_number_of_matches)
182 << "Verifying that \"" << forbidden_substring << "\" doesn't "
183 << "appear in the text of web contents of "
184 << shell()->web_contents()->GetURL().spec();
185 }
186 }
187
188 void increment(int* i, RenderFrameHost* /* unused */) { (*i)++; }
189
68 bool has_mhtml_callback_run() const { return has_mhtml_callback_run_; } 190 bool has_mhtml_callback_run() const { return has_mhtml_callback_run_; }
69 int64_t file_size() const { return file_size_; } 191 int64_t file_size() const { return file_size_; }
70 192
71 base::ScopedTempDir temp_dir_; 193 base::ScopedTempDir temp_dir_;
72 194
73 private: 195 private:
74 void MHTMLGenerated(base::Closure quit_closure, int64_t size) { 196 void MHTMLGenerated(base::Closure quit_closure, int64_t size) {
75 has_mhtml_callback_run_ = true; 197 has_mhtml_callback_run_ = true;
76 file_size_ = size; 198 file_size_ = size;
77 quit_closure.Run(); 199 quit_closure.Run();
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
192 // We expect that there was an error (file size -1 indicates an error.) 314 // We expect that there was an error (file size -1 indicates an error.)
193 EXPECT_EQ(-1, file_size()); 315 EXPECT_EQ(-1, file_size());
194 316
195 std::string mhtml; 317 std::string mhtml;
196 ASSERT_TRUE(base::ReadFileToString(path, &mhtml)); 318 ASSERT_TRUE(base::ReadFileToString(path, &mhtml));
197 319
198 // Make sure the contents are missing. 320 // Make sure the contents are missing.
199 EXPECT_THAT(mhtml, Not(HasSubstr("test body"))); 321 EXPECT_THAT(mhtml, Not(HasSubstr("test body")));
200 } 322 }
201 323
324 IN_PROC_BROWSER_TEST_F(MHTMLGenerationTest,
325 GenerateMHTMLIgnoreNoStoreSubFrame) {
326 base::FilePath path(temp_dir_.path());
327 path = path.Append(FILE_PATH_LITERAL("test.mht"));
328
329 GURL url(embedded_test_server()->GetURL("/page_with_nostore_iframe.html"));
330
331 // Generate MHTML, specifying the FAIL_FOR_NO_STORE_MAIN_FRAME policy.
332 MHTMLGenerationParams params(path);
333 params.cache_control_policy =
334 content::MHTMLCacheControlPolicy::FAIL_FOR_NO_STORE_MAIN_FRAME;
335
336 GenerateMHTML(params, url);
337 // We expect that there was no error (file size -1 indicates an error.)
338 EXPECT_LT(0, file_size());
339
340 std::string mhtml;
341 ASSERT_TRUE(base::ReadFileToString(path, &mhtml));
342
343 EXPECT_THAT(mhtml, HasSubstr("Main Frame"));
344 // Make sure that no-store subresources exist in this mode.
345 EXPECT_THAT(mhtml, HasSubstr("no-store test body"));
346 EXPECT_THAT(mhtml, ContainsRegex("Content-Location:.*nostore.jpg"));
347 }
348
349 IN_PROC_BROWSER_TEST_F(MHTMLGenerationTest, GenerateMHTMLObeyNoStoreSubFrame) {
350 base::FilePath path(temp_dir_.path());
351 path = path.Append(FILE_PATH_LITERAL("test.mht"));
352
353 GURL url(embedded_test_server()->GetURL("/page_with_nostore_iframe.html"));
354
355 // Generate MHTML, specifying the FAIL_FOR_NO_STORE_MAIN_FRAME policy.
356 MHTMLGenerationParams params(path);
357 params.cache_control_policy = content::MHTMLCacheControlPolicy::
358 SKIP_ANY_FRAME_OR_RESOURCE_MARKED_NO_STORE;
359
360 GenerateMHTML(params, url);
361 // We expect that there was no error (file size -1 indicates an error.)
362 EXPECT_LT(0, file_size());
363
364 std::string mhtml;
365 ASSERT_TRUE(base::ReadFileToString(path, &mhtml));
366
367 EXPECT_THAT(mhtml, HasSubstr("Main Frame"));
368 // Make sure the contents are missing.
369 EXPECT_THAT(mhtml, Not(HasSubstr("no-store test body")));
370 // This image comes from a resource marked no-store.
371 EXPECT_THAT(mhtml, Not(ContainsRegex("Content-Location:.*nostore.jpg")));
372 }
373
374 IN_PROC_BROWSER_TEST_F(
375 MHTMLGenerationTest,
376 ViewedMHTMLContainsNoStoreContentIfNoCacheControlPolicy) {
377 // Generate MHTML, specifying the FAIL_FOR_NO_STORE_MAIN_FRAME policy.
378 base::FilePath path(temp_dir_.path());
379 path = path.Append(FILE_PATH_LITERAL("test.mht"));
380 MHTMLGenerationParams params(path);
381
382 // No special cache control options so we should see both frames.
383 std::vector<std::string> expectations{
384 "Main Frame, normal headers.", "Cache-Control: no-store test body",
385 };
386 std::vector<std::string> forbidden;
387 TestOriginalVsSavedPage(
388 embedded_test_server()->GetURL("/page_with_nostore_iframe.html"), params,
389 2 /* expected number of frames */, expectations, forbidden);
390
391 std::string mhtml;
392 ASSERT_TRUE(base::ReadFileToString(params.file_path, &mhtml));
393 }
394
395 IN_PROC_BROWSER_TEST_F(MHTMLGenerationTest,
396 ViewedMHTMLDoesNotContainNoStoreContent) {
397 // Generate MHTML, specifying the FAIL_FOR_NO_STORE_MAIN_FRAME policy.
398 base::FilePath path(temp_dir_.path());
399 path = path.Append(FILE_PATH_LITERAL("test.mht"));
400 MHTMLGenerationParams params(path);
401 params.cache_control_policy = content::MHTMLCacheControlPolicy::
402 SKIP_ANY_FRAME_OR_RESOURCE_MARKED_NO_STORE;
403
404 // No special cache control options so we should see both frames.
405 std::vector<std::string> expectations{
406 "Main Frame, normal headers.",
407 };
408 std::vector<std::string> forbidden{
409 "Cache-Control: no-store test body",
410 };
411 TestOriginalVsSavedPage(
412 embedded_test_server()->GetURL("/page_with_nostore_iframe.html"), params,
413 2 /* expected number of frames */, expectations, forbidden);
414
415 std::string mhtml;
416 ASSERT_TRUE(base::ReadFileToString(params.file_path, &mhtml));
417 }
418
202 // Test suite that allows testing --site-per-process against cross-site frames. 419 // Test suite that allows testing --site-per-process against cross-site frames.
203 // See http://dev.chromium.org/developers/design-documents/site-isolation. 420 // See http://dev.chromium.org/developers/design-documents/site-isolation.
204 class MHTMLGenerationSitePerProcessTest : public MHTMLGenerationTest { 421 class MHTMLGenerationSitePerProcessTest : public MHTMLGenerationTest {
205 public: 422 public:
206 MHTMLGenerationSitePerProcessTest() {} 423 MHTMLGenerationSitePerProcessTest() {}
207 424
208 protected: 425 protected:
209 void SetUpCommandLine(base::CommandLine* command_line) override { 426 void SetUpCommandLine(base::CommandLine* command_line) override {
210 MHTMLGenerationTest::SetUpCommandLine(command_line); 427 MHTMLGenerationTest::SetUpCommandLine(command_line);
211 428
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
244 461
245 // Make sure that URLs of both frames are present 462 // Make sure that URLs of both frames are present
246 // (note that these are single-line regexes). 463 // (note that these are single-line regexes).
247 EXPECT_THAT( 464 EXPECT_THAT(
248 mhtml, 465 mhtml,
249 ContainsRegex("Content-Location:.*/frame_tree/page_with_one_frame.html")); 466 ContainsRegex("Content-Location:.*/frame_tree/page_with_one_frame.html"));
250 EXPECT_THAT(mhtml, ContainsRegex("Content-Location:.*/title1.html")); 467 EXPECT_THAT(mhtml, ContainsRegex("Content-Location:.*/title1.html"));
251 } 468 }
252 469
253 } // namespace content 470 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698