From 8ec8f934c32d373bdd27dde3a38935a33944048b Mon Sep 17 00:00:00 2001 From: Szabolcs David Date: Thu, 20 Apr 2023 18:02:40 +0200 Subject: [PATCH 1/2] Fix download button of PDF viewer plugin The basic case of saving a PDF is easily fixed by installing PDFWebContentsHelpers in ExtensionsAPIClientQt for the proper WebContents. If the PDF file contains input elements and the user modifies them, the download button offers an option to save the modified file instead of the original. We need a minimal implementation of FileSystemDelegate, because this happens through file system access dialogs instead of normal "save as" dialogs. Pick-to: 6.6 Task-number: QTBUG-104610 Task-number: QTBUG-113149 Change-Id: I756f9fe6c72ccb129b04282b2fda05602cbf54cd Reviewed-by: Allan Sandfeld Jensen Reviewed-by: Qt CI Bot --- src/core/CMakeLists.txt | 1 + .../extensions/extensions_api_client_qt.cpp | 13 ++ .../extensions/extensions_api_client_qt.h | 3 + .../extensions/file_system_delegate_qt.cpp | 146 ++++++++++++++++++ src/core/extensions/file_system_delegate_qt.h | 89 +++++++++++ src/core/select_file_dialog_factory_qt.cpp | 2 +- 6 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 src/core/extensions/file_system_delegate_qt.cpp create mode 100644 src/core/extensions/file_system_delegate_qt.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 1d183573f..50f202b86 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -268,6 +268,7 @@ foreach(arch ${archs}) extensions/extension_web_contents_observer_qt.cpp extensions/extension_web_contents_observer_qt.h extensions/extensions_api_client_qt.cpp extensions/extensions_api_client_qt.h extensions/extensions_browser_client_qt.cpp extensions/extensions_browser_client_qt.h + extensions/file_system_delegate_qt.cpp extensions/file_system_delegate_qt.h extensions/messaging_delegate_qt.cpp extensions/messaging_delegate_qt.h extensions/mime_handler_view_guest_delegate_qt.cpp extensions/mime_handler_view_guest_delegate_qt.h extensions/plugin_service_filter_qt.cpp extensions/plugin_service_filter_qt.h diff --git a/src/core/extensions/extensions_api_client_qt.cpp b/src/core/extensions/extensions_api_client_qt.cpp index 3d86c65f7..605368880 100644 --- a/src/core/extensions/extensions_api_client_qt.cpp +++ b/src/core/extensions/extensions_api_client_qt.cpp @@ -7,6 +7,7 @@ // found in the LICENSE file. #include "extensions_api_client_qt.h" +#include "file_system_delegate_qt.h" #include "messaging_delegate_qt.h" #include @@ -20,6 +21,7 @@ #if BUILDFLAG(ENABLE_PDF) #include "components/pdf/browser/pdf_web_contents_helper.h" +#include "printing/pdf_web_contents_helper_client_qt.h" #endif #if BUILDFLAG(ENABLE_PRINTING) && BUILDFLAG(ENABLE_PRINT_PREVIEW) @@ -39,6 +41,13 @@ AppViewGuestDelegate *ExtensionsAPIClientQt::CreateAppViewGuestDelegate() const return nullptr; } +FileSystemDelegate *ExtensionsAPIClientQt::GetFileSystemDelegate() +{ + if (!m_fileSystemDelegate) + m_fileSystemDelegate = std::make_unique(); + return m_fileSystemDelegate.get(); +} + std::unique_ptr ExtensionsAPIClientQt::CreateGuestViewManagerDelegate(content::BrowserContext *context) const { return std::make_unique(context); @@ -56,6 +65,10 @@ void ExtensionsAPIClientQt::AttachWebContentsHelpers(content::WebContents *web_c QtWebEngineCore::PrintViewManagerQt::CreateForWebContents(web_contents); #endif ExtensionWebContentsObserverQt::CreateForWebContents(web_contents); + +#if BUILDFLAG(ENABLE_PDF) + pdf::PDFWebContentsHelper::CreateForWebContentsWithClient(web_contents, std::make_unique()); +#endif } MessagingDelegate *ExtensionsAPIClientQt::GetMessagingDelegate() diff --git a/src/core/extensions/extensions_api_client_qt.h b/src/core/extensions/extensions_api_client_qt.h index 7eb74b96c..334917a43 100644 --- a/src/core/extensions/extensions_api_client_qt.h +++ b/src/core/extensions/extensions_api_client_qt.h @@ -13,6 +13,7 @@ namespace extensions { +class FileSystemDelegate; class MessagingDelegate; class ExtensionsAPIClientQt : public ExtensionsAPIClient @@ -22,6 +23,7 @@ public: // ExtensionsAPIClient implementation. AppViewGuestDelegate *CreateAppViewGuestDelegate() const override; + FileSystemDelegate *GetFileSystemDelegate() override; std::unique_ptr CreateGuestViewManagerDelegate(content::BrowserContext *context) const override; std::unique_ptr @@ -30,6 +32,7 @@ public: MessagingDelegate *GetMessagingDelegate() override; private: + std::unique_ptr m_fileSystemDelegate; std::unique_ptr m_messagingDelegate; }; diff --git a/src/core/extensions/file_system_delegate_qt.cpp b/src/core/extensions/file_system_delegate_qt.cpp new file mode 100644 index 000000000..7c1c5bbd8 --- /dev/null +++ b/src/core/extensions/file_system_delegate_qt.cpp @@ -0,0 +1,146 @@ +// 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 + +#include "file_system_delegate_qt.h" + +#include "select_file_dialog_factory_qt.h" +#include "type_conversion.h" + +#include "base/files/file_path.h" +#include "base/files/file_path.h" +#include "base/functional/callback.h" +#include "base/functional/callback.h" +#include "base/memory/ref_counted.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents.h" +#include "extensions/browser/api/file_system/file_system_delegate.h" +#include "extensions/browser/extension_function.h" +#include "ui/shell_dialogs/selected_file_info.h" + +#include + +namespace extensions { + +FileEntryPickerQt::FileEntryPickerQt( + content::WebContents *web_contents, + const base::FilePath &suggested_name, + const ui::SelectFileDialog::FileTypeInfo *file_type_info, + ui::SelectFileDialog::Type picker_type, + FileSystemDelegate::FilesSelectedCallback files_selected_callback, + base::OnceClosure file_selection_canceled_callback) + : m_filesSelectedCallback(std::move(files_selected_callback)) + , m_fileSelectionCanceledCallback(std::move(file_selection_canceled_callback)) +{ + const GURL caller = web_contents->GetPrimaryMainFrame()->GetLastCommittedURL(); + m_selectFileDialog = ui::SelectFileDialog::Create( + this, std::make_unique(web_contents)); + m_selectFileDialog->SelectFile( + picker_type, std::u16string(), suggested_name, file_type_info, 0, + base::FilePath::StringType(), nullptr, nullptr, &caller); +} + +FileEntryPickerQt::~FileEntryPickerQt() = default; + +void FileEntryPickerQt::FileSelected(const base::FilePath &path, + int index, + void *params) +{ + MultiFilesSelected({path}, params); +} + +void FileEntryPickerQt::FileSelectedWithExtraInfo(const ui::SelectedFileInfo& file, + int index, + void *params) +{ + FileSelected(file.file_path, index, params); +} + +void FileEntryPickerQt::MultiFilesSelected(const std::vector& files, + void* params) +{ + Q_UNUSED(params); + std::move(m_filesSelectedCallback).Run(files); + delete this; +} + +void FileEntryPickerQt::MultiFilesSelectedWithExtraInfo( + const std::vector &files, + void *params) +{ + std::vector paths; + for (const auto& file : files) + paths.push_back(file.file_path); + MultiFilesSelected(paths, params); +} + +void FileEntryPickerQt::FileSelectionCanceled(void *params) +{ + std::move(m_fileSelectionCanceledCallback).Run(); + delete this; +} + +FileSystemDelegateQt::FileSystemDelegateQt() +{ +} + +base::FilePath FileSystemDelegateQt::GetDefaultDirectory() +{ + return QtWebEngineCore::toFilePath( + QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); +} + +base::FilePath FileSystemDelegateQt::GetManagedSaveAsDirectory( + content::BrowserContext *browser_context, + const Extension &extension) +{ + Q_UNUSED(browser_context); + Q_UNUSED(extension); + return base::FilePath(); +} + +bool FileSystemDelegateQt::ShowSelectFileDialog( + scoped_refptr extension_function, + ui::SelectFileDialog::Type type, + const base::FilePath &default_path, + const ui::SelectFileDialog::FileTypeInfo *file_type_info, + FileSystemDelegate::FilesSelectedCallback files_selected_callback, + base::OnceClosure file_selection_canceled_callback) +{ + content::WebContents *web_contents = extension_function->GetSenderWebContents(); + if (!web_contents) + return false; + + new FileEntryPickerQt(web_contents, default_path, file_type_info, type, + std::move(files_selected_callback), + std::move(file_selection_canceled_callback)); + return true; +} + +void FileSystemDelegateQt::ConfirmSensitiveDirectoryAccess( + bool has_write_permission, + const std::u16string &app_name, + content::WebContents *web_contents, + base::OnceClosure on_accept, + base::OnceClosure on_cancel) +{ + Q_UNUSED(has_write_permission); + Q_UNUSED(app_name); + Q_UNUSED(web_contents); + Q_UNUSED(on_accept); + std::move(on_cancel).Run(); +} + +int FileSystemDelegateQt::GetDescriptionIdForAcceptType(const std::string &accept_type) +{ + Q_UNUSED(accept_type); + return 0; +} + +SavedFilesServiceInterface *FileSystemDelegateQt::GetSavedFilesService( + content::BrowserContext *browser_context) +{ + Q_UNUSED(browser_context); + return nullptr; +} + +} // namespace extensions diff --git a/src/core/extensions/file_system_delegate_qt.h b/src/core/extensions/file_system_delegate_qt.h new file mode 100644 index 000000000..1e9d87c38 --- /dev/null +++ b/src/core/extensions/file_system_delegate_qt.h @@ -0,0 +1,89 @@ +// 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 + +#ifndef FILE_SYSTEM_DELEGATE_QT_H +#define FILE_SYSTEM_DELEGATE_QT_H + +#include "extensions/browser/api/file_system/file_system_delegate.h" + +#include "base/files/file_path.h" +#include "base/functional/callback.h" +#include "base/memory/ref_counted.h" +#include "extensions/browser/extension_function.h" +#include "ui/shell_dialogs/select_file_dialog.h" + +#include +#include + +namespace content { +class BrowserContext; +} // namespace content + +namespace extensions { + +class FileEntryPickerQt : public ui::SelectFileDialog::Listener { +public: + FileEntryPickerQt( + content::WebContents *web_contents, + const base::FilePath &suggested_name, + const ui::SelectFileDialog::FileTypeInfo *file_type_info, + ui::SelectFileDialog::Type picker_type, + FileSystemDelegate::FilesSelectedCallback files_selected_callback, + base::OnceClosure file_selection_canceled_callback); + + FileEntryPickerQt(const FileEntryPickerQt &) = delete; + FileEntryPickerQt &operator=(const FileEntryPickerQt &) = delete; + +private: + ~FileEntryPickerQt() override; + + // ui::SelectFileDialog::Listener implementation. + void FileSelected(const base::FilePath &path, + int index, + void *params) override; + void FileSelectedWithExtraInfo(const ui::SelectedFileInfo &file, + int index, + void *params) override; + void MultiFilesSelected(const std::vector &files, + void *params) override; + void MultiFilesSelectedWithExtraInfo( + const std::vector &files, + void *params) override; + void FileSelectionCanceled(void *params) override; + + FileSystemDelegate::FilesSelectedCallback m_filesSelectedCallback; + base::OnceClosure m_fileSelectionCanceledCallback; + scoped_refptr m_selectFileDialog; +}; + +class FileSystemDelegateQt : public FileSystemDelegate +{ +public: + FileSystemDelegateQt(); + + // FileSystemDelegate implementation + virtual base::FilePath GetDefaultDirectory() override; + virtual base::FilePath GetManagedSaveAsDirectory( + content::BrowserContext *browser_context, + const Extension &extension) override; + virtual bool ShowSelectFileDialog( + scoped_refptr extension_function, + ui::SelectFileDialog::Type type, + const base::FilePath &default_path, + const ui::SelectFileDialog::FileTypeInfo *file_types, + FileSystemDelegate::FilesSelectedCallback files_selected_callback, + base::OnceClosure file_selection_canceled_callback) override; + virtual void ConfirmSensitiveDirectoryAccess( + bool has_write_permission, + const std::u16string &app_name, + content::WebContents *web_contents, + base::OnceClosure on_accept, + base::OnceClosure on_cancel) override; + virtual int GetDescriptionIdForAcceptType(const std::string &accept_type) override; + virtual SavedFilesServiceInterface *GetSavedFilesService( + content::BrowserContext *browser_context) override; +}; + +} // namespace extensions + +#endif // FILE_SYSTEM_DELEGATE_QT_H diff --git a/src/core/select_file_dialog_factory_qt.cpp b/src/core/select_file_dialog_factory_qt.cpp index 9da27a4cf..1f897a805 100644 --- a/src/core/select_file_dialog_factory_qt.cpp +++ b/src/core/select_file_dialog_factory_qt.cpp @@ -137,7 +137,7 @@ SelectFileDialogFactoryQt::Create(ui::SelectFileDialog::Listener *listener, std::unique_ptr policy) { content::WebContents *webContents = - static_cast(policy.get())->webContents(); + static_cast(policy.get())->webContents()->GetOutermostWebContents(); WebContentsAdapterClient *client = WebContentsViewQt::from(static_cast(webContents)->GetView()) ->client(); -- 2.43.0.windows.1 From 06cd6e1a9fc441847ddb4f2fd508f2e0678167f6 Mon Sep 17 00:00:00 2001 From: Szabolcs David Date: Mon, 11 Dec 2023 17:30:22 +0100 Subject: [PATCH 2/2] Fix printing from PDF plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. Pick-to: 6.5 6.6 6.7 Task-number: QTBUG-119878 Change-Id: I03b2bd62bebf5b38afc572e0629db106d024e89d Reviewed-by: Michael BrĂ¼ning --- src/core/CMakeLists.txt | 1 + .../pdf_iframe_navigation_throttle_qt.cpp | 15 +-- src/core/pdf_util_qt.cpp | 92 +++++++++++++++++++ src/core/pdf_util_qt.h | 34 +++++++ .../pdf_web_contents_helper_client_qt.cpp | 30 +----- src/core/printing/print_view_manager_qt.cpp | 7 +- .../print_web_view_helper_delegate_qt.cpp | 30 ++---- src/core/web_contents_adapter.cpp | 3 +- tests/auto/widgets/printing/tst_printing.cpp | 46 ++++++++++ 9 files changed, 197 insertions(+), 61 deletions(-) create mode 100644 src/core/pdf_util_qt.cpp create mode 100644 src/core/pdf_util_qt.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 50f202b86..230d0253f 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 5a93b4b84..d5400c219 100644 --- a/src/core/extensions/pdf_iframe_navigation_throttle_qt.cpp +++ b/src/core/extensions/pdf_iframe_navigation_throttle_qt.cpp @@ -25,12 +25,12 @@ #include "ui/base/webui/jstemplate_builder.h" #include "ui/base/webui/web_ui_util.h" +#include "pdf_util_qt.h" + #include 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 { @@ -74,10 +74,11 @@ bool IsPDFPluginEnabled(content::NavigationHandle *navigation_handle, bool *is_s content::WebPluginInfo plugin_info; // Will check WebEngineSettings by PluginServiceFilterQt return content::PluginService::GetInstance()->GetPluginInfo( - process_id, routing_id, nullptr, navigation_handle->GetURL(), - kPDFMimeType, - false /* allow_wildcard */, is_stale, &plugin_info, - nullptr /* actual_mime_type */); + process_id, routing_id, + navigation_handle->GetWebContents()->GetBrowserContext(), + navigation_handle->GetURL(), + QtWebEngineCore::kPDFMimeType, false /* allow_wildcard */, + is_stale, &plugin_info, nullptr /* actual_mime_type */); } std::string GetPDFPlaceholderHTML(const GURL &pdf_url) @@ -119,7 +120,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 + +#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(), 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) { - // 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 83066e811..787a6adce 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" @@ -1955,7 +1956,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 #include +#include #include #include #include @@ -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 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 pdf(document::load_from_raw_data(data.constData(), data.length())); + QScopedPointer 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() -- 2.43.0.windows.1