-
-
Notifications
You must be signed in to change notification settings - Fork 154
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
History back function does not reload article from ZIM in Firefox (regression) #366
Comments
Ha, @mossroy , the solution is laughably simple! It turns out that doing |
One other thought, @mossroy : it is possible to detect that the URL and the html loaded into the iframe do not correspond (because |
Possibly a better solution than manipulating window.history is suggested here: It does seem to have an effect on first trials. EDIT: Here is the documentation on it: https://developer.mozilla.org/en-US/docs/Web/API/Document/open See:
|
Hi @mossroy , the "Simple fix" 3b228c9 referenced above is the minimal change needed to fix this issue, based on the MDN-suggested fix referenced in the previous comment (you may not have received an email copy of that comment). However, I'd also recommend we do the "Suggested reorganization" 6972425. This rationalizes where we push a new history state, and also allows a bit more flexibility in what we push to the service worker and what we push to the jQuery routines. Please note that I haven't tested the "Suggested reorganization" with Service Worker mode. If you like the idea of the suggested reorganization, I'd ask you to test if it works with Service Worker. I've tested both of the above fixes fairly thoroughly in Edge, Firefox ESR, Firefox Quantum, IE11 and FFOS. I haven't tested in Chrome. Let me know which of these alternatives you'd like me to do a PR for. |
I agree with both the simple fix and with the suggested reorganization. BUT I think we should first test if we cannot replace the document.write() by a jQuery-way to do it (as suggested in #361 ) |
[This continues discussion of how to load the iframe from #361, since this fix most obviously fixes the history.back issue that is currently in master, and incidentally may also fix the TypeError issue, but that needs further testing.] So, we have good news! Commit 95fc2c3 on the The procedure is as follows:
This solves #366 , but I cannot easily test whether it solves #361 as I cannot reproduce that exact error. The code works reliably in Edge, Firefox, Internet Explorer and FFOS. I have not tested in Chrome. Some observations:
|
One further quick observation:
I don't understand why. I have inserted breakpoints in the code immediately prior to injection and immediately after injection. It is clearly there in htmlArticle before injection, and it is clearly gone when we inspect NB My JavaScript injection works with this code (I haven't pushed a rebase yet to git-hub, I've only done it locally). It's only because the client-js class is missing that the content in the headings does not collapse on click. |
Ah, of course! It's because we're using innerHTML instead of outerHTML... The documentElement is the html node. So obvious when you see it. Is outerHTML supported on FFOS? |
Cool! I'll try to test that. |
Turns out that any attempt to write to an iframe contentDocument's outerHTML causes a "NoModificationAllowedError" in Edge and Firefox. We may have to inject classes from the Hmm, that regression sounds strange... |
I think the regression in ServiceWorker mode comes from the onload event on the iframe, that is executed on each article change, where we only want it to be executed once, to display the first article. |
Hmm, that was by design, because we re-load the article.html on each new article as a way of completely voiding the iframe and its DOM (by setting |
That's true, but only in jQuery mode. In ServiceWorker mode, the function displayArticleInForm is only called for the first article to display (for the main page, or after a search, or after a click on random article). After that, refreshing the iframe is handled by the browser (that gets the content from the ServiceWorker). I suppose the same onload event is executed at this moment. I suppose removing the onload event after it's executed for the first time should be enough : I'll test that. Regarding coding-style, it might be better to not have 2 variables articleContent (with a different scope), that do not point to the same DOM element (one with .contentDocument, the other one without it). It's usually a source of misunderstanding or bugs. |
Maybe no need even to set that onload event at all in service worker, then. Just have to be sure to push the browser history, maybe it can be done outside the onload event after the Regarding re-use of the variable, I agree, but it was because the variable's pointer has become invalid at that point, so it seemed like an efficient way to reset it / void it. But maybe we should explicitly void it outside the onload event and then use a different variable name inside the event. I'm conscious of avoiding memory leaks, but maybe the browser's garbage collection will deal with it. |
That depends if setting the |
OK. Yes it is most definitely asynchronous! I just thought that if all you have to do is get the code into the iframe for service worker, then because the programme effectively stops at that point, you don't need to have an onload event for service worker. It will just receive the iframe when it is loaded. The .onload event doesn't then do anything further in service worker mode, it just stops, no (apart from pushing the browser history)? |
Sorry, I wasn't looking at the code. Yes, you need to load htmlArticle inside the onload function. Somehow I'd misremembered it being outside that function (because it used to be)... |
Commit 18a75fd adds to the body element any css classes stripped from the html element due to the use of the .innerHTML injection method. It's done generically. Of course it's not as satisfactory as having the class(es) in the html element where they should belong, but the presence of CSS in the html tag can only be being used as a "switch", since it's not possible otherwise to style the html tag (there are no styles that apply to it as far as I know). So for the intended purpose, adding it to the classList of the body tag should produce the same result. There doesn't seem to be any way of adding it back to the html tag in the iframe. Commit 5bc75d3 changes the variable names and turns off the iframe.onload function inside the iframe.onload event. |
Mmmmh... That looks good but, in the end, is it really simpler and safer than the document.write solution? I'm wondering if the arguments I gave for this solution were not wrong. What do you think? |
It seems to be, as we say in England, "six of one and half a dozen of the other". The innerHTML method seems to have the disadvantage of not being able to manipulate the html element. Document.write() has a certain stigma attached to it (some developers say "don't use it", others say "use it only if you know what you're doing", others say "it's the only cross-browser way of writing a document to the iframe"), and it has that difficulty with jQuery (that I believe we solve by using It seems the ultimate solution might be the srcdoc element (untested for us), but it's not yet fully cross-browser (and although the polyfill will work in Edge I believe, I'm not sure it will work in FFOS if it's needed there). At least we have options! So, I've written this in a way that it should be easy more or less to "drop in" |
So please test commit 9dd2887 especially with Service Worker in mind. And then test to see if this line is still necessary (or maybe it's just good practice?). If we end up squashing this commit (or similar), I think we should leave at least one intermediate step in the commit history showing how to do it with innerHTML (and so the above discussion can be retraced if necessary). |
I've pushed a commit with some proposed refactoring. It seems to work well in ServiceWorker mode, at least on Chromium. I tend to prefer this document.write solution, because, in the end, both solutions need to remove the jQuery cache, but this one does not need to add an onload event for ServiceWorker mode, and does not need to manually put back the html tag attributes (it's more generic). I agree that it would be good to keep the "blank html file + innerHTML" solution somewhere. But if we rebase/squash, the commit ids will change anyway. Maybe another way to keep this solution would be to create another git branch right after the merge, containing only this other solution. |
OK, good that you found a more complete way to remove the iframe document with jQuery. That mostly looks fine. There was a reason I included Service Worker mode in the .onload event, which is that if you want to support injection of inline scripts and inline events (JavaScript), then you'll need to do something like this:
This is from the |
Regarding inline javascript and ServiceWorkers, it might be dealt differently. |
So I've created a PR #368 for this. I've done it as a separate branch in order to leave the references to the experimental branches |
Fixed with #369 . |
This is a regression from #354. In Firefox ESR and Firefox Quantum, pressing the "Back" button to go back in the browsing history -- which calls the
history.back()
function -- replaces only the iframe's state back to the point at which we last calleddocument.write()
. In the released version 2.3 of Kiwix JS, Firefox correctly causes the required page to be re-read from the ZIM, resetting the state of the outer document as well. Since we now have aniframe.onload
event, in the current version of Kiwix JS theparseAnchors()
and other functions are called without voiding the iframe, which attempts to attach onclick events to all links again, even though onclick events are still somehow attached to the document referencing the previous page. This results in a mess (links to internal pages do not function, etc.).Curiously this only happens with Firefox, not with Edge. Firefox appears unique in maintaining a history state for the iframe window, whereas Edge and Chromium do not, so they correctly reset the state of the outer document as well on history back. See https://stackoverflow.com/questions/3254985/back-and-forward-buttons-in-an-iframe and in particular this:
It seems we need to intercept
history.back()
and ensure it is applied to the top frame, if that is possible. If that isn't possible, then we will need to intercept the iframe.onload event, derive the articleContent's title and path from the HTML, as it doesn't seem to be available in the URL (from initial testing) and manually reload the article.It is not enough merely to void the iframe.onload() event with a blank function (I have tested this): the article still remains in an inconsistent state, because the iframe hasn't been voided correctly. The inconsistent state, whereby event listeners for the previous article are still attached to elements in the DOM of the new article looks like a Firefox bug to me, related to its per-frame history. Or it could be a jQuery bug related to #361 (jQuery keeps hooks on the old article even after the new one is loaded).
The text was updated successfully, but these errors were encountered: