blob: 4594e3415ad5b0ffb8fad5b5ffed5bef6a9d0f60 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include "base/auto_reset.h"
#include "base/run_loop.h"
#include "chrome/browser/printing/print_view_manager_common.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/prefs/pref_service.h"
#include "components/printing/browser/print_composite_client.h"
#include "components/printing/common/print_messages.h"
#include "content/public/browser/browser_message_filter.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/test/browser_test_utils.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
namespace printing {
namespace {
static constexpr int kDefaultDocumentCookie = 1234;
class PrintPreviewObserver : PrintPreviewUI::TestingDelegate {
public:
PrintPreviewObserver() { PrintPreviewUI::SetDelegateForTesting(this); }
~PrintPreviewObserver() { PrintPreviewUI::SetDelegateForTesting(nullptr); }
void WaitUntilPreviewIsReady() {
if (rendered_page_count_ >= total_page_count_)
return;
base::RunLoop run_loop;
base::AutoReset<base::RunLoop*> auto_reset(&run_loop_, &run_loop);
run_loop.Run();
}
private:
// PrintPreviewUI::TestingDelegate implementation.
void DidGetPreviewPageCount(int page_count) override {
total_page_count_ = page_count;
}
// PrintPreviewUI::TestingDelegate implementation.
void DidRenderPreviewPage(content::WebContents* preview_dialog) override {
++rendered_page_count_;
CHECK(rendered_page_count_ <= total_page_count_);
if (rendered_page_count_ == total_page_count_ && run_loop_) {
run_loop_->Quit();
}
}
int total_page_count_ = 1;
int rendered_page_count_ = 0;
base::RunLoop* run_loop_ = nullptr;
};
class TestPrintFrameContentMsgFilter : public content::BrowserMessageFilter {
public:
TestPrintFrameContentMsgFilter(int document_cookie,
base::RepeatingClosure msg_callback)
: content::BrowserMessageFilter(PrintMsgStart),
document_cookie_(document_cookie),
task_runner_(base::SequencedTaskRunnerHandle::Get()),
msg_callback_(msg_callback) {}
bool OnMessageReceived(const IPC::Message& message) override {
// Only expect PrintHostMsg_DidPrintFrameContent message.
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(TestPrintFrameContentMsgFilter, message)
IPC_MESSAGE_HANDLER(PrintHostMsg_DidPrintFrameContent, CheckMessage)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
EXPECT_TRUE(handled);
task_runner_->PostTask(FROM_HERE, msg_callback_);
return true;
}
private:
~TestPrintFrameContentMsgFilter() override {}
void CheckMessage(int document_cookie,
const PrintHostMsg_DidPrintContent_Params& param) {
EXPECT_EQ(document_cookie, document_cookie_);
EXPECT_TRUE(param.metafile_data_handle.IsValid());
EXPECT_GT(param.data_size, 0u);
}
const int document_cookie_;
scoped_refptr<base::SequencedTaskRunner> task_runner_;
base::RepeatingClosure msg_callback_;
};
} // namespace
class PrintBrowserTest : public InProcessBrowserTest {
public:
PrintBrowserTest() {}
~PrintBrowserTest() override {}
void SetUp() override {
num_expected_messages_ = 1; // By default, only wait on one message.
num_received_messages_ = 0;
run_loop_.reset();
InProcessBrowserTest::SetUp();
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
}
void PrintAndWaitUntilPreviewIsReady(bool print_only_selection) {
PrintPreviewObserver print_preview_observer;
printing::StartPrint(browser()->tab_strip_model()->GetActiveWebContents(),
/*print_preview_disabled=*/false,
print_only_selection);
print_preview_observer.WaitUntilPreviewIsReady();
}
// The following are helper functions for having a wait loop in the test and
// exit when all expected messages are received.
void SetNumExpectedMessages(unsigned int num) {
num_expected_messages_ = num;
}
void WaitUntilMessagesReceived() {
run_loop_ = std::make_unique<base::RunLoop>();
run_loop_->Run();
}
void CheckForQuit() {
if (++num_received_messages_ == num_expected_messages_) {
run_loop_->QuitWhenIdle();
}
}
void AddFilterForFrame(content::RenderFrameHost* frame_host) {
auto filter = base::MakeRefCounted<TestPrintFrameContentMsgFilter>(
kDefaultDocumentCookie,
base::BindRepeating(&PrintBrowserTest::CheckForQuit,
base::Unretained(this)));
frame_host->GetProcess()->AddFilter(filter.get());
}
PrintMsg_PrintFrame_Params GetDefaultPrintParams() {
PrintMsg_PrintFrame_Params params;
gfx::Rect area(800, 600);
params.printable_area = area;
params.document_cookie = kDefaultDocumentCookie;
return params;
}
private:
unsigned int num_expected_messages_;
unsigned int num_received_messages_;
std::unique_ptr<base::RunLoop> run_loop_;
};
// Printing only a selection containing iframes is partially supported.
// Iframes aren't currently displayed. This test passes whenever the print
// preview is rendered (i.e. no timeout in the test).
// This test shouldn't crash. See https://crbug.com/732780.
IN_PROC_BROWSER_TEST_F(PrintBrowserTest, SelectionContainsIframe) {
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/selection_iframe.html"));
ui_test_utils::NavigateToURL(browser(), url);
PrintAndWaitUntilPreviewIsReady(/*print_only_selection=*/true);
}
// Printing frame content for the main frame of a generic webpage.
// This test passes when the printed result is sent back and checked in
// TestPrintFrameContentMsgFilter::CheckMessage().
IN_PROC_BROWSER_TEST_F(PrintBrowserTest, PrintFrameContent) {
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/test1.html"));
ui_test_utils::NavigateToURL(browser(), url);
content::WebContents* original_contents =
browser()->tab_strip_model()->GetActiveWebContents();
content::RenderFrameHost* rfh = original_contents->GetMainFrame();
AddFilterForFrame(rfh);
rfh->Send(new PrintMsg_PrintFrameContent(rfh->GetRoutingID(),
GetDefaultPrintParams()));
// The printed result will be received and checked in
// TestPrintFrameContentMsgFilter.
WaitUntilMessagesReceived();
}
// Printing frame content for a cross-site iframe.
// This test passes when the iframe responds to the print message.
// The response is checked in TestPrintFrameContentMsgFilter::CheckMessage().
IN_PROC_BROWSER_TEST_F(PrintBrowserTest, PrintSubframeContent) {
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(
embedded_test_server()->GetURL("/printing/content_with_iframe.html"));
ui_test_utils::NavigateToURL(browser(), url);
content::WebContents* original_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_EQ(2u, original_contents->GetAllFrames().size());
content::RenderFrameHost* test_frame = original_contents->GetAllFrames()[1];
ASSERT_TRUE(test_frame);
AddFilterForFrame(test_frame);
test_frame->Send(new PrintMsg_PrintFrameContent(test_frame->GetRoutingID(),
GetDefaultPrintParams()));
// The printed result will be received and checked in
// TestPrintFrameContentMsgFilter.
WaitUntilMessagesReceived();
}
// TODO(weili): Enabled the following tests when the render side change is
// completed.
// Printing frame content with a cross-site iframe which also has a cross-site
// iframe. The site reference chain is a.com --> b.com --> c.com.
// This test passes when both cross-site frames are printed and their
// responses which are checked in
// TestPrintFrameContentMsgFilter::CheckMessage().
IN_PROC_BROWSER_TEST_F(PrintBrowserTest, DISABLED_PrintSubframeChain) {
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL(
"/printing/content_with_iframe_chain.html"));
ui_test_utils::NavigateToURL(browser(), url);
content::WebContents* original_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_EQ(3u, original_contents->GetAllFrames().size());
// Create composite client so subframe print message can be forwarded.
PrintCompositeClient::CreateForWebContents(original_contents);
content::RenderFrameHost* main_frame = original_contents->GetMainFrame();
content::RenderFrameHost* child_frame = content::ChildFrameAt(main_frame, 0);
ASSERT_TRUE(child_frame);
ASSERT_NE(child_frame, main_frame);
bool oopif_enabled = child_frame->GetProcess() != main_frame->GetProcess();
content::RenderFrameHost* grandchild_frame =
content::ChildFrameAt(child_frame, 0);
ASSERT_TRUE(grandchild_frame);
ASSERT_NE(grandchild_frame, child_frame);
if (oopif_enabled) {
ASSERT_NE(grandchild_frame->GetProcess(), child_frame->GetProcess());
ASSERT_NE(grandchild_frame->GetProcess(), main_frame->GetProcess());
}
AddFilterForFrame(main_frame);
if (oopif_enabled) {
AddFilterForFrame(child_frame);
AddFilterForFrame(grandchild_frame);
}
main_frame->Send(new PrintMsg_PrintFrameContent(main_frame->GetRoutingID(),
GetDefaultPrintParams()));
// The printed result will be received and checked in
// TestPrintFrameContentMsgFilter.
SetNumExpectedMessages(oopif_enabled ? 3 : 1);
WaitUntilMessagesReceived();
}
// Printing frame content with a cross-site iframe who also has a cross site
// iframe, but this iframe resides in the same site as the main frame.
// The site reference loop is a.com --> b.com --> a.com.
// This test passes when both cross-site frames are printed and send back
// responses which are checked in
// TestPrintFrameContentMsgFilter::CheckMessage().
IN_PROC_BROWSER_TEST_F(PrintBrowserTest, DISABLED_PrintSubframeABA) {
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL(
"a.com", "/printing/content_with_iframe_loop.html"));
ui_test_utils::NavigateToURL(browser(), url);
content::WebContents* original_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_EQ(3u, original_contents->GetAllFrames().size());
// Create composite client so subframe print message can be forwarded.
PrintCompositeClient::CreateForWebContents(original_contents);
content::RenderFrameHost* main_frame = original_contents->GetMainFrame();
content::RenderFrameHost* child_frame = content::ChildFrameAt(main_frame, 0);
ASSERT_TRUE(child_frame);
ASSERT_NE(child_frame, main_frame);
bool oopif_enabled = main_frame->GetProcess() != child_frame->GetProcess();
content::RenderFrameHost* grandchild_frame =
content::ChildFrameAt(child_frame, 0);
ASSERT_TRUE(grandchild_frame);
ASSERT_NE(grandchild_frame, child_frame);
// |grandchild_frame| is in the same site as |frame|, so whether OOPIF is
// enabled, they will be in the same process.
ASSERT_EQ(grandchild_frame->GetProcess(), main_frame->GetProcess());
AddFilterForFrame(main_frame);
if (oopif_enabled)
AddFilterForFrame(child_frame);
main_frame->Send(new PrintMsg_PrintFrameContent(main_frame->GetRoutingID(),
GetDefaultPrintParams()));
// The printed result will be received and checked in
// TestPrintFrameContentMsgFilter.
SetNumExpectedMessages(oopif_enabled ? 3 : 1);
WaitUntilMessagesReceived();
}
} // namespace printing