Bug 1663652 part 1 - Implement combined CreateDefaultSettings and GetPaperList interface for nsPrinterBase r=emilio,mstriemer

This also changes the frontend to use this interface.

This will be a bit less efficient in the general case, as it will serialize the
PaperList and DefaultSettings fetches. But most of the large delays seem to
occur when the print server is slow to respond, and ultimately using a single
live connection for both these requests when possible (particularly with CUPS)
should improve the total latency when the printer or print server is not
available, or is very slow.

Differential Revision: https://phabricator.services.mozilla.com/D93688
This commit is contained in:
Emily McDonough 2020-11-03 20:35:38 +00:00
parent 9dea4325b4
commit 1fbcb914c4
8 changed files with 116 additions and 88 deletions

View File

@ -24,9 +24,8 @@ async function run() {
isnot(printer.name, "", "Printer name should never be empty.");
info(printer.name);
let paperList = await printer.paperList;
for (let paper of paperList) {
let printerInfo = await printer.printerInfo;
for (let paper of printerInfo.paperList) {
paper.QueryInterface(Ci.nsIPaper);
info(`${paper.name}: ${paper.width}x${paper.height}`);

View File

@ -22,7 +22,8 @@ async function run() {
printer.QueryInterface(Ci.nsIPrinter);
info(printer.name);
const settings = await printer.createDefaultSettings();
const printerInfo = await printer.printerInfo;
const settings = printerInfo.defaultSettings;
settings.QueryInterface(Ci.nsIPrintSettings);
is(typeof settings.printerName, "string", "Printer name should be a string.");

View File

@ -981,24 +981,26 @@ var PrintSettingsViewProxy = {
// Await the async printer data.
if (printerInfo.printer) {
let basePrinterInfo;
try {
[
printerInfo.supportsColor,
printerInfo.supportsMonochrome,
printerInfo.paperList,
printerInfo.defaultSettings,
basePrinterInfo,
] = await Promise.all([
printerInfo.printer.supportsColor,
printerInfo.printer.supportsMonochrome,
printerInfo.printer.paperList,
// get a set of default settings for this printer
printerInfo.printer.createDefaultSettings(),
printerInfo.printer.printerInfo,
]);
} catch (e) {
this.reportPrintingError("PRINTER_SETTINGS");
throw e;
}
printerInfo.defaultSettings.QueryInterface(Ci.nsIPrintSettings);
basePrinterInfo.QueryInterface(Ci.nsIPrinterInfo);
basePrinterInfo.defaultSettings.QueryInterface(Ci.nsIPrintSettings);
printerInfo.paperList = basePrinterInfo.paperList;
printerInfo.defaultSettings = basePrinterInfo.defaultSettings;
} else if (printerName == PrintUtils.SAVE_TO_PDF_PRINTER) {
// The Mozilla PDF pseudo-printer has no actual nsIPrinter implementation
printerInfo.defaultSettings = PSSVC.newPrintSettings;

View File

@ -6,8 +6,7 @@
add_task(async function testSanityCheckPaperList() {
const mockPrinterName = "Fake Printer";
await PrintHelper.withTestPage(async helper => {
let printer = helper.addMockPrinter(mockPrinterName);
printer.paperList = Promise.resolve([
let paperList = [
PrintHelper.createMockPaper({
id: "regular",
name: "Regular Paper",
@ -18,7 +17,8 @@ add_task(async function testSanityCheckPaperList() {
width: 720,
height: 1224,
}),
]);
];
helper.addMockPrinter(mockPrinterName, paperList);
await helper.startPrint();
await helper.dispatchSettingsChange({ printerName: mockPrinterName });
await helper.awaitAnimationFrame();

View File

@ -179,28 +179,26 @@ class PrintHelper {
};
}
addMockPrinter(name = "Mock Printer") {
addMockPrinter(name = "Mock Printer", paperList = []) {
let PSSVC = Cc["@mozilla.org/gfx/printsettings-service;1"].getService(
Ci.nsIPrintSettingsService
);
let defaultSettings = {
printerName: name,
toFileName: "",
outputFormat: Ci.nsIPrintSettings.kOutputFormatNative,
printToFile: false,
};
let defaultSettings = PSSVC.newPrintSettings;
defaultSettings.printerName = name;
defaultSettings.toFileName = "";
defaultSettings.outputFormat = Ci.nsIPrintSettings.kOutputFormatNative;
defaultSettings.printToFile = false;
let printer = {
name,
supportsColor: Promise.resolve(true),
supportsMonochrome: Promise.resolve(true),
paperList: Promise.resolve([]),
createDefaultSettings: () => {
let settings = PSSVC.newPrintSettings;
for (let [key, value] of Object.entries(defaultSettings)) {
settings[key] = value;
}
return settings;
},
printerInfo: Promise.resolve({
paperList,
defaultSettings,
QueryInterface: ChromeUtils.generateQI([Ci.nsIPrinterInfo]),
}),
QueryInterface: ChromeUtils.generateQI([Ci.nsIPrinter]),
};

View File

@ -4,8 +4,22 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsIPaper.idl"
#include "nsIPrintSettings.idl"
#include "nsISupports.idl"
[scriptable, uuid(855ae9dd-62a4-64aa-9c60-b1078ff028f1)]
interface nsIPrinterInfo : nsISupports
{
/**
* An array of nsIPaper instances that represents the available paper sizes.
*/
readonly attribute Array<nsIPaper> paperList;
/**
* nsIPrintSettings object containing the default settings for a printer.
*/
readonly attribute nsIPrintSettings defaultSettings;
};
[scriptable, uuid(d2dde9bb-df86-469c-bfcc-fd95a44b1db8)]
interface nsIPrinter : nsISupports
{
@ -23,20 +37,12 @@ interface nsIPrinter : nsISupports
readonly attribute AString systemName;
/**
* 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.
* Returns a Promise that resolves to a nsIPrinterInfo.
* This will contain the default printer settings, and the list of paper
* sizes supported by the printer.
*/
[implicit_jscontext]
Promise createDefaultSettings();
/**
* Returns a Promise that resolves to an array of
* nsIPaper instances with the list of available paper
* sizes.
*/
[implicit_jscontext]
readonly attribute Promise paperList;
readonly attribute Promise printerInfo;
/**
* Returns a Promise that resolves to true or false to indicate whether this

View File

@ -23,13 +23,55 @@ using mozilla::gfx::MarginDouble;
// correct size.
static constexpr double kPaperSizePointsEpsilon = 4.0;
void nsPrinterBase::CachePrintSettingsInitializer(
const PrintSettingsInitializer& aInitializer) {
if (mPrintSettingsInitializer.isNothing()) {
mPrintSettingsInitializer.emplace(aInitializer);
// Basic implementation of nsIPrinterInfo
class nsPrinterInfo : public nsIPrinterInfo {
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(nsPrinterInfo)
NS_DECL_NSIPRINTERINFO
nsPrinterInfo() = delete;
nsPrinterInfo(nsPrinterBase& aPrinter,
const nsPrinterBase::PrinterInfo& aPrinterInfo)
: mDefaultSettings(
CreatePlatformPrintSettings(aPrinterInfo.mDefaultSettings)) {
mPaperList.SetCapacity(aPrinterInfo.mPaperList.Length());
for (const PaperInfo& info : aPrinterInfo.mPaperList) {
mPaperList.AppendElement(MakeRefPtr<nsPaper>(aPrinter, info));
}
}
private:
virtual ~nsPrinterInfo() = default;
nsTArray<RefPtr<nsIPaper>> mPaperList;
RefPtr<nsIPrintSettings> mDefaultSettings;
};
NS_IMETHODIMP
nsPrinterInfo::GetPaperList(nsTArray<RefPtr<nsIPaper>>& aPaperList) {
aPaperList = mPaperList.Clone();
return NS_OK;
}
NS_IMETHODIMP
nsPrinterInfo::GetDefaultSettings(nsIPrintSettings** aDefaultSettings) {
NS_ENSURE_ARG_POINTER(aDefaultSettings);
MOZ_ASSERT(mDefaultSettings);
RefPtr<nsIPrintSettings> settings = mDefaultSettings;
settings.forget(aDefaultSettings);
return NS_OK;
}
NS_IMPL_CYCLE_COLLECTION(nsPrinterInfo, mPaperList, mDefaultSettings)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPrinterInfo)
NS_INTERFACE_MAP_ENTRY(nsIPrinterInfo)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPrinterInfo)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPrinterInfo)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsPrinterInfo)
template <typename Index, Index Size, typename Value>
inline void ImplCycleCollectionTraverse(
nsCycleCollectionTraversalCallback& aCallback,
@ -72,11 +114,16 @@ void ResolveOrReject(Promise& aPromise, nsPrinterBase& aPrinter,
template <>
void ResolveOrReject(Promise& aPromise, nsPrinterBase& aPrinter,
const PrintSettingsInitializer& aResult) {
aPrinter.CachePrintSettingsInitializer(aResult);
aPromise.MaybeResolve(
RefPtr<nsIPrintSettings>(CreatePlatformPrintSettings(aResult)));
}
template <>
void ResolveOrReject(Promise& aPromise, nsPrinterBase& aPrinter,
const nsPrinterBase::PrinterInfo& aResult) {
aPromise.MaybeResolve(MakeRefPtr<nsPrinterInfo>(aPrinter, aResult));
}
} // namespace mozilla
template <typename T, typename... Args>
@ -89,40 +136,12 @@ nsresult nsPrinterBase::AsyncPromiseAttributeGetter(
nsLiteralCString>
attributeKeys{"SupportsDuplex"_ns, "SupportsColor"_ns,
"SupportsMonochrome"_ns, "SupportsCollation"_ns,
"PaperList"_ns};
"PrinterInfo"_ns};
return mozilla::AsyncPromiseAttributeGetter(
*this, mAsyncAttributePromises[aAttribute], aCx, aResultPromise,
attributeKeys[aAttribute], aBackgroundTask, std::forward<Args>(aArgs)...);
}
NS_IMETHODIMP nsPrinterBase::CreateDefaultSettings(JSContext* aCx,
Promise** aResultPromise) {
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_ARG_POINTER(aResultPromise);
if (mPrintSettingsInitializer.isSome()) {
ErrorResult rv;
RefPtr<dom::Promise> promise =
dom::Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
if (MOZ_UNLIKELY(rv.Failed())) {
*aResultPromise = nullptr;
return rv.StealNSResult();
}
RefPtr<nsIPrintSettings> settings(
CreatePlatformPrintSettings(mPrintSettingsInitializer.ref()));
promise->MaybeResolve(settings);
promise.forget(aResultPromise);
return NS_OK;
}
return PrintBackgroundTaskPromise(*this, aCx, aResultPromise,
"DefaultSettings"_ns,
&nsPrinterBase::DefaultSettings);
}
NS_IMETHODIMP nsPrinterBase::GetSupportsDuplex(JSContext* aCx,
Promise** aResultPromise) {
return AsyncPromiseAttributeGetter(aCx, aResultPromise,
@ -151,11 +170,11 @@ NS_IMETHODIMP nsPrinterBase::GetSupportsCollation(JSContext* aCx,
&nsPrinterBase::SupportsCollation);
}
NS_IMETHODIMP nsPrinterBase::GetPaperList(JSContext* aCx,
Promise** aResultPromise) {
NS_IMETHODIMP nsPrinterBase::GetPrinterInfo(JSContext* aCx,
Promise** aResultPromise) {
return AsyncPromiseAttributeGetter(aCx, aResultPromise,
AsyncAttribute::PaperList,
&nsPrinterBase::PaperList);
AsyncAttribute::PrinterInfo,
&nsPrinterBase::CreatePrinterInfo);
}
void nsPrinterBase::QueryMarginsForPaper(Promise& aPromise,
@ -180,6 +199,10 @@ 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) {

View File

@ -31,12 +31,11 @@ class nsPrinterBase : public nsIPrinter {
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 GetSupportsMonochrome(JSContext*, Promise**) final;
NS_IMETHOD GetSupportsCollation(JSContext*, Promise**) final;
NS_IMETHOD GetPaperList(JSContext*, Promise**) final;
NS_IMETHOD GetPrinterInfo(JSContext*, Promise**) final;
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(nsPrinterBase)
@ -57,6 +56,12 @@ class nsPrinterBase : public nsIPrinter {
void CachePrintSettingsInitializer(
const PrintSettingsInitializer& aInitializer);
// Data to pass through to create the nsPrinterInfo
struct PrinterInfo {
nsTArray<mozilla::PaperInfo> mPaperList;
PrintSettingsInitializer mDefaultSettings;
};
private:
enum class AsyncAttribute {
// If you change this list you must update attributeKeys in
@ -65,7 +70,7 @@ class nsPrinterBase : public nsIPrinter {
SupportsColor,
SupportsMonochrome,
SupportsCollation,
PaperList,
PrinterInfo,
// Just a guard.
Last,
};
@ -79,13 +84,6 @@ class nsPrinterBase : public nsIPrinter {
BackgroundTask<T, Args...>,
Args... aArgs);
/**
* A cache to store the result of DefaultSettings() to ensure
* that subsequent calls to CreateDefaultSettings() will not
* have to spawn another background task to fetch the same info.
*/
Maybe<PrintSettingsInitializer> mPrintSettingsInitializer;
protected:
nsPrinterBase(const mozilla::CommonPaperInfoArray* aPaperInfoArray);
virtual ~nsPrinterBase();
@ -99,6 +97,7 @@ class nsPrinterBase : public nsIPrinter {
virtual bool SupportsCollation() const = 0;
virtual nsTArray<mozilla::PaperInfo> PaperList() const = 0;
virtual MarginDouble GetMarginsForPaper(nsString aPaperId) const = 0;
virtual PrinterInfo CreatePrinterInfo() const;
// 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(