List items
Items from the current list are shown below.
Blog
25 Dec 2023 : Day 118 #
It's Christmas Day. I've spent most of the day eating, opening presents and watching films. If you celebrate Christmas I hope you've had a wonderful day as well. Alongside general celbration and relaxation I've also had the chance to do a bit of gecko development. I'm very keen to get this printing user interface working correctly and it's definitely getting there.
As something a bit different, here's a photo of our Christmas tree before and after the decorations. But it comes with a warning: if you read on beyond this photo there will be backtraces. You have been warned!
The day before yesterday we got to the point where the hidden tabs were glowing red in the tab view, leaving the non-hidden tabs their usual colour. We don't actually want them to be red, but it does demonstrate that the hidden flag is being exposed correctly in the user interface.
Then yesterday I worked my way carefully through the code to come up with a plan for how to actually hide a tab based on the flag being set. My plan was to wait for the new tab to be activated, then immediately set the tab that was activated directly before as the active tab again.
If that happens quickly enough the user should hopefully not even be aware that it's happened.
So today I'm planning to harness that information in order to trigger the activation. With any luck we can get this done just by amending the front-end sailfish-browser code. No need to make changes to the gecko library itself.
To do this I'm going to keep track of the previous tab by recording the tab id in the DeclarativeWebContainer class. I've added the code to do this into the onNewTabRequested() method. As you can see, if the new tab has the hidden flag set we'll copy the tab ID into the mPreviousTabWhenHidden class member:
The only problem is that now the print doesn't complete. The printing starts, the tab opens, the tab closes, but then there's an error in the debug output and the data is never written to the file.
Here's the output from the debug console. Note that there are quite a few debug prints that I've added here to try to help me figure out what's going on, so they're non-standard and I'll remove them once I'm done.
When we execute the app the breakpoint gets hit three times. First on creation of the app when the page we're loading gets created. We can see this from the fact that onNewTabRequested() is in the backtrace. I then press the "Save page to PDF" option after which the blank print page is created, so that a call to onNewTabRequested() triggers the breakpoint again. Following that the tab will switch from the blank page back to the previous page. On this occasion there's no call to onNewTabRequested() Instead there's a call to activateTabById() which will trigger the breakpoint a third and final time.
This last call to activateTabById() is the one we just added.
Here are the backtraces for all three of these calls to updateStates(). As I mentioned yesterday, I'm very well aware that these backtraces make for horrible reading. I'm honestly sorry about this. I keep the backtraces here for anyone who's really keen to see all of the details, or for a future me who might need this information for reference. If you're a normal human I strongly recommend just to skip past this bit. You'll not lose anything by doing so.
So I've edited this method to remove the calls that perform the deactivation and suspension of the page. In theory this should leave the page not just running, but even rendering, even though it's no longer visible.
That'll be a task for Boxing Day!
If you'd like to read any of my other gecko diary entries, they're all available on my Gecko-dev Diary page.
As something a bit different, here's a photo of our Christmas tree before and after the decorations. But it comes with a warning: if you read on beyond this photo there will be backtraces. You have been warned!
The day before yesterday we got to the point where the hidden tabs were glowing red in the tab view, leaving the non-hidden tabs their usual colour. We don't actually want them to be red, but it does demonstrate that the hidden flag is being exposed correctly in the user interface.
Then yesterday I worked my way carefully through the code to come up with a plan for how to actually hide a tab based on the flag being set. My plan was to wait for the new tab to be activated, then immediately set the tab that was activated directly before as the active tab again.
If that happens quickly enough the user should hopefully not even be aware that it's happened.
So today I'm planning to harness that information in order to trigger the activation. With any luck we can get this done just by amending the front-end sailfish-browser code. No need to make changes to the gecko library itself.
To do this I'm going to keep track of the previous tab by recording the tab id in the DeclarativeWebContainer class. I've added the code to do this into the onNewTabRequested() method. As you can see, if the new tab has the hidden flag set we'll copy the tab ID into the mPreviousTabWhenHidden class member:
void DeclarativeWebContainer::onNewTabRequested(const Tab &tab) { if (tab.hidden()) { mPreviousTabWhenHidden = m_webPage->tabId(); } if (activatePage(tab, false)) { m_webPage->loadTab(tab.requestedUrl(), false); } }As we saw yesterday, once the tab has been created an activeTabChanged signal will be emitted which is connected to a DeclarativeWebContainer::onActiveTabChanged() slot. At that point I've added in some code to then switch back to the previous tab using the tab ID we recorded earlier:
void DeclarativeWebContainer::onActiveTabChanged(int activeTabId) { if (activeTabId <= 0) { return; } reload(false); if (m_model->activeTab().hidden()) { // Switch back to the old tab m_model->activateTabById(mPreviousTabWhenHidden); } }When I test this out it actually works, which I'm a little surprised about. There's a slight flicker when the new tab opens but then it's immediately hidden again. It's perceptible, but really looks okay to me.
The only problem is that now the print doesn't complete. The printing starts, the tab opens, the tab closes, but then there's an error in the debug output and the data is never written to the file.
Here's the output from the debug console. Note that there are quite a few debug prints that I've added here to try to help me figure out what's going on, so they're non-standard and I'll remove them once I'm done.
[D] unknown:0 - PRINT: onNewTabRequested pre activeTab: 10 [D] unknown:0 - PRINT: new tab is hidden; recording previous ID: 10 [D] unknown:0 - PRINT: onNewTabRequested post activeTab: 11 [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", "UNIQUE constraint failed: tab.tab_id") [D] unknown:0 - PRINT: onActiveTabChanged: 11 [D] unknown:0 - PRINT: onActiveTabChanged hidden: true [D] unknown:0 - PRINT: new tab is hidden, activating previous ID: 10 [D] unknown:0 - PRINT: activateTab: old: 11 [D] unknown:0 - PRINT: activateTab: new: 4 [D] unknown:0 - PRINT: activateTab: activate tab: 4 Tab(tabId = 10, parentId = 0, isValid = true, url = "https://jolla.com/", requested url = "", url resolved: true, title = "Jolla", thumbnailPath = "/home/defaultuser/.cache/org.sailfishos/browser/tab-10-thumb.jpg", desktopMode = false) [D] unknown:0 - PRINT: onActiveTabChanged: 10 [D] unknown:0 - PRINT: onActiveTabChanged hidden: false EmbedliteDownloadManager error: [Exception... "Abort" nsresult: "0x80004004 (NS_ERROR_ABORT)" location: "JS frame :: resource://gre/modules/DownloadCore.jsm :: DownloadError :: line 1755" data: no] [Parent 24026: Unnamed thread 7e54002670]: E/EmbedLite FUNC::virtual nsresult mozilla::embedlite::EmbedLiteAppChild::Observe(nsISupports*, const char*, const char16_t*):68 topic:embed:download [Parent 24026: Unnamed thread 7e54002670]: I/EmbedLite WARN: EmbedLite::virtual void* mozilla::embedlite::EmbedLitePuppetWidget::GetNativeData(uint32_t):127 EmbedLitePuppetWidget::GetNativeData not implemented for this type JavaScript error: , line 0: uncaught exception: Object JSScript: ContextMenuHandler.js loaded JSScript: SelectionPrototype.js loaded JSScript: SelectionHandler.js loaded JSScript: SelectAsyncHelper.js loaded JSScript: FormAssistant.js loaded JSScript: InputMethodHandler.js loaded EmbedHelper init called Available locales: en-US, fi, ru Frame script: embedhelper.js loaded CONSOLE message: [JavaScript Error: "uncaught exception: Object"] JavaScript error: resource://gre/modules/SessionStoreFunctions.jsm, line 120: NS_ERROR_FILE_NOT_FOUND: CONSOLE message:It's not quite clear to me whether it's the error shown here causing the problem, or the fact that the page is being deactivated and suspended. The latter could be causing problems, even though it might not necessarily trigger any error output (it would just freeze the page in the background). To explore the possibility of it being the page being set to inactive I've put a breakpoint on the place where this happens: the WebPages::updateStates() method. With any luck this will tell me where the deactivation takes place so I can disable it.
When we execute the app the breakpoint gets hit three times. First on creation of the app when the page we're loading gets created. We can see this from the fact that onNewTabRequested() is in the backtrace. I then press the "Save page to PDF" option after which the blank print page is created, so that a call to onNewTabRequested() triggers the breakpoint again. Following that the tab will switch from the blank page back to the previous page. On this occasion there's no call to onNewTabRequested() Instead there's a call to activateTabById() which will trigger the breakpoint a third and final time.
This last call to activateTabById() is the one we just added.
Here are the backtraces for all three of these calls to updateStates(). As I mentioned yesterday, I'm very well aware that these backtraces make for horrible reading. I'm honestly sorry about this. I keep the backtraces here for anyone who's really keen to see all of the details, or for a future me who might need this information for reference. If you're a normal human I strongly recommend just to skip past this bit. You'll not lose anything by doing so.
(gdb) b WebPages::updateStates Breakpoint 2 at 0x55555ad150: WebPages::updateStates. (2 locations) (gdb) c Continuing. Thread 1 "sailfish-browse" hit Breakpoint 2, WebPages::updateStates (this=0x55559bf880, oldActivePage=0x5555c4e0c0, newActivePage=0x7f8c063720) at ../core/webpages.cpp:203 203 if (oldActivePage) { (gdb) bt #0 WebPages::updateStates (this=0x55559bf880, oldActivePage=0x5555c4e0c0, newActivePage=0x7f8c063720) at ../core/webpages.cpp:203 #1 0x00000055555ad69c in WebPages::page (this=0x55559bf880, tab=...) at ../core/webpages.cpp:166 #2 0x000000555559148c in DeclarativeWebContainer::activatePage (this=this@entry=0x55559bf220, tab=..., force=force@entry=false) at include/c++/8.3.0/bits/atomic_base.h:390 #3 0x00000055555917a4 in DeclarativeWebContainer::onNewTabRequested (this=0x55559bf220, tab=...) at ../core/declarativewebcontainer.cpp:1062 #4 0x0000007fb7ec4204 in QMetaObject::activate(QObject*, int, int, void**) () from /usr/lib64/libQt5Core.so.5 #5 0x00000055555f7808 in DeclarativeTabModel::newTabRequested (this=this@entry=0x55559c69a0, _t1=...) at moc_declarativetabmodel.cpp:366 #6 0x00000055555c4428 in DeclarativeTabModel::newTab (this=0x55559c69a0, url=..., parentId=1, browsingContext=547754475280, hidden=<optimized out>) at ../history/declarativetabmodel.cpp:233 #7 0x00000055555d1900 in DeclarativeWebPageCreator::createView (this=0x55559c69f0, parentId=<optimized out>, parentBrowsingContext=<optimized out>, hidden=<optimized out>) at /usr/include/qt5/QtCore/qarraydata.h:240 #8 0x0000007fbfb71ef0 in QMozContextPrivate::CreateNewWindowRequested (this=<optimized out>, chromeFlags=<optimized out>, hidden=@0x7fffffe922: true, aParentView=0x5555bfad90, parentBrowsingContext=@0x7fffffe938: 547754475280) at qmozcontext.cpp:218 #9 0x0000007fbcb10eec in mozilla::embedlite::EmbedLiteApp:: CreateWindowRequested (this=0x555585a3a0, chromeFlags=@0x7fffffe928: 4094, hidden=@0x7fffffe922: true, parentId=@0x7fffffe924: 1, parentBrowsingContext=@0x7fffffe938: 547754475280) at mobile/sailfishos/EmbedLiteApp.cpp:543 #10 0x0000007fbcb1ea68 in mozilla::embedlite::EmbedLiteAppThreadParent:: RecvCreateWindow (this=<optimized out>, parentId=<optimized out>, parentBrowsingContext=<optimized out>, chromeFlags=<optimized out>, hidden=<optimized out>, createdID=0x7fffffe92c, cancel=0x7fffffe923) at mobile/sailfishos/embedthread/EmbedLiteAppThreadParent.cpp:70 #11 0x0000007fba183aa0 in mozilla::embedlite::PEmbedLiteAppParent:: OnMessageReceived (this=0x7f88a0fe90, msg__=..., reply__=@0x7fffffea38: 0x0) at PEmbedLiteAppParent.cpp:924 #12 0x0000007fba06b618 in mozilla::ipc::MessageChannel::DispatchSyncMessage (this=this@entry=0x7f88a0ff58, aProxy=aProxy@entry=0x5555c69ea0, aMsg=..., aReply=@0x7fffffea38: 0x0) at ${PROJECT}/obj-build-mer-qt-xr/dist/include/mozilla/ipc/ProtocolUtils.h:675 [...] #32 0x000000555557b360 in main (argc=<optimized out>, argv=<optimized out>) at main.cpp:201 (gdb) c Continuing. [New LWP 21735] Thread 1 "sailfish-browse" hit Breakpoint 2, WebPages::updateStates (this=<optimized out>, oldActivePage=<optimized out>, newActivePage=0x7f8c063720) at ../core/webpages.cpp:218 218 newActivePage->resumeView(); (gdb) bt #0 WebPages::updateStates (this=<optimized out>, oldActivePage=<optimized out>, newActivePage=0x7f8c063720) at ../core/webpages.cpp:218 #1 WebPages::updateStates (this=<optimized out>, oldActivePage=<optimized out>, newActivePage=0x7f8c063720) at ../core/webpages.cpp:201 #2 0x00000055555ad69c in WebPages::page (this=0x55559bf880, tab=...) at ../core/webpages.cpp:166 #3 0x000000555559148c in DeclarativeWebContainer::activatePage (this=this@entry=0x55559bf220, tab=..., force=force@entry=false) at include/c++/8.3.0/bits/atomic_base.h:390 #4 0x00000055555917a4 in DeclarativeWebContainer::onNewTabRequested (this=0x55559bf220, tab=...) at ../core/declarativewebcontainer.cpp:1062 #5 0x0000007fb7ec4204 in QMetaObject::activate(QObject*, int, int, void**) () from /usr/lib64/libQt5Core.so.5 #6 0x00000055555f7808 in DeclarativeTabModel::newTabRequested (this=this@entry=0x55559c69a0, _t1=...) at moc_declarativetabmodel.cpp:366 #7 0x00000055555c4428 in DeclarativeTabModel::newTab (this=0x55559c69a0, url=..., parentId=1, browsingContext=547754475280, hidden=<optimized out>) at ../history/declarativetabmodel.cpp:233 #8 0x00000055555d1900 in DeclarativeWebPageCreator::createView (this=0x55559c69f0, parentId=<optimized out>, parentBrowsingContext=<optimized out>, hidden=<optimized out>) at /usr/include/qt5/QtCore/qarraydata.h:240 #9 0x0000007fbfb71ef0 in QMozContextPrivate::CreateNewWindowRequested (this=<optimized out>, chromeFlags=<optimized out>, hidden=@0x7fffffe922: true, aParentView=0x5555bfad90, parentBrowsingContext=@0x7fffffe938: 547754475280) at qmozcontext.cpp:218 #10 0x0000007fbcb10eec in mozilla::embedlite::EmbedLiteApp:: CreateWindowRequested (this=0x555585a3a0, chromeFlags=@0x7fffffe928: 4094, hidden=@0x7fffffe922: true, parentId=@0x7fffffe924: 1, parentBrowsingContext=@0x7fffffe938: 547754475280) at mobile/sailfishos/ EmbedLiteApp.cpp:543 #11 0x0000007fbcb1ea68 in mozilla::embedlite::EmbedLiteAppThreadParent:: RecvCreateWindow (this=<optimized out>, parentId=<optimized out>, parentBrowsingContext=<optimized out>, chromeFlags=<optimized out>, hidden=<optimized out>, createdID=0x7fffffe92c, cancel=0x7fffffe923) at mobile/sailfishos/embedthread/EmbedLiteAppThreadParent.cpp:70 #12 0x0000007fba183aa0 in mozilla::embedlite::PEmbedLiteAppParent:: OnMessageReceived (this=0x7f88a0fe90, msg__=..., reply__=@0x7fffffea38: 0x0) at PEmbedLiteAppParent.cpp:924 #13 0x0000007fba06b618 in mozilla::ipc::MessageChannel::DispatchSyncMessage (this=this@entry=0x7f88a0ff58, aProxy=aProxy@entry=0x5555c69ea0, aMsg=..., aReply=@0x7fffffea38: 0x0) at ${PROJECT}/obj-build-mer-qt-xr/dist/include/mozilla/ipc/ProtocolUtils.h:675 [...] #33 0x000000555557b360 in main (argc=<optimized out>, argv=<optimized out>) at main.cpp:201 (gdb) c Continuing. [D] unknown:0 - PRINT: onNewTabRequested post activeTab: 11 [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", "UNIQUE constraint failed: tab.tab_id") [D] unknown:0 - PRINT: onActiveTabChanged: 11 [D] unknown:0 - PRINT: onActiveTabChanged hidden: true [D] unknown:0 - PRINT: new tab is hidden, activating previous ID: 10 [D] unknown:0 - PRINT: activateTab: old: 11 [D] unknown:0 - PRINT: activateTab: new: 4 [D] unknown:0 - PRINT: activateTab: activate tab: 4 Tab(tabId = 10, parentId = 0, isValid = true, url = "https://jolla.com/", requested url = "", url resolved: true, title = "Jolla", thumbnailPath = "/home/defaultuser/.cache/org.sailfishos/browser/tab-10-thumb.jpg", desktopMode = false) [D] unknown:0 - PRINT: onActiveTabChanged: 10 Thread 1 "sailfish-browse" hit Breakpoint 2, WebPages::updateStates (this=0x55559bf880, oldActivePage=0x7f8c063720, newActivePage=0x5555c4e0c0) at ../core/webpages.cpp:203 203 if (oldActivePage) { (gdb) bt #0 WebPages::updateStates (this=0x55559bf880, oldActivePage=0x7f8c063720, newActivePage=0x5555c4e0c0) at ../core/webpages.cpp:203 #1 0x00000055555ad69c in WebPages::page (this=0x55559bf880, tab=...) at ../core/webpages.cpp:166 #2 0x000000555559148c in DeclarativeWebContainer::activatePage (this=this@entry=0x55559bf220, tab=..., force=force@entry=true) at include/c++/8.3.0/bits/atomic_base.h:390 #3 0x00000055555928d4 in DeclarativeWebContainer::loadTab (this=0x55559bf220, tab=..., force=false) at ../core/declarativewebcontainer.cpp:1187 #4 0x0000005555592b78 in DeclarativeWebContainer::onActiveTabChanged (this=0x55559bf220, activeTabId=10) at ../core/declarativewebcontainer.cpp:960 #5 0x0000007fb7ec4204 in QMetaObject::activate(QObject*, int, int, void**) () from /usr/lib64/libQt5Core.so.5 #6 0x00000055555f7768 in DeclarativeTabModel::activeTabChanged (this=this@entry=0x55559c69a0, _t1=<optimized out>) at moc_declarativetabmodel.cpp:339 #7 0x00000055555c3eb4 in DeclarativeTabModel::updateActiveTab (this=this@entry=0x55559c69a0, activeTab=..., reload=reload@entry=false) at ../history/declarativetabmodel.cpp:429 #8 0x00000055555c4870 in DeclarativeTabModel::activateTab (this=this@entry=0x55559c69a0, index=4, reload=reload@entry=false) at ../history/declarativetabmodel.cpp:167 #9 0x00000055555c4d28 in DeclarativeTabModel::activateTabById (this=0x55559c69a0, tabId=<optimized out>) at ../history/declarativetabmodel.cpp:174 #10 0x0000005555592dbc in DeclarativeWebContainer::onActiveTabChanged (this=0x55559bf220, activeTabId=<optimized out>) at include/c++/8.3.0/bits/atomic_base.h:390 #11 0x0000007fb7ec4204 in QMetaObject::activate(QObject*, int, int, void**) () from /usr/lib64/libQt5Core.so.5 #12 0x00000055555f7768 in DeclarativeTabModel::activeTabChanged (this=this@entry=0x55559c69a0, _t1=<optimized out>) at moc_declarativetabmodel.cpp:339 #13 0x00000055555c3eb4 in DeclarativeTabModel::updateActiveTab (this=this@entry=0x55559c69a0, activeTab=..., reload=reload@entry=false) at ../history/declarativetabmodel.cpp:429 #14 0x00000055555c4004 in DeclarativeTabModel::addTab (this=this@entry=0x55559c69a0, tab=..., index=index@entry=5) at ../history/declarativetabmodel.cpp:78 #15 0x00000055555c4438 in DeclarativeTabModel::newTab (this=0x55559c69a0, url=..., parentId=1, browsingContext=547754475280, hidden=<optimized out>) at ../history/declarativetabmodel.cpp:235 #16 0x00000055555d1900 in DeclarativeWebPageCreator::createView (this=0x55559c69f0, parentId=<optimized out>, parentBrowsingContext=<optimized out>, hidden=<optimized out>) at /usr/include/qt5/QtCore/qarraydata.h:240 #17 0x0000007fbfb71ef0 in QMozContextPrivate::CreateNewWindowRequested (this=<optimized out>, chromeFlags=<optimized out>, hidden=@0x7fffffe922: true, aParentView=0x5555bfad90, parentBrowsingContext=@0x7fffffe938: 547754475280) at qmozcontext.cpp:218 #18 0x0000007fbcb10eec in mozilla::embedlite::EmbedLiteApp:: CreateWindowRequested (this=0x555585a3a0, chromeFlags=@0x7fffffe928: 4094, hidden=@0x7fffffe922: true, parentId=@0x7fffffe924: 1, parentBrowsingContext=@0x7fffffe938: 547754475280) at mobile/sailfishos/EmbedLiteApp.cpp:543 #19 0x0000007fbcb1ea68 in mozilla::embedlite::EmbedLiteAppThreadParent:: RecvCreateWindow (this=<optimized out>, parentId=<optimized out>, parentBrowsingContext=<optimized out>, chromeFlags=<optimized out>, hidden=<optimized out>, createdID=0x7fffffe92c, cancel=0x7fffffe923) at mobile/sailfishos/embedthread/EmbedLiteAppThreadParent.cpp:70 #20 0x0000007fba183aa0 in mozilla::embedlite::PEmbedLiteAppParent:: OnMessageReceived (this=0x7f88a0fe90, msg__=..., reply__=@0x7fffffea38: 0x0) at PEmbedLiteAppParent.cpp:924 #21 0x0000007fba06b618 in mozilla::ipc::MessageChannel::DispatchSyncMessage (this=this@entry=0x7f88a0ff58, aProxy=aProxy@entry=0x5555c69ea0, aMsg=..., aReply=@0x7fffffea38: 0x0) at ${PROJECT}/obj-build-mer-qt-xr/dist/include/mozilla/ipc/ProtocolUtils.h:675 [...] #41 0x000000555557b360 in main (argc=<optimized out>, argv=<optimized out>) at main.cpp:201 (gdb)From all of these backtraces we can see that WebPages::updateStates() will be a good place to look at to avoid the deactivation and suspension of the old page.
So I've edited this method to remove the calls that perform the deactivation and suspension of the page. In theory this should leave the page not just running, but even rendering, even though it's no longer visible.
void WebPages::updateStates(DeclarativeWebPage *oldActivePage, DeclarativeWebPage *newActivePage) { if (oldActivePage) { // Allow suspending only the current active page if it is not the // creator (parent). if (newActivePage->parentId() != (int)oldActivePage->uniqueId()) { if (oldActivePage->loading()) { //oldActivePage->stop(); } //oldActivePage->suspendView(); } else { // Sets parent to inactive and suspends rendering keeping // timeouts running. //oldActivePage->setActive(false); } } if (newActivePage) { newActivePage->resumeView(); newActivePage->update(); } }Unfortunately even after making this change the problem persists: the error still triggers and the PDF data still doesn't get stored in the file, which is created but left empty. Given this I'm going to have to look into the JavaScript error and try to figure out what that's happening there instead. Maybe it's something that I've not yet considered.
That'll be a task for Boxing Day!
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