summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSzabolcs David <davidsz@inf.u-szeged.hu>2024-01-11 13:14:33 +0100
committerPeter Varga <pvarga@inf.u-szeged.hu>2024-01-12 19:52:50 +0000
commit62735b0857a11689bc18f8047112cae42bb05985 (patch)
treef540a9beee37980fbf0c2e19ac42dd0bb7c11354
parent3a5ebfa97260ae7e19e4b78a7345ec39163bcc2f (diff)
Fix printing from PDF plugin
Update the plugin finder logic everywhere to match with Chrome. This comes with a small cleanup: collect PDF-related helper functions scattered around WebEngine in one pdf_util_qt implementation. Add auto test to catch this recurring issue earlier. Task-number: QTBUG-119878 Change-Id: I03b2bd62bebf5b38afc572e0629db106d024e89d Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io> (cherry picked from commit e09cf6e7a1f582d06f86ff2c166b7c2269fd4b47) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> (cherry picked from commit a1ffdacd23c13d97793b87f098e3ec7ab8ff1de6) Reviewed-by: Peter Varga <pvarga@inf.u-szeged.hu>
-rw-r--r--src/core/CMakeLists.txt1
-rw-r--r--src/core/extensions/pdf_iframe_navigation_throttle_qt.cpp8
-rw-r--r--src/core/pdf_util_qt.cpp92
-rw-r--r--src/core/pdf_util_qt.h34
-rw-r--r--src/core/printing/pdf_web_contents_helper_client_qt.cpp30
-rw-r--r--src/core/printing/print_view_manager_qt.cpp7
-rw-r--r--src/core/renderer/print_web_view_helper_delegate_qt.cpp30
-rw-r--r--src/core/web_contents_adapter.cpp3
-rw-r--r--tests/auto/widgets/printing/tst_printing.cpp46
9 files changed, 193 insertions, 58 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 75a41d6a6..c4e413351 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -152,6 +152,7 @@ foreach(arch ${archs})
ozone/platform_window_qt.cpp ozone/platform_window_qt.h
ozone/surface_factory_qt.cpp ozone/surface_factory_qt.h
permission_manager_qt.cpp permission_manager_qt.h
+ pdf_util_qt.cpp pdf_util_qt.h
platform_notification_service_qt.cpp platform_notification_service_qt.h
pointer_device_qt.cpp
pref_service_adapter.cpp pref_service_adapter.h
diff --git a/src/core/extensions/pdf_iframe_navigation_throttle_qt.cpp b/src/core/extensions/pdf_iframe_navigation_throttle_qt.cpp
index 9b6b38a52..9a2feb816 100644
--- a/src/core/extensions/pdf_iframe_navigation_throttle_qt.cpp
+++ b/src/core/extensions/pdf_iframe_navigation_throttle_qt.cpp
@@ -26,12 +26,12 @@
#include "ui/base/webui/jstemplate_builder.h"
#include "ui/base/webui/web_ui_util.h"
+#include "pdf_util_qt.h"
+
#include <QtGlobal>
namespace extensions {
-constexpr char kPDFMimeType[] = "application/pdf";
-
// Used to scope the posted navigation task to the lifetime of |web_contents|.
class PdfWebContentsLifetimeHelper : public content::WebContentsUserData<PdfWebContentsLifetimeHelper>
{
@@ -76,7 +76,7 @@ bool IsPDFPluginEnabled(content::NavigationHandle *navigation_handle, bool *is_s
process_id, routing_id,
navigation_handle->GetWebContents()->GetBrowserContext(),
navigation_handle->GetURL(),
- kPDFMimeType, false /* allow_wildcard */,
+ QtWebEngineCore::kPDFMimeType, false /* allow_wildcard */,
is_stale, &plugin_info, nullptr /* actual_mime_type */);
}
@@ -119,7 +119,7 @@ content::NavigationThrottle::ThrottleCheckResult PDFIFrameNavigationThrottleQt::
std::string mime_type;
response_headers->GetMimeType(&mime_type);
- if (mime_type != kPDFMimeType)
+ if (mime_type != QtWebEngineCore::kPDFMimeType)
return content::NavigationThrottle::PROCEED;
// We MUST download responses marked as attachments rather than showing
diff --git a/src/core/pdf_util_qt.cpp b/src/core/pdf_util_qt.cpp
new file mode 100644
index 000000000..9503f5910
--- /dev/null
+++ b/src/core/pdf_util_qt.cpp
@@ -0,0 +1,92 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+// Copyright 2021 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.Chromium file.
+
+#include "pdf_util_qt.h"
+
+#include <QtGlobal>
+
+#include "base/check.h"
+#include "chrome/common/webui_url_constants.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_contents.h"
+#include "extensions/buildflags/buildflags.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
+#include "extensions/common/constants.h"
+#endif // BUILDFLAG(ENABLE_EXTENSIONS)
+
+namespace QtWebEngineCore {
+
+bool IsPdfExtensionOrigin(const url::Origin &origin)
+{
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ return origin.scheme() == extensions::kExtensionScheme
+ && origin.host() == extension_misc::kPdfExtensionId;
+#else
+ Q_UNUSED(origin);
+ return false;
+#endif
+}
+
+bool IsPdfInternalPluginAllowedOrigin(const url::Origin &origin)
+{
+ if (IsPdfExtensionOrigin(origin))
+ return true;
+
+ // Allow embedding the internal PDF plugin in chrome://print.
+ if (origin == url::Origin::Create(GURL(chrome::kChromeUIPrintURL)))
+ return true;
+
+ // Only allow the PDF plugin in the known, trustworthy origins that are
+ // allowlisted above. See also https://crbug.com/520422 and
+ // https://crbug.com/1027173.
+ return false;
+}
+
+content::RenderFrameHost *GetFullPagePlugin(content::WebContents *contents)
+{
+ content::RenderFrameHost *full_page_plugin = nullptr;
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ contents->ForEachRenderFrameHostWithAction([&full_page_plugin](content::RenderFrameHost *rfh) {
+ auto* guest_view = extensions::MimeHandlerViewGuest::FromRenderFrameHost(rfh);
+ if (guest_view && guest_view->is_full_page_plugin()) {
+ DCHECK_EQ(guest_view->GetGuestMainFrame(), rfh);
+ full_page_plugin = rfh;
+ return content::RenderFrameHost::FrameIterationAction::kStop;
+ }
+ return content::RenderFrameHost::FrameIterationAction::kContinue;
+ });
+#endif // BUILDFLAG(ENABLE_EXTENSIONS)
+ return full_page_plugin;
+}
+
+content::RenderFrameHost *FindPdfChildFrame(content::RenderFrameHost *rfh)
+{
+ if (!rfh)
+ return nullptr;
+
+ if (!IsPdfExtensionOrigin(rfh->GetLastCommittedOrigin()))
+ return nullptr;
+
+ content::RenderFrameHost *pdf_rfh = nullptr;
+ rfh->ForEachRenderFrameHost([&pdf_rfh](content::RenderFrameHost *rfh) {
+ if (!rfh->GetProcess()->IsPdf())
+ return;
+
+ DCHECK(IsPdfExtensionOrigin(rfh->GetParent()->GetLastCommittedOrigin()));
+ DCHECK(!pdf_rfh);
+ pdf_rfh = rfh;
+ });
+
+ return pdf_rfh;
+}
+
+} // namespace QtWebEngineCore
diff --git a/src/core/pdf_util_qt.h b/src/core/pdf_util_qt.h
new file mode 100644
index 000000000..5ee211800
--- /dev/null
+++ b/src/core/pdf_util_qt.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+// Copyright 2021 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.Chromium file.
+
+#ifndef PDF_UTIL_QT_H
+#define PDF_UTIL_QT_H
+
+namespace content {
+class RenderFrameHost;
+class WebContents;
+} // namespace content
+
+namespace url {
+class Origin;
+} // namespace url
+
+namespace QtWebEngineCore {
+
+// from chrome/common/pdf_util.cc:
+constexpr char kPDFMimeType[] = "application/pdf";
+
+bool IsPdfExtensionOrigin(const url::Origin &origin);
+bool IsPdfInternalPluginAllowedOrigin(const url::Origin &origin);
+
+// from chrome/browser/pdf/pdf_frame_util.cc:
+content::RenderFrameHost *GetFullPagePlugin(content::WebContents *contents);
+content::RenderFrameHost *FindPdfChildFrame(content::RenderFrameHost *rfh);
+
+} // namespace QtWebEngineCore
+
+#endif // PDF_UTIL_QT_H
diff --git a/src/core/printing/pdf_web_contents_helper_client_qt.cpp b/src/core/printing/pdf_web_contents_helper_client_qt.cpp
index 4deb398c6..0ff7499c3 100644
--- a/src/core/printing/pdf_web_contents_helper_client_qt.cpp
+++ b/src/core/printing/pdf_web_contents_helper_client_qt.cpp
@@ -9,33 +9,7 @@
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
#include "extensions/common/constants.h"
-namespace {
-bool IsPdfExtensionOrigin(const url::Origin &origin)
-{
- return origin.scheme() == extensions::kExtensionScheme &&
- origin.host() == extension_misc::kPdfExtensionId;
-}
-
-// from chrome/browser/pdf/pdf_frame_util.cc:
-content::RenderFrameHost *FindPdfChildFrame(content::RenderFrameHost *rfh)
-{
- if (!IsPdfExtensionOrigin(rfh->GetLastCommittedOrigin()))
- return nullptr;
-
- content::RenderFrameHost *pdf_rfh = nullptr;
- rfh->ForEachRenderFrameHost(
- [&pdf_rfh](content::RenderFrameHost *rfh) {
- if (!rfh->GetProcess()->IsPdf())
- return;
-
- DCHECK(IsPdfExtensionOrigin(rfh->GetParent()->GetLastCommittedOrigin()));
- DCHECK(!pdf_rfh);
- pdf_rfh = rfh;
- });
-
- return pdf_rfh;
-}
-} // namespace
+#include "pdf_util_qt.h"
PDFWebContentsHelperClientQt::PDFWebContentsHelperClientQt() = default;
PDFWebContentsHelperClientQt::~PDFWebContentsHelperClientQt() = default;
@@ -43,7 +17,7 @@ PDFWebContentsHelperClientQt::~PDFWebContentsHelperClientQt() = default;
content::RenderFrameHost *PDFWebContentsHelperClientQt::FindPdfFrame(content::WebContents *contents)
{
content::RenderFrameHost *main_frame = contents->GetPrimaryMainFrame();
- content::RenderFrameHost *pdf_frame = FindPdfChildFrame(main_frame);
+ content::RenderFrameHost *pdf_frame = QtWebEngineCore::FindPdfChildFrame(main_frame);
return pdf_frame ? pdf_frame : main_frame;
}
diff --git a/src/core/printing/print_view_manager_qt.cpp b/src/core/printing/print_view_manager_qt.cpp
index 892c9e406..060417e5b 100644
--- a/src/core/printing/print_view_manager_qt.cpp
+++ b/src/core/printing/print_view_manager_qt.cpp
@@ -8,6 +8,7 @@
#include "print_view_manager_qt.h"
+#include "pdf_util_qt.h"
#include "type_conversion.h"
#include "web_contents_adapter_client.h"
#include "web_contents_view_qt.h"
@@ -238,7 +239,11 @@ bool PrintViewManagerQt::PrintToPDFInternal(const QPageLayout &pageLayout,
if (web_contents()->IsCrashed())
return false;
- content::RenderFrameHost* rfh = web_contents()->GetPrimaryMainFrame();
+ content::RenderFrameHost *rfh = web_contents()->GetPrimaryMainFrame();
+ // Use the plugin frame for printing if web_contents() is a PDF viewer guest
+ content::RenderFrameHost *full_page_plugin = GetFullPagePlugin(web_contents());
+ if (content::RenderFrameHost *pdf_rfh = FindPdfChildFrame(full_page_plugin ? full_page_plugin : rfh))
+ rfh = pdf_rfh;
GetPrintRenderFrame(rfh)->InitiatePrintPreview(mojo::PendingAssociatedRemote<printing::mojom::PrintRenderer>(), false);
DCHECK(!m_printPreviewRfh);
diff --git a/src/core/renderer/print_web_view_helper_delegate_qt.cpp b/src/core/renderer/print_web_view_helper_delegate_qt.cpp
index f77b6fbbc..f01568e65 100644
--- a/src/core/renderer/print_web_view_helper_delegate_qt.cpp
+++ b/src/core/renderer/print_web_view_helper_delegate_qt.cpp
@@ -15,8 +15,10 @@
#include "chrome/common/webui_url_constants.h"
#include "extensions/common/constants.h"
#include "third_party/blink/public/web/web_document.h"
+#include "extensions/renderer/guest_view/mime_handler_view/post_message_support.h"
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
+#include "pdf_util_qt.h"
#include "print_web_view_helper_delegate_qt.h"
#include "web_engine_library_info.h"
@@ -24,33 +26,13 @@ namespace QtWebEngineCore {
PrintWebViewHelperDelegateQt::~PrintWebViewHelperDelegateQt() {}
-bool IsPdfExtensionOrigin(const url::Origin& origin)
-{
-#if BUILDFLAG(ENABLE_EXTENSIONS)
- return origin.scheme() == extensions::kExtensionScheme
- && origin.host() == extension_misc::kPdfExtensionId;
-#else
- Q_UNUSED(origin);
- return false;
-#endif
-}
-
blink::WebElement PrintWebViewHelperDelegateQt::GetPdfElement(blink::WebLocalFrame *frame)
{
#if BUILDFLAG(ENABLE_EXTENSIONS)
- const url::Origin origin = frame->GetDocument().GetSecurityOrigin();
- bool inside_print_preview = origin == url::Origin::Create(GURL(chrome::kChromeUIPrintURL));
- bool inside_pdf_extension = IsPdfExtensionOrigin(origin);
- if (inside_print_preview || inside_pdf_extension) {
- // <object> with id="plugin" is created in
- // chrome/browser/resources/pdf/pdf_viewer_base.js.
- auto viewer_element = frame->GetDocument().GetElementById("viewer");
- if (!viewer_element.IsNull() && !viewer_element.ShadowRoot().IsNull()) {
- auto plugin_element = viewer_element.ShadowRoot().QuerySelector("#plugin");
- if (!plugin_element.IsNull())
- return plugin_element;
- }
- NOTREACHED();
+ if (frame->Parent() && IsPdfInternalPluginAllowedOrigin(frame->Parent()->GetSecurityOrigin())) {
+ auto plugin_element = frame->GetDocument().QuerySelector("embed");
+ DCHECK(!plugin_element.IsNull());
+ return plugin_element;
}
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
return blink::WebElement();
diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp
index fe30206ce..f44675171 100644
--- a/src/core/web_contents_adapter.cpp
+++ b/src/core/web_contents_adapter.cpp
@@ -15,6 +15,7 @@
#include "favicon_service_factory_qt.h"
#include "find_text_helper.h"
#include "media_capture_devices_dispatcher.h"
+#include "pdf_util_qt.h"
#include "profile_adapter.h"
#include "profile_qt.h"
#include "qwebengineloadinginfo.h"
@@ -1974,7 +1975,7 @@ WebContentsAdapter::LifecycleState WebContentsAdapter::determineRecommendedState
// Do not discard PDFs as they might contain entry that is not saved and they
// don't remember their scrolling positions. See crbug.com/547286 and
// crbug.com/65244.
- if (m_webContents->GetContentsMimeType() == "application/pdf")
+ if (m_webContents->GetContentsMimeType() == kPDFMimeType)
return LifecycleState::Frozen;
return LifecycleState::Discarded;
diff --git a/tests/auto/widgets/printing/tst_printing.cpp b/tests/auto/widgets/printing/tst_printing.cpp
index 1f9b5059c..605fb57b5 100644
--- a/tests/auto/widgets/printing/tst_printing.cpp
+++ b/tests/auto/widgets/printing/tst_printing.cpp
@@ -3,6 +3,7 @@
#include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h>
#include <QtWebEngineCore/qtwebenginecore-config.h>
+#include <QWebEngineSettings>
#include <QWebEngineView>
#include <QTemporaryDir>
#include <QTest>
@@ -22,6 +23,7 @@ private slots:
void printRequest();
#if QT_CONFIG(webengine_system_poppler)
void printToPdfPoppler();
+ void printFromPdfViewer();
#endif
void interruptPrinting();
};
@@ -116,6 +118,50 @@ void tst_Printing::printToPdfPoppler()
QVERIFY2(pdfPage->search(ustring::from_latin1("Hello Paper World"), rect, page::search_from_top,
case_sensitive ), "Could not find text");
}
+
+void tst_Printing::printFromPdfViewer()
+{
+ using namespace poppler;
+
+ QWebEngineView view;
+ view.page()->settings()->setAttribute(QWebEngineSettings::PluginsEnabled, true);
+ view.page()->settings()->setAttribute(QWebEngineSettings::PdfViewerEnabled, true);
+
+ // Load a basic HTML
+ QSignalSpy spy(&view, &QWebEngineView::loadFinished);
+ view.load(QUrl("qrc:///resources/basic_printing_page.html"));
+ QTRY_COMPARE(spy.size(), 1);
+
+ // Create a PDF
+ QTemporaryDir tempDir(QDir::tempPath() + "/tst_printing-XXXXXX");
+ QVERIFY(tempDir.isValid());
+ QString path = tempDir.path() + "/basic_page.pdf";
+ QSignalSpy savePdfSpy(view.page(), &QWebEnginePage::pdfPrintingFinished);
+ view.page()->printToPdf(path);
+ QTRY_COMPARE(savePdfSpy.size(), 1);
+
+ // Open the new file with the PDF viewer plugin
+ view.load(QUrl("file://" + path));
+ QTRY_COMPARE(spy.size(), 2);
+
+ // Print from the plugin
+ // loadFinished signal is not reliable when loading a PDF file, because it has multiple phases.
+ // Workaround: Try to print it a couple of times until the result matches the expected.
+ CallbackSpy<QByteArray> resultSpy;
+ bool ok = QTest::qWaitFor([&]() -> bool {
+ view.printToPdf(resultSpy.ref());
+ QByteArray data = resultSpy.waitForResult();
+
+ // Check if the result contains text from the original basic HTML
+ // This catches all the typical issues: empty result or printing the WebUI without PDF content.
+ QScopedPointer<document> pdf(document::load_from_raw_data(data.constData(), data.length()));
+ QScopedPointer<page> pdfPage(pdf->create_page(0));
+ rectf rect;
+ return pdfPage->search(ustring::from_latin1("Hello Paper World"), rect, page::search_from_top,
+ case_sensitive);
+ }, 10000);
+ QVERIFY(ok);
+}
#endif
void tst_Printing::interruptPrinting()