Bug 1657363 - Make nsIPrinterList.printers return a promise. r=jwatt

I intentionally removed the "move the default printer to the front" in
windows because that's not a guarantee that we provide in CUPS, but lmk
if you want it back.

I have zero idea about why the GlobalPrinters code was so ridiculously
complex.

Depends on D86396

Differential Revision: https://phabricator.services.mozilla.com/D86397
This commit is contained in:
Emilio Cobos Álvarez 2020-08-09 22:05:57 +00:00
Родитель b242b603bc
Коммит 2b6c7db4e8
16 изменённых файлов: 305 добавлений и 371 удалений

Просмотреть файл

@ -14,7 +14,9 @@ async function run() {
Ci.nsIPrinterList
);
for await (printer of printerList.printers) {
let printers = await printerList.printers;
for (let printer of printers) {
printer.QueryInterface(Ci.nsIPrinter);
info(`Listing basic attributes for ${printer.name}:`);
let [supportsDuplex, supportsColor] = await Promise.all([printer.supportsDuplex, printer.supportsColor]);
info(`* supportsDuplex: ${supportsDuplex}`);

Просмотреть файл

@ -13,11 +13,13 @@ async function run() {
let printerList = Cc["@mozilla.org/gfx/printerlist;1"].getService(
Ci.nsIPrinterList
);
if (printerList.printers.length == 0) {
let printers = await printerList.printers;
if (printers.length == 0) {
ok(true, "There were no printers to iterate through.");
}
for (let printer of printerList.printers) {
for (let printer of printers) {
printer.QueryInterface(Ci.nsIPrinter);
isnot(printer.name, null, "Printer name should never be null.");
isnot(printer.name, "", "Printer name should never be empty.");
info(printer.name);

Просмотреть файл

@ -0,0 +1,91 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef mozilla_PrintBackgroundTask_h_
#define mozilla_PrintBackgroundTask_h_
#include "mozilla/dom/Promise.h"
#include "mozilla/ErrorResult.h"
#include <utility>
#include <tuple>
// A helper to resolve a DOM Promise with the result of a const method, executed
// in another thread.
//
// Once in the main thread, the caller can turn the result of the method into a
// JSValue by specializing ResolveOrReject.
namespace mozilla {
template <typename T, typename Result>
void ResolveOrReject(dom::Promise& aPromise, T&, Result& aResult) {
aPromise.MaybeResolve(std::forward<Result>(aResult));
}
template <typename T, typename Result, typename... Args>
using PrintBackgroundTask = Result (T::*)(Args...) const;
template <typename T, typename Result, typename... Args>
void SpawnPrintBackgroundTask(
T& aReceiver, dom::Promise& aPromise,
PrintBackgroundTask<T, Result, Args...> aBackgroundTask, Args... aArgs) {
auto promiseHolder = MakeRefPtr<nsMainThreadPtrHolder<dom::Promise>>(
"nsPrinterBase::SpawnBackgroundTaskPromise", &aPromise);
// We actually want to allow to access the printer data from the callback, so
// disable strict checking. They should of course only access immutable
// members.
auto holder = MakeRefPtr<nsMainThreadPtrHolder<T>>(
"nsPrinterBase::SpawnBackgroundTaskPrinter", &aReceiver,
/* strict = */ false);
// See
// https://stackoverflow.com/questions/47496358/c-lambdas-how-to-capture-variadic-parameter-pack-from-the-upper-scope
// about the tuple shenanigans. It could be improved with C++20
NS_DispatchBackgroundTask(NS_NewRunnableFunction(
"SpawnPrintBackgroundTask",
[holder = std::move(holder), promiseHolder = std::move(promiseHolder),
aBackgroundTask, aArgs = std::make_tuple(std::forward<Args>(aArgs)...)] {
Result result = std::apply(
[&](auto&&... args) {
return (holder->get()->*aBackgroundTask)(args...);
},
std::move(aArgs));
NS_DispatchToMainThread(NS_NewRunnableFunction(
"SpawnPrintBackgroundTaskResolution",
[holder = std::move(holder),
promiseHolder = std::move(promiseHolder),
result = std::move(result)] {
ResolveOrReject(*promiseHolder->get(), *holder->get(), result);
}));
}));
}
// Resolves an async attribute via a background task, creating and storing a
// promise as needed in aPromiseSlot.
template <typename T, typename Result, typename... Args>
nsresult AsyncPromiseAttributeGetter(
T& aReceiver, RefPtr<dom::Promise>& aPromiseSlot, JSContext* aCx,
dom::Promise** aResultPromise,
PrintBackgroundTask<T, Result, Args...> aTask, Args... aArgs) {
if (RefPtr<dom::Promise> existing = aPromiseSlot) {
existing.forget(aResultPromise);
return NS_OK;
}
ErrorResult rv;
aPromiseSlot = dom::Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
if (MOZ_UNLIKELY(rv.Failed())) {
return rv.StealNSResult();
}
RefPtr<dom::Promise> promise = aPromiseSlot;
SpawnPrintBackgroundTask(aReceiver, *promise, aTask,
std::forward<Args>(aArgs)...);
promise.forget(aResultPromise);
return NS_OK;
}
} // namespace mozilla
#endif

Просмотреть файл

@ -56,7 +56,9 @@ interface nsIPrintSettingsService : nsISupports
readonly attribute nsIPrintSettings newPrintSettings;
/**
* The name of the last printer used, or else the system default printer.
* The name of the last printer used. Note that this may no longer be a valid
* printer anymore, the caller should check and fall back to the system
* default printer list otherwise.
*/
readonly attribute AString lastUsedPrinterName;

Просмотреть файл

@ -30,7 +30,7 @@ interface nsIPrinterList : nsISupports
readonly attribute AString systemDefaultPrinterName;
/**
* The list of printers.
* Returns a promise that resolves to an array of printers.
*/
readonly attribute Array<nsIPrinter> printers;
[implicit_jscontext] readonly attribute Promise printers;
};

Просмотреть файл

@ -890,36 +890,8 @@ nsPrintSettingsService::GetNewPrintSettings(
NS_IMETHODIMP
nsPrintSettingsService::GetLastUsedPrinterName(
nsAString& aLastUsedPrinterName) {
nsresult rv;
nsCOMPtr<nsIPrinterList> printerList =
do_GetService(NS_PRINTER_LIST_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
aLastUsedPrinterName.Truncate();
// Look up the printer from the last print job
nsAutoString lastUsedPrinterName;
Preferences::GetString(kPrinterName, lastUsedPrinterName);
if (!lastUsedPrinterName.IsEmpty()) {
// Verify it's still a valid printer
nsTArray<RefPtr<nsIPrinter>> printers;
rv = printerList->GetPrinters(printers);
if (NS_SUCCEEDED(rv)) {
for (nsIPrinter* printer : printers) {
nsAutoString printerName;
printer->GetName(printerName);
if (printerName.Equals(lastUsedPrinterName)) {
aLastUsedPrinterName = lastUsedPrinterName;
return NS_OK;
}
}
}
}
// There is no last printer preference, or it doesn't name a valid printer.
// Return the system default from the printer list.
printerList->GetSystemDefaultPrinterName(aLastUsedPrinterName);
Preferences::GetString(kPrinterName, aLastUsedPrinterName);
return NS_OK;
}

Просмотреть файл

@ -7,6 +7,7 @@
#include "nsPaperMargin.h"
#include <utility>
#include "nsPaper.h"
#include "PrintBackgroundTask.h"
#include "mozilla/dom/Promise.h"
using namespace mozilla;
@ -32,10 +33,7 @@ inline void ImplCycleCollectionUnlink(
}
}
template <typename T>
void ResolveOrReject(Promise& aPromise, nsPrinterBase&, T& aResult) {
aPromise.MaybeResolve(std::forward<T>(aResult));
}
namespace mozilla {
template <>
void ResolveOrReject(Promise& aPromise, nsPrinterBase&,
@ -55,86 +53,42 @@ void ResolveOrReject(Promise& aPromise, nsPrinterBase& aPrinter,
aPromise.MaybeResolve(result);
}
template <typename T, typename... Args>
void nsPrinterBase::SpawnBackgroundTask(
Promise& aPromise, BackgroundTask<T, Args...> aBackgroundTask,
Args... aArgs) {
auto promiseHolder = MakeRefPtr<nsMainThreadPtrHolder<Promise>>(
"nsPrinterBase::SpawnBackgroundTaskPromise", &aPromise);
// We actually want to allow to access the printer data from the callback, so
// disable strict checking. They should of course only access immutable
// members.
auto holder = MakeRefPtr<nsMainThreadPtrHolder<nsPrinterBase>>(
"nsPrinterBase::SpawnBackgroundTaskPrinter", this, /* strict = */ false);
// See
// https://stackoverflow.com/questions/47496358/c-lambdas-how-to-capture-variadic-parameter-pack-from-the-upper-scope
// about the tuple shenanigans. It could be improved with C++20
NS_DispatchBackgroundTask(NS_NewRunnableFunction(
"nsPrinterBase::SpawnBackgroundTask",
[holder = std::move(holder), promiseHolder = std::move(promiseHolder),
aBackgroundTask, aArgs = std::make_tuple(std::forward<Args>(aArgs)...)] {
T result = std::apply(
[&](auto&&... args) {
return (holder->get()->*aBackgroundTask)(args...);
},
std::move(aArgs));
NS_DispatchToMainThread(NS_NewRunnableFunction(
"nsPrinterBase::SpawnBackgroundTaskResolution",
[holder = std::move(holder),
promiseHolder = std::move(promiseHolder),
result = std::move(result)] {
ResolveOrReject(*promiseHolder->get(), *holder->get(), result);
}));
}));
}
} // namespace mozilla
template <typename T, typename... Args>
nsresult nsPrinterBase::AsyncPromiseAttributeGetter(
JSContext* aCx, Promise** aResultPromise, AsyncAttribute aAttribute,
BackgroundTask<T, Args...> aBackgroundTask, Args... aArgs) {
MOZ_ASSERT(NS_IsMainThread());
if (RefPtr<Promise> existing = mAsyncAttributePromises[aAttribute]) {
existing.forget(aResultPromise);
return NS_OK;
}
ErrorResult rv;
mAsyncAttributePromises[aAttribute] =
Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
if (MOZ_UNLIKELY(rv.Failed())) {
return rv.StealNSResult();
}
RefPtr<Promise> promise = mAsyncAttributePromises[aAttribute];
SpawnBackgroundTask(*promise, aBackgroundTask, aArgs...);
promise.forget(aResultPromise);
return NS_OK;
return mozilla::AsyncPromiseAttributeGetter(
*this, mAsyncAttributePromises[aAttribute], aCx, aResultPromise,
aBackgroundTask, std::forward<Args>(aArgs)...);
}
NS_IMETHODIMP nsPrinterBase::GetSupportsDuplex(JSContext* aCx,
Promise** aResultPromise) {
return AsyncPromiseAttributeGetter<bool>(aCx, aResultPromise,
AsyncAttribute::SupportsDuplex,
&nsPrinterBase::SupportsDuplex);
return AsyncPromiseAttributeGetter(aCx, aResultPromise,
AsyncAttribute::SupportsDuplex,
&nsPrinterBase::SupportsDuplex);
}
NS_IMETHODIMP nsPrinterBase::GetSupportsColor(JSContext* aCx,
Promise** aResultPromise) {
return AsyncPromiseAttributeGetter<bool>(aCx, aResultPromise,
AsyncAttribute::SupportsColor,
&nsPrinterBase::SupportsColor);
return AsyncPromiseAttributeGetter(aCx, aResultPromise,
AsyncAttribute::SupportsColor,
&nsPrinterBase::SupportsColor);
}
NS_IMETHODIMP nsPrinterBase::GetPaperList(JSContext* aCx,
Promise** aResultPromise) {
return AsyncPromiseAttributeGetter<nsTArray<PaperInfo>>(
aCx, aResultPromise, AsyncAttribute::PaperList,
&nsPrinterBase::PaperList);
return AsyncPromiseAttributeGetter(aCx, aResultPromise,
AsyncAttribute::PaperList,
&nsPrinterBase::PaperList);
}
void nsPrinterBase::QueryMarginsForPaper(Promise& aPromise, uint64_t aPaperId) {
return SpawnBackgroundTask<MarginDouble, uint64_t>(
aPromise, &nsPrinterBase::GetMarginsForPaper, aPaperId);
return SpawnPrintBackgroundTask(*this, aPromise,
&nsPrinterBase::GetMarginsForPaper, aPaperId);
}
NS_IMPL_CYCLE_COLLECTION(nsPrinterBase, mAsyncAttributePromises)

Просмотреть файл

@ -8,6 +8,7 @@
#include "mozilla/gfx/Rect.h"
#include "nsIPrinter.h"
#include "nsTArray.h"
#include "nsCycleCollectionParticipant.h"
#include "nsISupportsImpl.h"
#include "mozilla/EnumeratedArray.h"
@ -50,12 +51,8 @@ class nsPrinterBase : public nsIPrinter {
Last,
};
template <typename T, typename... Args>
using BackgroundTask = T (nsPrinterBase::*)(Args...) const;
// Resolves a promise when a background task finishes.
template <typename T, typename... Args>
void SpawnBackgroundTask(Promise&, BackgroundTask<T, Args...>, Args... aArgs);
template <typename Result, typename... Args>
using BackgroundTask = Result (nsPrinterBase::*)(Args...) const;
// Resolves an async attribute via a background task.
template <typename T, typename... Args>

Просмотреть файл

@ -9,15 +9,6 @@
using namespace mozilla;
nsPrinterCUPS::nsPrinterCUPS(const nsCUPSShim& aShim, cups_dest_t* aPrinter,
const nsAString& aDisplayName)
: mDisplayName(aDisplayName), mShim(aShim) {
MOZ_ASSERT(aPrinter);
DebugOnly<const int> numCopied = aShim.cupsCopyDest(aPrinter, 0, &mPrinter);
MOZ_ASSERT(numCopied == 1);
mPrinterInfo = aShim.cupsCopyDestInfo(CUPS_HTTP_DEFAULT, mPrinter);
}
nsPrinterCUPS::~nsPrinterCUPS() {
if (mPrinterInfo) {
mShim.cupsFreeDestInfo(mPrinterInfo);
@ -29,13 +20,6 @@ nsPrinterCUPS::~nsPrinterCUPS() {
}
}
// static
already_AddRefed<nsPrinterCUPS> nsPrinterCUPS::Create(
const nsCUPSShim& aShim, cups_dest_t* aPrinter,
const nsAString& aDisplayName) {
return do_AddRef(new nsPrinterCUPS(aShim, aPrinter, aDisplayName));
}
NS_IMETHODIMP
nsPrinterCUPS::GetName(nsAString& aName) {
if (mDisplayName.IsEmpty()) {

Просмотреть файл

@ -28,24 +28,21 @@ class nsPrinterCUPS final : public nsPrinterBase {
nsPrinterCUPS() = delete;
/**
* @p aPrinter must not be null.
*/
static already_AddRefed<nsPrinterCUPS> Create(
const nsCUPSShim& aShim, cups_dest_t* aPrinter,
const nsAString& aDisplayname = EmptyString());
nsPrinterCUPS(const nsCUPSShim& aShim, nsString aDisplayName,
cups_dest_t* aPrinter, cups_dinfo_t* aPrinterInfo)
: mShim(aShim),
mDisplayName(std::move(aDisplayName)),
mPrinter(aPrinter),
mPrinterInfo(aPrinterInfo) {}
private:
nsPrinterCUPS(const nsCUPSShim& aShim, cups_dest_t* aPrinter,
const nsAString& aDisplayName = EmptyString());
~nsPrinterCUPS();
// Little util for getting support flags using the direct CUPS names.
bool Supports(const char* option, const char* value) const;
nsString mDisplayName;
const nsCUPSShim& mShim;
nsString mDisplayName;
cups_dest_t* mPrinter;
cups_dinfo_t* mPrinterInfo;
};

Просмотреть файл

@ -4,8 +4,43 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsPrinterListBase.h"
#include "PrintBackgroundTask.h"
#include "mozilla/ErrorResult.h"
#include "xpcpublic.h"
using mozilla::ErrorResult;
using PrinterInfo = nsPrinterListBase::PrinterInfo;
nsPrinterListBase::nsPrinterListBase() = default;
nsPrinterListBase::~nsPrinterListBase() = default;
NS_IMPL_ISUPPORTS(nsPrinterListBase, nsIPrinterList)
NS_IMPL_CYCLE_COLLECTION(nsPrinterListBase, mPrintersPromise)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPrinterListBase)
NS_INTERFACE_MAP_ENTRY(nsIPrinterList)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPrinterList)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPrinterListBase)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsPrinterListBase)
namespace mozilla {
template <>
void ResolveOrReject(dom::Promise& aPromise, nsPrinterListBase& aList,
const nsTArray<PrinterInfo>& aInfo) {
nsTArray<RefPtr<nsIPrinter>> printers;
printers.SetCapacity(aInfo.Length());
for (auto& info : aInfo) {
printers.AppendElement(aList.CreatePrinter(info));
}
aPromise.MaybeResolve(printers);
}
} // namespace mozilla
NS_IMETHODIMP nsPrinterListBase::GetPrinters(JSContext* aCx,
Promise** aResult) {
return mozilla::AsyncPromiseAttributeGetter(
*this, mPrintersPromise, aCx, aResult, &nsPrinterListBase::GetPrinters);
}

Просмотреть файл

@ -7,11 +7,33 @@
#define nsPrinterListBase_h__
#include "nsIPrinterList.h"
#include "nsCycleCollectionParticipant.h"
#include "nsISupportsImpl.h"
#include "nsString.h"
class nsPrinterListBase : public nsIPrinterList {
public:
NS_DECL_ISUPPORTS
using Promise = mozilla::dom::Promise;
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(nsPrinterListBase)
NS_IMETHOD GetPrinters(JSContext*, Promise**) final;
struct PrinterInfo {
// Both windows and CUPS: The name of the printer.
nsString mName;
// CUPS only: Two handles to owned cups_dest_t / cups_dinfo_t objects.
std::array<void*, 2> mCupsHandles{};
};
// Called off the main thread, collect information to create an appropriate
// list of printers.
virtual nsTArray<PrinterInfo> GetPrinters() const = 0;
// Create an nsIPrinter object given the information we obtained from the
// background thread.
virtual RefPtr<nsIPrinter> CreatePrinter(PrinterInfo) const = 0;
// No copy or move, we're an identity.
nsPrinterListBase(const nsPrinterListBase&) = delete;
@ -20,6 +42,8 @@ class nsPrinterListBase : public nsIPrinterList {
protected:
nsPrinterListBase();
virtual ~nsPrinterListBase();
RefPtr<Promise> mPrintersPromise;
};
#endif

Просмотреть файл

@ -11,34 +11,52 @@
#include "prenv.h"
static nsCUPSShim sCupsShim;
using PrinterInfo = nsPrinterListBase::PrinterInfo;
NS_IMETHODIMP
nsPrinterListCUPS::GetPrinters(nsTArray<RefPtr<nsIPrinter>>& aPrinters) {
nsTArray<PrinterInfo> nsPrinterListCUPS::GetPrinters() const {
if (!sCupsShim.EnsureInitialized()) {
return NS_ERROR_FAILURE;
return {};
}
nsTArray<PrinterInfo> printerInfoList;
cups_dest_t* printers = nullptr;
auto numPrinters = sCupsShim.cupsGetDests(&printers);
aPrinters.SetCapacity(numPrinters);
printerInfoList.SetCapacity(numPrinters);
for (auto i : mozilla::IntegerRange(0, numPrinters)) {
cups_dest_t* dest = printers + i;
nsString displayName;
GetDisplayNameForPrinter(*dest, displayName);
RefPtr<nsPrinterCUPS> cupsPrinter =
nsPrinterCUPS::Create(sCupsShim, dest, displayName);
cups_dest_t* ownedDest = nullptr;
mozilla::DebugOnly<const int> numCopied =
sCupsShim.cupsCopyDest(dest, 0, &ownedDest);
MOZ_ASSERT(numCopied == 1);
aPrinters.AppendElement(cupsPrinter);
cups_dinfo_t* ownedInfo =
sCupsShim.cupsCopyDestInfo(CUPS_HTTP_DEFAULT, ownedDest);
nsString name;
GetDisplayNameForPrinter(*dest, name);
printerInfoList.AppendElement(
PrinterInfo{std::move(name), {ownedDest, ownedInfo}});
}
sCupsShim.cupsFreeDests(numPrinters, printers);
return NS_OK;
return printerInfoList;
}
RefPtr<nsIPrinter> nsPrinterListCUPS::CreatePrinter(PrinterInfo aInfo) const {
return mozilla::MakeRefPtr<nsPrinterCUPS>(
sCupsShim, std::move(aInfo.mName),
static_cast<cups_dest_t*>(aInfo.mCupsHandles[0]),
static_cast<cups_dinfo_t*>(aInfo.mCupsHandles[1]));
}
NS_IMETHODIMP
nsPrinterListCUPS::GetSystemDefaultPrinterName(nsAString& aName) {
aName.Truncate();
if (!sCupsShim.EnsureInitialized()) {
return NS_ERROR_FAILURE;
}
@ -48,7 +66,7 @@ nsPrinterListCUPS::GetSystemDefaultPrinterName(nsAString& aName) {
sCupsShim.cupsGetNamedDest(CUPS_HTTP_DEFAULT, /* name */ nullptr,
/* instance */ nullptr);
if (!dest) {
return NS_ERROR_GFX_PRINTER_NO_PRINTER_AVAILABLE;
return NS_OK;
}
GetDisplayNameForPrinter(*dest, aName);

Просмотреть файл

@ -12,7 +12,12 @@
struct cups_dest_s;
class nsPrinterListCUPS final : public nsPrinterListBase {
NS_DECL_NSIPRINTERLIST
NS_IMETHOD InitPrintSettingsFromPrinter(const nsAString&,
nsIPrintSettings*) final;
NS_IMETHOD GetSystemDefaultPrinterName(nsAString&) final;
nsTArray<PrinterInfo> GetPrinters() const final;
RefPtr<nsIPrinter> CreatePrinter(PrinterInfo) const final;
#ifdef XP_MACOSX
// This is implemented in nsDeviceContextSpecX. We could add a new class to

Просмотреть файл

@ -55,45 +55,8 @@ using namespace mozilla::widget;
static const wchar_t kDriverName[] = L"WINSPOOL";
//----------------------------------------------------------------------------------
// The printer data is shared between the PrinterList and the
// nsDeviceContextSpecWin The PrinterList creates the printer info but the
// nsDeviceContextSpecWin cleans it up If it gets created (via the Page Setup
// Dialog) but the user never prints anything then it will never be delete, so
// this class takes care of that.
class GlobalPrinters {
public:
static GlobalPrinters* GetInstance() { return &mGlobalPrinters; }
~GlobalPrinters() { FreeGlobalPrinters(); }
void FreeGlobalPrinters();
bool PrintersAreAllocated() { return mPrinters != nullptr; }
LPWSTR GetItemFromList(int32_t aInx) {
return mPrinters ? mPrinters->ElementAt(aInx) : nullptr;
}
nsresult EnumeratePrinterList();
void GetDefaultPrinterName(nsAString& aDefaultPrinterName);
uint32_t GetNumPrinters() { return mPrinters ? mPrinters->Length() : 0; }
protected:
GlobalPrinters() {}
nsresult EnumerateNativePrinters();
void ReallocatePrinters();
static GlobalPrinters mGlobalPrinters;
static nsTArray<LPWSTR>* mPrinters;
};
//---------------
// static members
GlobalPrinters GlobalPrinters::mGlobalPrinters;
nsTArray<LPWSTR>* GlobalPrinters::mPrinters = nullptr;
struct AutoFreeGlobalPrinters {
~AutoFreeGlobalPrinters() {
GlobalPrinters::GetInstance()->FreeGlobalPrinters();
}
};
//----------------------------------------------------------------------------------
nsDeviceContextSpecWin::nsDeviceContextSpecWin()
: mDevMode(nullptr)
@ -111,15 +74,33 @@ NS_IMPL_ISUPPORTS(nsDeviceContextSpecWin, nsIDeviceContextSpec)
nsDeviceContextSpecWin::~nsDeviceContextSpecWin() {
SetDevMode(nullptr);
nsCOMPtr<nsIPrintSettingsWin> psWin(do_QueryInterface(mPrintSettings));
if (psWin) {
psWin->SetDeviceName(EmptyString());
psWin->SetDriverName(EmptyString());
psWin->SetDevMode(nullptr);
if (nsCOMPtr<nsIPrintSettingsWin> ps = do_QueryInterface(mPrintSettings)) {
ps->SetDeviceName(EmptyString());
ps->SetDriverName(EmptyString());
ps->SetDevMode(nullptr);
}
}
static void GetDefaultPrinterName(nsAString& aDefaultPrinterName) {
aDefaultPrinterName.Truncate();
DWORD length = 0;
GetDefaultPrinterW(nullptr, &length);
if (length) {
aDefaultPrinterName.SetLength(length);
if (GetDefaultPrinterW((LPWSTR)aDefaultPrinterName.BeginWriting(),
&length)) {
// `length` includes the terminating null, so we subtract that from our
// string length.
aDefaultPrinterName.SetLength(length - 1);
} else {
aDefaultPrinterName.Truncate();
}
}
// Free them, we won't need them for a while
GlobalPrinters::GetInstance()->FreeGlobalPrinters();
PR_PL(("DEFAULT PRINTER [%s]\n",
NS_ConvertUTF16toUTF8(aDefaultPrinterName).get()));
}
//----------------------------------------------------------------------------------
@ -137,7 +118,7 @@ NS_IMETHODIMP nsDeviceContextSpecWin::Init(nsIWidget* aWidget,
// If there is no name then use the default printer
if (printerName.IsEmpty()) {
GlobalPrinters::GetInstance()->GetDefaultPrinterName(printerName);
GetDefaultPrinterName(printerName);
}
// Gather telemetry on the print target type.
@ -415,17 +396,6 @@ nsresult nsDeviceContextSpecWin::GetDataFromPrinter(const nsAString& aName,
nsIPrintSettings* aPS) {
nsresult rv = NS_ERROR_FAILURE;
if (!GlobalPrinters::GetInstance()->PrintersAreAllocated()) {
rv = GlobalPrinters::GetInstance()->EnumeratePrinterList();
if (NS_FAILED(rv)) {
PR_PL(
("***** nsDeviceContextSpecWin::GetDataFromPrinter - Couldn't "
"retrieve printers!\n"));
DISPLAY_LAST_ERROR
}
NS_ENSURE_SUCCESS(rv, rv);
}
nsHPRINTER hPrinter = nullptr;
const nsString& flat = PromiseFlatString(aName);
wchar_t* name =
@ -512,13 +482,55 @@ nsresult nsDeviceContextSpecWin::GetDataFromPrinter(const nsAString& aName,
// Printer List
//***********************************************************
nsPrinterListWin::~nsPrinterListWin() {
GlobalPrinters::GetInstance()->FreeGlobalPrinters();
nsPrinterListWin::~nsPrinterListWin() = default;
nsTArray<nsPrinterListBase::PrinterInfo> nsPrinterListWin::GetPrinters() const {
PR_PL(("EnumerateNativePrinters\n"));
const DWORD kLevel = 4;
using RecType = PRINTER_INFO_4;
DWORD needed = 0;
DWORD count = 0;
const DWORD kFlags = PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS;
BOOL ok = EnumPrinters(kFlags,
nullptr, // Name
kLevel, // Level
nullptr, // pPrinterEnum
0, // cbBuf (buffer size)
&needed, // Bytes needed in buffer
&count);
AutoTArray<BYTE, 1024> buffer;
if (needed > 0) {
buffer.SetLength(needed);
ok = EnumPrinters(kFlags, nullptr, kLevel, buffer.Elements(),
buffer.Length(), &needed, &count);
}
if (!ok || !count) {
PR_PL(("[No printers found]\n"));
return {};
}
auto* printers = reinterpret_cast<const RecType*>(buffer.Elements());
nsTArray<PrinterInfo> list;
for (unsigned i = 0; i < count; i++) {
list.AppendElement(PrinterInfo{nsString(printers[i].pPrinterName)});
PR_PL(("Printer Name: %s\n",
NS_ConvertUTF16toUTF8(printers[i].pPrinterName).get()));
}
return list;
}
RefPtr<nsIPrinter> nsPrinterListWin::CreatePrinter(PrinterInfo aInfo) const {
return nsPrinterWin::Create(std::move(aInfo.mName));
}
NS_IMETHODIMP
nsPrinterListWin::GetSystemDefaultPrinterName(nsAString& aName) {
GlobalPrinters::GetInstance()->GetDefaultPrinterName(aName);
GetDefaultPrinterName(aName);
return NS_OK;
}
@ -541,12 +553,6 @@ nsPrinterListWin::InitPrintSettingsFromPrinter(
RefPtr<nsDeviceContextSpecWin> devSpecWin = new nsDeviceContextSpecWin();
if (!devSpecWin) return NS_ERROR_OUT_OF_MEMORY;
if (NS_FAILED(GlobalPrinters::GetInstance()->EnumeratePrinterList())) {
return NS_ERROR_FAILURE;
}
AutoFreeGlobalPrinters autoFreeGlobalPrinters;
// If the settings have already been initialized from prefs then pass these to
// GetDataFromPrinter, so that they are saved to the printer.
bool initializedFromPrefs;
@ -586,164 +592,3 @@ nsPrinterListWin::InitPrintSettingsFromPrinter(
return NS_OK;
}
NS_IMETHODIMP
nsPrinterListWin::GetPrinters(nsTArray<RefPtr<nsIPrinter>>& aPrinters) {
nsresult rv = GlobalPrinters::GetInstance()->EnumeratePrinterList();
if (NS_FAILED(rv)) {
PR_PL(
("***** nsDeviceContextSpecWin::GetPrinters - Couldn't "
"retrieve printers!\n"));
return rv;
}
uint32_t numPrinters = GlobalPrinters::GetInstance()->GetNumPrinters();
for (uint32_t printerInx = 0; printerInx < numPrinters; ++printerInx) {
// wchar_t (used in LPWSTR) is 16 bits on Windows.
// https://docs.microsoft.com/en-us/cpp/cpp/char-wchar-t-char16-t-char32-t?view=vs-2019
LPWSTR name = GlobalPrinters::GetInstance()->GetItemFromList(printerInx);
nsAutoString printerName(name);
if (RefPtr<nsPrinterWin> printer = nsPrinterWin::Create(printerName)) {
aPrinters.AppendElement(printer.forget());
}
}
return NS_OK;
}
//----------------------------------------------------------------------------------
//-- Global Printers
//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
// THe array hold the name and port for each printer
void GlobalPrinters::ReallocatePrinters() {
if (PrintersAreAllocated()) {
FreeGlobalPrinters();
}
mPrinters = new nsTArray<LPWSTR>();
NS_ASSERTION(mPrinters, "Printers Array is NULL!");
}
//----------------------------------------------------------------------------------
void GlobalPrinters::FreeGlobalPrinters() {
if (mPrinters != nullptr) {
for (uint32_t i = 0; i < mPrinters->Length(); i++) {
free(mPrinters->ElementAt(i));
}
delete mPrinters;
mPrinters = nullptr;
}
}
//----------------------------------------------------------------------------------
nsresult GlobalPrinters::EnumerateNativePrinters() {
nsresult rv = NS_ERROR_GFX_PRINTER_NO_PRINTER_AVAILABLE;
PR_PL(("-----------------------\n"));
PR_PL(("EnumerateNativePrinters\n"));
const DWORD kLevel = 4;
using RecType = PRINTER_INFO_4;
DWORD needed = 0;
DWORD count = 0;
const DWORD kFlags = PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS;
BOOL ok = EnumPrinters(kFlags,
nullptr, // Name
kLevel, // Level
nullptr, // pPrinterEnum
0, // cbBuf (buffer size)
&needed, // Bytes needed in buffer
&count);
AutoTArray<BYTE, 1024> buffer;
if (needed > 0) {
buffer.SetLength(needed);
ok = EnumPrinters(kFlags, nullptr, kLevel, buffer.Elements(),
buffer.Length(), &needed, &count);
}
if (ok && count) {
const RecType* printers =
reinterpret_cast<const RecType*>(buffer.Elements());
for (unsigned i = 0; i < count; i++) {
mPrinters->AppendElement(wcsdup(printers[i].pPrinterName));
PR_PL(("Printer Name: %s\n",
NS_ConvertUTF16toUTF8(printers[i].pPrinterName).get()));
}
rv = NS_OK;
} else {
PR_PL(("[No printers found]\n"));
}
PR_PL(("-----------------------\n"));
return rv;
}
//------------------------------------------------------------------
// Uses the GetProfileString to get the default printer from the registry
void GlobalPrinters::GetDefaultPrinterName(nsAString& aDefaultPrinterName) {
aDefaultPrinterName.Truncate();
DWORD length = 0;
GetDefaultPrinterW(nullptr, &length);
if (length) {
aDefaultPrinterName.SetLength(length);
if (GetDefaultPrinterW((LPWSTR)aDefaultPrinterName.BeginWriting(),
&length)) {
// `length` includes the terminating null, so we subtract that from our
// string length.
aDefaultPrinterName.SetLength(length - 1);
} else {
aDefaultPrinterName.Truncate();
}
}
PR_PL(("DEFAULT PRINTER [%s]\n",
NS_ConvertUTF16toUTF8(aDefaultPrinterName).get()));
}
//----------------------------------------------------------------------------------
// This goes and gets the list of available printers and puts
// the default printer at the beginning of the list
nsresult GlobalPrinters::EnumeratePrinterList() {
// reallocate and get a new list each time it is asked for
// this deletes the list and re-allocates them
ReallocatePrinters();
// any of these could only fail with an OUT_MEMORY_ERROR
// PRINTER_ENUM_LOCAL should get the network printers on Win95
nsresult rv = EnumerateNativePrinters();
if (NS_FAILED(rv)) return rv;
// get the name of the default printer
nsAutoString defPrinterName;
GetDefaultPrinterName(defPrinterName);
// put the default printer at the beginning of list
if (!defPrinterName.IsEmpty()) {
for (uint32_t i = 0; i < mPrinters->Length(); i++) {
LPWSTR name = mPrinters->ElementAt(i);
if (defPrinterName.Equals(name)) {
if (i > 0) {
LPWSTR ptr = mPrinters->ElementAt(0);
mPrinters->ElementAt(0) = name;
mPrinters->ElementAt(i) = ptr;
}
break;
}
}
}
// make sure we at least tried to get the printers
if (!PrintersAreAllocated()) {
PR_PL(
("***** nsDeviceContextSpecWin::EnumeratePrinterList - Printers aren`t "
"allocated\n"));
return NS_ERROR_FAILURE;
}
return NS_OK;
}

Просмотреть файл

@ -85,7 +85,13 @@ class nsDeviceContextSpecWin : public nsIDeviceContextSpec {
//-------------------------------------------------------------------------
class nsPrinterListWin final : public nsPrinterListBase {
public:
NS_DECL_NSIPRINTERLIST
NS_IMETHOD InitPrintSettingsFromPrinter(const nsAString&,
nsIPrintSettings*) final;
NS_IMETHOD GetSystemDefaultPrinterName(nsAString&) final;
nsTArray<PrinterInfo> GetPrinters() const final;
RefPtr<nsIPrinter> CreatePrinter(PrinterInfo) const final;
nsPrinterListWin() = default;
private: