Bug 1663652 part 2 - Add combined printerInfo method to nsPrinterCUPS r=emilio

Since we no longer are using the PaperList or CreateDefaultSettings methods on
nsPrinterBase, and we can remove them from nsPrinterBase.

This should dramatically improve latency when the remote printer is not
available, or when the print server is slower. In the best-case scenario it may
increase the latency to displaying the print preview somewhat.

Differential Revision: https://phabricator.services.mozilla.com/D94482
This commit is contained in:
Emily McDonough 2020-11-03 20:35:38 +00:00
Родитель 1fbcb914c4
Коммит 6635212e1a
6 изменённых файлов: 231 добавлений и 215 удалений

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

@ -199,10 +199,6 @@ nsPrinterBase::nsPrinterBase(const CommonPaperInfoArray* aPaperInfoArray)
}
nsPrinterBase::~nsPrinterBase() = default;
nsPrinterBase::PrinterInfo nsPrinterBase::CreatePrinterInfo() const {
return PrinterInfo{PaperList(), DefaultSettings()};
}
const PaperInfo* nsPrinterBase::FindCommonPaperSize(
const gfx::SizeDouble& aSize) const {
for (const PaperInfo& paper : *mCommonPaperInfo) {

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

@ -90,14 +90,12 @@ class nsPrinterBase : public nsIPrinter {
// Implementation-specific methods. These must not make assumptions about
// which thread they run on.
virtual PrintSettingsInitializer DefaultSettings() const = 0;
virtual bool SupportsDuplex() const = 0;
virtual bool SupportsColor() const = 0;
virtual bool SupportsMonochrome() const = 0;
virtual bool SupportsCollation() const = 0;
virtual nsTArray<mozilla::PaperInfo> PaperList() const = 0;
virtual MarginDouble GetMarginsForPaper(nsString aPaperId) const = 0;
virtual PrinterInfo CreatePrinterInfo() const;
virtual PrinterInfo CreatePrinterInfo() const = 0;
// Searches our built-in list of commonly used PWG paper sizes for a matching,
// localized PaperInfo. Returns null if there is no matching size.
const mozilla::PaperInfo* FindCommonPaperSize(

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

@ -90,78 +90,6 @@ nsPrinterCUPS::~nsPrinterCUPS() {
}
}
PrintSettingsInitializer nsPrinterCUPS::DefaultSettings() const {
nsString printerName;
GetPrinterName(printerName);
auto printerInfoLock = mPrinterInfoMutex.Lock();
TryEnsurePrinterInfo(*printerInfoLock);
cups_dinfo_t* const printerInfo = printerInfoLock->mPrinterInfo;
cups_size_t media;
// cupsGetDestMediaDefault appears to return more accurate defaults on macOS,
// and the IPP attribute appears to return more accurate defaults on Linux.
#ifdef XP_MACOSX
bool hasDefaultMedia =
mShim.cupsGetDestMediaDefault(CUPS_HTTP_DEFAULT, mPrinter, printerInfo,
CUPS_MEDIA_FLAGS_DEFAULT, &media);
#else
ipp_attribute_t* defaultMediaIPP = mShim.cupsFindDestDefault(
CUPS_HTTP_DEFAULT, mPrinter, printerInfo, "media");
const char* defaultMediaName =
mShim.ippGetString(defaultMediaIPP, 0, nullptr);
bool hasDefaultMedia = mShim.cupsGetDestMediaByName(
CUPS_HTTP_DEFAULT, mPrinter, printerInfo, defaultMediaName,
CUPS_MEDIA_FLAGS_DEFAULT, &media);
#endif
if (!hasDefaultMedia) {
return PrintSettingsInitializer{
std::move(printerName),
PaperInfo(),
SupportsColor(),
};
}
// Check if this is a localized fallback paper size, in which case we can
// avoid using the CUPS localization methods.
const gfx::SizeDouble sizeDouble{
media.width * kPointsPerHundredthMillimeter,
media.length * kPointsPerHundredthMillimeter};
if (const PaperInfo* const paperInfo = FindCommonPaperSize(sizeDouble)) {
return PrintSettingsInitializer{
std::move(printerName),
MakePaperInfo(paperInfo->mName, media),
SupportsColor(),
};
}
// blocking call
http_t* connection = mShim.cupsConnectDest(mPrinter, CUPS_DEST_FLAGS_NONE,
/* timeout(ms) */ 5000,
/* cancel */ nullptr,
/* resource */ nullptr,
/* resourcesize */ 0,
/* callback */ nullptr,
/* user_data */ nullptr);
// XXX Do we actually have the guarantee that this is utf-8?
NS_ConvertUTF8toUTF16 localizedName{
connection ? LocalizeMediaName(*connection, media) : ""};
if (connection) {
mShim.httpClose(connection);
}
return PrintSettingsInitializer{
std::move(printerName),
MakePaperInfo(localizedName, media),
SupportsColor(),
};
}
NS_IMETHODIMP
nsPrinterCUPS::GetName(nsAString& aName) {
GetPrinterName(aName);
@ -230,6 +158,11 @@ bool nsPrinterCUPS::SupportsCollation() const {
return type & CUPS_PRINTER_COLLATE;
}
nsPrinterBase::PrinterInfo nsPrinterCUPS::CreatePrinterInfo() const {
Connection connection{mShim};
return PrinterInfo{PaperList(connection), DefaultSettings(connection)};
}
bool nsPrinterCUPS::Supports(const char* aOption, const char* aValue) const {
auto printerInfoLock = mPrinterInfoMutex.Lock();
TryEnsurePrinterInfo(*printerInfoLock);
@ -263,28 +196,101 @@ bool nsPrinterCUPS::IsCUPSVersionAtLeast(uint64_t aCUPSMajor,
return aCUPSPatch <= printerInfoLock->mCUPSPatch;
}
nsTArray<PaperInfo> nsPrinterCUPS::PaperList() const {
// blocking call
http_t* connection = mShim.cupsConnectDest(mPrinter, CUPS_DEST_FLAGS_NONE,
/* timeout(ms) */ 5000,
/* cancel */ nullptr,
/* resource */ nullptr,
/* resourcesize */ 0,
/* callback */ nullptr,
/* user_data */ nullptr);
http_t* nsPrinterCUPS::Connection::GetConnection(cups_dest_t* aDest) {
if (mWasInited) {
return mConnection;
}
mWasInited = true;
if (!connection) {
connection = CUPS_HTTP_DEFAULT;
// blocking call
http_t* const connection = mShim.cupsConnectDest(aDest, CUPS_DEST_FLAGS_NONE,
/* timeout(ms) */ 5000,
/* cancel */ nullptr,
/* resource */ nullptr,
/* resourcesize */ 0,
/* callback */ nullptr,
/* user_data */ nullptr);
if (connection) {
mConnection = connection;
}
return mConnection;
}
nsPrinterCUPS::Connection::~Connection() {
if (mWasInited && mConnection) {
mShim.httpClose(mConnection);
}
}
PrintSettingsInitializer nsPrinterCUPS::DefaultSettings(
Connection& aConnection) const {
nsString printerName;
GetPrinterName(printerName);
auto printerInfoLock = mPrinterInfoMutex.Lock();
TryEnsurePrinterInfo(*printerInfoLock);
cups_dinfo_t* const printerInfo = printerInfoLock->mPrinterInfo;
cups_size_t media;
// cupsGetDestMediaDefault appears to return more accurate defaults on macOS,
// and the IPP attribute appears to return more accurate defaults on Linux.
#ifdef XP_MACOSX
bool hasDefaultMedia =
mShim.cupsGetDestMediaDefault(CUPS_HTTP_DEFAULT, mPrinter, printerInfo,
CUPS_MEDIA_FLAGS_DEFAULT, &media);
#else
ipp_attribute_t* defaultMediaIPP = mShim.cupsFindDestDefault(
CUPS_HTTP_DEFAULT, mPrinter, printerInfo, "media");
const char* defaultMediaName =
mShim.ippGetString(defaultMediaIPP, 0, nullptr);
bool hasDefaultMedia = mShim.cupsGetDestMediaByName(
CUPS_HTTP_DEFAULT, mPrinter, printerInfo, defaultMediaName,
CUPS_MEDIA_FLAGS_DEFAULT, &media);
#endif
if (!hasDefaultMedia) {
return PrintSettingsInitializer{
std::move(printerName),
PaperInfo(),
SupportsColor(),
};
}
// Check if this is a localized fallback paper size, in which case we can
// avoid using the CUPS localization methods.
const gfx::SizeDouble sizeDouble{
media.width * kPointsPerHundredthMillimeter,
media.length * kPointsPerHundredthMillimeter};
if (const PaperInfo* const paperInfo = FindCommonPaperSize(sizeDouble)) {
return PrintSettingsInitializer{
std::move(printerName),
MakePaperInfo(paperInfo->mName, media),
SupportsColor(),
};
}
http_t* const connection = aConnection.GetConnection(mPrinter);
// XXX Do we actually have the guarantee that this is utf-8?
NS_ConvertUTF8toUTF16 localizedName{
connection ? LocalizeMediaName(*connection, media) : ""};
return PrintSettingsInitializer{
std::move(printerName),
MakePaperInfo(localizedName, media),
SupportsColor(),
};
}
nsTArray<mozilla::PaperInfo> nsPrinterCUPS::PaperList(
Connection& aConnection) const {
http_t* const connection = aConnection.GetConnection(mPrinter);
auto printerInfoLock = mPrinterInfoMutex.Lock();
TryEnsurePrinterInfo(*printerInfoLock, connection);
cups_dinfo_t* const printerInfo = printerInfoLock->mPrinterInfo;
if (!printerInfo) {
if (connection) {
mShim.httpClose(connection);
}
return {};
}
@ -317,10 +323,6 @@ nsTArray<PaperInfo> nsPrinterCUPS::PaperList() const {
}
}
if (connection) {
mShim.httpClose(connection);
}
return paperList;
}

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

@ -21,12 +21,11 @@ class nsPrinterCUPS final : public nsPrinterBase {
public:
NS_IMETHOD GetName(nsAString& aName) override;
NS_IMETHOD GetSystemName(nsAString& aName) override;
PrintSettingsInitializer DefaultSettings() const final;
bool SupportsDuplex() const final;
bool SupportsColor() const final;
bool SupportsMonochrome() const final;
bool SupportsCollation() const final;
nsTArray<mozilla::PaperInfo> PaperList() const final;
PrinterInfo CreatePrinterInfo() const final;
MarginDouble GetMarginsForPaper(nsString aPaperId) const final {
MOZ_ASSERT_UNREACHABLE(
"The CUPS API requires us to always get the margin when fetching the "
@ -82,6 +81,22 @@ class nsPrinterCUPS final : public nsPrinterBase {
bool IsCUPSVersionAtLeast(uint64_t aCUPSMajor, uint64_t aCUPSMinor,
uint64_t aCUPSPatch) const;
class Connection {
public:
http_t* GetConnection(cups_dest_t* aDest);
inline explicit Connection(const nsCUPSShim& aShim) : mShim(aShim) {}
Connection() = delete;
~Connection();
protected:
const nsCUPSShim& mShim;
http_t* mConnection = CUPS_HTTP_DEFAULT;
bool mWasInited = false;
};
PrintSettingsInitializer DefaultSettings(Connection& aConnection) const;
nsTArray<mozilla::PaperInfo> PaperList(Connection& aConnection) const;
/**
* Attempts to populate the CUPSPrinterInfo object.
* This usually works with the CUPS default connection,

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

@ -34,110 +34,6 @@ already_AddRefed<nsPrinterWin> nsPrinterWin::Create(
return do_AddRef(new nsPrinterWin(aArray, aName));
}
nsTArray<uint8_t> nsPrinterWin::CopyDefaultDevmodeW() const {
nsTArray<uint8_t> devmodeStorageW;
auto devmodeStorageWLock = mDefaultDevmodeWStorage.Lock();
if (devmodeStorageWLock->IsEmpty()) {
nsHPRINTER hPrinter = nullptr;
// OpenPrinter could fail if, for example, the printer has been removed
// or otherwise become inaccessible since it was selected.
if (NS_WARN_IF(!::OpenPrinterW(mName.get(), &hPrinter, nullptr))) {
return devmodeStorageW;
}
nsAutoPrinter autoPrinter(hPrinter);
// Allocate devmode storage of the correct size.
LONG bytesNeeded = ::DocumentPropertiesW(nullptr, autoPrinter.get(),
mName.get(), nullptr, nullptr, 0);
// Note that we must cast the sizeof() to a signed type so that comparison
// with the signed, potentially-negative bytesNeeded will work!
MOZ_ASSERT(bytesNeeded >= LONG(sizeof(DEVMODEW)),
"DocumentPropertiesW failed to get valid size");
if (bytesNeeded < LONG(sizeof(DEVMODEW))) {
return devmodeStorageW;
}
// Allocate extra space in case of bad drivers that return a too-small
// result from DocumentProperties.
// (See https://bugzilla.mozilla.org/show_bug.cgi?id=1664530#c5)
devmodeStorageWLock->SetLength(bytesNeeded * 2);
auto* devmode =
reinterpret_cast<DEVMODEW*>(devmodeStorageWLock->Elements());
LONG ret = ::DocumentPropertiesW(nullptr, autoPrinter.get(), mName.get(),
devmode, nullptr, DM_OUT_BUFFER);
MOZ_ASSERT(ret == IDOK, "DocumentPropertiesW failed");
if (ret != IDOK) {
return devmodeStorageW;
}
}
devmodeStorageW.Assign(devmodeStorageWLock.ref());
return devmodeStorageW;
}
PrintSettingsInitializer nsPrinterWin::DefaultSettings() const {
// Initialize to something reasonable, in case we fail to get usable data
// from the devmode below.
nsString paperIdString(u"1"); // DMPAPER_LETTER
bool color = true;
nsTArray<uint8_t> devmodeWStorage = CopyDefaultDevmodeW();
if (devmodeWStorage.IsEmpty()) {
return {};
}
const auto* devmode =
reinterpret_cast<const DEVMODEW*>(devmodeWStorage.Elements());
// XXX If DM_PAPERSIZE is not set, or is not a known value, should we
// be returning a "Custom" name of some kind?
if (devmode->dmFields & DM_PAPERSIZE) {
paperIdString.Truncate(0);
paperIdString.AppendInt(devmode->dmPaperSize);
}
if (devmode->dmFields & DM_COLOR) {
// See comment for PrintSettingsInitializer.mPrintInColor
color = devmode->dmColor != DMCOLOR_MONOCHROME;
}
nsAutoHDC printerDc(::CreateICW(nullptr, mName.get(), nullptr, devmode));
MOZ_ASSERT(printerDc, "CreateICW failed");
if (!printerDc) {
return {};
}
int pixelsPerInchY = ::GetDeviceCaps(printerDc, LOGPIXELSY);
int physicalHeight = ::GetDeviceCaps(printerDc, PHYSICALHEIGHT);
double heightInInches = double(physicalHeight) / pixelsPerInchY;
int pixelsPerInchX = ::GetDeviceCaps(printerDc, LOGPIXELSX);
int physicalWidth = ::GetDeviceCaps(printerDc, PHYSICALWIDTH);
double widthInches = double(physicalWidth) / pixelsPerInchX;
if (devmode->dmFields & DM_ORIENTATION &&
devmode->dmOrientation == DMORIENT_LANDSCAPE) {
std::swap(widthInches, heightInInches);
}
SizeDouble paperSize(widthInches * kPointsPerInch,
heightInInches * kPointsPerInch);
gfx::MarginDouble margin =
WinUtils::GetUnwriteableMarginsForDeviceInInches(printerDc);
margin.top *= kPointsPerInch;
margin.right *= kPointsPerInch;
margin.bottom *= kPointsPerInch;
margin.left *= kPointsPerInch;
// Using Y to match existing code for print scaling calculations.
int resolution = pixelsPerInchY;
// We don't actually need the paper name in the settings because the
// paperIdString is used to look up the paper details in the front end.
nsString paperName;
return PrintSettingsInitializer{
mName, PaperInfo(paperIdString, paperName, paperSize, Some(margin)),
color, resolution, std::move(devmodeWStorage)};
}
template <class T>
static nsTArray<T> GetDeviceCapabilityArray(const LPWSTR aPrinterName,
WORD aCapabilityID, int& aCount) {
@ -248,6 +144,78 @@ bool nsPrinterWin::SupportsCollation() const {
return ::DeviceCapabilitiesW(mName.get(), nullptr, DC_COLLATE, nullptr,
nullptr) == 1;
}
nsPrinterBase::PrinterInfo nsPrinterWin::CreatePrinterInfo() const {
return PrinterInfo{PaperList(), DefaultSettings()};
}
mozilla::gfx::MarginDouble nsPrinterWin::GetMarginsForPaper(
nsString aPaperId) const {
gfx::MarginDouble margin;
nsTArray<uint8_t> devmodeWStorage = CopyDefaultDevmodeW();
if (devmodeWStorage.IsEmpty()) {
return margin;
}
auto* devmode = reinterpret_cast<DEVMODEW*>(devmodeWStorage.Elements());
devmode->dmFields = DM_PAPERSIZE;
devmode->dmPaperSize = _wtoi((const wchar_t*)aPaperId.BeginReading());
nsAutoHDC printerDc(::CreateICW(nullptr, mName.get(), nullptr, devmode));
MOZ_ASSERT(printerDc, "CreateICW failed");
if (!printerDc) {
return margin;
}
margin = WinUtils::GetUnwriteableMarginsForDeviceInInches(printerDc);
margin.top *= kPointsPerInch;
margin.right *= kPointsPerInch;
margin.bottom *= kPointsPerInch;
margin.left *= kPointsPerInch;
return margin;
}
nsTArray<uint8_t> nsPrinterWin::CopyDefaultDevmodeW() const {
nsTArray<uint8_t> devmodeStorageW;
auto devmodeStorageWLock = mDefaultDevmodeWStorage.Lock();
if (devmodeStorageWLock->IsEmpty()) {
nsHPRINTER hPrinter = nullptr;
// OpenPrinter could fail if, for example, the printer has been removed
// or otherwise become inaccessible since it was selected.
if (NS_WARN_IF(!::OpenPrinterW(mName.get(), &hPrinter, nullptr))) {
return devmodeStorageW;
}
nsAutoPrinter autoPrinter(hPrinter);
// Allocate devmode storage of the correct size.
LONG bytesNeeded = ::DocumentPropertiesW(nullptr, autoPrinter.get(),
mName.get(), nullptr, nullptr, 0);
// Note that we must cast the sizeof() to a signed type so that comparison
// with the signed, potentially-negative bytesNeeded will work!
MOZ_ASSERT(bytesNeeded >= LONG(sizeof(DEVMODEW)),
"DocumentPropertiesW failed to get valid size");
if (bytesNeeded < LONG(sizeof(DEVMODEW))) {
return devmodeStorageW;
}
// Allocate extra space in case of bad drivers that return a too-small
// result from DocumentProperties.
// (See https://bugzilla.mozilla.org/show_bug.cgi?id=1664530#c5)
devmodeStorageWLock->SetLength(bytesNeeded * 2);
auto* devmode =
reinterpret_cast<DEVMODEW*>(devmodeStorageWLock->Elements());
LONG ret = ::DocumentPropertiesW(nullptr, autoPrinter.get(), mName.get(),
devmode, nullptr, DM_OUT_BUFFER);
MOZ_ASSERT(ret == IDOK, "DocumentPropertiesW failed");
if (ret != IDOK) {
return devmodeStorageW;
}
}
devmodeStorageW.Assign(devmodeStorageWLock.ref());
return devmodeStorageW;
}
nsTArray<mozilla::PaperInfo> nsPrinterWin::PaperList() const {
// Paper IDs are returned as WORDs.
@ -305,29 +273,65 @@ nsTArray<mozilla::PaperInfo> nsPrinterWin::PaperList() const {
return paperList;
}
mozilla::gfx::MarginDouble nsPrinterWin::GetMarginsForPaper(
nsString aPaperId) const {
gfx::MarginDouble margin;
PrintSettingsInitializer nsPrinterWin::DefaultSettings() const {
// Initialize to something reasonable, in case we fail to get usable data
// from the devmode below.
nsString paperIdString(u"1"); // DMPAPER_LETTER
bool color = true;
nsTArray<uint8_t> devmodeWStorage = CopyDefaultDevmodeW();
if (devmodeWStorage.IsEmpty()) {
return margin;
return {};
}
auto* devmode = reinterpret_cast<DEVMODEW*>(devmodeWStorage.Elements());
const auto* devmode =
reinterpret_cast<const DEVMODEW*>(devmodeWStorage.Elements());
// XXX If DM_PAPERSIZE is not set, or is not a known value, should we
// be returning a "Custom" name of some kind?
if (devmode->dmFields & DM_PAPERSIZE) {
paperIdString.Truncate(0);
paperIdString.AppendInt(devmode->dmPaperSize);
}
if (devmode->dmFields & DM_COLOR) {
// See comment for PrintSettingsInitializer.mPrintInColor
color = devmode->dmColor != DMCOLOR_MONOCHROME;
}
devmode->dmFields = DM_PAPERSIZE;
devmode->dmPaperSize = _wtoi((const wchar_t*)aPaperId.BeginReading());
nsAutoHDC printerDc(::CreateICW(nullptr, mName.get(), nullptr, devmode));
MOZ_ASSERT(printerDc, "CreateICW failed");
if (!printerDc) {
return margin;
return {};
}
margin = WinUtils::GetUnwriteableMarginsForDeviceInInches(printerDc);
int pixelsPerInchY = ::GetDeviceCaps(printerDc, LOGPIXELSY);
int physicalHeight = ::GetDeviceCaps(printerDc, PHYSICALHEIGHT);
double heightInInches = double(physicalHeight) / pixelsPerInchY;
int pixelsPerInchX = ::GetDeviceCaps(printerDc, LOGPIXELSX);
int physicalWidth = ::GetDeviceCaps(printerDc, PHYSICALWIDTH);
double widthInches = double(physicalWidth) / pixelsPerInchX;
if (devmode->dmFields & DM_ORIENTATION &&
devmode->dmOrientation == DMORIENT_LANDSCAPE) {
std::swap(widthInches, heightInInches);
}
SizeDouble paperSize(widthInches * kPointsPerInch,
heightInInches * kPointsPerInch);
gfx::MarginDouble margin =
WinUtils::GetUnwriteableMarginsForDeviceInInches(printerDc);
margin.top *= kPointsPerInch;
margin.right *= kPointsPerInch;
margin.bottom *= kPointsPerInch;
margin.left *= kPointsPerInch;
return margin;
// Using Y to match existing code for print scaling calculations.
int resolution = pixelsPerInchY;
// We don't actually need the paper name in the settings because the
// paperIdString is used to look up the paper details in the front end.
nsString paperName;
return PrintSettingsInitializer{
mName, PaperInfo(paperIdString, paperName, paperSize, Some(margin)),
color, resolution, std::move(devmodeWStorage)};
}

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

@ -14,12 +14,11 @@ class nsPrinterWin final : public nsPrinterBase {
public:
NS_IMETHOD GetName(nsAString& aName) override;
NS_IMETHOD GetSystemName(nsAString& aName) override;
PrintSettingsInitializer DefaultSettings() const final;
bool SupportsDuplex() const final;
bool SupportsColor() const final;
bool SupportsMonochrome() const final;
bool SupportsCollation() const final;
nsTArray<mozilla::PaperInfo> PaperList() const final;
PrinterInfo CreatePrinterInfo() const final;
MarginDouble GetMarginsForPaper(nsString aPaperId) const final;
nsPrinterWin() = delete;
@ -33,6 +32,8 @@ class nsPrinterWin final : public nsPrinterBase {
~nsPrinterWin() = default;
nsTArray<uint8_t> CopyDefaultDevmodeW() const;
nsTArray<mozilla::PaperInfo> PaperList() const;
PrintSettingsInitializer DefaultSettings() const;
const nsString mName;
mutable mozilla::DataMutex<nsTArray<uint8_t>> mDefaultDevmodeWStorage;