Bug 1082579 - Introduce PPrinting.ipdl and proxies for opening printing UI
The hard part is not opening up the dialogs. The hard part is sending back useful information to the content.
roc says:
"I think you probably need PuppetWidget implementations of
nsIPrintSettings and nsIDeviceContextSpec. Your nsIPrintSettings would
basically be a content-side proxy for the nsIPrintSettings constructed
by the chrome process, and you'll pass this from chrome to content when
printing is initiated.
Your nsIDeviceContextSpec implementation would likewise proxy for a
chrome-process nsIDeviceContextSpec. Its GetSurfaceForPrinter
implementation will probably need to return a Moz2D recording target
(CreateRecordingDrawTarget) with some way to capture the
DrawEventRecorder output and spool it to the chrome process where it can
be replayed to the real drawing target (at every
nsIDeviceContextSpec::EndPage I guess). I don't know any of the details
of how the Moz2D recorder works."
Let's experiment with an idea:
Suppose I don't kick off printing from the child process. Suppose I go with roc's idea here. How would that work?
So… we tell the child, "start to print"… and perhaps we send down
minimal serialized, non-platform-specific print settings which it uses
to lay things out.
Then the child gets everything prepared, and then to print, it creates an instance of nsIDeviceContextSpec…
So according to an archive of
http://blog.vlad1.com/2007/12/11/graphics-in-mozilla/
at
https://web.archive.org/web/20080712065153/http://blog.vlad1.com/2007/12/11/graphics-in-mozilla/
:
nsIDeviceContext and nsIRenderingContext are old (old at 2007!)
interfaces for providing a set of basic rendering capabilities for every
platform.
gfx* stuff is Thebes.
So it looks like, according to
https://wiki.mozilla.org/Platform/GFX/Moz2D#Making_a_recording
, it might be possible to record the instructions being sent to a
surface… serialize those, send those over to the parent, and then replay
them onto a "real" surface for printing.
So, I talked to blassey and jimm, and here's what we're going to try.
We're not going to go with Moz2D for now unless there's really no other option.
So let's talk Windows, and how it opens dialogs. I need to find out how it opens dialogs, and then what it does with them.
The two prefs that allow us to print on windows are:
print.always_print_silent -> true
print.show_print_progress -> false
With my current patches, where do we stand? Hrm…
Well, while it's building, let's assume that
we currently work with print.always_print_silent to true and print.show_print_progress also true.
If print.always_print_silent is set to false, what happens?
We get the printing prompt service… calls ShowPrintDialog, passing the
domWin for the document being printed, nsIWebBrowserPrint for the doc
viewer, and some nsIPrintSettings.
I assume ShowPrintDialog is expected to update nsIPrintSettings.
When then init the nsDeviceContextSpec with the print settings.
So I think I just have to make sure my proxy returns a nsIPrintSettings
that makes sense for whichever platform we're on. We'll start with
Windows.
Ok, let's investigate ways of serializing nsIPrintSettings. It's
already saved sometimes in prefs, so there must be some internal support
for this. Let's check it out.
Unfortunately, they're not serialized into a string / single pref. A
bunch
of prefs get stored.
Here they are:
attribute long startPageRange;
attribute long endPageRange;
attribute double edgeTop; /* these are in inches */
attribute double edgeLeft;
attribute double edgeBottom;
attribute double edgeRight;
attribute double marginTop; /* these are in inches */
attribute double marginLeft;
attribute double marginBottom;
attribute double marginRight;
attribute double unwriteableMarginTop; /* these are in inches */
attribute double unwriteableMarginLeft;
attribute double unwriteableMarginBottom;
attribute double unwriteableMarginRight;
attribute double scaling; /* values 0.0 - 1.0 */
attribute boolean printBGColors; /* Print Background Colors */
attribute boolean printBGImages; /* Print Background Images */
attribute short printRange;
attribute wstring title;
attribute wstring docURL;
attribute wstring headerStrLeft;
attribute wstring headerStrCenter;
attribute wstring headerStrRight;
attribute wstring footerStrLeft;
attribute wstring footerStrCenter;
attribute wstring footerStrRight;
attribute short howToEnableFrameUI; /* indicates how to enable the frameset UI */
attribute boolean isCancelled; /* indicates whether the print job has been cancelled */
attribute short printFrameTypeUsage; /* indicates whether to use the interal value or not */
attribute short printFrameType;
attribute boolean printSilent; /* print without putting up the dialog */
attribute boolean shrinkToFit; /* shrinks content to fit on page */
attribute boolean showPrintProgress; /* indicates whether the progress dialog should be shown */
attribute wstring paperName; /* name of paper */
attribute short paperSizeType; /* use native data or is defined here */
attribute short paperData; /* native data value */
attribute double paperWidth; /* width of the paper in inches or mm */
attribute double paperHeight; /* height of the paper in inches or mm */
attribute short paperSizeUnit; /* paper is in inches or mm */
attribute wstring plexName; /* name of plex mode (like "simplex", "duplex",
* "tumble" and various custom values) */
attribute wstring colorspace; /* device-specific name of colorspace, overrides |printInColor| */
attribute wstring resolutionName;/* device-specific identifer of resolution or quality
* (like "600", "600x300", "600x300x12", "high-res",
* "med-res". "low-res", etc.) */
attribute boolean downloadFonts; /* enable font download to printer? */
attribute boolean printReversed;
attribute boolean printInColor; /* a false means grayscale */
attribute long orientation; /* see orientation consts */
attribute wstring printCommand;
attribute long numCopies;
attribute wstring printerName; /* name of destination printer */
attribute boolean printToFile;
attribute wstring toFileName;
attribute short outputFormat;
attribute long printPageDelay; /* in milliseconds */
attribute long resolution; /* print resolution (dpi) */
attribute long duplex; /* duplex mode */
attribute boolean isInitializedFromPrinter;
attribute boolean isInitializedFromPrefs;
attribute boolean persistMarginBoxSettings;
attribute long endPageRange;
attribute double edgeTop; /* these are in inches */
attribute double edgeLeft;
attribute double edgeBottom;
attribute double edgeRight;
attribute double marginTop; /* these are in inches */
attribute double marginLeft;
attribute double marginBottom;
attribute double marginRight;
attribute double unwriteableMarginTop; /* these are in inches */
attribute double unwriteableMarginLeft;
attribute double unwriteableMarginBottom;
attribute double unwriteableMarginRight;
attribute double scaling; /* values 0.0 - 1.0 */
attribute boolean printBGColors; /* Print Background Colors */
attribute boolean printBGImages; /* Print Background Images */
attribute short printRange;
attribute wstring title;
attribute wstring docURL;
attribute wstring headerStrLeft;
attribute wstring headerStrCenter;
attribute wstring headerStrRight;
attribute wstring footerStrLeft;
attribute wstring footerStrCenter;
attribute wstring footerStrRight;
attribute short howToEnableFrameUI; /* indicates how to enable the frameset UI */
attribute boolean isCancelled; /* indicates whether the print job has been cancelled */
attribute short printFrameTypeUsage; /* indicates whether to use the interal value or not */
attribute short printFrameType;
attribute boolean printSilent; /* print without putting up the dialog */
attribute boolean shrinkToFit; /* shrinks content to fit on page */
attribute boolean showPrintProgress; /* indicates whether the progress dialog should be shown */
attribute wstring paperName; /* name of paper */
attribute short paperSizeType; /* use native data or is defined here */
attribute short paperData; /* native data value */
attribute double paperWidth; /* width of the paper in inches or mm */
attribute double paperHeight; /* height of the paper in inches or mm */
attribute short paperSizeUnit; /* paper is in inches or mm */
attribute wstring plexName; /* name of plex mode (like "simplex", "duplex",
* "tumble" and various custom values) */
attribute wstring colorspace; /* device-specific name of colorspace, overrides |printInColor| */
attribute wstring resolutionName;/* device-specific identifer of resolution or quality
* (like "600", "600x300", "600x300x12", "high-res",
* "med-res". "low-res", etc.) */
attribute boolean downloadFonts; /* enable font download to printer? */
attribute boolean printReversed;
attribute boolean printInColor; /* a false means grayscale */
attribute long orientation; /* see orientation consts */
attribute wstring printCommand;
attribute long numCopies;
attribute wstring printerName; /* name of destination printer */
attribute boolean printToFile;
attribute wstring toFileName;
attribute short outputFormat;
attribute long printPageDelay; /* in milliseconds */
attribute long resolution; /* print resolution (dpi) */
attribute long duplex; /* duplex mode */
attribute boolean isInitializedFromPrinter;
attribute boolean isInitializedFromPrefs;
attribute boolean persistMarginBoxSettings;
Note that we're not creating and returning a new nsIPrintSettings - we're getting passed one, and we're modifying it.
So, let's think it through again. Proxy is passed an nsIPrintSettings.
We serialize that, and hand it off to the parent. Parent creates an
nsIPrintSettings from what it gets handed, maybe spoofs a bunch of
things (that might be tricky) that the child has taken care of (like
print session), and hands it to the nsIPrintingPromptService to show the
dialog. Dialog gets shown.
Dialog fills nsIPrintSettings with maybe some platform specific stuff that our serialization needs to know about.
nsIPrintSettings comes back to parent, which serializes and sends to
child. Child interprets serialization and uses operator= to update the
child copy. This will wipe out the original DevMode…
Child then also has to read the platform specific stuff. For Windows,
this means a DevMode... I'll need to create a new one, and init it with
the nsIPrintSettings I get back, actually. That works out... I think.
"CreateGlobalDevModeAndInit"...also some stuff for nsIPrintSettingsWin.
That's DeviceName, DriverName, DevMode.
Ok, to, to sum - for Windows, serialize all the attributes I've listed above, plus DeviceName and DriverName.
On the parent side, the nsIWebBrowserPrint passed to the printing
prompt services are going to be bogus, but they need to be able to do
certain things.
For example, on Windows, it's important that we be able to do:
aWebBrowserPrint->GetIsFramesetDocument(&isFramesetDocument);
aWebBrowserPrint->GetIsFramesetFrameSelected(&isFramesetFrameSelected);
aWebBrowserPrint->GetIsIFrameSelected(&isIFrameSelected);
aWebBrowserPrint->GetIsRangeSelection(&isRangeSelection);
aWebBrowserPrint->GetIsFramesetFrameSelected(&isFramesetFrameSelected);
aWebBrowserPrint->GetIsIFrameSelected(&isIFrameSelected);
aWebBrowserPrint->GetIsRangeSelection(&isRangeSelection);
On OS X, we do:
nsresult rv = aWebBrowserPrint->EnumerateDocumentNames(&titleCount, &docTitles);
Linux... nothing special it seems. So we have to create some kind of mock nsIWebBrowserPrint that satisfies the above.
Ok, I have the communications for opening the dialog set up. Now I need:
-
Ability to convert nsIPrintSettings and an nsIWebBrowserPrint to a
PrintData, including the platform-specific stuff I listed above
-
A fake nsIWebBrowserPrint that can take an PrintData and parrot back
results for GetIsFramesetDocument. This is what we end up passing to the
real nsIPrintingPromptService.
-
Ability to convert PrintData back to an nsIPrintSettings.
-
Ability to create native-y things on the child side - like the DevMode on Windows.
Create a utility function for nsPrintingPromptServiceProxy that takes
nsIPrintSettings and nsIWebBrowserPrint, and packs a PrintData with the
right info (including platform-specific stuff)
Create a utility function accessible both for
nsPrintingPromptServiceProxy and PrintingParent that takes an
PrintDataand returns an nsIPrintSettings.
Create a utility function accessible for PrintingParent that takes a
PrintData, wraps it, and becomes an nsIWebBrowserPrint that parrots out
the information that Windows and OS X need.
I think I can start on the above tonight, even away from my Windows machine.
Before I proceed - I want to make sure: nsIPrintSettings doesn't get
packed with native stuff _before_ it goes off to the print settings
dialog, right? Right… that seems to be true.
OK! I have a dialog showing up on Windows! This is good good good.
Funny how we don't have the end number of pages available in the print
dialog… that's weird. But that also seems to be the behaviour without
e10s.¯\_(ツ)_/¯
So now that I've got this dialog… I need to get the information from
the dialog back to the child, and then the circuit will be complete.
Here's how things work for Windows:
We pass in the nsIPrintSettings to show the dialog, and the actual
showing happens in nsPrintDialogUtil.cpp in a function called
ShowNativePrintDialog. We construct a devmode… we also create a
temporary nsIPrintSettingsWin, and use it to set the device name and
driver name, and then …
Wait. Wait, this is wrong. We're actually passing an nsPrintSettingsWin
to ShowNativePrintDialog as an nsIPrintSettings, and then QI'ing it
within that function to an nsIPrintSettingsWin. Shoot.
Ah, ok, how about this:
We have the nsIPrintOptions service that can return the right nsIPrintSettings implementation. How about I:
Add SerializeToPrintData to nsPrintOptionsImpl.cpp to do common field
copying from a PrintData (take nsIPrintSettings and nsIWebBrowserPrint)
Add DeserializeFromPrintData to nsPrintOptionsImpl.cpp to do common field copying to a PrintData
Add implementations of that function to nsPrintOptionsWin.cpp that
calls the parent implementation, and then reads/writes the native
information to/from that PrintData.
That way, PrintingParent.cpp can stay relatively platform agnostic, and
we offload the platform specific stuff to stuff that's…well, already
platform specific.
I… I think I like that idea. Let's do it.
It… works? If this lands, I'll need to file some follow-ups:
ShowPrintDialog should probably not be a sync call. smaug says: "
Couldn't you create a PrintDialog protocol, and then in __delete__
continue. While you're waiting __delete__ you'd call
NS_ProcessNextEvent"
Progress dialog does not show progress or close the dialog - need to proxy that information
Support printing in e10s on Linux
Cancelling from the print settings dialog shows an alert "An error occurred while printing"
File bug for long-term plan