Bug 1822519 - When qcms_profile_from_memory fails, default to sRGB for display space...
[gecko.git] / widget / nsPrinterListCUPS.cpp
blob6b215e21f4efc01eadb4dc6a58b328f6da052644
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"
12 #include "nsString.h"
13 #include "prenv.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;
19 return sCupsShim;
22 using PrinterInfo = nsPrinterListBase::PrinterInfo;
24 /**
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,
29 nsAString& aName) {
30 // macOS clients expect prettified printer names
31 // while GTK clients expect non-prettified names.
32 // If you change this, please change NamedPrinter accordingly.
33 #ifdef XP_MACOSX
34 const char* displayName = CupsShim().cupsGetOption(
35 "printer-info", aDest.num_options, aDest.options);
36 if (displayName) {
37 CopyUTF8toUTF16(MakeStringSpan(displayName), aName);
39 #endif
42 NS_IMETHODIMP
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");
52 if (!path) {
53 path = PR_GetEnv("HOME");
56 if (path) {
57 CopyUTF8toUTF16(mozilla::MakeStringSpan(path), filename);
58 filename.AppendLiteral("/mozilla.pdf");
59 } else {
60 filename.AssignLiteral("mozilla.pdf");
63 aPrintSettings->SetToFileName(filename);
66 aPrintSettings->SetIsInitializedFromPrinter(true);
67 return NS_OK;
70 static int CupsDestCallback(void* user_data, unsigned aFlags,
71 cups_dest_t* aDest) {
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);
80 nsString name;
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()) {
90 return {};
93 auto FreeDestsAndClear = [](nsTArray<PrinterInfo>& aArray) {
94 for (auto& info : aArray) {
95 CupsShim().cupsFreeDests(1, static_cast<cups_dest_t*>(info.mCupsHandle));
97 aArray.Clear();
100 nsTArray<PrinterInfo> printerInfoList;
101 // cupsGetDests2 returns list of found printers without duplicates, unlike
102 // cupsEnumDests
103 cups_dest_t* printers = nullptr;
104 const auto numPrinters = CupsShim().cupsGetDests2(nullptr, &printers);
105 if (numPrinters > 0) {
106 for (auto i : mozilla::IntegerRange(0, numPrinters)) {
107 cups_dest_t* ownedDest = nullptr;
108 mozilla::DebugOnly<const int> numCopied =
109 CupsShim().cupsCopyDest(printers + i, 0, &ownedDest);
110 MOZ_ASSERT(numCopied == 1);
112 nsString name;
113 GetDisplayNameForPrinter(*(printers + i), name);
114 printerInfoList.AppendElement(PrinterInfo{std::move(name), ownedDest});
116 CupsShim().cupsFreeDests(numPrinters, printers);
117 return printerInfoList;
120 // An error occurred - retry with CUPS_PRINTER_DISCOVERED masked out (since
121 // it looks like there are a lot of error cases for that in cupsEnumDests):
122 if (CupsShim().cupsEnumDests(
123 CUPS_DEST_FLAGS_NONE,
124 0 /* 0 timeout should be okay when masking CUPS_PRINTER_DISCOVERED */,
125 nullptr /* cancel* */, CUPS_PRINTER_LOCAL,
126 CUPS_PRINTER_FAX | CUPS_PRINTER_SCANNER | CUPS_PRINTER_DISCOVERED,
127 &CupsDestCallback, &printerInfoList)) {
128 return printerInfoList;
131 // Another error occurred. Maybe printerInfoList could be partially
132 // populated, so perhaps we could return it without clearing it in the hope
133 // that there are some usable dests. However, presuambly CUPS doesn't
134 // guarantee that any dests that it added are complete and safe to use when
135 // an error occurs?
136 FreeDestsAndClear(printerInfoList);
137 return {};
140 RefPtr<nsIPrinter> nsPrinterListCUPS::CreatePrinter(PrinterInfo aInfo) const {
141 return mozilla::MakeRefPtr<nsPrinterCUPS>(
142 mCommonPaperInfo, CupsShim(), std::move(aInfo.mName),
143 static_cast<cups_dest_t*>(aInfo.mCupsHandle));
146 mozilla::Maybe<PrinterInfo> nsPrinterListCUPS::PrinterByName(
147 nsString aPrinterName) const {
148 mozilla::Maybe<PrinterInfo> rv;
149 if (!CupsShim().InitOkay()) {
150 return rv;
153 // Will contain the printer, if found. This must be fully owned, and not a
154 // member of another array of printers.
155 cups_dest_t* printer = nullptr;
157 #ifdef XP_MACOSX
158 // On OS X the printer name given to this function is the readable/display
159 // name and not the CUPS name, so we iterate over all the printers for now.
160 // See bug 1659807 for one approach to improve perf here.
162 nsAutoCString printerName;
163 CopyUTF16toUTF8(aPrinterName, printerName);
164 cups_dest_t* printers = nullptr;
165 const auto numPrinters = CupsShim().cupsGetDests(&printers);
166 for (auto i : mozilla::IntegerRange(0, numPrinters)) {
167 const char* const displayName = CupsShim().cupsGetOption(
168 "printer-info", printers[i].num_options, printers[i].options);
169 if (printerName == displayName) {
170 // The second arg to CupsShim().cupsCopyDest is called num_dests, but
171 // it actually copies num_dests + 1 elements.
172 CupsShim().cupsCopyDest(printers + i, 0, &printer);
173 break;
176 CupsShim().cupsFreeDests(numPrinters, printers);
178 #else
179 // On GTK, we only ever show the CUPS name of printers, so we can use
180 // cupsGetNamedDest directly.
182 const auto printerName = NS_ConvertUTF16toUTF8(aPrinterName);
183 printer = CupsShim().cupsGetNamedDest(CUPS_HTTP_DEFAULT, printerName.get(),
184 nullptr);
186 #endif
188 if (printer) {
189 // Since the printer name had to be passed by-value, we can move the
190 // name from that.
191 rv.emplace(PrinterInfo{std::move(aPrinterName), printer});
193 return rv;
196 mozilla::Maybe<PrinterInfo> nsPrinterListCUPS::PrinterBySystemName(
197 nsString aPrinterName) const {
198 mozilla::Maybe<PrinterInfo> rv;
199 if (!CupsShim().InitOkay()) {
200 return rv;
203 const auto printerName = NS_ConvertUTF16toUTF8(aPrinterName);
204 if (cups_dest_t* const printer = CupsShim().cupsGetNamedDest(
205 CUPS_HTTP_DEFAULT, printerName.get(), nullptr)) {
206 rv.emplace(PrinterInfo{std::move(aPrinterName), printer});
208 return rv;
211 nsresult nsPrinterListCUPS::SystemDefaultPrinterName(nsAString& aName) const {
212 aName.Truncate();
214 if (!CupsShim().InitOkay()) {
215 return NS_OK;
218 // Passing in nullptr for the name will return the default, if any.
219 cups_dest_t* dest =
220 CupsShim().cupsGetNamedDest(CUPS_HTTP_DEFAULT, /* name */ nullptr,
221 /* instance */ nullptr);
222 if (!dest) {
223 return NS_OK;
226 GetDisplayNameForPrinter(*dest, aName);
227 if (aName.IsEmpty()) {
228 CopyUTF8toUTF16(mozilla::MakeStringSpan(dest->name), aName);
231 CupsShim().cupsFreeDests(1, dest);
232 return NS_OK;