blob: 81bab80c38b23628abbae549277c4b42ec42263e [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/pdf/pdf_extension_test_base.h"
#include <string>
#include <vector>
#include "base/path_service.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/pdf/pdf_extension_test_util.h"
#include "chrome/browser/pdf/pdf_frame_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/guest_view/browser/guest_view_manager.h"
#include "components/guest_view/browser/guest_view_manager_delegate.h"
#include "components/guest_view/browser/test_guest_view_manager.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/hit_test_region_observer.h"
#include "extensions/browser/api/extensions_api_client.h"
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/input/web_mouse_event.h"
#include "ui/gfx/geometry/point.h"
using ::content::WebContents;
using ::extensions::ExtensionsAPIClient;
using ::extensions::MimeHandlerViewGuest;
using ::guest_view::GuestViewManager;
using ::guest_view::TestGuestViewManager;
using ::pdf_extension_test_util::GetOnlyMimeHandlerView;
PDFExtensionTestBase::PDFExtensionTestBase() {
GuestViewManager::set_factory_for_testing(&factory_);
}
void PDFExtensionTestBase::SetUpCommandLine(
base::CommandLine* /*command_line*/) {
feature_list_.InitWithFeatures(GetEnabledFeatures(), GetDisabledFeatures());
}
void PDFExtensionTestBase::SetUpOnMainThread() {
extensions::ExtensionApiTest::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
content::SetupCrossSiteRedirector(embedded_test_server());
embedded_test_server()->StartAcceptingConnections();
}
void PDFExtensionTestBase::TearDownOnMainThread() {
ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
extensions::ExtensionApiTest::TearDownOnMainThread();
}
// Serve paths prefixed with _test_resources/ from chrome/test/data.
base::FilePath PDFExtensionTestBase::GetTestResourcesParentDir() {
base::FilePath test_root_path;
base::PathService::Get(chrome::DIR_TEST_DATA, &test_root_path);
return test_root_path;
}
bool PDFExtensionTestBase::PdfIsExpectedToLoad(const std::string& pdf_file) {
const char* const kFailingPdfs[] = {
// clang-format off
"pdf/test-ranges.pdf",
"pdf_private/accessibility_crash_1.pdf",
"pdf_private/cfuzz5.pdf",
"pdf_private/js.pdf",
"pdf_private/segv-ecx.pdf",
"pdf_private/tests.pdf",
// clang-format on
};
for (const char* failing_pdf : kFailingPdfs) {
if (failing_pdf == pdf_file) {
return false;
}
}
return true;
}
// Load the PDF at the given URL and ensure it has finished loading. Return
// true if it loads successfully or false if it fails. If it doesn't finish
// loading the test will hang. This is done from outside of the BrowserPlugin
// guest to ensure sending messages to/from the plugin works correctly from
// there, since the PdfScriptingApi relies on doing this as well.
testing::AssertionResult PDFExtensionTestBase::LoadPdf(const GURL& url) {
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
WebContents* web_contents = GetActiveWebContents();
return pdf_extension_test_util::EnsurePDFHasLoaded(web_contents);
}
// Same as LoadPDF(), but loads into a new tab.
testing::AssertionResult PDFExtensionTestBase::LoadPdfInNewTab(
const GURL& url) {
ui_test_utils::NavigateToURLWithDisposition(
browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
WebContents* web_contents = GetActiveWebContents();
return pdf_extension_test_util::EnsurePDFHasLoaded(web_contents);
}
// Same as LoadPdf(), but also returns a pointer to the `MimeHandlerViewGuest`
// for the loaded PDF. Returns nullptr if the load fails.
MimeHandlerViewGuest* PDFExtensionTestBase::LoadPdfGetMimeHandlerView(
const GURL& url) {
if (!LoadPdf(url)) {
return nullptr;
}
return GetOnlyMimeHandlerView(GetActiveWebContents());
}
// Same as LoadPdf(), but also returns a pointer to the `MimeHandlerViewGuest`
// for the loaded PDF in a new tab. Returns nullptr if the load fails.
MimeHandlerViewGuest* PDFExtensionTestBase::LoadPdfInNewTabGetMimeHandlerView(
const GURL& url) {
if (!LoadPdfInNewTab(url)) {
return nullptr;
}
return GetOnlyMimeHandlerView(GetActiveWebContents());
}
void PDFExtensionTestBase::TestGetSelectedTextReply(const GURL& url,
bool expect_success) {
MimeHandlerViewGuest* guest = LoadPdfGetMimeHandlerView(url);
ASSERT_TRUE(guest);
// Reach into the guest and hook into it such that it posts back a 'flush'
// message after every getSelectedTextReply message sent.
ASSERT_TRUE(
content::ExecuteScript(guest->GetGuestMainFrame(),
"viewer.overrideSendScriptingMessageForTest();"));
// Add an event listener for flush messages and request the selected text.
// If we get a flush message without receiving getSelectedText we know that
// the message didn't come through.
bool success = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
GetActiveWebContents(),
"window.addEventListener('message', function(event) {"
" if (event.data == 'flush')"
" window.domAutomationController.send(false);"
" if (event.data.type == 'getSelectedTextReply')"
" window.domAutomationController.send(true);"
"});"
"document.getElementsByTagName('embed')[0].postMessage("
" {type: 'getSelectedText'});",
&success));
ASSERT_EQ(expect_success, success);
}
WebContents* PDFExtensionTestBase::GetActiveWebContents() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
TestGuestViewManager* PDFExtensionTestBase::GetGuestViewManager(
content::BrowserContext* profile) {
if (!profile) {
profile = browser()->profile();
}
// TODO(wjmaclean): Re-implement FromBrowserContext in the
// TestGuestViewManager class to avoid all callers needing this cast.
auto* manager = static_cast<TestGuestViewManager*>(
TestGuestViewManager::FromBrowserContext(profile));
// Test code may access the TestGuestViewManager before it would be created
// during creation of the first guest.
if (!manager) {
manager =
static_cast<TestGuestViewManager*>(GuestViewManager::CreateWithDelegate(
profile, ExtensionsAPIClient::Get()->CreateGuestViewManagerDelegate(
profile)));
}
return manager;
}
content::RenderFrameHost* PDFExtensionTestBase::GetPluginFrame(
MimeHandlerViewGuest* guest) const {
return pdf_frame_util::FindPdfChildFrame(guest->GetGuestMainFrame());
}
int PDFExtensionTestBase::CountPDFProcesses() {
return pdf_extension_test_util::CountPdfPluginProcesses(browser());
}
void PDFExtensionTestBase::SimulateMouseClickAt(
extensions::MimeHandlerViewGuest* guest,
int modifiers,
blink::WebMouseEvent::Button button,
const gfx::Point& point_in_guest) {
auto* guest_main_frame = guest->GetGuestMainFrame();
content::WaitForHitTestData(guest_main_frame);
const gfx::Point point_in_root_coords =
guest_main_frame->GetView()->TransformPointToRootCoordSpace(
point_in_guest);
content::SimulateMouseClickAt(guest->embedder_web_contents(), modifiers,
button, point_in_root_coords);
}
std::vector<base::test::FeatureRef> PDFExtensionTestBase::GetEnabledFeatures()
const {
return {};
}
std::vector<base::test::FeatureRef> PDFExtensionTestBase::GetDisabledFeatures()
const {
return {};
}