Index: content/browser/download/mhtml_generation_browsertest.cc |
diff --git a/content/browser/download/mhtml_generation_browsertest.cc b/content/browser/download/mhtml_generation_browsertest.cc |
index 1a71a9e50db3a2bce64d885faf1b41c85c6ed4ec..cf3f158a03e4323ead03629787da6a09eac9e228 100644 |
--- a/content/browser/download/mhtml_generation_browsertest.cc |
+++ b/content/browser/download/mhtml_generation_browsertest.cc |
@@ -15,10 +15,11 @@ |
#include "base/strings/utf_string_conversions.h" |
#include "base/test/histogram_tester.h" |
#include "base/threading/thread_restrictions.h" |
+#include "content/browser/frame_host/frame_tree_node.h" |
#include "content/browser/renderer_host/render_process_host_impl.h" |
+#include "content/browser/web_contents/web_contents_impl.h" |
#include "content/common/frame_messages.h" |
#include "content/public/browser/render_process_host.h" |
-#include "content/public/browser/web_contents.h" |
#include "content/public/common/mhtml_generation_params.h" |
#include "content/public/test/browser_test_utils.h" |
#include "content/public/test/content_browser_test.h" |
@@ -107,7 +108,7 @@ class MHTMLGenerationTest : public ContentBrowserTest { |
} |
void GenerateMHTML(const MHTMLGenerationParams& params, const GURL& url) { |
- NavigateToURL(shell(), url); |
+ ASSERT_TRUE(NavigateToURL(shell(), url)); |
GenerateMHTMLForCurrentPage(params); |
} |
@@ -144,7 +145,7 @@ class MHTMLGenerationTest : public ContentBrowserTest { |
// are met (this is mostly a sanity check - a failure to meet |
// expectations would probably mean that there is a test bug |
// (i.e. that we got called with wrong expected_foo argument). |
- NavigateToURL(shell(), url); |
+ ASSERT_TRUE(NavigateToURL(shell(), url)); |
if (!skip_verification_of_original_page) { |
AssertExpectationsAboutCurrentTab(expected_number_of_frames, |
expected_substrings, |
@@ -162,7 +163,8 @@ class MHTMLGenerationTest : public ContentBrowserTest { |
// met (i.e. if the same expectations are met for "after" |
// [saved version of the page] as for the "before" |
// [the original version of the page]. |
- NavigateToURL(shell(), net::FilePathToFileURL(params.file_path)); |
+ ASSERT_TRUE( |
+ NavigateToURL(shell(), net::FilePathToFileURL(params.file_path))); |
AssertExpectationsAboutCurrentTab(expected_number_of_frames, |
expected_substrings, |
forbidden_substrings_in_saved_page); |
@@ -222,6 +224,8 @@ IN_PROC_BROWSER_TEST_F(MHTMLGenerationTest, GenerateMHTML) { |
path = path.Append(FILE_PATH_LITERAL("test.mht")); |
GenerateMHTML(path, embedded_test_server()->GetURL("/simple_page.html")); |
+ |
+ // Checks that no other test failure was detected so far. |
ASSERT_FALSE(HasFailure()); |
// Make sure the actual generated file has some contents. |
@@ -242,6 +246,120 @@ IN_PROC_BROWSER_TEST_F(MHTMLGenerationTest, GenerateMHTML) { |
static_cast<int>(MhtmlSaveStatus::SUCCESS), 1); |
} |
+// Removes all children of the root tree node once the first MHTML serialization |
+// response (regarding the main frame) is received from the renderer but before |
+// that message is actually handled by the browser UI thread. |
+class ChildFrameRemoverFilter : public BrowserMessageFilter { |
+ public: |
+ ChildFrameRemoverFilter(WebContentsImpl* web_contents) |
+ : BrowserMessageFilter(FrameMsgStart), web_contents_(web_contents) { |
+ DCHECK(web_contents_); |
+ } |
+ |
+ protected: |
+ ~ChildFrameRemoverFilter() override {} |
+ |
+ private: |
+ bool OnMessageReceived(const IPC::Message& message) override { |
+ DCHECK_CURRENTLY_ON(BrowserThread::IO); |
+ if (message.type() == FrameHostMsg_SerializeAsMHTMLResponse::ID) { |
+ if (!already_received_response_) { |
+ already_received_response_ = true; |
+ BrowserThread::PostTask( |
+ BrowserThread::UI, FROM_HERE, |
+ base::Bind(&ChildFrameRemoverFilter::RemoveAllChildFrames, |
+ base::Unretained(this))); |
+ } else { |
+ ADD_FAILURE() |
+ << "Should not receive another MHTML serialization response"; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ void RemoveAllChildFrames() { |
+ DCHECK_CURRENTLY_ON(BrowserThread::UI); |
+ int tree_node_count = 1; |
+ for (auto tree_node : web_contents_->GetFrameTree()->Nodes()) |
+ tree_node_count += tree_node->child_count(); |
+ // Note: at least 2 children nodes are needed to test all code paths. |
+ ASSERT_GE(tree_node_count, 3); |
+ FrameTreeNode* root = web_contents_->GetFrameTree()->root(); |
+ while (root->child_count()) |
+ root->RemoveChild(root->child_at(0)); |
+ ASSERT_EQ(0u, root->child_count()); |
+ } |
+ |
+ WebContentsImpl* web_contents_; |
+ bool already_received_response_ = false; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ChildFrameRemoverFilter); |
+}; |
+ |
+// Tests that if child frames are removed while saving a MHTML page the |
+// operation fails by default. |
+IN_PROC_BROWSER_TEST_F(MHTMLGenerationTest, FailDueToMissingIframe) { |
+ ASSERT_TRUE(NavigateToURL( |
+ shell(), embedded_test_server()->GetURL("/site_per_process_main.html"))); |
+ |
+ scoped_refptr<BrowserMessageFilter> filter = new ChildFrameRemoverFilter( |
+ static_cast<WebContentsImpl*>(shell()->web_contents())); |
+ shell()->web_contents()->GetRenderProcessHost()->AddFilter(filter.get()); |
+ |
+ base::FilePath path(temp_dir_.GetPath()); |
+ path = path.Append(FILE_PATH_LITERAL("test.mht")); |
+ |
+ MHTMLGenerationParams params(path); |
+ |
+ // Verifies the default is to not ignore missing iframes. |
+ ASSERT_FALSE(params.ignore_missing_frames); |
+ |
+ GenerateMHTMLForCurrentPage(params); |
+ |
+ // Checks that no other test failure was detected so far. |
+ ASSERT_FALSE(HasFailure()); |
+ |
+ // Verify the size reported by the callback is negative (an error happened). |
+ EXPECT_LT(file_size(), 0); |
+ |
+ // Checks that the final status reported to UMA is correct. |
+ histogram_tester()->ExpectUniqueSample( |
+ "PageSerialization.MhtmlGeneration.FinalSaveStatus", |
+ static_cast<int>(MhtmlSaveStatus::FRAME_NO_LONGER_EXISTS), 1); |
+} |
+ |
+// Tests that a MHTML save operation does not fail when frames are removed if |
+// the caller configures it to ignore missing frames. |
+IN_PROC_BROWSER_TEST_F(MHTMLGenerationTest, SucceedIgnoringMissingIframe) { |
+ ASSERT_TRUE(NavigateToURL( |
+ shell(), embedded_test_server()->GetURL("/site_per_process_main.html"))); |
+ |
+ scoped_refptr<BrowserMessageFilter> filter = new ChildFrameRemoverFilter( |
+ static_cast<WebContentsImpl*>(shell()->web_contents())); |
+ shell()->web_contents()->GetRenderProcessHost()->AddFilter(filter.get()); |
+ |
+ base::FilePath path(temp_dir_.GetPath()); |
+ path = path.Append(FILE_PATH_LITERAL("test.mht")); |
+ |
+ MHTMLGenerationParams params(path); |
+ |
+ // Enables the ignoring of missing iframes. |
+ params.ignore_missing_frames = true; |
+ |
+ GenerateMHTMLForCurrentPage(params); |
+ |
+ // Checks that no other test failure was detected so far. |
+ ASSERT_FALSE(HasFailure()); |
+ |
+ // Verify the size reported by the callback is positive. |
+ EXPECT_GT(file_size(), 0); |
+ |
+ // Checks that the final status reported to UMA is correct. |
+ histogram_tester()->ExpectUniqueSample( |
+ "PageSerialization.MhtmlGeneration.FinalSaveStatus", |
+ static_cast<int>(MhtmlSaveStatus::SUCCESS), 1); |
+} |
+ |
class GenerateMHTMLAndExitRendererMessageFilter : public BrowserMessageFilter { |
public: |
GenerateMHTMLAndExitRendererMessageFilter( |
@@ -254,6 +372,7 @@ class GenerateMHTMLAndExitRendererMessageFilter : public BrowserMessageFilter { |
private: |
bool OnMessageReceived(const IPC::Message& message) override { |
+ DCHECK_CURRENTLY_ON(BrowserThread::IO); |
if (message.type() == FrameHostMsg_SerializeAsMHTMLResponse::ID) { |
// After |return false| below, this IPC message will be handled by the |
// product code as illustrated below. (1), (2), (3) depict points in time |
@@ -317,6 +436,7 @@ class GenerateMHTMLAndExitRendererMessageFilter : public BrowserMessageFilter { |
}; |
void TaskX() { |
+ DCHECK_CURRENTLY_ON(BrowserThread::UI); |
BrowserThread::PostTask( |
BrowserThread::FILE, FROM_HERE, base::Bind( |
&GenerateMHTMLAndExitRendererMessageFilter::TaskY, |
@@ -324,6 +444,7 @@ class GenerateMHTMLAndExitRendererMessageFilter : public BrowserMessageFilter { |
} |
void TaskY() { |
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
BrowserThread::PostTask( |
BrowserThread::UI, FROM_HERE, base::Bind( |
&GenerateMHTMLAndExitRendererMessageFilter::TaskZ, |
@@ -331,6 +452,7 @@ class GenerateMHTMLAndExitRendererMessageFilter : public BrowserMessageFilter { |
} |
void TaskZ() { |
+ DCHECK_CURRENTLY_ON(BrowserThread::UI); |
render_process_host_->FastShutdownIfPossible(); |
} |
@@ -341,7 +463,8 @@ class GenerateMHTMLAndExitRendererMessageFilter : public BrowserMessageFilter { |
// Regression test for the crash/race from https://crbug.com/612098. |
IN_PROC_BROWSER_TEST_F(MHTMLGenerationTest, GenerateMHTMLAndExitRenderer) { |
- NavigateToURL(shell(), embedded_test_server()->GetURL("/simple_page.html")); |
+ ASSERT_TRUE(NavigateToURL( |
+ shell(), embedded_test_server()->GetURL("/simple_page.html"))); |
RenderProcessHostImpl* render_process_host = |
static_cast<RenderProcessHostImpl*>( |