flypig.co.uk

List items

Items from the current list are shown below.

Gecko

24 Dec 2023 : Day 117 #
It's Christmas eve as I write this, there's a sense of excitement and anticipation in the air. I like Christmas for many reasons, but mostly because it's a time for relaxation and not worrying about things for a day or two. Since gecko development is one of my means of relaxation I still plan to write an update tomorrow. I don't expect anyone to be reading it though. Given this, let me take the chance now to wish everybody reading this a very Merry Christmas. It amazes me how many of you continue to read it and today I want to give a special shout-out to Sylvain (Sthocs) who said nice things today on the Sailfish Forum. I'm thoroughly uplifted when such kind posts.

And because it's Christmas eve, here's one of Thigg's amazing images to add some colour and energy to the post today.
 
A lizard running through an autumnal forest carrying a pig (with wings) on its back

Now on to today's development. Yesterday we were able to confirm that the hidden flag was making it all the way from the print request out to the QML user interface of sailfish-browser. Today we have to think of something useful to do with the information.

I spent some time earlier today sitting in a coffee shop in the centre of Cambridge looking over the code and trying to think of what might be the right way to approach this. I've concluded that it might be a little tricky. But there are some things to try.

Because most of the changes will either happen in the QML or in the sailfish-browser code it should be possible to get a pretty swift turnaround on the possibilities I plan to try, which will hopefully mean we'll arrive at a solution all the more quickly.

To get the clearest picture it'll help to understand what happens when a new window is created, so let's break that down a bit.

The request comes in to the DeclarativeWebPageCreator::createView() method which looks like this:
quint32 DeclarativeWebPageCreator::createView(const quint32 &parentId,
    const uintptr_t &parentBrowsingContext, bool hidden)
{
    QPointer<DeclarativeWebPage> oldPage = m_activeWebPage;
    m_model->newTab(QString(), parentId, parentBrowsingContext, hidden);

    if (m_activeWebPage && oldPage != m_activeWebPage) {
        return m_activeWebPage->uniqueId();
    }
    return 0;
}
Notice that before and after the call to m_model->newTab() there's an expectation that the value of m_activeWebPage will change and that's because it's triggering a lot more stuff that's happening in the background. The newTab() call goes through to DeclarativeTabModel::newTab() which creates a new instance of the Tab class and adds it to the tab model. This will trigger a dataChanged signal through a call to addTab(), as well as a newTabRequested signal sent explicitly in the newTab code.

The newTabRequested signal is connected in declarativewebcontainer.cpp to the onNewTabRequested() method:
    connect(m_model.data(), &DeclarativeTabModel::newTabRequested,
            this, &DeclarativeWebContainer::onNewTabRequested);
This onNewTabRequested() method ends up calling two other methods: activatePage() and loadTab(), like this:
void DeclarativeWebContainer::onNewTabRequested(const Tab &tab)
{
    if (activatePage(tab, false)) {
        m_webPage->loadTab(tab.requestedUrl(), false);
    }
}
The activatePage() method does quite a lot of work here, but in particular activates the page. I'm thinking that we probably do want this to happen, but then might want to switch back immediately to the previous page. Otherwise the new tab is going to get shown to the user, which we want to avoid.

It might also be helpful to compare this flow against that which happens when a user selects a tab from the tab view. In that case the TabItem component sends out a TabView.activateTab() signal. This results in the following bit of code from the Overlay QML component getting called:
	onActivateTab: {
		webView.tabModel.activateTab(index)
		pageStack.pop()
	}
Although this bit of code is in the Overlay component, that component itself doesn't directly have a webView property. That's because both the Overlay and webView can be found in BrowserPage.qml. I must admit I'm not too keen on this type of approach, where a QML component relies on some implied features of its parent component. Nevertheless it's not uncommon in QML code and perfectly legal. Here's where the webView property is defined in the BrowserPage.qml file:
    Shared.WebView {
        id: webView
[...]
The Shared.WebView component is an instance of DeclarativeWebContainer, which we can see is due to this call in the DeclarativeWebContainer C++ constructor code:
DeclarativeWebContainer::DeclarativeWebContainer(QWindow *parent)
[...]
{
[...]
    setTitle("BrowserContent");
    setObjectName("WebView");
[...]
The tabModel property is an instance of DeclarativeTabModel, which has a compatible activateTab() overload available:
    Q_INVOKABLE void activateTab(int index, bool reload = false);
With the method definition looking like this:
void DeclarativeTabModel::activateTab(int index, bool reload)
{
    if (m_tabs.isEmpty()) {
        return;
    }

    index = qBound<int>(0, index, m_tabs.count() - 1);
    const Tab &newActiveTab = m_tabs.at(index);
    updateActiveTab(newActiveTab, reload);
}
Finally the call to updateActiveTab() sends out the dataChanged signal and then an activeTabChanged signal too. The latter is tied to onActiveTabChanged() due to this line in declarativewebcontainer.cpp:
	connect(m_model.data(), &DeclarativeTabModel::activeTabChanged,
		    this, &DeclarativeWebContainer::onActiveTabChanged);
Which therefore causes this code to be run:
void DeclarativeWebContainer::onActiveTabChanged(int activeTabId)
{
    if (activeTabId <= 0) {
        return;
    }

    reload(false);
}
The reload() method contains a call to loadTab() which looks just like the onNewTabRequested() method that was called in the case of the new window creation route:
void DeclarativeWebContainer::loadTab(const Tab& tab, bool force)
{
    if (activatePage(tab, true) || force) {
        // Note: active pages containing a "link" between each other (parent-child relationship)
        // are not destroyed automatically e.g. in low memory notification.
        // Hence, parentId is not necessary over here.
        m_webPage->loadTab(tab.url(), force);
    }
}
Which means we're back to the same place, in particular the activatePage() method which actually does the work. I've gone through both the process for creating a new tab and the process for switching tabs to highlight the crucial bit that we're interested in. We want to switch tabs right after the new tab has been activated, so it's the point where these two paths converge that's likely to make the best place for us to amend the code.

As well as working through the code as we have done above, we can also find the place where the two paths converge using backtraces in the debugger. Now I know these backtraces can be a bit hard to parse on a website, especially on a small screen. If you're a human reading this then please do feel free to just skip these, they're more for my future reference, as it's really helpful for me to keep a record of them. In any case here's the "changing tab" case in backtrace form.
(gdb) bt
#0  DeclarativeWebContainer::setWebPage (this=0x55559bd820, webPage=0x5555ff3960,
    triggerSignals=false) at ../core/declarativewebcontainer.cpp:165
#1  0x00000055555914a0 in DeclarativeWebContainer::activatePage
    (this=0x55559bd820, tab=..., force=true)
    at ../core/declarativewebcontainer.cpp:589
#2  0x000000555559257c in DeclarativeWebContainer::loadTab (this=0x55559bd820,
    tab=..., force=false) at ../core/declarativewebcontainer.cpp:1171
#3  0x0000007fb7ec4204 in QMetaObject::activate(QObject*, int, int, void**) ()
    from /usr/lib64/libQt5Core.so.5
#4  0x00000055555f6ce0 in DeclarativeTabModel::activeTabChanged
    (this=this@entry=0x55559d2b30, _t1=<optimized out>)
    at moc_declarativetabmodel.cpp:339
#5  0x00000055555c37b4 in DeclarativeTabModel::updateActiveTab
    (this=this@entry=0x55559d2b30, activeTab=..., reload=reload@entry=false)
    at ../history/declarativetabmodel.cpp:426
#6  0x00000055555c3f38 in DeclarativeTabModel::activateTab
    (this=this@entry=0x55559d2b30, index=<optimized out>,
    reload=reload@entry=false)
    at ../history/declarativetabmodel.cpp:164
#7  0x00000055555f70ec in DeclarativeTabModel::qt_static_metacall
    (_o=_o@entry=0x55559d2b30, _c=_c@entry=QMetaObject::InvokeMetaMethod,
    _id=_id@entry=16, 
    _a=_a@entry=0x7fffff64c8) at moc_declarativetabmodel.cpp:180
[...]
#17 0x0000007fb8643bf8 in QQmlJavaScriptExpression::evaluate(QV4::CallData*,
    bool*) () from /usr/lib64/libQt5Qml.so.5
#18 0x0000007fa83c2410 in ?? ()
#19 0x00000055556d2c90 in ?? ()
(gdb) 
So, in short, it looks like if we emit an activeTabChanged() signal straight after the new tab has been created using the index of the previous tab (the one that was active before the window opened) then with a bit of luck the browser will immediately (and maybe imperceptible?.. we'll have to see about that) switch back to the previous tab.

I'm keen to try this out, but all of this digging through code has left me a bit exhausted, so testing it out will have to wait until 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