Bug 1090448 - Make e10s printing work on Linux
Tricky - it looks like there's a native GTK dialog (orchestrated by
nsPrintDialogGTK.cpp), and a fallback XUL printer dialog if we can't get
a handle on the native stuff.
Let's do the native dialog first.
The native dialog case
So, like before, the native dialog imports some settings from an
nsIPrintSettings (likely casting them to an nsPrintSettingsGTK), and
then spins a modal event loop before exporting new settings.
The things the dialog seems to want to get from the nsIPrintSettings…
yeah, it casts it to an nsPrintSettingsGTK, and then extracts the
GtkPrintSettings* from it. Also the GtkPageSetup.
Then gets a "shrink to fit" bool, "print background colours" bool, "print background images" bool.
With those last three bools, it sets dialog checkboxes to various
states based on their value. Then it sets the other settings with the
GtkPrintSettings* and the same goes for the GtkPageSetup*.
So… what is a GtkPrintSettings, and can I serialize it?
This looks useful - I can iterate each key-value pair...
Huh… here's a snippet of code
I found from here
:
gtk_print_settings_foreach (settings, print_settings_add_to_key_file, key_file);
static void
print_settings_add_to_key_file (const gchar *key,
const gchar *value,
gpointer data)
{
GKeyFile *key_file = data;
g_key_file_set_value (key_file, PRINT_SETTINGS_NAME, key, value);
}
So… so I think every key and value can be serialized to a string this way? Okay….
Pretty sure we can serialize the GtkPrinter* by just getting the name
and sending it over. I'm pretty sure constructing a GtkPrinter with
gtk_printer_new on the child side with that name will work.
OVER TWO MONTHS PASS
Aaaaaand we're back. This is an M5 bug now, and just hit the top of my stack. So let's get back to it.
What I'd like, ideally, is parity with our current e10s printing support for Windows and OS X. At the very least, anyhow.
So, let's get a game plan together here.
The child process asks for the print settings dialog. Ugh, it looks
like the child is blocked while we wait for the settings to come
back down. Not great. That's
bug 1090439.
Let's not worry about that just this second.
So we synchronously ask the parent for information in nsPrintingPromptServiceProxy::ShowPrintDialog.
Parent receives it in PrintingParent::RecvShowPrintDialog. Gets the
nsIPrintOptions service, and creates a new nsIPrintSettings. Deserialize
the default settings that came up from the child. Then we show the
dialog, which blocks until it returns some value.
Then we serialize the settings we got back from the dialog into a PrintData again, and send them down to the child.
Initial work that needs to be studied is
bug 1082579
Is it good enough to just serialize the name of the printer, and find it on the content side?
Are the gtk-print-settings easily serializable? Settable?
What _exactly_ is it that GTK needs from the front-end that we don't already serialize?
Interestingly, nsPrintDialogGTK already seems to have the ability to
"export" and "import" GTK-specific settings from "NS" (Netscape?) print
settings structures. This code is oooooooooold.
" /* Code to copy between GTK and NS print settings structures.
* In the following,
* "Import" means to copy from NS to GTK
* "Export" means to copy from GTK to NS
*/
"
from:
https://hg.mozilla.org/mozilla-central/file/29b05d283b00/widget/gtk/nsPrintDialogGTK.cpp#l144
for ImportSettings, ExportSettings.
Also, let's just see "what happens" when I try to print on Linux right now. A crash? Let's find out.
Yep. A crash. I guess that's not so surprising. Probably something in
RecvShowPrintDialog. Wait, no that's wrong - we successfully exit
from the parent process. The problem is in the content process after we
return to it.
#0 __strlen_sse2_bsf () at ../sysdeps/i386/i686/multiarch/strlen-sse2-bsf.S:50
#1 0xafdc3da8 in nsCharTraits<char>::length (aStr=0x0) at
/media/Projects/mozilla/mozilla-central/xpcom/string/nsCharTraits.h:498
#2 0xafdc4d01 in nsDependentCString::nsDependentCString
(this=0xbff36584, aData=0x0) at
/media/Projects/mozilla/mozilla-central/xpcom/string/nsTDependentString.h:54
#3 0xb269bb9c in nsDeviceContextSpecGTK::GetSurfaceForPrinter
(this=0xa5bf0000, aSurface=0xa989c838) at
/media/Projects/mozilla/mozilla-central/widget/gtk/nsDeviceContextSpecG.cpp:177
#4 0xb0c37fad in nsDeviceContext::InitForPrinting
(this=0xa989c800, aDevice=0xa5bf0000) at
/media/Projects/mozilla/mozilla-central/gfx/src/nsDeviceContext.cpp:491
#5 0xb2cbc9e8 in nsPrintEngine::DoCommonPrint (this=0xa5967a10,
aIsPrintPreview=false, aPrintSettings=0x0, aWebProgressListener=0x0,
aDoc=0xa5a331f4)
at /media/Projects/mozilla/mozilla-central/layout/printing/nsPrintEngine.cpp:663
#6 0xb2cbb5d9 in nsPrintEngine::CommonPrint (this=0xa5967a10,
aIsPrintPreview=false, aPrintSettings=0x0, aWebProgressListener=0x0,
aDoc=0xa5a331f4)
at /media/Projects/mozilla/mozilla-central/layout/printing/nsPrintEngine.cpp:419
#7 0xb2cbcf22 in nsPrintEngine::Print (this=0xa5967a10,
aPrintSettings=0x0, aWebProgressListener=0x0) at
/media/Projects/mozilla/mozilla-central/layout/printing/nsPrintEngine.cpp:781
Here's the incriminating line in nsDeviceContextSpecG.cpp:
} else if (nsDependentCString(fmtGTK).EqualsIgnoreCase("pdf")) {
fmtGTK is probably nullptr. It's set earlier in that same method here:
const gchar* fmtGTK = gtk_print_settings_get(mGtkPrintSettings, GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT);
Right, so we've not properly serialized/deserialized that value.
Ok, I've added the serialize / deserialize functions to
nsPrintOptionsGTK. I think this is where most of the work is going to
be.
Serialization is, I think, going to be easy, but verbose. I just need
to grab everything inside the GtkPrintSettings that is relevant,
stuff it into the PrintData. No biggie.
Deserialization… how will that work. When I deserialize in
PrintingParent, I have a newly created nsIPrintSettingsGTK that I pass
in. When I deserialize in the nsPrintingPromptServiceProxy, I actually
pass in the old nsIPrintSettingsGTK that we'd sent up, and have it be
overwritten.
The nsPrintingPromptServiceProxy is fine. But is there anything I need
to do to initialize a newly created nsIPrintSettingsGTK in the parent
before I deserialize? Let's check...
Ok, on construction of an nsPrintSettingsGTK, we
instantiate mPrintSettings, so that's good… ok, and someone later
on, I guess, sets mGTKPrinter via SetGtkPrinter. So I need to do that
too, I guess...
There are a bunch of reimplementations of getters and settings in
nsPrintSettingsGTK that do reading and writing from the
GtkPrintSettings - so I might be getting a bunch of serialization for
free already.
Yeah, lots are freebies! Nice. The ones I don't seem to get
for free, I've added to the list of things to serialize / deserialize.
There's also, I believe, straight-up accessing the mPrintSettings, so
there might be even more to serialize / deserialize… let's look at where
the mPrintSettings get accessed.
Huh. So we do have direct access to the mGtkPrintSettings object,
and fiddling there, but really, I can't trust that Gecko is telling me
everything that's being used here by the user. Really, I kinda need
to serialize every damn thing that's not already done by
nsPrintSettingsImpl. *sigh*. Ok, what are those things.
I'll list what I can hold inside an GtkPrintSettings, and strike out
the things that we got for free because nsIPrintSettings wrapped the
getters / setters and our nsPrintOptions serialization
already works with them...
GtkPageOrientation gtk_print_settings_get_orientation ()
void gtk_print_settings_set_orientation ()
Remark: It looks like this is being set in a GtkPageSetup thing, but I might as well send it over as well.
GtkPaperSize * gtk_print_settings_get_paper_size ()
void gtk_print_settings_set_paper_size ()
Remark: Crap - GtkPaperSize is a complicated object that I'll need to
serialize. :( (Actually - it turns out that it's not so bad! - read
below!)
gdouble gtk_print_settings_get_paper_width ()
void gtk_print_settings_set_paper_width ()
gdouble gtk_print_settings_get_paper_height ()
void gtk_print_settings_set_paper_height ()
gboolean gtk_print_settings_get_collate ()
void gtk_print_settings_set_collate ()
GtkPrintQuality gtk_print_settings_get_quality ()
void gtk_print_settings_set_quality ()
gint gtk_print_settings_get_number_up ()
void gtk_print_settings_set_number_up ()
GtkNumberUpLayout gtk_print_settings_get_number_up_layout ()
void gtk_print_settings_set_number_up_layout ()
void gtk_print_settings_set_resolution_xy ()
gint gtk_print_settings_get_resolution_x ()
gint gtk_print_settings_get_resolution_y ()
gdouble gtk_print_settings_get_printer_lpi ()
void gtk_print_settings_set_printer_lpi ()
GtkPageSet gtk_print_settings_get_page_set ()
void gtk_print_settings_set_page_set ()
const gchar * gtk_print_settings_get_default_source ()
void gtk_print_settings_set_default_source ()
const gchar * gtk_print_settings_get_media_type ()
void gtk_print_settings_set_media_type ()
const gchar * gtk_print_settings_get_dither ()
void gtk_print_settings_set_dither ()
const gchar * gtk_print_settings_get_finishings ()
void gtk_print_settings_set_finishings ()
const gchar * gtk_print_settings_get_output_bin ()
void gtk_print_settings_set_output_bin ()
What… what is the relationship between GtkPageSetup and GtkPaperSize?
GtkPaperSize seems to be something you can get from GtkPageSetup… and
you can set a GtkPaperSize on it too with gtk_page_setup_set_paper_size.
And then there's gtk_page_setup_set_paper_size_and_default_margins.
Ahhhhh - a GtkPaperSize represents the default paper sizes that
exist within the system. So I think I can just serialize the name, and
send it over. Right? YES - I can get the name of the GtkPaperSize, and
then when I instantiate one, I can pass in that name, and it'll assume
the right values. Good! OH! And Paper Name is already being serialized
and sent over by nsPrintSettingsImpl! \o/
So, when deserializing, I think I just need to create a
custom GtkPaperSize with the name I was passed, and then set that
on the GtkPageSetup.
I can use SetGtkPageSetup to save the GtkPageSetup, which contains the GtkPaperSize information, to the GtkPrintSettings.
Last piece of the puzzle, I think. What the hell is a GtkPrintBackend?
I… I don't think it matters. Instead of instantiating the GtkPrinter,
I'll enumerate one until I find the one the user specified.
Roadblock
:
Apparently, when deserializing the PrintSettings on the parent side
(the first time, just passing up the default values), we fail an
assertion for SetDuplex:
MOZ_ASSERT(aDuplex >= GTK_PRINT_DUPLEX_SIMPLEX &&
aDuplex <= GTK_PRINT_DUPLEX_VERTICAL,
"value is out of bounds for GtkPrintDuplex enum");
So somehow we're getting an invalid duplex passed up from the child.
Maybe we have a bad default there, and we're just passing in random
memory. Let's see...
Ok, on parent side, according to gdb, duplex is:
$4 = (int32_t &) @0xbfa86068: -1376538432
Yeah, that doesn't sound right.
GTK_PRINT_DUPLEX_SIMPLEX is belongs to an enum with only 3 values. So we're wayyyyy out of range here.
Ah, so the problem was on the child side. The GtkPrintSettings,
on initialization, doesn't have a duplex value set, so GetDuplex returns
NS_ERROR_FAILURE. NS_ERROR_FAILURE maps to -1376538432 as a signed
int, and we dumbly were serializing that into the duplex field in the
PrintData.
So, new TODO: I need to make nsPrintOptionsImpl smartly serialize
things that might not exist. That means coming up with some sane
defaults.
No. Wait. That's too complicated. Wayyyyy to complicated. The far
simpler solution is just to have nsPrintSettingsGTK default to having
duplex set to GTK_PRINT_DUPLEX_SIMPLEX (which apparently means "no
duplex"). Yeah, jeebus, that's way better.
Wooo - just by serializing the GTKPrinter, I got something out:
But.. I'm crashing on shutdown. I'm crashing because I'm failing an assertion:
#0 0xb770f424 in __kernel_vsyscall ()
#1 0xb7485366 in nanosleep () at ../sysdeps/unix/syscall-template.S:81
#2 0xb748510d in __sleep (seconds=0) at ../sysdeps/unix/sysv/linux/sleep.c:137
#3 0xb2a307d0 in ah_crap_handler (signum=6) at /media/Projects/mozilla/mozilla-central/toolkit/xre/nsSigHandlers.cpp:101
#4 0xb2a14034 in nsProfileLock::FatalSignalHandler (signo=6,
info=0xbff5808c, context=0xbff5810c) at
/media/Projects/mozilla/mozilla-central/profile/dirserviceprovider/nsProfileLock.cpp:190
#5 <signal handler called>
#6 0xb770f424 in __kernel_vsyscall ()
#7 0xb73fd827 in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
#8 0xb7400c53 in __GI_abort () at abort.c:89
#9 0xb73f6977 in __assert_fail_base (fmt=0xb7534b94 "%s%s%s:%u:
%s%sAssertion `%s' failed.\n%n", assertion=assertion@entry=0xae4d437f
"hash_table->live_entries == 0",
file=file@entry=0xae4d42e0
"/build/buildd/cairo-1.13.0~20140204/src/cairo-hash.c",
line=line@entry=217, function=function@entry=0xae4d44b0
"_cairo_hash_table_destroy") at assert.c:92
#10 0xb73f6a27 in __GI___assert_fail (assertion=0xae4d437f
"hash_table->live_entries == 0", file=0xae4d42e0
"/build/buildd/cairo-1.13.0~20140204/src/cairo-hash.c", line=217,
function=0xae4d44b0 "_cairo_hash_table_destroy")
at assert.c:101
#11 0xae41de6e in ?? () from /usr/lib/i386-linux-gnu/libcairo.so.2
#12 0xae471a03 in ?? () from /usr/lib/i386-linux-gnu/libcairo.so.2
#13 0xae414a4d in cairo_debug_reset_static_data () from /usr/lib/i386-linux-gnu/libcairo.so.2
#14 0xb2a242b5 in MOZ_gdk_display_close (display=0xb71d10a0) at
/media/Projects/mozilla/mozilla-central/toolkit/xre/nsAppRunner.cpp:2804
#15 0xb2a285fa in XREMain::XRE_main (this=0xbff5884c, argc=4,
argv=0xbff59b84, aAppData=0xbff589bc) at
/media/Projects/mozilla/mozilla-central/toolkit/xre/nsAppRunner.cpp:4310
#16 0xb2a286d9 in XRE_main (argc=4, argv=0xbff59b84,
aAppData=0xbff589bc, aFlags=0) at
/media/Projects/mozilla/mozilla-central/toolkit/xre/nsAppRunner.cpp:4456
#17 0x0804bcdf in do_main (argc=4, argv=0xbff59b84,
xreDirectory=0xb715f480) at
/media/Projects/mozilla/mozilla-central/browser/app/nsBrowserApp.cpp:294
#18 0x0804c18b in main (argc=4, argv=0xbff59b84) at /media/Projects/mozilla/mozilla-central/browser/app/nsBrowserApp.cpp:667
Note that this is happening in the parent process. I'm going to guess
that this is likely our mGtkPrinter being leaked? I think so. I don't
even need to initiate a print to cause this crash. I just need to
open the dialog - which causes the deserialization on the parent
side which refs the new printer.
OH - I might need to unref the old printer! … wait, that's not the
case - nsPrintSettingsGTK::SetGtkPrinter automatically unrefs the old
printer before swapping in the new one.
Let me get rid of the mGTKPrinter bits anyways to see if that helps.
Ah, it doesn't. I'm leaking somewhere else. Let's work it backwards...
Facts:
- The leak is happening in the parent process
- The leak doesn't require me to initiate a print job in order to occur - I just need to open a printing dialog
- The leak detection occurs in cairo_debug_reset_static_data, of all places. That makes me think it's some gtk_ foo not being free'd.
I'm going to try to use valgrind to narrow things down. Going to be
slow as ass, but it seems like the best solution as opposed to guessing.
This document
tells me that I need to add some stuff to my .mozconfig
(--disable-jemalloc and --enable-valgrind), so I'm waiting for that to
build. In the meantime,
==20134== Thread 1:
==20134== Conditional jump or move depends on uninitialised value(s)
==20134== at 0x8E4B088: Pickle::WriteBool(bool) (pickle.h:103)
==20134== by 0x8E4B14C:
IPC::ParamTraitsFundamental<bool>::Write(IPC::Message*, bool
const&) (ipc_message_utils.h:138)
==20134== by 0x93C7B1F: void
IPC::WriteParam<bool>(IPC::Message*, bool const&)
(ipc_message_utils.h:115)
==20134== by 0x93CC5C1: void
mozilla::embedding::PPrintingParent::Write<bool>(bool const&,
IPC::Message*) (PPrintingParent.h:255)
==20134== by 0x93C4657:
mozilla::embedding::PPrintingParent::Write(mozilla::embedding::PrintData
const&, IPC::Message*) (PPrintingParent.cpp:585)
==20134== by 0x93C3B13:
mozilla::embedding::PPrintingParent::OnMessageReceived(IPC::Message
const&, IPC::Message*&) (PPrintingParent.cpp:349)
==20134== by 0x96324CD:
mozilla::dom::PContentParent::OnMessageReceived(IPC::Message const&,
IPC::Message*&) (PContentParent.cpp:4874)
==20134== by 0x92C5B08:
mozilla::ipc::MessageChannel::DispatchSyncMessage(IPC::Message
const&) (MessageChannel.cpp:1197)
==20134== by 0x92C588B:
mozilla::ipc::MessageChannel::DispatchMessage(IPC::Message const&)
(MessageChannel.cpp:1154)
==20134== by 0x92C57CF: mozilla::ipc::MessageChannel::OnMaybeDequeueOne() (MessageChannel.cpp:1142)
==20134== by 0x92D7B0B: void
DispatchToMethod<mozilla::ipc::MessageChannel, bool
(mozilla::ipc::MessageChannel::*)()>(mozilla::ipc::MessageChannel*,
bool (mozilla::ipc::MessageChannel::*)(), Tuple0 const&)
(tuple.h:383)
==20134== by 0x92D777A:
RunnableMethod<mozilla::ipc::MessageChannel, bool
(mozilla::ipc::MessageChannel::*)(), Tuple0>::Run() (task.h:310)
==20134==
==20134== Thread 4 Gecko_IOThread:
==20134== Syscall param sendmsg(msg.msg_iov[0]) points to uninitialised byte(s)
==20134== at 0x4066328: sendmsg (socket.S:98)
==20134== by 0x926B823: IPC::Channel::ChannelImpl::ProcessOutgoingMessages() (
ipc_channel_posix.cc:719
)
==20134== by 0x926BAB8: IPC::Channel::ChannelImpl::Send(IPC::Message*) (
ipc_channel_posix.cc:792
)
==20134== by 0x926C1E8: IPC::Channel::Send(IPC::Message*) (
ipc_channel_posix.cc:997
)
==20134== by 0x92D78E1: void
DispatchToMethod<IPC::Channel, bool (IPC::Channel::*)(IPC::Message*),
IPC::Message*>(IPC::Channel*, bool (IPC::Channel::*)(IPC::Message*),
Tuple1<IPC::Message*> const&) (tuple.h:393)
==20134== by 0x92D7342:
RunnableMethod<IPC::Channel, bool (IPC::Channel::*)(IPC::Message*),
Tuple1<IPC::Message*> >::Run() (task.h:310)
==20134== by 0x9279F74: MessageLoop::RunTask(Task*) (
message_loop.cc:361
)
==20134== by 0x9279FDA: MessageLoop::DeferOrRunPendingTask(MessageLoop::PendingTask const&) (
message_loop.cc:369
)
==20134== by 0x927A36E: MessageLoop::DoWork() (
message_loop.cc:447
)
==20134== by 0x9259B9B: base::MessagePumpLibevent::Run(base::MessagePump::Delegate*) (
message_pump_libevent.cc:311
)
==20134== by 0x9279A58: MessageLoop::RunInternal() (
message_loop.cc:233
)
==20134== by 0x92799E6: MessageLoop::RunHandler() (
message_loop.cc:226
)
==20134== Address 0x17e4ba54 is 44 bytes inside a block of size 1,024 alloc'd
==20134== at 0x402C324: realloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==20134== by 0x927C9A3: Pickle::Resize(unsigned int) (
pickle.cc:635
)
==20134== by 0x927C31B: Pickle::BeginWrite(unsigned int, unsigned int) (
pickle.cc:513
)
==20134== by 0x927C599: Pickle::WriteBytes(void const*, int, unsigned int) (
pickle.cc:558
)
==20134== by 0x8E4B129: Pickle::WriteUInt64(unsigned long long) (pickle.h:139)
==20134== by 0x8E4B0F2: Pickle::WriteSize(unsigned int) (pickle.h:127)
==20134== by 0x8E4B18A:
IPC::ParamTraitsLibC<unsigned int>::Write(IPC::Message*, unsigned
int const&) (ipc_message_utils.h:344)
==20134== by 0x92F8422: void
IPC::WriteParam<unsigned int>(IPC::Message*, unsigned int
const&) (ipc_message_utils.h:115)
==20134== by 0x92F8AA9:
IPC::ParamTraits<nsAString_internal>::Write(IPC::Message*,
nsAString_internal const&) (IPCMessageUtils.h:337)
==20134== by 0x93C7C25: void
IPC::WriteParam<nsString>(IPC::Message*, nsString const&)
(ipc_message_utils.h:115)
==20134== by 0x93CC697: void
mozilla::embedding::PPrintingParent::Write<nsString>(nsString
const&, IPC::Message*) (PPrintingParent.h:255)
==20134== by 0x93C4BE2:
mozilla::embedding::PPrintingParent::Write(mozilla::embedding::PrintData
const&, IPC::Message*) (PPrintingParent.cpp:628)
==20134==
==20134== 3,575 (896 direct, 2,679 indirect) bytes in 8 blocks are definitely lost in loss record 20,610 of 21,200
==20134== at 0x4DA6BB1: g_type_create_instance (in /usr/lib/i386-linux-gnu/libgobject-2.0.so.0.4000.0)
==20134== by 0x4D898FD: ??? (in /usr/lib/i386-linux-gnu/libgobject-2.0.so.0.4000.0)
==20134== by 0x4D8BD96: g_object_new_valist (in /usr/lib/i386-linux-gnu/libgobject-2.0.so.0.4000.0)
==20134== by 0x4D8BFEF: g_object_new (in /usr/lib/i386-linux-gnu/libgobject-2.0.so.0.4000.0)
==20134== by 0x245E642B: gtk_printer_cups_new (in
/usr/lib/i386-linux-gnu/gtk-2.0/2.10.0/printbackends/libprintbackend-cups.so)
==20134== by 0x245E34B8: ??? (in
/usr/lib/i386-linux-gnu/gtk-2.0/2.10.0/printbackends/libprintbackend-cups.so)
==20134== by 0x245E4BD2: ??? (in
/usr/lib/i386-linux-gnu/gtk-2.0/2.10.0/printbackends/libprintbackend-cups.so)
==20134== by 0x245E3846: ??? (in
/usr/lib/i386-linux-gnu/gtk-2.0/2.10.0/printbackends/libprintbackend-cups.so)
==20134== by 0x4E100A6: g_main_context_dispatch (in /lib/i386-linux-gnu/libglib-2.0.so.0.4000.0)
==20134== by 0x4E10467: ??? (in /lib/i386-linux-gnu/libglib-2.0.so.0.4000.0)
==20134== by 0x4E10527: g_main_context_iteration (in /lib/i386-linux-gnu/libglib-2.0.so.0.4000.0)
==20134== by 0xB62DCF7: nsAppShell::ProcessNextNativeEvent(bool) (nsAppShell.cpp:156)
==20134==
This looks like it's been around for a while!
https://bugzilla.mozilla.org/show_bug.cgi?id=1129557
I'm tried to bisect and haven't found a good state - I went back to around Firefox 4.
So, decision:
ignore the leak. It's not my bag, baby.
OK, attack plan:
Serialize GtkPaperSize stuff
Deserialize it and generate custom GtkPaperSize with the same dimensions, names, etc.
Serialize GtkPageSetup stuff
When deserializing, create a new GtkPrintSettings, and set the GtkPageSetup on it with SetGtkPageSetup
Serialize all GtkPrintSettings stuff
Deserialize all of the GtkPrintSettings stuff
Deserialize the GtkPrinter by enumerating the printers until I find one with the same name
Stuff all that shit into the nsPrintSettingsGtk
GtkPaperSize
(via gtk_paper_size_new_custom)
gtk_paper_size_get_name (const gchar *) - this is already serialized as paperName
gtk_paper_size_set_name (gchar *)
gtk_paper_size_get_display_name (gchar *)
gtk_paper_size_set_display_name (gchar *)
Things to serialize / deserialize:
GtkPageSetup
GtkPrinter (already being sent over by nsPrintSettingsImpl, but needs deserialization!)
InitUnwriteableMargin? "Re-initialize mUnwriteableMargin with values
from mPageSetup. Should be called whenever mPageSetup is
initialized or overwritten.
GtkPrintSettings (see below)
GtkPrintSettings to make sure we transfer:
gtk_print_settings_get_orientation (enum GtkPageOrientation)
gtk_print_settings_set_orientation (enum GtkPageOrientation)
gtk_print_settings_get_collate (gboolean)
gtk_print_settings_set_collate (gboolean)
gtk_print_settings_get_quality (enum GtkPrintQuality)
gtk_print_settings_set_quality (enum GtkPrintQuality)
gtk_print_settings_get_number_up (gint)
gtk_print_settings_set_number_up (gint)
gtk_print_settings_get_number_up_layout (enum GtkNumberUpLayout)
gtk_print_settings_set_number_up_layout (enum GtkNumberUpLayout)
gtk_print_settings_set_resolution_xy (gint)
gtk_print_settings_get_resolution_x (gint)
gtk_print_settings_get_resolution_y (gint)
gtk_print_settings_get_printer_lpi (gdouble)
gtk_print_settings_set_printer_lpi (gdouble)
gtk_print_settings_get_page_set (enum GtkPageSet)
gtk_print_settings_set_page_set (enum GtkPageSet)
gtk_print_settings_get_default_source (const gchar * )
gtk_print_settings_set_default_source (const gchar * )
gtk_print_settings_get_media_type (const gchar * )
gtk_print_settings_set_media_type (const gchar * )
gtk_print_settings_get_dither (const gchar * )
gtk_print_settings_set_dither (const gchar * )
gtk_print_settings_get_finishings (const gchar * )
gtk_print_settings_set_finishings (const gchar * )
gtk_print_settings_get_output_bin (const gchar * )
gtk_print_settings_set_output_bin (const gchar * )
GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT
GTK_PRINT_SETTINGS_OUTPUT_URI
GTK_PRINT_SETTINGS_PAPER_FORMAT (set by GtkPaperSize via gtk_print_settings_set_paper_size)
GTK_PRINT_SETTINGS_PAPER_WIDTH (set by GtkPaperSize via gtk_print_settings_set_paper_size)
GTK_PRINT_SETTINGS_PAPER_HEIGHT (set by GtkPaperSize via gtk_print_settings_set_paper_size)
GtkPageSetup to make sure we transfer:
gtk_page_setup_set_paper_size_and_default_margins (GtkPaperSize) (this
needs to be constructed first with the right GtkPaperSize name!)
gtk_page_setup_get_orientation (enum GtkPageOrientation)
gtk_page_setup_set_orientation (enum GtkPageOrientation)
gtk_page_setup_get_top_margin (gdouble)
gtk_page_setup_set_top_margin (gdouble)
gtk_page_setup_get_bottom_margin (gdouble)
gtk_page_setup_set_bottom_margin (gdouble)
gtk_page_setup_get_left_margin (gdouble)
gtk_page_setup_set_left_margin (gdouble)
gtk_page_setup_get_right_margin (gdouble)
gtk_page_setup_set_right_margin (gdouble)
Feedback from karlt:
I wonder why all these details need to be accessed on both the content and
browser sides. Could there be a cross-process object that is opaque on the
content side? Is there only a subset of this info that is needed by layout?
I don't have good ideas on how to deal with finding the GtkPrinter
synchronously. If it needs to pass between processes then perhaps determining
the GtkPrinter can be delayed until printing, which is already an async
process, I assume.
GtkPrintSettings is basically a hash table of strings keyed by strings. All
the accessor methods just convert between string and the particular format.
The keys aren't necessarily limited to the strings with GTK_PRINT_SETTINGS\_\*
defines. "The predefined keys try to use shared values as much as possible so
that moving such a document between systems still works", but I suspect
special printer backends might use other values. However, strings are
reasonably easy to serialize anyway, I assume, so, if this needs to be passed
between processes, then I suspect it would be simpler to use
gtk_print_settings_foreach() and gtk_print_settings_set(), with an array of
pairs of strings in PrintData.
I haven't yet looked at how GtkPageSetup and GtkPaperSize are used. I wonder
why the GtkPaperSize doesn't come from gtk_print_settings_get_paper_size().
I think we need to do whatever is necessary to avoid running arbitrary
events in DeserializeToPrintSettings(). Nested event loops are evil.
They unroll only in FILO order, and they surprise callers who don't
expect the world to change while they weren't watching.
Is it necessary to pass the printer between content and browser
processes? Is it the printer selection or the printing that happens in
the content process? Could they both happen in the browser process?
Replace custom GTK things in PPrinting.ipdl's PrintData with an array of string pairs.
Ok, create a struct with key and value nsCStrings, and then have PrintData hold onto an array of those structs.
Serialize GtkPrintSettings back and forth by iterating the keys / values.
Find a way of getting at the GtkPrinter without enumerating them
Grawrrr, GtkPrintBackend is woefully documented. But I think I can make this work...
I can emulate enumerate_printers, like this:
void
gtk_enumerate_printers (GtkPrinterFunc func,
gpointer data,
GDestroyNotify destroy,
gboolean wait)
{
PrinterList *printer_list;
GList *node, *next;
GtkPrintBackend *backend;
printer_list = g_new0 (PrinterList, 1);
printer_list->func = func;
printer_list->data = data;
printer_list->destroy = destroy;
if (g_module_supported ())
printer_list->backends = gtk_print_backend_load_modules ();
if (printer_list->backends == NULL)
{
free_printer_list (printer_list);
return;
}
for (node = printer_list->backends; node != NULL; node = next)
{
next = node->next;
backend = GTK_PRINT_BACKEND (node->data);
if (list_printers_init (printer_list, backend))
return;
}
if (wait && printer_list->backends)
{
printer_list->loop = g_main_loop_new (NULL, FALSE);
GDK_THREADS_LEAVE ();
g_main_loop_run (printer_list->loop);
GDK_THREADS_ENTER ();
}
}
Then don't forget to g_list_free(printer_list->backends);!
Evernote screwed up the formatting, but you get it. Basically I
create a backend list, iterate that list, and for each backend, I can
use gtk_print_backend_find_printer.
As soon as I find one that matches, I bail out. Easy… peasy… I think. *sigh*, why didn't GTK just supply this.
Graaarwwrwaeraera fuck.
The gtk_print_backend stuff isn't accessible. It's all private. I can't use this stuff! :(((((
karlt suggests looking into whether or not we can delay printer
assignment until the very end of printing, when things are already
asynchronous. I'll look into that next.
So here I am again, ass deep in nsPrintEngine.cpp.
15:04
(mconley)
karlt: ping, if you're awake
15:05
(karlt)
hi mconley
15:05
(mconley)
karlt: hi!
15:05
(mconley)
so I'm waist deep into GTK printing stuff
15:05
(mconley)
and it's a pretty sad story
15:06
(mconley)
karlt: I was just looking at your suggestion for waiting until we've
kicked off the print job in order to enumerate the printers
asynchronously
15:06
(mconley)
so as to not spin yet another event loop
15:07
(mconley)
it looks as if nsDeviceContextSpecG constructs a GtkPrintJob right at
the start of the printing work, in
nsDeviceContextSpecGTK::BeginDocument, and needs that in order to send
the job to the printer
15:07
(karlt)
hmm
15:07
(mconley)
I'm wondering if you have any suggestions on how best to break this up to make the enumeration asynchrounous
15:07
(mconley)
or
15:08
karlt
looks for GtkPrintJob docs
15:08
(mconley)
failing that, if you had any ideas on how else I could query for the
printers - perhaps by accessing the GtkPrintBackend stuff (although from
what I can tell, that's mostly private)
15:10
(karlt)
yes, i looked at the print backend stuff too, and it seemed to be out of reach, on first impressions at least
15:10
(mconley)
mucking about in that private stuff is also probably not a great idea. :/
15:16
(karlt)
mconley: so the print dialog runs in the parent process, then the
content process uses info from the dialog to draw and send to the
printer?
15:16
(mconley)
karlt: yep
15:16
(mconley)
the content process has no ability to open windows
15:16
(mconley)
which is why it asks the parent to do it
15:17
(mconley)
however, the backend has not yet been rejigged to actually kick off the printing in the parent.
15:17
(mconley)
though that's a long-term goal
15:17
(karlt)
ok, print dialog in the parent makes sense, and i understand you want something for now in the content process
15:17
mconley
nods
15:18
(karlt)
mconley: mPrintJob in nsDeviceContextSpecGTK seems to be
unused until EndDocument(), which does the gtk_print_job_send()
15:19
(mconley)
karlt: ah, yes. I suppose in EndDocument, we can try to grab the
printer... if that errors out, it's a bit of a shame to do all of that
processing only to die there
15:19
(mconley)
but yes, that's one point where we could do the query for printers
15:21
(karlt)
mconley: currently BeginDocument only errors out if
!GTK_IS_PRINTER(mGtkPrinter); i'm thinking that if we got a GtkPrinter
in the parent process from the print dialog, and so have its name, we
can reasonably expect to find that in the content process
15:21
(mconley)
true
15:21
(mconley)
karlt: hrm - mGtkPrinter is also needed here:
https://dxr.mozilla.org/mozilla-central/source/widget/gtk/nsDeviceContextSpecG.cpp#161
15:22
(karlt)
oh, i see
15:22
heycam|away is now known as heycam
15:23
(karlt)
mconley: could we get that info in the parent?
15:24
ferjm has left IRC (Quit: Textual IRC Client:
www.textualapp.com
)
15:26
(karlt)
and store it in the nsPrintSettingsGTK?
15:30
mconley
thinks
15:30
(mconley)
for the multi-process case, yes, that works
15:30
(mconley)
karlt: it sounds like we might have some single-process and multi-process branching in here now though. :/
15:32
(karlt)
mconley: i think we could use the same code for supports pdf etc, and branch on mGtkPrinter when creating the print job
15:33
(karlt)
if you can spawn an event to find the printer, then that event can run
the event loop knowing that there is nothing on the stack that will be
disrupted by other events that run
15:34
(karlt)
nested event loops still only unroll in filo order, but the code can
be written so that the effects of that are only a deeper stack
15:34
milan has left IRC (Connection closed)
15:35
milan has joined (milan@
moz-i5m.05u.207.66.IP
)
15:37
mconley
thinks
15:37
dbaron has joined (dbaron@
moz-m26.8gt.193.116.IP
)
15:37
ChanServ has changed mode: +qo dbaron dbaron
15:41
(mconley)
karlt: ok, I understand the first bit to re-use the accepts-pdf stuff
in the print settings object for single and multi-process - that makes
sense
15:42
(mconley)
karlt: when we branch in BeginDocument, we'll start looking for a printer - we'll call enumerate_printers but not block?
15:42
(mconley)
if that's the case, what happens if EndDocument gets called before we're done enumerating the printers?
15:43
(karlt)
nsPrintDialogWidgetGTK::ExportSettings() might be the place to set the output format to ps or pdf according to the printer
15:43
mattwoodrow|away is now known as mattwoodrow
15:44
(karlt)
mconley: the simplest is probably to move gtk_print_job_new out of BeginDocument, probably for both single and multiprocess
15:44
(karlt)
so instead
15:45
(karlt)
EndDocument spawns the event to find a printer and then call print_job_send
15:46
(mconley)
ah
15:47
(mconley)
karlt: ok, yes, I think I can work with that. Thanks!
Ok… I think this will work. In sum:
Make it so that nsDeviceContextSpecG does not need to know about the
GtkPrinter until EndDocument is called, at which time it
asynchronously queries for the right printer, and sends the job off.
This will mean ripping out some old stuff that reads the mGTKPrinter
from the nsIPrintSettings, and instead sending that information
through
nsPrintSettingsGTK instead.
Add SupportsPDF getter and setter to nsPrintSettingsGTK
Set the SupportsPDF setter in nsPrintDialogWidgetGTK::ExportSettings
In the Serialization/Deserialization methods, get and set SupportsPDF
Change the code in nsDeviceContextSpecGTK::GetSurfaceForPrinter to use SupportsPDF instead of mGtkPrinter.
Make nsDeviceContextSpecGTK::EndDocument asynchronously fetch the printer if one doesn't already exist
Move the creation of the GtkPrintJob out of BeginDocument to EndDocument
void
(*GtkPrintJobCompleteFunc) (GtkPrintJob *print_job,
gpointer user_data,
GError *error);
gboolean
(*GtkPrinterFunc) (GtkPrinter *printer,
gpointer data);
static void job_complete we'll do the same thing.
printer_enumerator
In nsPrintOptionsGTK.h remove PPrinting.h, and forwards declare the PrintData struct instead
Either remove the using namespace in nsPrintOptionsGTK.cpp, or actually make use of it when referring to PrintData
Try calling SetGtkPrintSettings after deserialization, in
DeserializeToPrintSettings, and remove the SetDuplex inside the
nsPrintSettingsGTK Initializer
Get SerializeGTKPrintSettingsToPrintData out of the nsPrintSettingsGTK header
Remove unused gtkPageSetup and gtkPaperSize from deserialization method
In DeserializeToPrintSettings, instead of re-using the GtkPrintSettings, destroy it and create a new one.
Why isn't the printer name getting sent down to the content process properly?
The problem was that the printer name was getting wiped out, since
we rely on the mGtkPrintSettings inside nsIPrintSettings to hold on to
the printer name. After deserializing a bunch of stuff in
nsPrintOptionsGTK::DeserializeToPrintSettings, I was chucking out the
GtkPrintSettings inside nsIPrintSettings and replacing it with one that
just had the GTK-specific settings set on it.
The solution was to create the new GtkPrintSettings and set it on the
nsIPrintSettings _before_ attempting to deserialize the PrintData from
the parent. That way, we can properly get the printer name and
enumerate/find the target printer.
In nsDeviceContextSpecGTK, make the printer enumerator and start print job methods private or protected. Probably private.
Remove acceptsPDF bool from PrintData, and have nsPrintDialogGTK set
the output format instead. So basically, stash this value in the output
format.
Move the printer enumerator in nsDeviceContextSpecGTK into a runnable
Replace nsCOMPtr<nsIPrintSettings> mPrintSettings with
nsCOMPtr<nsPrintSettingsGTK> mPrintSettings in
nsDeviceContextSpecGTK
"NS_ConvertUTF16toUTF8 is a class holding an extra copy of the string,
so please use mTitle.Truncate() and AppendUTF16toUTF8(aTitle, mTitle)"
Rename KeyValue to CStringKeyValue?
Address dw-dev's feedback from bug 1088070
EndDocument() is called from the nsPrintData destructor, so I don't think we
can assume that this is a safe place to run the event loop. Instead dispatch
an event that will call gtk_enumerate_printers(). There could be issues with
asynchronicity if GetSurfaceForPrinter() calls are repeated after
EndDocument(),
so either assert this doesn't happen, or pass mSpoolFile
to the event.
Do the above… maybe ask karlt what he means here.
Flesh out comment in nsDeviceContextSpecGTK::EndDocument clearing up what the hell is going on
TODO
Stow GTK-specific print settings inside PrintData in PPrinting.ipdl
Set print.enable_e10s_testing flag to true for Linux
Yes! r+ from karlt!
Now a try push for safety...
Amusement