flypig.co.uk

List items

Items from the current list are shown below.

Gecko

16 Dec 2023 : Day 109 #
It's back to trying to hide the extraneous print window today. If you've been following over the last few days, you'll know that the "Save page to PDF" functionality is now working, but plagued by an errant window that insists on opening during the print.

The reason for the window needing to exist is completely clear: the page needs to be cloned into a browser context and the process of creating a browser context involves creating a window for it to live in. This wasn't a problem for ESR 78... I'm not exactly sure why and that's something I should look into.

But first I'm going to look at the code in WindowCreator.cpp that lives in the EmbedLite portion of the gecko project. Recall that we were working through this yesterday and it looked like this:
  if (isForPrinting) {
    return NS_OK;
  }

  mChild->CreateWindow(parentID, reinterpret_cast(parentBrowsingContext.get()), aChromeFlags, &createdID, aCancel);

  if (*aCancel) {
    return NS_OK;
  }

  nsresult rv(NS_OK);
  nsCOMPtr<nsIWebBrowserChrome> browser;
  nsCOMPtr<nsIThread> thread;
  NS_GetCurrentThread(getter_AddRefs(thread));
  while (!browser && NS_SUCCEEDED(rv)) {
    bool processedEvent;
    rv = thread->ProcessNextEvent(true, &processedEvent);
    if (NS_SUCCEEDED(rv) && !processedEvent) {
      rv = NS_ERROR_UNEXPECTED;
    }
    EmbedLiteViewChildIface* view = mChild->GetViewByID(createdID);
    if (view) {
      view->GetBrowserChrome(getter_AddRefs(browser));
    }
  }

  // check to make sure that we made a new window
  if (_retval) {
      NS_ADDREF(*_retval = browser);
      return NS_OK;
  }
I've included slightly more more of the code today because the chunk in the middle is important. Just to dissect this a little, the first conditional return is code I added in the hope that it would avoid creation of the window. The problem with this is that it means _retval never gets set and this is the return value needed in order for the browser context to be created.

We can tell more than this though. In order for _retval to be set we need browser to be set and that will only happen if:
  1. Execution goes inside the while loop;
  2. and createdID has been set;
  3. which requires that CreateWindow() is called.
In summary, we can't avoid creating the window at this point. The fact that the while loop is waiting for the browser chrome to exist makes it look like we can't avoid creating the window at all.

But that doesn't mean we can't hide it, so let's pursue that goal for now.

Checking the WindowCreator.h header we can see that the mChild that handles the call to CreateWindow() is a class that implements the EmbedLiteAppChildIface interface. There's actually only one concrete class that does this which is EmbedLiteAppChild. All this does is send a CreateWindow message.

There are a few candidates for classes that might receive this. It could be EmbedLiteAppThreadParent, EmbedLiteAppProcessParent or ContentParent. Only the first two are part of the EmbedLite code and they both end up doing the same thing, which is making the following call:
  *createdID = mApp->CreateWindowRequested(chromeFlags, parentId, parentBrowsingContext);
In both cases mApp is an instance of EmbedLiteApp. I'm wondering why we have both of these. I'm wondering if one is used for the browser and the other is used for the WebView. Or maybe Sailfish OS only uses one of them. I must remember to investigate this further at some point.

Also worth noting is that the createdID returned by this call is exactly the value we're interested in. We need this to be set.

Let's continue on into EmbedLiteApp.

In this method there's a snippet of code that searches for the parent based on the parent ID and then calls this:
  uint32_t viewId = mListener ? mListener->CreateNewWindowRequested(
    chromeFlags, view, parentBrowsingContext) : 0;
Once again it's the return value that we need. The mListener is an instance of EmbedLiteAppListener, that's actually defined in the same header file as EmbedLiteApp. This is an interface and we need something to implement it. But there's nothing in gecko that does.

After some scrabbling around and scratching of my head the reason becomes clear: there's nothing that implements it in gecko because the class that implements it is in qtmozembed. Which means we've finally broken through to the sailfish-browser frontend. The implementation is in qmozcontext.cpp:
uint32_t QMozContextPrivate::CreateNewWindowRequested(const uint32_t &chromeFlags,
  EmbedLiteView *aParentView, const uintptr_t &parentBrowsingContext)
{
    Q_UNUSED(chromeFlags)

    uint32_t parentId = aParentView ? aParentView->GetUniqueID() : 0;
    qCDebug(lcEmbedLiteExt) << "QtMozEmbedContext new Window requested: parent:"
      << (void *)aParentView << parentId;
    uint32_t viewId = QMozContext::instance()->createView(parentId,
      parentBrowsingContext);
    return viewId;
}
Once again, it's the return value, viewId, that we're particularly concerned about. The call to createView() is bounced by the QMozContext instance (it's presumably a singleton) to mViewCreator which is an instance of QMozViewCreator which is an abstract class that has no implementation in qtmozembed.

And that's because the implementation comes from the sailfish-browser repository in the form of the DeclarativeWebPageCreator class.

Here's the implementation:
quint32 DeclarativeWebPageCreator::createView(const quint32 &parentId,
  const uintptr_t &parentBrowsingContext)
{
    QPointer<DeclarativeWebPage> oldPage = m_activeWebPage;
    m_model->newTab(QString(), parentId, parentBrowsingContext);

    if (m_activeWebPage && oldPage != m_activeWebPage) {
        return m_activeWebPage->uniqueId();
    }
    return 0;
}
Now it really feels like we're getting close! We're switching terminology from windows to tabs. Plus we have no more repositories to go in to. Somewhere around here we're going to have to start thinking about adding in some way to hide the window, if we're going to go down that route.

Alright, my train is coming in to King's Cross and I have a busy evening tonight with my work Christmas Party, so this is likely to be all I have time for today. It feels like we made good progress though and are reaching a point where I might be able to start making changes to the code to actually fix the issue. That's always the goal!

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