1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "nsPrinterListCUPS.h"
7 #include "mozilla/IntegerRange.h"
8 #include "mozilla/Maybe.h"
9 #include "mozilla/StaticPrefs_print.h"
10 #include "nsCUPSShim.h"
11 #include "nsPrinterCUPS.h"
15 // Use a local static to initialize the CUPS shim lazily, when it's needed.
16 // This is used in order to avoid a global constructor.
17 static nsCUPSShim
& CupsShim() {
18 static nsCUPSShim sCupsShim
;
22 using PrinterInfo
= nsPrinterListBase::PrinterInfo
;
25 * Retrieves a human-readable name for the printer from CUPS.
26 * https://www.cups.org/doc/cupspm.html#basic-destination-information
28 static void GetDisplayNameForPrinter(const cups_dest_t
& aDest
,
30 // macOS clients expect prettified printer names
31 // while GTK clients expect non-prettified names.
32 // If you change this, please change NamedPrinter accordingly.
34 const char* displayName
= CupsShim().cupsGetOption(
35 "printer-info", aDest
.num_options
, aDest
.options
);
37 CopyUTF8toUTF16(MakeStringSpan(displayName
), aName
);
43 nsPrinterListCUPS::InitPrintSettingsFromPrinter(
44 const nsAString
& aPrinterName
, nsIPrintSettings
* aPrintSettings
) {
45 MOZ_ASSERT(aPrintSettings
);
47 // Set a default file name.
48 nsAutoString filename
;
49 nsresult rv
= aPrintSettings
->GetToFileName(filename
);
50 if (NS_FAILED(rv
) || filename
.IsEmpty()) {
51 const char* path
= PR_GetEnv("PWD");
53 path
= PR_GetEnv("HOME");
57 CopyUTF8toUTF16(mozilla::MakeStringSpan(path
), filename
);
58 filename
.AppendLiteral("/mozilla.pdf");
60 filename
.AssignLiteral("mozilla.pdf");
63 aPrintSettings
->SetToFileName(filename
);
66 aPrintSettings
->SetIsInitializedFromPrinter(true);
70 static int CupsDestCallback(void* user_data
, unsigned aFlags
,
72 MOZ_ASSERT(user_data
);
73 auto* printerInfoList
= static_cast<nsTArray
<PrinterInfo
>*>(user_data
);
75 cups_dest_t
* ownedDest
= nullptr;
76 mozilla::DebugOnly
<const int> numCopied
=
77 CupsShim().cupsCopyDest(aDest
, 0, &ownedDest
);
78 MOZ_ASSERT(numCopied
== 1);
81 GetDisplayNameForPrinter(*aDest
, name
);
83 printerInfoList
->AppendElement(PrinterInfo
{std::move(name
), ownedDest
});
85 return aFlags
== CUPS_DEST_FLAGS_MORE
? 1 : 0;
88 nsTArray
<PrinterInfo
> nsPrinterListCUPS::Printers() const {
89 if (!CupsShim().InitOkay()) {
93 auto FreeDestsAndClear
= [](nsTArray
<PrinterInfo
>& aArray
) {
94 for (auto& info
: aArray
) {
95 CupsShim().cupsFreeDests(1, static_cast<cups_dest_t
*>(info
.mCupsHandle
));
100 nsTArray
<PrinterInfo
> printerInfoList
;
101 if (CupsShim().cupsEnumDests(
102 CUPS_DEST_FLAGS_NONE
,
103 int(StaticPrefs::print_cups_enum_dests_timeout_ms()),
104 nullptr /* cancel* */, CUPS_PRINTER_LOCAL
,
105 CUPS_PRINTER_FAX
| CUPS_PRINTER_SCANNER
, &CupsDestCallback
,
107 return printerInfoList
;
110 // An error occurred - retry with CUPS_PRINTER_DISCOVERED masked out (since
111 // it looks like there are a lot of error cases for that in cupsEnumDests):
112 FreeDestsAndClear(printerInfoList
);
113 if (CupsShim().cupsEnumDests(
114 CUPS_DEST_FLAGS_NONE
,
115 0 /* 0 timeout should be okay when masking CUPS_PRINTER_DISCOVERED */,
116 nullptr /* cancel* */, CUPS_PRINTER_LOCAL
,
117 CUPS_PRINTER_FAX
| CUPS_PRINTER_SCANNER
| CUPS_PRINTER_DISCOVERED
,
118 &CupsDestCallback
, &printerInfoList
)) {
119 return printerInfoList
;
122 // Another error occurred. Maybe printerInfoList could be partially
123 // populated, so perhaps we could return it without clearing it in the hope
124 // that there are some usable dests. However, presuambly CUPS doesn't
125 // guarantee that any dests that it added are complete and safe to use when
127 FreeDestsAndClear(printerInfoList
);
131 RefPtr
<nsIPrinter
> nsPrinterListCUPS::CreatePrinter(PrinterInfo aInfo
) const {
132 return mozilla::MakeRefPtr
<nsPrinterCUPS
>(
133 mCommonPaperInfo
, CupsShim(), std::move(aInfo
.mName
),
134 static_cast<cups_dest_t
*>(aInfo
.mCupsHandle
));
137 mozilla::Maybe
<PrinterInfo
> nsPrinterListCUPS::PrinterByName(
138 nsString aPrinterName
) const {
139 mozilla::Maybe
<PrinterInfo
> rv
;
140 if (!CupsShim().InitOkay()) {
144 // Will contain the printer, if found. This must be fully owned, and not a
145 // member of another array of printers.
146 cups_dest_t
* printer
= nullptr;
149 // On OS X the printer name given to this function is the readable/display
150 // name and not the CUPS name, so we iterate over all the printers for now.
151 // See bug 1659807 for one approach to improve perf here.
153 nsAutoCString printerName
;
154 CopyUTF16toUTF8(aPrinterName
, printerName
);
155 cups_dest_t
* printers
= nullptr;
156 const auto numPrinters
= CupsShim().cupsGetDests(&printers
);
157 for (auto i
: mozilla::IntegerRange(0, numPrinters
)) {
158 const char* const displayName
= CupsShim().cupsGetOption(
159 "printer-info", printers
[i
].num_options
, printers
[i
].options
);
160 if (printerName
== displayName
) {
161 // The second arg to CupsShim().cupsCopyDest is called num_dests, but
162 // it actually copies num_dests + 1 elements.
163 CupsShim().cupsCopyDest(printers
+ i
, 0, &printer
);
167 CupsShim().cupsFreeDests(numPrinters
, printers
);
170 // On GTK, we only ever show the CUPS name of printers, so we can use
171 // cupsGetNamedDest directly.
173 const auto printerName
= NS_ConvertUTF16toUTF8(aPrinterName
);
174 printer
= CupsShim().cupsGetNamedDest(CUPS_HTTP_DEFAULT
, printerName
.get(),
180 // Since the printer name had to be passed by-value, we can move the
182 rv
.emplace(PrinterInfo
{std::move(aPrinterName
), printer
});
187 mozilla::Maybe
<PrinterInfo
> nsPrinterListCUPS::PrinterBySystemName(
188 nsString aPrinterName
) const {
189 mozilla::Maybe
<PrinterInfo
> rv
;
190 if (!CupsShim().InitOkay()) {
194 const auto printerName
= NS_ConvertUTF16toUTF8(aPrinterName
);
195 if (cups_dest_t
* const printer
= CupsShim().cupsGetNamedDest(
196 CUPS_HTTP_DEFAULT
, printerName
.get(), nullptr)) {
197 rv
.emplace(PrinterInfo
{std::move(aPrinterName
), printer
});
202 nsresult
nsPrinterListCUPS::SystemDefaultPrinterName(nsAString
& aName
) const {
205 if (!CupsShim().InitOkay()) {
209 // Passing in nullptr for the name will return the default, if any.
211 CupsShim().cupsGetNamedDest(CUPS_HTTP_DEFAULT
, /* name */ nullptr,
212 /* instance */ nullptr);
217 GetDisplayNameForPrinter(*dest
, aName
);
218 if (aName
.IsEmpty()) {
219 CopyUTF8toUTF16(mozilla::MakeStringSpan(dest
->name
), aName
);
222 CupsShim().cupsFreeDests(1, dest
);