mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
GetDefaultPrinterDPI() creates a blank GtkPrintSettings and reads its resolution, which returns 0 for uninitialized settings. With DPI=0, SetPrintableAreaIfValid() computes a zero scale factor, producing empty page dimensions that fail PrintMsgPrintParamsIsValid(). Fall back to kDefaultPdfDpi (72) when GTK returns 0, matching the existing Windows fallback pattern when CreateDC fails.
226 lines
7.8 KiB
C++
226 lines
7.8 KiB
C++
// Copyright (c) 2024 Microsoft, GmbH.
|
|
// Use of this source code is governed by the MIT license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "shell/browser/printing/printing_utils.h"
|
|
|
|
#include "base/apple/scoped_typeref.h"
|
|
#include "base/strings/sys_string_conversions.h"
|
|
#include "base/strings/utf_string_conversions.h"
|
|
#include "base/task/task_traits.h"
|
|
#include "base/task/thread_pool.h"
|
|
#include "chrome/browser/browser_process.h"
|
|
#include "components/pdf/browser/pdf_frame_util.h"
|
|
#include "content/public/browser/browser_thread.h"
|
|
#include "content/public/browser/render_frame_host.h"
|
|
#include "content/public/browser/web_contents.h"
|
|
#include "electron/buildflags/buildflags.h"
|
|
#include "printing/backend/print_backend.h" // nogncheck
|
|
#include "printing/units.h"
|
|
#include "shell/common/thread_restrictions.h"
|
|
|
|
#if BUILDFLAG(ENABLE_PDF_VIEWER)
|
|
#include "pdf/pdf_features.h"
|
|
#endif
|
|
|
|
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
|
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
|
|
#endif
|
|
|
|
#if BUILDFLAG(IS_MAC)
|
|
#include <ApplicationServices/ApplicationServices.h>
|
|
#endif
|
|
|
|
#if BUILDFLAG(IS_LINUX)
|
|
#include <gtk/gtk.h>
|
|
#endif
|
|
|
|
#if BUILDFLAG(IS_WIN)
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
namespace electron {
|
|
|
|
gfx::Size GetDefaultPrinterDPI(const std::u16string& device_name) {
|
|
#if BUILDFLAG(IS_MAC)
|
|
return gfx::Size(printing::kDefaultMacDpi, printing::kDefaultMacDpi);
|
|
#elif BUILDFLAG(IS_WIN)
|
|
HDC hdc =
|
|
CreateDC(L"WINSPOOL", base::as_wcstr(device_name), nullptr, nullptr);
|
|
if (hdc == nullptr)
|
|
return gfx::Size(printing::kDefaultPdfDpi, printing::kDefaultPdfDpi);
|
|
|
|
int dpi_x = GetDeviceCaps(hdc, LOGPIXELSX);
|
|
int dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);
|
|
DeleteDC(hdc);
|
|
|
|
return gfx::Size(dpi_x, dpi_y);
|
|
#else
|
|
GtkPrintSettings* print_settings = gtk_print_settings_new();
|
|
int dpi = gtk_print_settings_get_resolution(print_settings);
|
|
g_object_unref(print_settings);
|
|
if (dpi <= 0)
|
|
dpi = printing::kDefaultPdfDpi;
|
|
return {dpi, dpi};
|
|
#endif
|
|
}
|
|
|
|
bool IsDeviceNameValid(const std::u16string& device_name) {
|
|
#if BUILDFLAG(IS_MAC)
|
|
base::apple::ScopedCFTypeRef<CFStringRef> new_printer_id(
|
|
base::SysUTF16ToCFStringRef(device_name));
|
|
PMPrinter new_printer = PMPrinterCreateFromPrinterID(new_printer_id.get());
|
|
bool printer_exists = new_printer != nullptr;
|
|
PMRelease(new_printer);
|
|
return printer_exists;
|
|
#else
|
|
scoped_refptr<printing::PrintBackend> print_backend =
|
|
printing::PrintBackend::CreateInstance(
|
|
g_browser_process->GetApplicationLocale());
|
|
return print_backend->IsValidPrinter(base::UTF16ToUTF8(device_name));
|
|
#endif
|
|
}
|
|
|
|
namespace {
|
|
|
|
#if BUILDFLAG(ENABLE_PDF_VIEWER)
|
|
// Duplicated from chrome/browser/printing/print_view_manager_common.cc
|
|
content::RenderFrameHost* GetFullPagePlugin(content::WebContents* contents) {
|
|
content::RenderFrameHost* full_page_plugin = nullptr;
|
|
#if BUILDFLAG(ENABLE_ELECTRON_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_ELECTRON_EXTENSIONS)
|
|
return full_page_plugin;
|
|
}
|
|
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
|
|
|
|
} // namespace
|
|
|
|
// Pick the right RenderFrameHost based on the WebContents.
|
|
// Modified from chrome/browser/printing/print_view_manager_common.cc
|
|
content::RenderFrameHost* GetRenderFrameHostToUse(
|
|
content::WebContents* contents) {
|
|
#if BUILDFLAG(ENABLE_PDF_VIEWER)
|
|
// Pick the plugin frame host if `contents` is a PDF viewer guest. If using
|
|
// OOPIF PDF viewer, pick the PDF extension frame host.
|
|
content::RenderFrameHost* full_page_pdf_embedder_host =
|
|
chrome_pdf::features::IsOopifPdfEnabled()
|
|
? pdf_frame_util::FindFullPagePdfExtensionHost(contents)
|
|
: GetFullPagePlugin(contents);
|
|
content::RenderFrameHost* pdf_rfh = pdf_frame_util::FindPdfChildFrame(
|
|
full_page_pdf_embedder_host ? full_page_pdf_embedder_host
|
|
: contents->GetPrimaryMainFrame());
|
|
if (pdf_rfh) {
|
|
return pdf_rfh;
|
|
}
|
|
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
|
|
auto* focused_frame = contents->GetFocusedFrame();
|
|
return (focused_frame && focused_frame->HasSelection())
|
|
? focused_frame
|
|
: contents->GetPrimaryMainFrame();
|
|
}
|
|
|
|
std::pair<std::string, std::u16string> GetDeviceNameToUse(
|
|
const std::u16string& device_name) {
|
|
#if BUILDFLAG(IS_WIN)
|
|
// Blocking is needed here because Windows printer drivers are oftentimes
|
|
// not thread-safe and have to be accessed on the UI thread.
|
|
ScopedAllowBlockingForElectron allow_blocking;
|
|
#endif
|
|
|
|
if (!device_name.empty()) {
|
|
if (!IsDeviceNameValid(device_name))
|
|
return std::make_pair("Invalid deviceName provided", std::u16string());
|
|
return std::make_pair(std::string(), device_name);
|
|
}
|
|
|
|
scoped_refptr<printing::PrintBackend> print_backend =
|
|
printing::PrintBackend::CreateInstance(
|
|
g_browser_process->GetApplicationLocale());
|
|
std::string printer_name;
|
|
printing::mojom::ResultCode code =
|
|
print_backend->GetDefaultPrinterName(printer_name);
|
|
|
|
// We don't want to return if this fails since some devices won't have a
|
|
// default printer.
|
|
if (code != printing::mojom::ResultCode::kSuccess)
|
|
LOG(ERROR) << "Failed to get default printer name";
|
|
|
|
if (printer_name.empty()) {
|
|
printing::PrinterList printers;
|
|
if (print_backend->EnumeratePrinters(printers) !=
|
|
printing::mojom::ResultCode::kSuccess)
|
|
return std::make_pair("Failed to enumerate printers", std::u16string());
|
|
if (printers.empty())
|
|
return std::make_pair("No printers available on the network",
|
|
std::u16string());
|
|
|
|
printer_name = printers.front().printer_name;
|
|
}
|
|
|
|
return std::make_pair(std::string(), base::UTF8ToUTF16(printer_name));
|
|
}
|
|
|
|
// Copied from
|
|
// chrome/browser/ui/webui/print_preview/local_printer_handler_default.cc:L36-L54
|
|
scoped_refptr<base::TaskRunner> CreatePrinterHandlerTaskRunner() {
|
|
// USER_VISIBLE because the result is displayed in the print preview dialog.
|
|
#if !BUILDFLAG(IS_WIN)
|
|
static constexpr base::TaskTraits kTraits = {
|
|
base::MayBlock(), base::TaskPriority::USER_VISIBLE};
|
|
#endif
|
|
|
|
#if defined(USE_CUPS)
|
|
// CUPS is thread safe.
|
|
return base::ThreadPool::CreateTaskRunner(kTraits);
|
|
#elif BUILDFLAG(IS_WIN)
|
|
// Windows drivers are likely not thread-safe and need to be accessed on the
|
|
// UI thread.
|
|
return content::GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE});
|
|
#else
|
|
// Be conservative on unsupported platforms.
|
|
return base::ThreadPool::CreateSingleThreadTaskRunner(kTraits);
|
|
#endif
|
|
}
|
|
|
|
std::optional<gfx::Size> GetPrinterDefaultPaperSize(
|
|
const std::string& printer_name) {
|
|
#if BUILDFLAG(IS_WIN)
|
|
// Blocking is needed here because Windows printer drivers are oftentimes
|
|
// not thread-safe and have to be accessed on the UI thread.
|
|
ScopedAllowBlockingForElectron allow_blocking;
|
|
#endif
|
|
|
|
if (printer_name.empty())
|
|
return std::nullopt;
|
|
|
|
scoped_refptr<printing::PrintBackend> print_backend =
|
|
printing::PrintBackend::CreateInstance(
|
|
g_browser_process->GetApplicationLocale());
|
|
|
|
if (!print_backend)
|
|
return std::nullopt;
|
|
|
|
printing::PrinterSemanticCapsAndDefaults caps;
|
|
printing::mojom::ResultCode result =
|
|
print_backend->GetPrinterSemanticCapsAndDefaults(printer_name, &caps);
|
|
if (result != printing::mojom::ResultCode::kSuccess)
|
|
return std::nullopt;
|
|
|
|
if (!caps.default_paper.size_um().IsEmpty())
|
|
return caps.default_paper.size_um();
|
|
return std::nullopt;
|
|
}
|
|
|
|
} // namespace electron
|