blob: 5f10996501b76cdd76e99f9181a9bbe74bcee735 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/escape.h"
#include "base/test/bind.h"
#include "chrome/browser/plugins/chrome_plugin_service_filter.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/pdf_util.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/plugin_service.h"
#include "content/public/browser/plugin_service_filter.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/prerender_test_util.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "url/gurl.h"
using content::JsReplace;
using content::RenderFrameHost;
using content::TestNavigationManager;
using content::WebContents;
using content::test::PrerenderHostObserver;
using content::test::PrerenderHostRegistryObserver;
// We'll use this to block the PDF plugin from loading to force the HTML
// fallback in the NavigationThrottle.
class BlockAllPluginServiceFilter : public content::PluginServiceFilter {
public:
bool IsPluginAvailable(content::BrowserContext* browser_context,
const content::WebPluginInfo& plugin) override {
return false;
}
bool CanLoadPlugin(int render_process_id,
const base::FilePath& path) override {
return false;
}
};
class PDFIFrameNavigationThrottleBrowserTest : public InProcessBrowserTest {
public:
PDFIFrameNavigationThrottleBrowserTest()
: prerender_helper_(base::BindRepeating(
&PDFIFrameNavigationThrottleBrowserTest::web_contents,
base::Unretained(this))) {}
content::WebContents* web_contents() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
void SetUp() override {
prerender_helper_.SetUp(embedded_test_server());
InProcessBrowserTest::SetUp();
}
void SetUpOnMainThread() override {
InProcessBrowserTest::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(embedded_test_server()->Start());
content::PluginService* plugin_service =
content::PluginService::GetInstance();
plugin_service->Init();
old_plugin_service_filter_ = plugin_service->GetFilter();
plugin_service->SetFilter(&block_all_plugins_);
}
void TearDownOnMainThread() override {
ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
content::PluginService::GetInstance()->SetFilter(
old_plugin_service_filter_);
InProcessBrowserTest::TearDownOnMainThread();
}
protected:
content::test::PrerenderTestHelper prerender_helper_;
raw_ptr<content::PluginServiceFilter> old_plugin_service_filter_;
BlockAllPluginServiceFilter block_all_plugins_;
};
// TODO(1205920): The PDF viewer cannot currently be prerendered correctly. Once
// this is supported, this test should be re-enabled.
// This test checks that the throttle is able to navigate the iframe'd PDF to
// the fallback HTML content even while it is prerendering.
IN_PROC_BROWSER_TEST_F(PDFIFrameNavigationThrottleBrowserTest,
DISABLED_HTMLFallbackInPrerender) {
const GURL kUrl(embedded_test_server()->GetURL("/empty.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kUrl));
const GURL kPrerenderUrl =
embedded_test_server()->GetURL("/pdf/test-iframe.html");
const GURL kPdfUrl =
embedded_test_server()->GetURL("/pdf/test-bookmarks.pdf");
const std::string html = GetPDFPlaceholderHTML(kPdfUrl);
const GURL kFallbackPdfUrl("data:text/html," + base::EscapePath(html));
TestNavigationManager pdf_navigation(web_contents(), kFallbackPdfUrl);
// Trigger a prerender of a page containing an iframe with a pdf file.
{
PrerenderHostRegistryObserver registry_observer(*web_contents());
prerender_helper_.AddPrerenderAsync(kPrerenderUrl);
registry_observer.WaitForTrigger(kPrerenderUrl);
}
// The PDFIFrameNavigationThrottle will cancel the navigation to the pdf file
// and navigate the frame anew to a fallback data: URL. Since its in a
// prerendering frame tree, and data: URLs are cross-origin with the main
// frame, we expect the navigation to be deferred during WillStartRequest
// until the prerender is activated.
{
ASSERT_TRUE(pdf_navigation.WaitForFirstYieldAfterDidStartNavigation());
EXPECT_FALSE(pdf_navigation.GetNavigationHandle()->HasCommitted());
EXPECT_TRUE(pdf_navigation.GetNavigationHandle()->IsDeferredForTesting());
}
// Now navigate the primary page to the prerendered URL so that we activate
// the prerender.
{
PrerenderHostObserver prerender_observer(*web_contents(), kPrerenderUrl);
ASSERT_TRUE(ExecJs(web_contents()->GetPrimaryMainFrame(),
JsReplace("location = $1", kPrerenderUrl)));
prerender_observer.WaitForActivation();
}
// Now that we're activated, the fallback navigation should be able to
// finish. The initial PDF navigation should be cancelled by the throttle and
// fallback content loaded in its place.
{
ASSERT_TRUE(pdf_navigation.WaitForNavigationFinished());
EXPECT_TRUE(pdf_navigation.was_committed());
EXPECT_TRUE(pdf_navigation.was_successful());
content::RenderFrameHost* child_frame =
ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 0);
ASSERT_TRUE(child_frame);
EXPECT_EQ(child_frame->GetLastCommittedURL(), kFallbackPdfUrl);
}
}