forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			233 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			233 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* 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;
 | |
| }
 | 
