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;

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);

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:

Enable e10s printing on Windows and OS X (can probably tag this on the end of bug 1082579)
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

https://tbpl.mozilla.org/?tree=Try&rev=f1aa062a86f9
https://tbpl.mozilla.org/?tree=Try&rev=2413f6719d70