List items
Items from the current list are shown below.
Blog
15 Dec 2023 : Day 108 #
This morning: a successful build. That's not really so surprising given the minimal changes I made yesterday, but I've messed up smaller pieces of code before, so you can never be sure.
So, packages built, installed and run, and what do we have? In order to get the debug output I have to set the MOZ_LOG environment variable to include "EmbedLite:5" so that the LOGE() messages will show. Here's what happens when I press the "Save web page as PDF" option:
But the rest is less encouraging. Apart from the logging I also added an early return to the method to prevent the window from actually being created. I got the method to return NS_OK in the hope that whatever happens further down the stack might not notice. But from this debug output we can see that it did notice, throwing an exception "The request is not allowed."
From DOMException.h we can see that this NS_ERROR_DOM_NOT_ALLOWED_ERR that we're getting is equivalent to NotAllowedError which appears one in DownloadCore.jsm. However in this particular instance it's just some code conditioned on the error. What we need is some code that's actually generating the error. Looking through the rest of the code, it all looks a bit peculiar: this error is usually triggered by a an authentication failure, which doesn't fit with what we're doing here at all.
There are only a few places where it seems to be used for other purposes. One of them is the StyleSheet::InsertRuleIntoGroup() method where it seems to be caused by a failed attempt to modify a group.
But the breakpoint isn't triggered, so it's not this bit of code anyway. After grepping the code a bit more and sifting carefully through various files, I eventually realise that there's an instance of this error that could potentially be generated directly after our call to OpenInternal() in nsGlobalWindowOuter.cpp:
The obvious follow-up question is why the lack of window is preventing the browsing context from getting created. Well have to follow the metaphorical rabbit down the rabbit hole to find out. So the context is returned as the last parameter of the OpenInternal() call:
So finally we reach high enough up the stack that we're in the EmbedLite code, and the reason for the null return is immediately clear from looking at the WindowCreator::CreateChromeWindow() implementation. In this method it's the _retval variable that's of interest and the code I added causes the method to return before it gets set.
If you'd like to read any of my other gecko diary entries, they're all available on my Gecko-dev Diary page.
So, packages built, installed and run, and what do we have? In order to get the debug output I have to set the MOZ_LOG environment variable to include "EmbedLite:5" so that the LOGE() messages will show. Here's what happens when I press the "Save web page as PDF" option:
$ EMBED_CONSOLE=1 MOZ_LOG="EmbedLite:5" sailfish-browser [...] [Parent 15259: Unnamed thread 7060002670]: E/EmbedLite FUNC::virtual nsresult mozilla::embedlite::EmbedLiteAppChild::Observe(nsISupports*, const char*, const char16_t*):68 topic:embed:download [Parent 15259: Unnamed thread 7060002670]: W/EmbedLite ERROR: EmbedLite::virtual nsresult WindowCreator::CreateChromeWindow (nsIWebBrowserChrome*, uint32_t, nsIOpenWindowInfo*, bool*, nsIWebBrowserChrome**):61 PRINT: isForPrinting: 1 EmbedliteDownloadManager error: [Exception... "The request is not allowed." nsresult: "0x80530021 (NS_ERROR_DOM_NOT_ALLOWED_ERR)" location: "JS frame :: resource://gre/modules/DownloadCore.jsm :: DownloadError :: line 1755" data: no] [Parent 15259: Unnamed thread 7060002670]: E/EmbedLite FUNC::virtual nsresult mozilla::embedlite::EmbedLiteAppChild::Observe(nsISupports*, const char*, const char16_t*):68 topic:embed:download JavaScript error: , line 0: uncaught exception: Object CONSOLE message: [JavaScript Error: "uncaught exception: Object"]That's a bit messy, but if you look through carefully it's possible to see the output PRINT: isForPrinting: 1. That's a good sign: it shows that in WindowCreator::CreateChromeWindow() we can find out whether this is a window that needs to be hidden or not.
But the rest is less encouraging. Apart from the logging I also added an early return to the method to prevent the window from actually being created. I got the method to return NS_OK in the hope that whatever happens further down the stack might not notice. But from this debug output we can see that it did notice, throwing an exception "The request is not allowed."
From DOMException.h we can see that this NS_ERROR_DOM_NOT_ALLOWED_ERR that we're getting is equivalent to NotAllowedError which appears one in DownloadCore.jsm. However in this particular instance it's just some code conditioned on the error. What we need is some code that's actually generating the error. Looking through the rest of the code, it all looks a bit peculiar: this error is usually triggered by a an authentication failure, which doesn't fit with what we're doing here at all.
There are only a few places where it seems to be used for other purposes. One of them is the StyleSheet::InsertRuleIntoGroup() method where it seems to be caused by a failed attempt to modify a group.
nsresult StyleSheet::InsertRuleIntoGroup(const nsACString& aRule, css::GroupRule* aGroup, uint32_t aIndex) { NS_ASSERTION(IsComplete(), "No inserting into an incomplete sheet!"); // check that the group actually belongs to this sheet! if (this != aGroup->GetStyleSheet()) { return NS_ERROR_INVALID_ARG; } if (IsReadOnly()) { return NS_OK; } if (ModificationDisallowed()) { return NS_ERROR_DOM_NOT_ALLOWED_ERR; } [...]I'm running sailfish-browser through the debugger with a break point on this method to see whether this is where it's coming from. If it is, I'm not sure what that will tell us.
But the breakpoint isn't triggered, so it's not this bit of code anyway. After grepping the code a bit more and sifting carefully through various files, I eventually realise that there's an instance of this error that could potentially be generated directly after our call to OpenInternal() in nsGlobalWindowOuter.cpp:
[...] aError = OpenInternal(u""_ns, u""_ns, u""_ns, false, // aDialog false, // aContentModal true, // aCalledNoScript false, // aDoJSFixups true, // aNavigate nullptr, nullptr, // No args nullptr, // aLoadState false, // aForceNoOpener printKind, getter_AddRefs(bc)); if (NS_WARN_IF(aError.Failed())) { return nullptr; } } if (!bc) { aError.ThrowNotAllowedError("No browsing context"); return nullptr; }That looks like a far more promising case. The debugger won't let me put a breakpoint directly on the line that's throwing the exception here, but it will let me put one on the OpenInternal() call, so I can set that and step through to check whether this error is the one causing the output.
(gdb) break nsGlobalWindowOuter.cpp:5329 Breakpoint 4 at 0x7fba96fad0: file dom/base/nsGlobalWindowOuter.cpp, line 5329. (gdb) c Continuing. [LWP 17314 exited] [Parent 16702: Unnamed thread 7f88002670]: E/EmbedLite FUNC::virtual nsresult mozilla::embedlite::EmbedLiteAppChild::Observe(nsISupports*, const char*, const char16_t*):68 topic:embed:download [Switching to LWP 16938] Thread 8 "GeckoWorkerThre" hit Breakpoint 4, nsGlobalWindowOuter::Print (nsIPrintSettings*, nsIWebProgressListener*, nsIDocShell*, nsGlobalWindowOuter::IsPreview, nsGlobalWindowOuter::IsForWindowDotPrint, std::function<void (mozilla::dom::PrintPreviewResultInfo const&)>&&, mozilla::ErrorResult&) (this=this@entry=0x7f88564870, aPrintSettings=aPrintSettings@entry=0x7e352ed060, aListener=aListener@entry=0x7f89bfa6b0, aDocShellToCloneInto=aDocShellToCloneInto@entry=0x0, aIsPreview=aIsPreview@entry=nsGlobalWindowOuter::IsPreview::No, aForWindowDotPrint=aForWindowDotPrint@entry=nsGlobalWindowOuter:: IsForWindowDotPrint::No, aPrintPreviewCallback=..., aError=...) at dom/base/nsGlobalWindowOuter.cpp:5329 5329 aError = OpenInternal(u""_ns, u""_ns, u""_ns, (gdb) n [Parent 16702: Unnamed thread 7f88002670]: W/EmbedLite ERROR: EmbedLite::virtual nsresult WindowCreator::CreateChromeWindow (nsIWebBrowserChrome*, uint32_t, nsIOpenWindowInfo*, bool*, nsIWebBrowserChrome**):61 PRINT: isForPrinting: 1 5329 aError = OpenInternal(u""_ns, u""_ns, u""_ns, (gdb) n 30 ${PROJECT}/obj-build-mer-qt-xr/dist/include/nsError.h: No such file or directory. (gdb) n 5343 if (!bc) { (gdb) p bc $1 = {mRawPtr = 0x0} (gdb) n 5344 aError.ThrowNotAllowedError("No browsing context"); (gdb) n 5345 return nullptr; (gdb)So that's the one. It's also clear from this why the error is happening: by not creating the window we're obviously also causing the creation of the browser context bc to fail.
The obvious follow-up question is why the lack of window is preventing the browsing context from getting created. Well have to follow the metaphorical rabbit down the rabbit hole to find out. So the context is returned as the last parameter of the OpenInternal() call:
nsresult nsGlobalWindowOuter::OpenInternal( const nsAString& aUrl, const nsAString& aName, const nsAString& aOptions, bool aDialog, bool aContentModal, bool aCalledNoScript, bool aDoJSFixups, bool aNavigate, nsIArray* argv, nsISupports* aExtraArgument, nsDocShellLoadState* aLoadState, bool aForceNoOpener, PrintKind aPrintKind, BrowsingContext** aReturn)In practice it's the domReturn variable inside this method that interests us. This is set as the last parameter of OpenWindow2() called inside this method:
rv = pwwatch->OpenWindow2(this, url, name, options, /* aCalledFromScript = */ true, aDialog, aNavigate, argv, isPopupSpamWindow, forceNoOpener, forceNoReferrer, wwPrintKind, aLoadState, getter_AddRefs(domReturn));And then this comes back from the call to OpenWindowInternal() that's being called from inside this method, again as the last parameter:
return OpenWindowInternal(aParent, aUrl, aName, aFeatures, aCalledFromScript, dialog, aNavigate, argv, aIsPopupSpam, aForceNoOpener, aForceNoReferrer, aPrintKind, aLoadState, aResult);In this method the variable we're interested in is newBC which ends up turning in to the returned browser context value. Now this doesn't get directly returned by the next level. Instead there's some code that looks like this:
/* We can give the window creator some hints. The only hint at this time is whether the opening window is in a situation that's likely to mean this is an unrequested popup window we're creating. However we're not completely honest: we clear that indicator if the opener is chrome, so that the downstream consumer can treat the indicator to mean simply that the new window is subject to popup control. */ rv = CreateChromeWindow(parentChrome, chromeFlags, openWindowInfo, getter_AddRefs(newChrome)); if (parentTopInnerWindow) { parentTopInnerWindow->Resume(); } if (newChrome) { /* It might be a chrome AppWindow, in which case it won't have an nsIDOMWindow (primary content shell). But in that case, it'll be able to hand over an nsIDocShellTreeItem directly. */ nsCOMPtr<nsPIDOMWindowOuter> newWindow(do_GetInterface(newChrome)); nsCOMPtr<nsIDocShellTreeItem> newDocShellItem; if (newWindow) { newDocShellItem = newWindow->GetDocShell(); } if (!newDocShellItem) { newDocShellItem = do_GetInterface(newChrome); } if (!newDocShellItem) { rv = NS_ERROR_FAILURE; } newBC = newDocShellItem->GetBrowsingContext(); }Looking at this code, the most likely explanation for newBC being null is that newChrome is being returned as null from the CreateChromeWindow() call. So let's follow this lead into CreateChromeWindow(). Now we're interested in the newWindowChrome variable and we have some code that looks like this:
bool cancel = false; nsCOMPtr<nsIWebBrowserChrome> newWindowChrome; nsresult rv = mWindowCreator->CreateChromeWindow( aParentChrome, aChromeFlags, aOpenWindowInfo, &cancel, getter_AddRefs(newWindowChrome)); if (NS_SUCCEEDED(rv) && cancel) { newWindowChrome = nullptr; return NS_ERROR_ABORT; } newWindowChrome.forget(aResult);The mWindowCreator->CreateChromeWindow() call there is important, because that's the line calling the method which we've hacked around with. I carefully arranged things so that the method would leave NS_SUCCEEDED(rv) as true, so it must be the last parameter which is returning null.
So finally we reach high enough up the stack that we're in the EmbedLite code, and the reason for the null return is immediately clear from looking at the WindowCreator::CreateChromeWindow() implementation. In this method it's the _retval variable that's of interest and the code I added causes the method to return before it gets set.
if (isForPrinting) { return NS_OK; } mChild->CreateWindow(parentID, reinterpret_cast<uintptr_t> (parentBrowsingContext.get()), aChromeFlags, &createdID, aCancel); [...] // check to make sure that we made a new window if (_retval) { NS_ADDREF(*_retval = browser); return NS_OK; }We're going to need a better way to solve this. Unfortunately that won't happen this evening as I have a very early start tomorrow, so I'm going to have to leave it there for today. Still, this will be a good place — with something tangible — to pick up from 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