Bug 1658299 - Part 1: Add createDefaultSettings() to nsIPrinter for CUPS r=jwatt,emilio,geckoview-reviewers,owlish

This patch adds a createDefaultSettings() method to nsIPrinter to
initialize a default print settings object specific to that printer.
It implements the functionality for Linux and macOS but adds only stubs
for Windows.

Differential Revision: https://phabricator.services.mozilla.com/D87125
This commit is contained in:
Erik Nordin 2020-08-20 00:23:59 +00:00
Родитель 872ba72cf7
Коммит 851b8f7c70
22 изменённых файлов: 342 добавлений и 33 удалений

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

@ -70,6 +70,7 @@ tags = openwindow
[test_leaf_layers_partition_browser_window.xhtml]
skip-if = true # Bug 992311
[test_prerendered_transforms.html]
[test_printer_default_settings.html]
[test_printpreview.xhtml]
skip-if = (os == "linux" && bits == 32) || (verify && (os == 'win')) # Disabled on Linux32 for bug 1278957
[test_printpreview_bug396024.xhtml]

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

@ -0,0 +1,66 @@
<!DOCTYPE HTML>
<html>
<head>
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
</head>
<body onload="run()">
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
async function run() {
try {
let printerList = Cc["@mozilla.org/gfx/printerlist;1"].getService(
Ci.nsIPrinterList
);
let printers = await printerList.printers;
if (printers.length == 0) {
ok(true, "There were no printers to iterate through.");
}
for (let printer of printers) {
printer.QueryInterface(Ci.nsIPrinter);
info(printer.name);
const settings = await printer.createDefaultSettings();
settings.QueryInterface(Ci.nsIPrintSettings);
is(typeof settings.printerName, "string", "Printer name should be a string.");
let todo_if_win = navigator.platform.startsWith("Win") ? todo_is : is;
todo_if_win(settings.printerName, printer.name, "Print settings' printer should match the printer that created them.");
console.log(typeof settings.paperWidth);
is(typeof settings.paperName, "string", "Paper name should never be null.");
is(typeof settings.paperWidth, "number", "Paper width hould never be null.");
is(typeof settings.paperHeight, "number", "Paper height hould never be null.");
if (settings.paperName != "") {
info(`Paper: ${settings.paperName}`);
info(`Size: (${settings.paperWidth} x ${settings.paperHeight})`);
ok(settings.paperWidth > 0.0, "Paper width should be greater than zero.");
ok(settings.paperHeight > 0.0, "Paper height should be greater than zero.");
}
ok(settings.marginTop >= 0.0, "Paper margins should be greater than or equal to zero.");
ok(settings.marginRight >= 0.0, "Paper margins should be greater than or equal to zero.");
ok(settings.marginBottom >= 0.0, "Paper margins should be greater than or equal to zero.");
ok(settings.marginLeft >= 0.0, "Paper margins should be greater than or equal to zero.");
todo_if_win(settings.printInColor, await printer.supportsColor, "Print settings' color mode should match the printer's color support.");
ok(settings.isInitializedFromPrinter, "Print settings were initialized from printer");
ok(!settings.isInitializedFromPrefs);
}
} catch (e) {
ok(false, `Shouldn't throw: ${e}`);
Cu.reportError(e);
}
SimpleTest.finish();
}
</script>
</body>
</html>

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

@ -24,3 +24,10 @@ nsresult nsPrintSettingsServiceAndroid::_CreatePrintSettings(
nsIPrintSettings::kInitSaveAll);
return NS_OK;
}
already_AddRefed<nsIPrintSettings> CreatePlatformPrintSettings(
const mozilla::PrintSettingsInitializer& aSettings) {
RefPtr<nsPrintSettings> settings = new nsPrintSettingsAndroid();
settings->InitWithInitializer(aSettings);
return settings.forget();
}

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

@ -22,6 +22,7 @@ class nsPrintSettingsX : public nsPrintSettings {
NS_DECL_ISUPPORTS_INHERITED
nsPrintSettingsX();
explicit nsPrintSettingsX(const PrintSettingsInitializer& aSettings);
nsresult Init();
NSPrintInfo* GetCocoaPrintInfo() { return mPrintInfo; }
void SetCocoaPrintInfo(NSPrintInfo* aPrintInfo);

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

@ -47,6 +47,14 @@ nsPrintSettingsX::nsPrintSettingsX() : mAdjustedPaperWidth{0.0}, mAdjustedPaperH
NS_OBJC_END_TRY_ABORT_BLOCK;
}
already_AddRefed<nsIPrintSettings> CreatePlatformPrintSettings(
const PrintSettingsInitializer& aSettings) {
RefPtr<nsPrintSettings> settings = new nsPrintSettingsX();
settings->InitWithInitializer(aSettings);
settings->SetDefaultFileName();
return settings.forget();
}
nsPrintSettingsX::nsPrintSettingsX(const nsPrintSettingsX& src) { *this = src; }
nsPrintSettingsX::~nsPrintSettingsX() {

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

@ -50,6 +50,14 @@ nsPrintSettingsGTK::nsPrintSettingsGTK()
SetOutputFormat(nsIPrintSettings::kOutputFormatNative);
}
already_AddRefed<nsIPrintSettings> CreatePlatformPrintSettings(
const PrintSettingsInitializer& aSettings) {
RefPtr<nsPrintSettings> settings = new nsPrintSettingsGTK();
settings->InitWithInitializer(aSettings);
settings->SetDefaultFileName();
return settings.forget();
}
/** ---------------------------------------------------
*/
nsPrintSettingsGTK::~nsPrintSettingsGTK() {

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

@ -31,6 +31,7 @@ class nsPrintSettingsGTK : public nsPrintSettings {
NS_DECLARE_STATIC_IID_ACCESSOR(NS_PRINTSETTINGSGTK_IID)
nsPrintSettingsGTK();
explicit nsPrintSettingsGTK(const PrintSettingsInitializer& aSettings);
// We're overriding these methods because we want to read/write with GTK
// objects, not local variables. This allows a simpler settings implementation

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

@ -35,6 +35,7 @@ class nsCUPSShim {
X(cupsCopyDestInfo) \
X(cupsFreeDestInfo) \
X(cupsFreeDests) \
X(cupsGetDestMediaDefault) \
X(cupsGetDestMediaCount) \
X(cupsGetDestMediaByIndex) \
X(cupsGetDest) \

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

@ -273,3 +273,11 @@ interface nsIPrintSettings : nsISupports
*/
[noscript] void GetPageRanges(in IntegerArray aPages);
};
%{ C++
namespace mozilla {
struct PrintSettingsInitializer;
}
already_AddRefed<nsIPrintSettings> CreatePlatformPrintSettings(const mozilla::PrintSettingsInitializer&);
%}

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

@ -14,6 +14,14 @@ interface nsIPrinter : nsISupports
*/
readonly attribute AString name;
/**
* Creates a Promise that will resolve to an nsIPrintSettings object containing
* the default settings for this printer. For convenience, a new, mutable
* nsIPrintSettings object is created for each call.
*/
[implicit_jscontext]
Promise createDefaultSettings();
/**
* Returns a Promise that resolves to an array of
* nsIPaper instances with the list of available paper

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

@ -10,6 +10,7 @@
#include "mozilla/dom/Promise.h"
using mozilla::ErrorResult;
using mozilla::PaperInfo;
NS_IMPL_CYCLE_COLLECTION(nsPaper, mMarginPromise, mPrinter)

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

@ -25,12 +25,21 @@ struct PaperInfo {
using MarginDouble = mozilla::gfx::MarginDouble;
using SizeDouble = mozilla::gfx::SizeDouble;
PaperInfo() = default;
PaperInfo(const nsAString& aName, const SizeDouble& aSize,
const Maybe<MarginDouble>& aUnwriteableMargin,
uint64_t aPaperId = 0)
: mName(aName),
mSize(aSize),
mUnwriteableMargin(aUnwriteableMargin),
mPaperId(aPaperId) {}
const nsString mName;
SizeDouble mSize;
// The margins may not be known by some back-ends.
const Maybe<MarginDouble> mUnwriteableMargin;
const Maybe<MarginDouble> mUnwriteableMargin{Nothing()};
// The paper id from the device, this is only useful on Windows, right now.
uint64_t mPaperId{0};

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

@ -4,6 +4,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsPrintSettingsImpl.h"
#include "nsCoord.h"
#include "nsPaper.h"
#include "nsReadableUtils.h"
#include "nsIPrintSession.h"
#include "mozilla/RefPtr.h"
@ -57,6 +60,29 @@ nsPrintSettings::nsPrintSettings()
mFooterStrs[2].AssignLiteral("&D");
}
void nsPrintSettings::InitWithInitializer(
const PrintSettingsInitializer& aSettings) {
const double kInchesPerPoint = 1.0 / 72.0;
SetPrinterName(aSettings.mPrinter);
SetPrintInColor(aSettings.mPrintInColor);
SetPaperName(aSettings.mPaperInfo.mName);
SetPaperWidth(aSettings.mPaperInfo.mSize.Width() * kInchesPerPoint);
SetPaperHeight(aSettings.mPaperInfo.mSize.Height() * kInchesPerPoint);
SetPaperSizeUnit(nsIPrintSettings::kPaperSizeInches);
if (aSettings.mPaperInfo.mUnwriteableMargin) {
const auto& margin = aSettings.mPaperInfo.mUnwriteableMargin.value();
SetUnwriteableMarginTop(NS_POINTS_TO_TWIPS(margin.top));
SetUnwriteableMarginRight(NS_POINTS_TO_TWIPS(margin.right));
SetUnwriteableMarginBottom(NS_POINTS_TO_TWIPS(margin.bottom));
SetUnwriteableMarginLeft(NS_POINTS_TO_TWIPS(margin.left));
}
// Set this last because other setters may overwrite its value.
SetIsInitializedFromPrinter(true);
}
nsPrintSettings::nsPrintSettings(const nsPrintSettings& aPS) { *this = aPS; }
nsPrintSettings::~nsPrintSettings() = default;
@ -797,3 +823,23 @@ nsPrintSettings& nsPrintSettings::operator=(const nsPrintSettings& rhs) {
return *this;
}
void nsPrintSettings::SetDefaultFileName() {
nsAutoString filename;
nsresult rv = 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");
}
SetToFileName(filename);
}
}

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

@ -10,6 +10,7 @@
#include "nsIPrintSettings.h"
#include "nsIWeakReferenceUtils.h"
#include "nsMargin.h"
#include "nsPaper.h"
#include "nsString.h"
#define NUM_HEAD_FOOT 3
@ -18,16 +19,41 @@
//*** nsPrintSettings
//*****************************************************************************
namespace mozilla {
/**
* A struct that can be used off the main thread to collect printer-specific
* info that can be used to initialized a default nsIPrintSettings object.
*/
struct PrintSettingsInitializer {
nsString mPrinter;
PaperInfo mPaperInfo;
bool mPrintInColor = false;
};
} // namespace mozilla
class nsPrintSettings : public nsIPrintSettings {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRINTSETTINGS
using PrintSettingsInitializer = mozilla::PrintSettingsInitializer;
nsPrintSettings();
nsPrintSettings(const nsPrintSettings& aPS);
/**
* Initialize relevant members from the PrintSettingsInitializer.
* This is specifically not a constructor so that we can ensure that the
* relevant setters are dynamically dispatched to derived classes.
*/
void InitWithInitializer(const PrintSettingsInitializer& aSettings);
nsPrintSettings& operator=(const nsPrintSettings& rhs);
// Sets a default file name for the print settings.
void SetDefaultFileName();
protected:
virtual ~nsPrintSettings();

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

@ -7,6 +7,7 @@
#include "nsPaperMargin.h"
#include <utility>
#include "nsPaper.h"
#include "nsIPrintSettings.h"
#include "PrintBackgroundTask.h"
#include "mozilla/dom/Promise.h"
@ -53,6 +54,13 @@ void ResolveOrReject(Promise& aPromise, nsPrinterBase& aPrinter,
aPromise.MaybeResolve(result);
}
template <>
void ResolveOrReject(Promise& aPromise, nsPrinterBase& aPrinter,
const PrintSettingsInitializer& aResult) {
aPromise.MaybeResolve(
RefPtr<nsIPrintSettings>(CreatePlatformPrintSettings(aResult)));
}
} // namespace mozilla
template <typename T, typename... Args>
@ -65,6 +73,14 @@ nsresult nsPrinterBase::AsyncPromiseAttributeGetter(
aBackgroundTask, std::forward<Args>(aArgs)...);
}
NS_IMETHODIMP nsPrinterBase::CreateDefaultSettings(JSContext* aCx,
Promise** aResultPromise) {
// This doesn't yet implement caching. See bug 1659551.
MOZ_ASSERT(NS_IsMainThread());
return PrintBackgroundTaskPromise(*this, aCx, aResultPromise,
&nsPrinterBase::DefaultSettings);
}
NS_IMETHODIMP nsPrinterBase::GetSupportsDuplex(JSContext* aCx,
Promise** aResultPromise) {
return AsyncPromiseAttributeGetter(aCx, aResultPromise,

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

@ -17,6 +17,7 @@
namespace mozilla {
struct PaperInfo;
struct PrintSettingsInitializer;
namespace dom {
class Promise;
@ -28,7 +29,9 @@ class nsPrinterBase : public nsIPrinter {
public:
using Promise = mozilla::dom::Promise;
using MarginDouble = mozilla::gfx::MarginDouble;
using PrintSettingsInitializer = mozilla::PrintSettingsInitializer;
NS_IMETHOD CreateDefaultSettings(JSContext*, Promise**) final;
NS_IMETHOD GetSupportsDuplex(JSContext*, Promise**) final;
NS_IMETHOD GetSupportsColor(JSContext*, Promise**) final;
NS_IMETHOD GetSupportsCollation(JSContext*, Promise**) final;
@ -68,6 +71,7 @@ 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 SupportsCollation() const = 0;

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

@ -6,9 +6,24 @@
#include "nsPrinterCUPS.h"
#include "nsPaper.h"
#include "nsPrinterBase.h"
#include "nsPrintSettingsImpl.h"
#include "plstr.h"
static PaperInfo MakePaperInfo(const char* aName, const cups_size_t& aMedia) {
// XXX Do we actually have the guarantee that this is utf-8?
NS_ConvertUTF8toUTF16 name(aName ? aName : aMedia.media);
const double kPointsPerHundredthMillimeter = 0.0283465;
return PaperInfo(
name,
{aMedia.width * kPointsPerHundredthMillimeter,
aMedia.length * kPointsPerHundredthMillimeter},
Some(MarginDouble{aMedia.top * kPointsPerHundredthMillimeter,
aMedia.right * kPointsPerHundredthMillimeter,
aMedia.bottom * kPointsPerHundredthMillimeter,
aMedia.left * kPointsPerHundredthMillimeter}));
}
nsPrinterCUPS::~nsPrinterCUPS() {
if (mPrinterInfo) {
mShim.cupsFreeDestInfo(mPrinterInfo);
@ -20,15 +35,69 @@ nsPrinterCUPS::~nsPrinterCUPS() {
}
}
PrintSettingsInitializer nsPrinterCUPS::DefaultSettings() const {
nsString printerName;
GetPrinterName(printerName);
cups_size_t media;
bool hasDefaultMedia =
mShim.cupsGetDestMediaDefault(CUPS_HTTP_DEFAULT, mPrinter, mPrinterInfo,
CUPS_MEDIA_FLAGS_DEFAULT, &media);
if (!hasDefaultMedia) {
Nothing();
return PrintSettingsInitializer{
std::move(printerName),
PaperInfo(),
SupportsColor(),
};
}
const char* localizedName = nullptr;
// 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);
if (connection) {
localizedName = LocalizeMediaName(*connection, media);
mShim.httpClose(connection);
}
return PrintSettingsInitializer{
std::move(printerName),
MakePaperInfo(localizedName, media),
SupportsColor(),
};
}
NS_IMETHODIMP
nsPrinterCUPS::GetName(nsAString& aName) {
GetPrinterName(aName);
return NS_OK;
}
void nsPrinterCUPS::GetPrinterName(nsAString& aName) const {
if (mDisplayName.IsEmpty()) {
aName.Truncate();
CopyUTF8toUTF16(MakeStringSpan(mPrinter->name), aName);
} else {
aName = mDisplayName;
}
return NS_OK;
}
const char* nsPrinterCUPS::LocalizeMediaName(http_t& aConnection,
cups_size_t& aMedia) const {
// The returned string is owned by mPrinterInfo.
// https://www.cups.org/doc/cupspm.html#cupsLocalizeDestMedia
return mShim.cupsLocalizeDestMedia(&aConnection, mPrinter, mPrinterInfo,
CUPS_MEDIA_FLAGS_DEFAULT, &aMedia);
}
bool nsPrinterCUPS::SupportsDuplex() const {
@ -81,37 +150,17 @@ nsTArray<PaperInfo> nsPrinterCUPS::PaperList() const {
nsTArray<PaperInfo> paperList;
for (int i = 0; i < paperCount; ++i) {
cups_size_t info;
cups_size_t media;
int getInfoSucceded =
mShim.cupsGetDestMediaByIndex(CUPS_HTTP_DEFAULT, mPrinter, mPrinterInfo,
i, CUPS_MEDIA_FLAGS_DEFAULT, &info);
i, CUPS_MEDIA_FLAGS_DEFAULT, &media);
if (!getInfoSucceded) {
continue;
}
// localizedName is owned by mPrinterInfo.
// https://www.cups.org/doc/cupspm.html#cupsLocalizeDestMedia
const char* localizedName = mShim.cupsLocalizeDestMedia(
connection, mPrinter, mPrinterInfo, CUPS_MEDIA_FLAGS_DEFAULT, &info);
if (!localizedName) {
continue;
}
// XXX Do we actually have the guarantee that this is utf-8?
NS_ConvertUTF8toUTF16 name(localizedName);
const double kPointsPerHundredthMillimeter = 0.0283465;
paperList.AppendElement(PaperInfo{
std::move(name),
{info.width * kPointsPerHundredthMillimeter,
info.length * kPointsPerHundredthMillimeter},
Some(MarginDouble{info.top * kPointsPerHundredthMillimeter,
info.right * kPointsPerHundredthMillimeter,
info.bottom * kPointsPerHundredthMillimeter,
info.left * kPointsPerHundredthMillimeter}),
});
paperList.AppendElement(
MakePaperInfo(LocalizeMediaName(*connection, media), media));
}
mShim.httpClose(connection);

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

@ -7,6 +7,7 @@
#define nsPrinterCUPS_h___
#include "nsPrinterBase.h"
#include "nsPrintSettingsImpl.h"
#include "nsCUPSShim.h"
#include "nsString.h"
@ -16,6 +17,7 @@
class nsPrinterCUPS final : public nsPrinterBase {
public:
NS_IMETHOD GetName(nsAString& aName) override;
PrintSettingsInitializer DefaultSettings() const final;
bool SupportsDuplex() const final;
bool SupportsColor() const final;
bool SupportsCollation() const final;
@ -39,6 +41,13 @@ class nsPrinterCUPS final : public nsPrinterBase {
private:
~nsPrinterCUPS();
/**
* Retrieves the localized name for a given media (paper).
*/
const char* LocalizeMediaName(http_t& aConnection, cups_size_t& aMedia) const;
void GetPrinterName(nsAString& aName) const;
// Little util for getting support flags using the direct CUPS names.
bool Supports(const char* option, const char* value) const;

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

@ -6,6 +6,7 @@
#include "mozilla/ArrayUtils.h"
#include "nsCRT.h"
#include "nsDeviceContextSpecWin.h"
#include "WinUtils.h"
// Using paper sizes from wingdi.h and the units given there, plus a little
@ -159,6 +160,40 @@ nsPrintSettingsWin::nsPrintSettingsWin(const nsPrintSettingsWin& aPS)
*this = aPS;
}
already_AddRefed<nsIPrintSettings> CreatePlatformPrintSettings(
const PrintSettingsInitializer& aSettings) {
auto settings = MakeRefPtr<nsPrintSettingsWin>();
settings->InitWithInitializer(aSettings);
// When printing to PDF on Windows there is no associated printer driver.
int16_t outputFormat;
settings->GetOutputFormat(&outputFormat);
if (outputFormat == nsIPrintSettings::kOutputFormatPDF) {
return settings.forget();
}
RefPtr<nsDeviceContextSpecWin> devSpecWin = new nsDeviceContextSpecWin();
nsString name;
settings->GetPrinterName(name);
devSpecWin->GetDataFromPrinter(name);
LPDEVMODEW devmode;
devSpecWin->GetDevMode(devmode);
if (NS_WARN_IF(!devmode)) {
return settings.forget();
}
// TODO(nordzilla, 1658299)
// We need to get information from the device as well.
// See InitPrintSettingsFromPrinter call to CreateICW and the code
// below it. The issue is that we can't do it here. It needs to
// happen async, off the main thread, similar to the code that
// populates the PrintSettingsInitializer argument.
return settings.forget();
}
/** ---------------------------------------------------
* See documentation in nsPrintSettingsWin.h
* @update

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

@ -23,6 +23,7 @@ class nsPrintSettingsWin : public nsPrintSettings, public nsIPrintSettingsWin {
nsPrintSettingsWin();
nsPrintSettingsWin(const nsPrintSettingsWin& aPS);
explicit nsPrintSettingsWin(const PrintSettingsInitializer&);
/**
* Makes a new copy

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

@ -24,6 +24,14 @@ already_AddRefed<nsPrinterWin> nsPrinterWin::Create(const nsAString& aName) {
return do_AddRef(new nsPrinterWin(aName));
}
// TODO(nordzilla, 165829) This needs to be implemented for windows.
// It should basically collect the same information as
// nsPrinterListWin::InitPrintSettingsFromPrinter.
PrintSettingsInitializer nsPrinterWin::DefaultSettings() const {
PrintSettingsInitializer settings;
return settings;
}
template <class T>
static nsTArray<T> GetDeviceCapabilityArray(const LPWSTR aPrinterName,
WORD aCapabilityID) {
@ -119,12 +127,8 @@ nsTArray<mozilla::PaperInfo> nsPrinterWin::PaperList() const {
// We don't resolve the margins eagerly because they're really expensive (on
// the order of seconds for some drivers).
nsDependentSubstring name(paperNames[i].cbegin(), nameLength);
paperList.AppendElement(mozilla::PaperInfo{
nsString(name),
{width, height},
Nothing(),
paperIds[i],
});
paperList.AppendElement(mozilla::PaperInfo(nsString(name), {width, height},
Nothing(), paperIds[i]));
}
return paperList;

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

@ -11,7 +11,7 @@
class nsPrinterWin final : public nsPrinterBase {
public:
NS_IMETHOD GetName(nsAString& aName) override;
PrintSettingsInitializer DefaultSettings() const final;
bool SupportsDuplex() const final;
bool SupportsColor() const final;
bool SupportsCollation() const final;