diff options
Diffstat (limited to 'widget/nsPrinterListCUPS.cpp')
-rw-r--r-- | widget/nsPrinterListCUPS.cpp | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/widget/nsPrinterListCUPS.cpp b/widget/nsPrinterListCUPS.cpp new file mode 100644 index 0000000000..2629d7626e --- /dev/null +++ b/widget/nsPrinterListCUPS.cpp @@ -0,0 +1,233 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsPrinterListCUPS.h" + +#include "mozilla/IntegerRange.h" +#include "mozilla/Maybe.h" +#include "mozilla/StaticPrefs_print.h" +#include "nsCUPSShim.h" +#include "nsPrinterCUPS.h" +#include "nsString.h" +#include "prenv.h" + +// Use a local static to initialize the CUPS shim lazily, when it's needed. +// This is used in order to avoid a global constructor. +static nsCUPSShim& CupsShim() { + static nsCUPSShim sCupsShim; + return sCupsShim; +} + +using PrinterInfo = nsPrinterListBase::PrinterInfo; + +/** + * Retrieves a human-readable name for the printer from CUPS. + * https://www.cups.org/doc/cupspm.html#basic-destination-information + */ +static void GetDisplayNameForPrinter(const cups_dest_t& aDest, + nsAString& aName) { +// macOS clients expect prettified printer names +// while GTK clients expect non-prettified names. +// If you change this, please change NamedPrinter accordingly. +#ifdef XP_MACOSX + const char* displayName = CupsShim().cupsGetOption( + "printer-info", aDest.num_options, aDest.options); + if (displayName) { + CopyUTF8toUTF16(mozilla::MakeStringSpan(displayName), aName); + } +#endif +} + +NS_IMETHODIMP +nsPrinterListCUPS::InitPrintSettingsFromPrinter( + const nsAString& aPrinterName, nsIPrintSettings* aPrintSettings) { + MOZ_ASSERT(aPrintSettings); + + // Set a default file name. + nsAutoString filename; + nsresult rv = aPrintSettings->GetToFileName(filename); + if (NS_FAILED(rv) || filename.IsEmpty()) { + const char* path = PR_GetEnv("PWD"); + if (!path) { + path = PR_GetEnv("HOME"); + } + + if (path) { + CopyUTF8toUTF16(mozilla::MakeStringSpan(path), filename); + filename.AppendLiteral("/mozilla.pdf"); + } else { + filename.AssignLiteral("mozilla.pdf"); + } + + aPrintSettings->SetToFileName(filename); + } + + aPrintSettings->SetIsInitializedFromPrinter(true); + return NS_OK; +} + +static int CupsDestCallback(void* user_data, unsigned aFlags, + cups_dest_t* aDest) { + MOZ_ASSERT(user_data); + auto* printerInfoList = static_cast<nsTArray<PrinterInfo>*>(user_data); + + cups_dest_t* ownedDest = nullptr; + mozilla::DebugOnly<const int> numCopied = + CupsShim().cupsCopyDest(aDest, 0, &ownedDest); + MOZ_ASSERT(numCopied == 1); + + nsString name; + GetDisplayNameForPrinter(*aDest, name); + + printerInfoList->AppendElement(PrinterInfo{std::move(name), ownedDest}); + + return aFlags == CUPS_DEST_FLAGS_MORE ? 1 : 0; +} + +nsTArray<PrinterInfo> nsPrinterListCUPS::Printers() const { + if (!CupsShim().InitOkay()) { + return {}; + } + + auto FreeDestsAndClear = [](nsTArray<PrinterInfo>& aArray) { + for (auto& info : aArray) { + CupsShim().cupsFreeDests(1, static_cast<cups_dest_t*>(info.mCupsHandle)); + } + aArray.Clear(); + }; + + nsTArray<PrinterInfo> printerInfoList; + // cupsGetDests2 returns list of found printers without duplicates, unlike + // cupsEnumDests + cups_dest_t* printers = nullptr; + const auto numPrinters = CupsShim().cupsGetDests2(nullptr, &printers); + if (numPrinters > 0) { + for (auto i : mozilla::IntegerRange(0, numPrinters)) { + cups_dest_t* ownedDest = nullptr; + mozilla::DebugOnly<const int> numCopied = + CupsShim().cupsCopyDest(printers + i, 0, &ownedDest); + MOZ_ASSERT(numCopied == 1); + + nsString name; + GetDisplayNameForPrinter(*(printers + i), name); + printerInfoList.AppendElement(PrinterInfo{std::move(name), ownedDest}); + } + CupsShim().cupsFreeDests(numPrinters, printers); + return printerInfoList; + } + + // An error occurred - retry with CUPS_PRINTER_DISCOVERED masked out (since + // it looks like there are a lot of error cases for that in cupsEnumDests): + if (CupsShim().cupsEnumDests( + CUPS_DEST_FLAGS_NONE, + 0 /* 0 timeout should be okay when masking CUPS_PRINTER_DISCOVERED */, + nullptr /* cancel* */, CUPS_PRINTER_LOCAL, + CUPS_PRINTER_FAX | CUPS_PRINTER_SCANNER | CUPS_PRINTER_DISCOVERED, + &CupsDestCallback, &printerInfoList)) { + return printerInfoList; + } + + // Another error occurred. Maybe printerInfoList could be partially + // populated, so perhaps we could return it without clearing it in the hope + // that there are some usable dests. However, presuambly CUPS doesn't + // guarantee that any dests that it added are complete and safe to use when + // an error occurs? + FreeDestsAndClear(printerInfoList); + return {}; +} + +RefPtr<nsIPrinter> nsPrinterListCUPS::CreatePrinter(PrinterInfo aInfo) const { + return mozilla::MakeRefPtr<nsPrinterCUPS>( + mCommonPaperInfo, CupsShim(), std::move(aInfo.mName), + static_cast<cups_dest_t*>(aInfo.mCupsHandle)); +} + +mozilla::Maybe<PrinterInfo> nsPrinterListCUPS::PrinterByName( + nsString aPrinterName) const { + mozilla::Maybe<PrinterInfo> rv; + if (!CupsShim().InitOkay()) { + return rv; + } + + // Will contain the printer, if found. This must be fully owned, and not a + // member of another array of printers. + cups_dest_t* printer = nullptr; + +#ifdef XP_MACOSX + // On OS X the printer name given to this function is the readable/display + // name and not the CUPS name, so we iterate over all the printers for now. + // See bug 1659807 for one approach to improve perf here. + { + nsAutoCString printerName; + CopyUTF16toUTF8(aPrinterName, printerName); + cups_dest_t* printers = nullptr; + const auto numPrinters = CupsShim().cupsGetDests(&printers); + for (auto i : mozilla::IntegerRange(0, numPrinters)) { + const char* const displayName = CupsShim().cupsGetOption( + "printer-info", printers[i].num_options, printers[i].options); + if (printerName == displayName) { + // The second arg to CupsShim().cupsCopyDest is called num_dests, but + // it actually copies num_dests + 1 elements. + CupsShim().cupsCopyDest(printers + i, 0, &printer); + break; + } + } + CupsShim().cupsFreeDests(numPrinters, printers); + } +#else + // On GTK, we only ever show the CUPS name of printers, so we can use + // cupsGetNamedDest directly. + { + const auto printerName = NS_ConvertUTF16toUTF8(aPrinterName); + printer = CupsShim().cupsGetNamedDest(CUPS_HTTP_DEFAULT, printerName.get(), + nullptr); + } +#endif + + if (printer) { + // Since the printer name had to be passed by-value, we can move the + // name from that. + rv.emplace(PrinterInfo{std::move(aPrinterName), printer}); + } + return rv; +} + +mozilla::Maybe<PrinterInfo> nsPrinterListCUPS::PrinterBySystemName( + nsString aPrinterName) const { + mozilla::Maybe<PrinterInfo> rv; + if (!CupsShim().InitOkay()) { + return rv; + } + + const auto printerName = NS_ConvertUTF16toUTF8(aPrinterName); + if (cups_dest_t* const printer = CupsShim().cupsGetNamedDest( + CUPS_HTTP_DEFAULT, printerName.get(), nullptr)) { + rv.emplace(PrinterInfo{std::move(aPrinterName), printer}); + } + return rv; +} + +nsresult nsPrinterListCUPS::SystemDefaultPrinterName(nsAString& aName) const { + aName.Truncate(); + + if (!CupsShim().InitOkay()) { + return NS_OK; + } + + // Passing in nullptr for the name will return the default, if any. + cups_dest_t* dest = + CupsShim().cupsGetNamedDest(CUPS_HTTP_DEFAULT, /* name */ nullptr, + /* instance */ nullptr); + if (!dest) { + return NS_OK; + } + + GetDisplayNameForPrinter(*dest, aName); + if (aName.IsEmpty()) { + CopyUTF8toUTF16(mozilla::MakeStringSpan(dest->name), aName); + } + + CupsShim().cupsFreeDests(1, dest); + return NS_OK; +} |