flypig.co.uk

List items

Items from the current list are shown below.

Gecko

19 Dec 2023 : Day 112 #
I'm still feeling poorly today, which is very frustrating. When I've got a temperature I just can't focus well on the code, everything swims around and refuses to settle in to a comprehensible form. I find myself drifting from one file to another and losing the thread of where I've been and why I'm here. So you'll have to excuse me if some things I write today look a little nonsensical.

There's a silver lining though, in the form of the kind words I received from Thigg, Valorsoguerriero97, poetaster and (privately) throwaway69 via the Sailfish Forum. Thank you! It really helps motivate me to continue onwards. And it amazes me that you have the stamina to keep up with these posts!

As we discussed yesterday, looking at this bit of code inside nsGlobalWindowOuter::Print() we can see that if the condition is entered into the browser context is just copied over directly from the source to the new context:
  nsAutoSyncOperation sync(docToPrint, SyncOperationBehavior::eAllowInput);
  AutoModalState modalState(*this);

  nsCOMPtr<nsIContentViewer> cv;
  RefPtr<BrowsingContext> bc;
  bool hasPrintCallbacks = false;
  if (docToPrint->IsStaticDocument() &&
      (aIsPreview == IsPreview::Yes ||
       StaticPrefs::print_tab_modal_enabled())) {
    if (aForWindowDotPrint == IsForWindowDotPrint::Yes) {
      aError.ThrowNotSupportedError(
          "Calling print() from a print preview is unsupported, did you intend "
          "to call printPreview() instead?");
      return nullptr;
    }
    // We're already a print preview window, just reuse our browsing context /
    // content viewer.
    bc = sourceBC;
I'm left wondering whether, if we went through that branch, we might end up just using the existing context. It's clear from the code later that if this were to happen, the code that opens the new window would be skipped.

It's also clearly not meant to be doing this: forcing execution through this branch would be a dubious long shot at best. This bit of code is only supposed to be used if the print preview window context is to be re-used. We're not creating a print preview window, so if we hack this, it'll be attempting to use the actual context for the page instead.

So while I don't expect it to produce good results, I'm interested to see what will happen.

Looking at the condition that's gating the code block, the following part of the condition will be false:
  aIsPreview == IsPreview::Yes
Therefore what we'll need is for both docToPrint->IsStaticDocument() and StaticPrefs::print_tab_modal_enabled() to be true. Checking the about:config page I note that the print.tab_model.enabled value is already set to true, so we just need the docToPrint->IsStaticDocument() call to return true for the condition to hold.

To test all this out I put a breakpoint just before this block of code using the debugger and examine the state of the system when it hits. It turns out that docToPrint->IsStaticDocument() is indeed false, but we can switch its value using the debugger to force our way into the conditional code block.
$ EMBED_CONSOLE=1 MOZ_LOG="EmbedLite:5" gdb sailfish-browser
[...]
(gdb) b nsGlobalWindowOuter.cpp:5287
Breakpoint 1 at 0x7fba96f364: file dom/base/nsGlobalWindowOuter.cpp, line 5287.
(gdb) c
Continuing.
[...]
Thread 8 "GeckoWorkerThre" hit Breakpoint 1, nsGlobalWindowOuter::Print
    (nsIPrintSettings*, nsIWebProgressListener*, nsIDocShell*,
    nsGlobalWindowOuter::IsPreview, nsGlobalWindowOuter::IsForWindowDotPrint,
    std::function<void (mozilla::dom::PrintPreviewResultInfo const&)>&&,
    mozilla::ErrorResult&) (this=this@entry=0x7f88ab8100,
    aPrintSettings=aPrintSettings@entry=0x7e3553a790,
    aListener=aListener@entry=0x7f8a6730d0,
    aDocShellToCloneInto=aDocShellToCloneInto@entry=0x0,
    aIsPreview=aIsPreview@entry=nsGlobalWindowOuter::IsPreview::No,
    aForWindowDotPrint=aForWindowDotPrint@entry=nsGlobalWindowOuter::
    IsForWindowDotPrint::No, aPrintPreviewCallback=..., aError=...)
    at dom/base/nsGlobalWindowOuter.cpp:5287
5287      nsAutoSyncOperation sync(docToPrint, SyncOperationBehavior::eAllowInput);
(gdb) n
5288      AutoModalState modalState(*this);
(gdb) p docToPrint->IsStaticDocument()
Attempt to take address of value not located in memory.
(gdb) p docToPrint
$1 = {mRawPtr = 0x7f89077650}
(gdb) p aIsPreview
$2 = nsGlobalWindowOuter::IsPreview::No
(gdb) p docToPrint.mRawPtr.mIsStaticDocument
$3 = false
(gdb) set variable docToPrint.mRawPtr.mIsStaticDocument = true
(gdb) p docToPrint.mRawPtr.mIsStaticDocument
$4 = true
(gdb) c
Continuing.
[New LWP 19986]

Thread 13 "Socket Thread" received signal SIGPIPE, Broken pipe.

Thread 8 "GeckoWorkerThre" received signal SIGSEGV, Segmentation fault.
nsPrintJob::FindFocusedDocument (this=this@entry=0x7f885e3020,
    aDoc=aDoc@entry=0x7f89077650)
    at layout/printing/nsPrintJob.cpp:2411
2411      nsPIDOMWindowOuter* window = aDoc->GetOriginalDocument()->GetWindow();
(gdb) bt
#0  nsPrintJob::FindFocusedDocument (this=this@entry=0x7f885e3020,
    aDoc=aDoc@entry=0x7f89077650) at layout/printing/nsPrintJob.cpp:2411
#1  0x0000007fbc3bac6c in nsPrintJob::DoCommonPrint
    (this=this@entry=0x7f885e3020, aIsPrintPreview=aIsPrintPreview@entry=false,
    aPrintSettings=aPrintSettings@entry=0x7e3553a790,
    aWebProgressListener=aWebProgressListener@entry=0x7f8a6730d0,
    aDoc=aDoc@entry=0x7f89077650) at layout/printing/nsPrintJob.cpp:548
#2  0x0000007fbc3bb718 in nsPrintJob::CommonPrint
    (this=this@entry=0x7f885e3020, aIsPrintPreview=aIsPrintPreview@entry=false,
    aPrintSettings=aPrintSettings@entry=0x7e3553a790,
    aWebProgressListener=aWebProgressListener@entry=0x7f8a6730d0,
    aSourceDoc=aSourceDoc@entry=0x7f89077650)
    at layout/printing/nsPrintJob.cpp:488
#3  0x0000007fbc3bb840 in nsPrintJob::Print (this=this@entry=0x7f885e3020,
    aSourceDoc=<optimized out>, aPrintSettings=aPrintSettings@entry=0x7e3553a790,
    aWebProgressListener=aWebProgressListener@entry=0x7f8a6730d0)
    at layout/printing/nsPrintJob.cpp:824
#4  0x0000007fbc108fe4 in nsDocumentViewer::Print (this=0x7f8905e190,
    aPrintSettings=0x7e3553a790, aWebProgressListener=0x7f8a6730d0)
    at ${PROJECT}/obj-build-mer-qt-xr/dist/include/nsCOMPtr.h:859
#5  0x0000007fba96f49c in nsGlobalWindowOuter::Print(nsIPrintSettings*,
    nsIWebProgressListener*, nsIDocShell*, nsGlobalWindowOuter::IsPreview,
    nsGlobalWindowOuter::IsForWindowDotPrint, std::function<void
    (mozilla::dom::PrintPreviewResultInfo const&)>&&, mozilla::ErrorResult&)
    (this=this@entry=0x7f88ab8100,
    aPrintSettings=aPrintSettings@entry=0x7e3553a790,
    aListener=aListener@entry=0x7f8a6730d0,
    aDocShellToCloneInto=aDocShellToCloneInto@entry=0x0,
    aIsPreview=aIsPreview@entry=nsGlobalWindowOuter::IsPreview::No,
    aForWindowDotPrint=aForWindowDotPrint@entry=nsGlobalWindowOuter::
    IsForWindowDotPrint::No, aPrintPreviewCallback=..., aError=...)
    at ${PROJECT}/obj-build-mer-qt-xr/dist/include/nsCOMPtr.h:859
#6  0x0000007fbc7eb714 in mozilla::dom::CanonicalBrowsingContext::Print
    (this=this@entry=0x7f88cb8b70, aPrintSettings=0x7e3553a790, aRv=...)
    at include/c++/8.3.0/bits/std_function.h:402
#7  0x0000007fbab82f08 in mozilla::dom::CanonicalBrowsingContext_Binding::print
    (args=..., void_self=0x7f88cb8b70, obj=..., cx_=0x7f881df400)
    at BrowsingContextBinding.cpp:4674
#8  mozilla::dom::CanonicalBrowsingContext_Binding::print_promiseWrapper
    (cx=0x7f881df400, obj=..., void_self=0x7f88cb8b70, args=...)
    at BrowsingContextBinding.cpp:4688
[...]
#34 0x0000007fbd16635c in js::jit::MaybeEnterJit (cx=0x7f881df400, state=...)
    at js/src/jit/Jit.cpp:207
#35 0x0000007f8824be41 in ?? ()
Backtrace stopped: Cannot access memory at address 0x56e206215288
(gdb)
Okay, so this plan clearly isn't going to work.

Putting this dead-end to one side, we still have two options on the table:
  1. Figure out what's happening in ESR 78 where the window isn't being created.
  2. Re-code the front-end to hide the additional window.
The more I look at the code the more I think I'm going to have to go down the second route. Nevertheless I'd like to try to compare execution with ESR 78 one more time to try to figure out how it can get away without creating a clone. In particular, there is this BuildNestedPrintObjects() method in ESR 78 which I still suspect is performing the role of the cloning. This code still exists in ESR 91 and the debugger tells me it's not being called. But for comparison I'd really like to know whether it's being called in ESR 78.

Unfortunately the debugger just refuses to work properly on the ESR 78 code as installed from the repository:
$ EMBED_CONSOLE=1 MOZ_LOG="EmbedLite:5" gdb sailfish-browser
(gdb) r
(gdb) b BuildNestedPrintObjects
Breakpoint 1 at 0x7fbc20b7a8: file layout/printing/nsPrintJob.cpp, line 403.
(gdb) c
Continuing.

Thread 8 "GeckoWorkerThre" hit Breakpoint 1, BuildNestedPrintObjects (aDocument=
dwarf2read.c:10473: internal-error: process_die_scope::process_die_scope
    (die_info*, dwarf2_cu*): Assertion `!m_die->in_process' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Quit this debugging session? (y or n) n

This is a bug, please report it.  For instructions, see:
<http://www.gnu.org/software/gdb/bugs/>.

dwarf2read.c:10473: internal-error: process_die_scope::process_die_scope
    (die_info*, dwarf2_cu*): Assertion `!m_die->in_process' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Create a core file of GDB? (y or n) n
Command aborted.
(gdb) 
This is basically scuppering any attempt I make to breakpoint on these functions, or indeed anything nearby. I've tried this on two separate devices now and get the same results, so I'm pretty sure this is due to the debug symbols in the repository or a bug in gdb rather than some specific misconfiguration of gdb on my phone.

To try to address this, I'm going to rebuild ESR 78 and install completely new debug packages on my device. That will at least discount the possibility of it being something corrupt about the debug symbols coming from the official repositories. But to do that, I'll need to rebuild the ESR 78 code.

I've set the build going. While it builds I can't do much else except read through the code some more. But I also decide to try to ask around on the Mozilla Matrix "Printing" channel to see if anyone has any advice about how to tackle this. Here's the message I posted:
 
Hi. I have a query about cloning the document for printing PDF to file. I'm upgrading gecko from ESR78 to ESR91 for Sailfish OS (a mobile Linux variant) where we have a "Save page to PDF" option in the Qt UI. In ESR78 the nsPrintJob::DoCommonPrint() method is called but in ESR91 this no longer seems to work so I call CanonicalBrowsingContext::Print() instead (seems to relate to D87063). The former clones the doc using (I think) BuildNestedPrintObjects(), but the latter seems to call OpenInternal() inside nsGlobalWindowOuter::Print() to open a new window for it instead. Is this correct, or am I misunderstanding the changes? The reason I ask is that the latter opens a new blank window for the clone to go into, which I'm trying to avoid.

I post this at 15:47 and wait. By the evening I've received a reply from emilio:
 
We still clone the old doc. In order to avoid the new window you need to handle OPEN_PRINT_BROWSER flag

That's really helpful; it suggests that hiding the extra window in the user interface really is the right way to address this. I post a follow-up to get clarification on this.
 
Thanks for your reply emilio. So I have to allow the window to be created, but hide it in the front-end based on the fact the OPEN_PRINT_BROWSER flag (from nsOpenWindowInfo::GetIsForPrinting()) is set?

As I write this I'm still awaiting a reply, but I've already come to a conclusion on this: I'll need to add code so that some windows can be created but hidden from the front-end. I already have some ideas for how this can work. It will require some bigger changes than I was hoping for, but on the plus side, most if not all of the changes will happen in Sailfish code, rather than gecko code.

And you never know, there may be some other use for the functionality in the future as well.

The ESR 78 build is still chugging away. It's quite late here and I'm feeling rotten. I've got barely a fraction of the things I was hoping to get completed today done, but I have at least reached a conclusion for how to proceed with this printing situation. So the day hasn't been a complete wipe-out.

I really hope I'm feeling better in the morning.

If you'd like to read any of my other gecko diary entries, they're all available on my Gecko-dev Diary page.

Comments

Uncover Disqus comments