flypig.co.uk

List items

Items from the current list are shown below.

Blog

14 Dec 2023 : Day 107 #
We're still splashing around in the docks looking at printing today, hoping to set sail. Yesterday it actually felt like we made pretty good progress getting the print promise to act as expected, so that the user interface works as it should. The remaining issue is that there's a blank window opening every time we print. The window closes once the print is complete, but it's a messy experience for the end user.

Yesterday it was possible to narrow down the code that's triggering the window to open. There's a call to nsGlobalWindowOuter::OpenInternal() in nsGlobalWindowOuter::Print() that eventually leads to a call to and nsWindowWatcher::OpenWindowInternal() which seems to be doing most of the work.

What I'm interested in today is the link between this and the Qt code in sailfish-browser that handles the windows (or "tabs" in sailfish-browser parlance) and actually creates the window on screen.

There's a parameter passed in to nsWindowWatcher::OpenWindowInternal() which indicates that the window is being created for the purposes of: the PrintKind aPrintKind parameter. If this parameter can be accessed from the Sailfish-specific part of the code then it may just be possible to persuade sailfish-browser to open the window "in the background" so that the user doesn't know it's there.

While I'm looking into this I'll also be trying to figure out whether we can avoid the call to open the window altogether. Everything up to nsWindowWatcher::OpenWindowInternal() in the call stack is essential, because it's there that the browser context is created and that's a bit we definitely need. We need a browser context to clone the document into. But the actual window chrome being displayed on screen? Hopefully that part can be skipped.

I've placed a breakpoint on nsWindowWatcher::OpenWindowInternal() and plan to see where that takes us.

Once the breakpoint hits and after stepping through most of the method I eventually get to this:
939             parentTopInnerWindow->Suspend();
(gdb) 
[LWP 30328 exited]
948           rv = CreateChromeWindow(parentChrome, chromeFlags, openWindowInfo,
(gdb) n
359     ${PROJECT}/obj-build-mer-qt-xr/dist/include/nsCOMPtr.h:
        No such file or directory.
(gdb) 
[W] unknown:0 - bool DBWorker::execute(QSqlQuery&) failed execute query
[W] unknown:0 - "INSERT INTO tab (tab_id, tab_history_id) VALUES (?,?);"
[W] unknown:0 - QSqlError("19", "Unable to fetch row",Gecko-dev
                          "UNIQUE constraint failed: tab.tab_id")
[LWP 30138 exited]
[LWP 30313 exited]
[LWP 30314 exited]
[LWP 30123 exited]
950           if (parentTopInnerWindow) {
(gdb) 
That group of LWPs ("lightweight processes" or threads as they are otherwise known) being created are as a result of the window opening. So it's clearly the CreateChromeWindow() call that's triggering the window to open. The errors that follow it could well be coming from sailfish-browser rather than the gecko library, but I'm not sure whether they're errors to worry about, or just artefacts of everything being slowed down due to debugging. I don't recall having seem them when running the code normally.

Let's follow this code a bit more. The nsWindowWatcher::CreateChromeWindow() method is mercifully short. The active ingredient of the method is this bit here:
  nsCOMPtr<nsIWebBrowserChrome> newWindowChrome;
  nsresult rv = mWindowCreator->CreateChromeWindow(
      aParentChrome, aChromeFlags, aOpenWindowInfo, &cancel,
      getter_AddRefs(newWindowChrome));
The mWindowCreator variable is an instance of nsIWindowCreator so the next step is to find out what that is. Stepping through gives us a clue.
419       nsCOMPtr newWindowChrome;
(gdb) 
1363    ${PROJECT}/obj-build-mer-qt-xr/dist/include/nsCOMPtr.h:
        No such file or directory.
(gdb) s
WindowCreator::CreateChromeWindow (this=0x7f889ce190, aParent=0x7f88ba5450,
    aChromeFlags=4094, aOpenWindowInfo=0x7f8854a7b0, aCancel=0x7f9f3d019f, 
    _retval=0x7f9f3d01a0) at mobile/sailfishos/utils/WindowCreator.cpp:44
44        NS_ENSURE_ARG_POINTER(_retval);
(gdb) 
So we've finally reached some Sailfish-specific code. If there's some way to check whether this is a print window or not, it may be possible to stop the window being shown at this point. There is this aOpenWindowInfo object being passed in which is of type nsIOpenWindowInfo. Checking the nsIOpenWindowInfo.idl file we can see that there is a relevant attribute that's part of the object:
  /** Whether this is a window opened for printing */
  [infallible]
  readonly attribute boolean isForPrinting;
Disappointingly the object refuses to yield its contents using the debugger.
(gdb) p aOpenWindowInfo
$3 = (nsIOpenWindowInfo *) 0x7f8854a7b0
(gdb) p *aOpenWindowInfo
$4 = {<nsISupports> = {_vptr.nsISupports = 0x7fbf7dfc00
      <vtable for nsOpenWindowInfo+16>}, <No data fields>}
(gdb) p aOpenWindowInfo->GetIsForPrinting()
Cannot evaluate function -- may be inlined
(gdb) 
Never mind, let's continue digging down into the code. So from here the method calls mChild->CreateWindow() which sends the stack down a rabbit hole of different calls which I've not yet followed to the end. However I do notice that the aOpenWindowInfo object doesn't go any further. So if the info about this being a print window needs extracting, it has to be done here.

I'm going to put some debug printw in here, but I'll also amend the code to cancel the window opening at this on condition that the window is a print window. Then I'll have to build the library to see how that's worked out.

Here's the small piece of code I've added, just before the window is created (which you can see on the last line):
  bool isForPrinting = aOpenWindowInfo->GetIsForPrinting();
  LOGE("PRINT: isForPrinting: %d", isForPrinting);

  if (isForPrinting) {
    return NS_OK;
  }

  mChild->CreateWindow(parentID, reinterpret_cast<uintptr_t>
    (parentBrowsingContext.get()), aChromeFlags, &createdID, aCancel);
I've set it building, which may take a little time, so I'm going to take a break from this while it does. I'll return with some results, hopefully, tomorrow.

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