Bug 1826326 [wpt PR 39360] - Get rid of ResourceWarnings produced by the tests, a...
[gecko.git] / widget / windows / nsDeviceContextSpecWin.cpp
blob9382098accaea431ba8943926c78c3ae031a98a3
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsDeviceContextSpecWin.h"
8 #include "mozilla/ArrayUtils.h"
9 #include "mozilla/gfx/PrintPromise.h"
10 #include "mozilla/gfx/PrintTargetPDF.h"
11 #include "mozilla/gfx/PrintTargetWindows.h"
12 #include "mozilla/Logging.h"
13 #include "mozilla/Preferences.h"
14 #include "mozilla/RefPtr.h"
15 #include "mozilla/Telemetry.h"
16 #include "nsAnonymousTemporaryFile.h"
18 #include <wchar.h>
19 #include <windef.h>
20 #include <winspool.h>
22 #include "nsIWidget.h"
24 #include "nsTArray.h"
25 #include "nsIPrintSettingsWin.h"
27 #include "nsComponentManagerUtils.h"
28 #include "nsPrinterWin.h"
29 #include "nsReadableUtils.h"
30 #include "nsString.h"
32 #include "gfxWindowsSurface.h"
34 #include "nsIFileStreams.h"
35 #include "nsWindowsHelpers.h"
37 #include "mozilla/gfx/Logging.h"
39 #ifdef MOZ_ENABLE_SKIA_PDF
40 # include "mozilla/gfx/PrintTargetSkPDF.h"
41 # include "mozilla/gfx/PrintTargetEMF.h"
42 # include "nsIUUIDGenerator.h"
43 # include "nsDirectoryServiceDefs.h"
44 # include "nsPrintfCString.h"
45 # include "nsThreadUtils.h"
46 #endif
48 extern mozilla::LazyLogModule gPrintingLog;
49 #define PR_PL(_p1) MOZ_LOG(gPrintingLog, mozilla::LogLevel::Debug, _p1)
51 using namespace mozilla;
52 using namespace mozilla::gfx;
54 #ifdef MOZ_ENABLE_SKIA_PDF
55 using namespace mozilla::widget;
56 #endif
58 static const wchar_t kDriverName[] = L"WINSPOOL";
60 //----------------------------------------------------------------------------------
61 //---------------
62 // static members
63 //----------------------------------------------------------------------------------
64 nsDeviceContextSpecWin::nsDeviceContextSpecWin() = default;
66 //----------------------------------------------------------------------------------
68 NS_IMPL_ISUPPORTS(nsDeviceContextSpecWin, nsIDeviceContextSpec)
70 nsDeviceContextSpecWin::~nsDeviceContextSpecWin() {
71 SetDevMode(nullptr);
73 if (mTempFile) {
74 mTempFile->Remove(/* recursive = */ false);
77 if (nsCOMPtr<nsIPrintSettingsWin> ps = do_QueryInterface(mPrintSettings)) {
78 ps->SetDeviceName(u""_ns);
79 ps->SetDriverName(u""_ns);
80 ps->SetDevMode(nullptr);
84 static bool GetDefaultPrinterName(nsAString& aDefaultPrinterName) {
85 DWORD length = 0;
86 GetDefaultPrinterW(nullptr, &length);
88 if (length) {
89 aDefaultPrinterName.SetLength(length);
90 if (GetDefaultPrinterW((LPWSTR)aDefaultPrinterName.BeginWriting(),
91 &length)) {
92 // `length` includes the terminating null, so we subtract that from our
93 // string length.
94 aDefaultPrinterName.SetLength(length - 1);
95 PR_PL(("DEFAULT PRINTER [%s]\n",
96 NS_ConvertUTF16toUTF8(aDefaultPrinterName).get()));
97 return true;
101 aDefaultPrinterName.Truncate();
102 PR_PL(("NO DEFAULT PRINTER\n"));
103 return false;
106 //----------------------------------------------------------------------------------
107 NS_IMETHODIMP nsDeviceContextSpecWin::Init(nsIPrintSettings* aPrintSettings,
108 bool aIsPrintPreview) {
109 mPrintSettings = aPrintSettings;
111 // Get the Printer Name to be used and output format.
112 nsAutoString printerName;
113 if (mPrintSettings) {
114 mOutputFormat = mPrintSettings->GetOutputFormat();
115 mPrintSettings->GetPrinterName(printerName);
118 // If there is no name then use the default printer
119 if (printerName.IsEmpty()) {
120 GetDefaultPrinterName(printerName);
123 // Gather telemetry on the print target type.
125 // Unfortunately, if we're not using our own internal save-to-pdf codepaths,
126 // there isn't a good way to determine whether a print is going to be to a
127 // physical printer or to a file or some other non-physical output. We do our
128 // best by checking for what seems to be the most common save-to-PDF virtual
129 // printers.
131 // We use StringBeginsWith below, since printer names are often followed by a
132 // version number or other product differentiating string. (True for doPDF,
133 // novaPDF, PDF-XChange and Soda PDF, for example.)
134 if (mOutputFormat == nsIPrintSettings::kOutputFormatPDF) {
135 Telemetry::ScalarAdd(Telemetry::ScalarID::PRINTING_TARGET_TYPE,
136 u"pdf_file"_ns, 1);
137 } else if (StringBeginsWith(printerName, u"Microsoft Print to PDF"_ns) ||
138 StringBeginsWith(printerName, u"Adobe PDF"_ns) ||
139 StringBeginsWith(printerName, u"Bullzip PDF Printer"_ns) ||
140 StringBeginsWith(printerName, u"CutePDF Writer"_ns) ||
141 StringBeginsWith(printerName, u"doPDF"_ns) ||
142 StringBeginsWith(printerName, u"Foxit Reader PDF Printer"_ns) ||
143 StringBeginsWith(printerName, u"Nitro PDF Creator"_ns) ||
144 StringBeginsWith(printerName, u"novaPDF"_ns) ||
145 StringBeginsWith(printerName, u"PDF-XChange"_ns) ||
146 StringBeginsWith(printerName, u"PDF24 PDF"_ns) ||
147 StringBeginsWith(printerName, u"PDFCreator"_ns) ||
148 StringBeginsWith(printerName, u"PrimoPDF"_ns) ||
149 StringBeginsWith(printerName, u"Soda PDF"_ns) ||
150 StringBeginsWith(printerName, u"Solid PDF Creator"_ns) ||
151 StringBeginsWith(printerName,
152 u"Universal Document Converter"_ns)) {
153 Telemetry::ScalarAdd(Telemetry::ScalarID::PRINTING_TARGET_TYPE,
154 u"pdf_file"_ns, 1);
155 } else if (printerName.EqualsLiteral("Microsoft XPS Document Writer")) {
156 Telemetry::ScalarAdd(Telemetry::ScalarID::PRINTING_TARGET_TYPE,
157 u"xps_file"_ns, 1);
158 } else {
159 nsAString::const_iterator start, end;
160 printerName.BeginReading(start);
161 printerName.EndReading(end);
162 if (CaseInsensitiveFindInReadable(u"pdf"_ns, start, end)) {
163 Telemetry::ScalarAdd(Telemetry::ScalarID::PRINTING_TARGET_TYPE,
164 u"pdf_unknown"_ns, 1);
165 } else {
166 Telemetry::ScalarAdd(Telemetry::ScalarID::PRINTING_TARGET_TYPE,
167 u"unknown"_ns, 1);
171 nsresult rv = NS_ERROR_GFX_PRINTER_NO_PRINTER_AVAILABLE;
172 if (aPrintSettings) {
173 #ifdef MOZ_ENABLE_SKIA_PDF
174 nsAutoString printViaPdf;
175 Preferences::GetString("print.print_via_pdf_encoder", printViaPdf);
176 if (printViaPdf.EqualsLiteral("skia-pdf")) {
177 mPrintViaSkPDF = true;
179 #endif
181 // If we're in the child or we're printing to PDF we only need information
182 // from the print settings.
183 if (XRE_IsContentProcess() ||
184 mOutputFormat == nsIPrintSettings::kOutputFormatPDF) {
185 return NS_OK;
188 nsCOMPtr<nsIPrintSettingsWin> psWin(do_QueryInterface(aPrintSettings));
189 if (psWin) {
190 nsAutoString deviceName;
191 nsAutoString driverName;
192 psWin->GetDeviceName(deviceName);
193 psWin->GetDriverName(driverName);
195 LPDEVMODEW devMode;
196 psWin->GetDevMode(&devMode); // creates new memory (makes a copy)
198 if (!deviceName.IsEmpty() && !driverName.IsEmpty() && devMode) {
199 // Scaling is special, it is one of the few
200 // devMode items that we control in layout
201 if (devMode->dmFields & DM_SCALE) {
202 double scale = double(devMode->dmScale) / 100.0f;
203 if (scale != 1.0) {
204 aPrintSettings->SetScaling(scale);
205 devMode->dmScale = 100;
209 SetDeviceName(deviceName);
210 SetDriverName(driverName);
211 SetDevMode(devMode);
213 return NS_OK;
214 } else {
215 PR_PL(
216 ("***** nsDeviceContextSpecWin::Init - "
217 "deviceName/driverName/devMode was NULL!\n"));
218 if (devMode) ::HeapFree(::GetProcessHeap(), 0, devMode);
221 } else {
222 PR_PL(("***** nsDeviceContextSpecWin::Init - aPrintSettingswas NULL!\n"));
225 if (printerName.IsEmpty()) {
226 return rv;
229 return GetDataFromPrinter(printerName, mPrintSettings);
232 //----------------------------------------------------------
234 already_AddRefed<PrintTarget> nsDeviceContextSpecWin::MakePrintTarget() {
235 NS_ASSERTION(mDevMode || mOutputFormat == nsIPrintSettings::kOutputFormatPDF,
236 "DevMode can't be NULL here unless we're printing to PDF.");
238 #ifdef MOZ_ENABLE_SKIA_PDF
239 if (mPrintViaSkPDF) {
240 double width, height;
241 mPrintSettings->GetEffectivePageSize(&width, &height);
242 if (width <= 0 || height <= 0) {
243 return nullptr;
246 // convert twips to points
247 width /= TWIPS_PER_POINT_FLOAT;
248 height /= TWIPS_PER_POINT_FLOAT;
249 IntSize size = IntSize::Ceil(width, height);
251 if (mOutputFormat == nsIPrintSettings::kOutputFormatPDF) {
252 nsString filename;
253 // TODO(dshin):
254 // - Does this handle bug 1659470?
255 // - Should this code path be enabled, we should use temporary files and
256 // then move the file in `EndDocument()`.
257 mPrintSettings->GetToFileName(filename);
259 nsAutoCString printFile(NS_ConvertUTF16toUTF8(filename).get());
260 auto skStream = MakeUnique<SkFILEWStream>(printFile.get());
261 return PrintTargetSkPDF::CreateOrNull(std::move(skStream), size);
264 if (mDevMode) {
265 NS_WARNING_ASSERTION(!mDriverName.IsEmpty(), "No driver!");
266 HDC dc =
267 ::CreateDCW(mDriverName.get(), mDeviceName.get(), nullptr, mDevMode);
268 if (!dc) {
269 gfxCriticalError(gfxCriticalError::DefaultOptions(false))
270 << "Failed to create device context in GetSurfaceForPrinter";
271 return nullptr;
273 return PrintTargetEMF::CreateOrNull(dc, size);
276 #endif
278 if (mOutputFormat == nsIPrintSettings::kOutputFormatPDF) {
279 double width, height;
280 mPrintSettings->GetEffectiveSheetSize(&width, &height);
281 if (width <= 0 || height <= 0) {
282 return nullptr;
285 // convert twips to points
286 width /= TWIPS_PER_POINT_FLOAT;
287 height /= TWIPS_PER_POINT_FLOAT;
289 auto stream = [&]() -> nsCOMPtr<nsIOutputStream> {
290 if (mPrintSettings->GetOutputDestination() ==
291 nsIPrintSettings::kOutputDestinationStream) {
292 nsCOMPtr<nsIOutputStream> out;
293 mPrintSettings->GetOutputStream(getter_AddRefs(out));
294 return out;
297 // Even if the destination may be a named path, write to a temp file -
298 // this is consistent with behaviour of `PrintTarget` on other platforms.
299 nsCOMPtr<nsIFile> file;
300 nsresult rv = NS_OpenAnonymousTemporaryNsIFile(getter_AddRefs(mTempFile));
301 file = mTempFile;
303 if (NS_WARN_IF(NS_FAILED(rv))) {
304 return nullptr;
307 nsCOMPtr<nsIFileOutputStream> stream =
308 do_CreateInstance("@mozilla.org/network/file-output-stream;1");
309 if (NS_FAILED(stream->Init(file, -1, -1, 0))) {
310 return nullptr;
312 return stream;
313 }();
315 return PrintTargetPDF::CreateOrNull(stream, IntSize::Ceil(width, height));
318 if (mDevMode) {
319 NS_WARNING_ASSERTION(!mDriverName.IsEmpty(), "No driver!");
320 HDC dc =
321 ::CreateDCW(mDriverName.get(), mDeviceName.get(), nullptr, mDevMode);
322 if (!dc) {
323 gfxCriticalError(gfxCriticalError::DefaultOptions(false))
324 << "Failed to create device context in GetSurfaceForPrinter";
325 return nullptr;
328 // The PrintTargetWindows takes over ownership of this DC
329 return PrintTargetWindows::CreateOrNull(dc);
332 return nullptr;
335 RefPtr<PrintEndDocumentPromise> nsDeviceContextSpecWin::EndDocument() {
336 if (mPrintSettings->GetOutputDestination() !=
337 nsIPrintSettings::kOutputDestinationFile ||
338 mOutputFormat != nsIPrintSettings::kOutputFormatPDF) {
339 return PrintEndDocumentPromise::CreateAndResolve(true, __func__);
342 #ifdef MOZ_ENABLE_SKIA_PDF
343 if (mPrintViaSkPDF) {
344 return PrintEndDocumentPromise::CreateAndResolve(true, __func__);
346 #endif
348 MOZ_ASSERT(mTempFile, "No handle to temporary PDF file.");
350 nsAutoString targetPath;
351 mPrintSettings->GetToFileName(targetPath);
353 if (targetPath.IsEmpty()) {
354 return PrintEndDocumentPromise::CreateAndResolve(true, __func__);
357 // We still need to move the file to its actual destination.
358 nsCOMPtr<nsIFile> destFile;
359 auto rv = NS_NewLocalFile(targetPath, false, getter_AddRefs(destFile));
360 if (NS_FAILED(rv)) {
361 return PrintEndDocumentPromise::CreateAndReject(rv, __func__);
364 return nsIDeviceContextSpec::EndDocumentAsync(
365 __func__,
366 [destFile = std::move(destFile),
367 tempFile = std::move(mTempFile)]() -> nsresult {
368 nsAutoString destLeafName;
369 MOZ_TRY(destFile->GetLeafName(destLeafName));
371 nsCOMPtr<nsIFile> destDir;
372 MOZ_TRY(destFile->GetParent(getter_AddRefs(destDir)));
374 // This should be fine - Windows API calls usually prevent moving
375 // between different volumes (See Win32 API's `MOVEFILE_COPY_ALLOWED`
376 // flag), but we handle that down this call.
377 MOZ_TRY(tempFile->MoveTo(destDir, destLeafName));
378 return NS_OK;
382 //----------------------------------------------------------------------------------
383 void nsDeviceContextSpecWin::SetDeviceName(const nsAString& aDeviceName) {
384 mDeviceName = aDeviceName;
387 //----------------------------------------------------------------------------------
388 void nsDeviceContextSpecWin::SetDriverName(const nsAString& aDriverName) {
389 mDriverName = aDriverName;
392 //----------------------------------------------------------------------------------
393 void nsDeviceContextSpecWin::SetDevMode(LPDEVMODEW aDevMode) {
394 if (mDevMode) {
395 ::HeapFree(::GetProcessHeap(), 0, mDevMode);
398 mDevMode = aDevMode;
401 //------------------------------------------------------------------
402 void nsDeviceContextSpecWin::GetDevMode(LPDEVMODEW& aDevMode) {
403 aDevMode = mDevMode;
406 #define DISPLAY_LAST_ERROR
408 //----------------------------------------------------------------------------------
409 // Setup the object's data member with the selected printer's data
410 nsresult nsDeviceContextSpecWin::GetDataFromPrinter(const nsAString& aName,
411 nsIPrintSettings* aPS) {
412 nsresult rv = NS_ERROR_FAILURE;
414 nsHPRINTER hPrinter = nullptr;
415 const nsString& flat = PromiseFlatString(aName);
416 wchar_t* name =
417 (wchar_t*)flat.get(); // Windows APIs use non-const name argument
419 BOOL status = ::OpenPrinterW(name, &hPrinter, nullptr);
420 if (status) {
421 nsAutoPrinter autoPrinter(hPrinter);
423 LPDEVMODEW pDevMode;
425 // Allocate a buffer of the correct size.
426 LONG needed =
427 ::DocumentPropertiesW(nullptr, hPrinter, name, nullptr, nullptr, 0);
428 if (needed < 0) {
429 PR_PL(
430 ("**** nsDeviceContextSpecWin::GetDataFromPrinter - Couldn't get "
431 "size of DEVMODE using DocumentPropertiesW(pDeviceName = \"%s\"). "
432 "GetLastEror() = %08lx\n",
433 NS_ConvertUTF16toUTF8(aName).get(), GetLastError()));
434 return NS_ERROR_FAILURE;
437 // Some drivers do not return the correct size for their DEVMODE, so we
438 // over-allocate to try and compensate.
439 // (See https://bugzilla.mozilla.org/show_bug.cgi?id=1664530#c5)
440 needed *= 2;
441 pDevMode =
442 (LPDEVMODEW)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, needed);
443 if (!pDevMode) return NS_ERROR_FAILURE;
445 // Get the default DevMode for the printer and modify it for our needs.
446 LONG ret = ::DocumentPropertiesW(nullptr, hPrinter, name, pDevMode, nullptr,
447 DM_OUT_BUFFER);
449 if (ret == IDOK && aPS) {
450 nsCOMPtr<nsIPrintSettingsWin> psWin = do_QueryInterface(aPS);
451 MOZ_ASSERT(psWin);
452 psWin->CopyToNative(pDevMode);
453 // Sets back the changes we made to the DevMode into the Printer Driver
454 ret = ::DocumentPropertiesW(nullptr, hPrinter, name, pDevMode, pDevMode,
455 DM_IN_BUFFER | DM_OUT_BUFFER);
457 // We need to copy the final DEVMODE settings back to our print settings,
458 // because they may have been set from invalid prefs.
459 if (ret == IDOK) {
460 // We need to get information from the device as well.
461 nsAutoHDC printerDC(::CreateICW(kDriverName, name, nullptr, pDevMode));
462 if (NS_WARN_IF(!printerDC)) {
463 ::HeapFree(::GetProcessHeap(), 0, pDevMode);
464 return NS_ERROR_FAILURE;
467 psWin->CopyFromNative(printerDC, pDevMode);
471 if (ret != IDOK) {
472 ::HeapFree(::GetProcessHeap(), 0, pDevMode);
473 PR_PL(
474 ("***** nsDeviceContextSpecWin::GetDataFromPrinter - "
475 "DocumentProperties call failed code: %ld/0x%lx\n",
476 ret, ret));
477 DISPLAY_LAST_ERROR
478 return NS_ERROR_FAILURE;
481 SetDevMode(
482 pDevMode); // cache the pointer and takes responsibility for the memory
484 SetDeviceName(aName);
486 SetDriverName(nsDependentString(kDriverName));
488 rv = NS_OK;
489 } else {
490 rv = NS_ERROR_GFX_PRINTER_NAME_NOT_FOUND;
491 PR_PL(
492 ("***** nsDeviceContextSpecWin::GetDataFromPrinter - Couldn't open "
493 "printer: [%s]\n",
494 NS_ConvertUTF16toUTF8(aName).get()));
495 DISPLAY_LAST_ERROR
497 return rv;
500 //***********************************************************
501 // Printer List
502 //***********************************************************
504 nsPrinterListWin::~nsPrinterListWin() = default;
506 // Helper to get the array of PRINTER_INFO_4 records from the OS into a
507 // caller-supplied byte array; returns the number of records present.
508 static unsigned GetPrinterInfo4(nsTArray<BYTE>& aBuffer) {
509 const DWORD kLevel = 4;
510 DWORD needed = 0;
511 DWORD count = 0;
512 const DWORD kFlags = PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS;
513 BOOL ok = ::EnumPrintersW(kFlags,
514 nullptr, // Name
515 kLevel, // Level
516 nullptr, // pPrinterEnum
517 0, // cbBuf (buffer size)
518 &needed, // Bytes needed in buffer
519 &count);
520 if (needed > 0) {
521 if (!aBuffer.SetLength(needed, fallible)) {
522 return 0;
524 ok = ::EnumPrintersW(kFlags, nullptr, kLevel, aBuffer.Elements(),
525 aBuffer.Length(), &needed, &count);
527 if (!ok) {
528 return 0;
530 return count;
533 nsTArray<nsPrinterListBase::PrinterInfo> nsPrinterListWin::Printers() const {
534 PR_PL(("nsPrinterListWin::Printers\n"));
536 AutoTArray<BYTE, 1024> buffer;
537 unsigned count = GetPrinterInfo4(buffer);
539 if (!count) {
540 PR_PL(("[No printers found]\n"));
541 return {};
544 const auto* printers =
545 reinterpret_cast<const _PRINTER_INFO_4W*>(buffer.Elements());
546 nsTArray<PrinterInfo> list;
547 for (unsigned i = 0; i < count; i++) {
548 // For LOCAL printers, we check whether OpenPrinter succeeds, and omit
549 // them from the list if not. This avoids presenting printers that the
550 // user cannot actually use (e.g. due to Windows permissions).
551 // For NETWORK printers, this check may block for a long time (waiting for
552 // network timeout), so we skip it; if the user tries to access a printer
553 // that isn't available, we'll have to show an error later.
554 // (We always need to be able to handle an error, anyhow, as the printer
555 // could get disconnected after we've created the list, for example.)
556 bool isAvailable = false;
557 if (printers[i].Attributes & PRINTER_ATTRIBUTE_NETWORK) {
558 isAvailable = true;
559 } else if (printers[i].Attributes & PRINTER_ATTRIBUTE_LOCAL) {
560 HANDLE handle;
561 if (::OpenPrinterW(printers[i].pPrinterName, &handle, nullptr)) {
562 ::ClosePrinter(handle);
563 isAvailable = true;
566 if (isAvailable) {
567 list.AppendElement(PrinterInfo{nsString(printers[i].pPrinterName)});
568 PR_PL(("Printer Name: %s\n",
569 NS_ConvertUTF16toUTF8(printers[i].pPrinterName).get()));
573 if (!count) {
574 PR_PL(("[No usable printers found]\n"));
575 return {};
578 return list;
581 Maybe<nsPrinterListBase::PrinterInfo> nsPrinterListWin::PrinterByName(
582 nsString aName) const {
583 Maybe<PrinterInfo> rv;
585 AutoTArray<BYTE, 1024> buffer;
586 unsigned count = GetPrinterInfo4(buffer);
588 const auto* printers =
589 reinterpret_cast<const _PRINTER_INFO_4W*>(buffer.Elements());
590 for (unsigned i = 0; i < count; ++i) {
591 if (aName.Equals(nsString(printers[i].pPrinterName))) {
592 rv.emplace(PrinterInfo{aName});
593 break;
597 return rv;
600 Maybe<nsPrinterListBase::PrinterInfo> nsPrinterListWin::PrinterBySystemName(
601 nsString aName) const {
602 return PrinterByName(std::move(aName));
605 RefPtr<nsIPrinter> nsPrinterListWin::CreatePrinter(PrinterInfo aInfo) const {
606 return nsPrinterWin::Create(mCommonPaperInfo, std::move(aInfo.mName));
609 nsresult nsPrinterListWin::SystemDefaultPrinterName(nsAString& aName) const {
610 if (!GetDefaultPrinterName(aName)) {
611 NS_WARNING("Uh oh, GetDefaultPrinterName failed");
612 // Indicate failure by leaving aName untouched, i.e. the empty string.
614 return NS_OK;
617 NS_IMETHODIMP
618 nsPrinterListWin::InitPrintSettingsFromPrinter(
619 const nsAString& aPrinterName, nsIPrintSettings* aPrintSettings) {
620 NS_ENSURE_ARG_POINTER(aPrintSettings);
622 if (aPrinterName.IsEmpty()) {
623 return NS_OK;
626 // When printing to PDF on Windows there is no associated printer driver.
627 int16_t outputFormat = aPrintSettings->GetOutputFormat();
628 if (outputFormat == nsIPrintSettings::kOutputFormatPDF) {
629 return NS_OK;
632 RefPtr<nsDeviceContextSpecWin> devSpecWin = new nsDeviceContextSpecWin();
633 if (!devSpecWin) return NS_ERROR_OUT_OF_MEMORY;
635 // If the settings have already been initialized from prefs then pass these to
636 // GetDataFromPrinter, so that they are saved to the printer.
637 bool initializedFromPrefs;
638 nsresult rv =
639 aPrintSettings->GetIsInitializedFromPrefs(&initializedFromPrefs);
640 if (NS_WARN_IF(NS_FAILED(rv))) {
641 return rv;
643 if (initializedFromPrefs) {
644 // If we pass in print settings to GetDataFromPrinter it already copies
645 // things back to the settings, so we can return here.
646 return devSpecWin->GetDataFromPrinter(aPrinterName, aPrintSettings);
649 devSpecWin->GetDataFromPrinter(aPrinterName);
651 LPDEVMODEW devmode;
652 devSpecWin->GetDevMode(devmode);
653 if (NS_WARN_IF(!devmode)) {
654 return NS_ERROR_FAILURE;
657 aPrintSettings->SetPrinterName(aPrinterName);
659 // We need to get information from the device as well.
660 const nsString& flat = PromiseFlatString(aPrinterName);
661 char16ptr_t printerName = flat.get();
662 HDC dc = ::CreateICW(kDriverName, printerName, nullptr, devmode);
663 if (NS_WARN_IF(!dc)) {
664 return NS_ERROR_FAILURE;
667 nsCOMPtr<nsIPrintSettingsWin> psWin = do_QueryInterface(aPrintSettings);
668 MOZ_ASSERT(psWin);
669 psWin->CopyFromNative(dc, devmode);
670 ::DeleteDC(dc);
672 return NS_OK;