.onload called multiple times from Firefox extension - javascript

I'm developing a Firefox extension and have the following code:
function initialize() {
// For accessing browser window from sidebar code.
var mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIWebNavigation)
.QueryInterface(Components.interfaces.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindow);
var gBrowser = mainWindow.gBrowser;
gBrowser.onload = function() {
alert('loaded');
};
}
When I open the extension (a sidebar) and proceed to open a new tab within the Firefox window, there are three alert boxes.
When I refresh a page, there are two alert boxes.
When a page finishes loading, there is only one alert box.
When I change tabs, an alert is fired.
I use .onload rather than DOMContentLoaded or readystatechange as I need to wait until all other javascript has finished loading on a page before I run mine.
Any ideas as to why multiple events are being triggered (and for things that the event shouldn't be triggered for)?
SOLUTION
Following from MatrixFrog's suggestion, here is the solution I came to:
function initialize() {
// For accessing browser window from sidebar code.
var mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIWebNavigation)
.QueryInterface(Components.interfaces.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindow);
var gBrowser = mainWindow.gBrowser;
if (gBrowser.addEventListener) {
gBrowser.addEventListener("load",pageLoaded,true);
}
}
function pageLoaded(aEvent) {
if ((aEvent.originalTarget.nodeName == '#document') &&
(aEvent.originalTarget.defaultView.location.href == gBrowser.currentURI.spec))
{
alert('loaded');
}
}
aEvent.originalTarget.nodeName == '#document' checks that the page is loaded and not favicons.
(aEvent.originalTarget.defaultView.location.href == gBrowser.currentURI.spec)) checks that the element that fired the event is the page in the tab, and not one of its IFRAMEs
gBrowser.onload would only fire for xul-image and not for #document so it was replaced with gBrowser.addEventListener("load",pageLoaded,true);
If you want to avoid firing the event for new blank tabs, make sure gBrowser.currentURI.spec != "about:blank"

From https://developer.mozilla.org/en/Code_snippets/On_page_load
Current Firefox trunk nightlies will fire the onPageLoad function for not only documents, but xul:images (favicons in tabbrowser). If you only want to handle documents, ensure aEvent.originalTarget.nodeName == "#document"
If you're still seeing extraneous 'load' events firing, you may want to inspect the event target to figure out what's being loaded, and use similar logic to avoid calling your extension's logic in certain specific cases.

Related

Why does my popup window lose its event beforeunload after a reload?

I have a small application that opens a new popup. I store the window in a variable:
popup = window.open("sites/display.html", "_blank");
After that I add a beforeunload Eventlistener:
$(popup).on('beforeunload', function(){
// Do something
});
I then later reload the window with a button:
popup.location = popup.location;
After that if I close the window the Event beforeunload isn't fired anymore. I think it has something to do with the reload because if I dont reload the page everything works fine.
How can I fix this so the event is fired everytime the window closes?
Exact code I use:
function startClock(allowRestart) {
saveSettings(allowRestart);
if ($("#separated-display").is(":checked")) {
// Separated mode activated
if (popup == undefined) {
openPopup();
} else {
if (popup.closed) {
openPopup();
}else{
popup.location.reload();
}
}
} else {
// Separated mode deactivated
if (popup != null && popup.closed == false) {
$(popup).unbind();
popup.close();
}
window.location = "sites/display.html"; // Open the clock in same window
}
}
function openPopup(){
// Open new popup window
popup = window.open("sites/display.html", "_blank");
// TO-DO: Fix event not fired after 1. window reload 2. window close
popup.addEventListener('beforeunload', function(){
console.log("unload");
});
}
As you're trying to access the same origin (with the relative path) window using window.open(), Access error shouldn't be displayed.
popup = window.open("/sites/display.html", "_blank")
popup variable would refer to the newly created window which is a thin wrapper representing a WindowProxy object, which indeed has all features of window available.
When the page reloads everything is set to default, and the window loses its properties set before. Therefore, the unload event attached earlier is not anymore attached. This would happen for other events as well.
Hence the problem here that the event is being attached to the popup window just once on opening the popup, which is reset on page reload. The best way to go forward would be to add the unload event in the js file which loads specifically on sites/display.html page. There, every time when sites/display.html page loads you could access the new window object and attach events in window.load / document.ready (according to your use case).
You won't able to attach the event to pop up before or after invoking page reload as you're doing it currently as, the property would be reset if you try setting it before/after page reload as it might be executed asynchronously.
NOTE:
You should rather use the reload() function exposed by window.location instead of updating the location property. As updating the property doesn't skip browser cache.
popup.location.reload()
The support for window.open is unknown for most browsers, though I was able to use it on Chrome 84.

Use parent jquery library in child Iframe

I have a two Iframe in my HTML page. I want to use one single jquery file which is loaded on parent page. The code give below is working fine every browser but it has one issue with chrome with ctrl+f5 key. When page is loading first time and when we are pressing ctrl+f5 the it is giving error in chrome otherwise it is working fine. Here is some snapshot. Parent page and iframe are in same domain.
if (typeof(jQuery) == "undefined") {
var iframeBody = document.getElementsByTagName("body")[0];
var jQuery = function (selector) { return parent.jQuery(selector, iframeBody); };
var $ = jQuery;
}
Add the jQuery script in both pages to avoid race condition. When one is loaded, the other will be retrieved from the browser cache.

Get hosting XULDocument for a ContentWindow or HTMLDocument in Firefox extension?

I have an Firefox Extension with a XUL overlay containing some Javascript with some event handlers. Among those is an EventListener for DOMContentLoaded. I have a second overlay I only want to load when visiting a certain website.
Here's some of the code I have so far:
var appcontent = document.getElementById("appcontent"); // browser
if (appcontent) appcontent.addEventListener("DOMContentLoaded", onPageLoad, true);
function onPageLoad(event) {
var doc = iEvent.originalTarget; // doc is document that triggered "onload" event
var win = doc.defaultView;
if (doc.nodeName != 'document') return; // only documents
if (win != win.top) return; //only top window.
if (win.frameElement) return; // skip iframes/frames
if(doc.location.href.search('http://somewebsite.com/') > -1) {
//Find XULDocument somehow
//var xulDoc = ??????;
xulDoc.loadOverlay('chrome://myextension/content/secondoverlay.xul', null);
}
}
How can I retrieve the XULDocument hosting the DOM, given the DOMContentLoaded event data?
Well, the XUL window is just window, aka. the global scope.
So the following two lines should both work and should be the same:
window.document.loadOverlay(...);
document.loadOverlay(...);
However, this most is not really what you want, because the XUL window is still the main browser.xul which hosts all content windows. There is no dedicated XUL window per content window!
Loading the second overlay will overlay the whole browser window, not just the "tab" (or whatever) and will stay once you close the tab (content window) or navigate away.
Now, the question is: What do you really want to achieve?
Display some UI (toolbar button, menu, whatever) only for certain pages? Then usually you'd overlay all your stuff once (on the initial load, i.e. in the "first" overlay) and just hide/show your UI according to your rules. See Tabbed Browser for some code snippets when dealing with tab switching and/or page loads.
Or you really want to apply something to the content window itself. Then you'd usually just modify the DOM of the content window directly.

Which XUL element to use when a url is entered and loaded for calling JS?

I am developing a Firefox add-on using XUL Overlay and want to call a specific js when the current page loads after entering the URL. I want to know which XUL element would be affected and should be used to call said JS, such as page or tab or window or ??? Also, which event would be best for the element? Or is my logic wrong?
Also,the js's function is to record tab title and/or url so i need to know when to call js and with corresponding event. Thanks.. :)
The XUL element you should be watching is the tabbrowser. In the browser window (which means also in overlays applied to the browser window) it can be accessed via the global gBrowser variable. If you want to know when a page finishes loading you can listen to the DOMContentLoaded event. Something like this (untested code):
// Declare an own namespace for extension's functions to avoid
// name conflicts with other extensions.
var MyExtension = {};
MyExtension.init = function()
{
gBrowser.addEventListener("DOMContentLoaded", MyExtension.onPageLoad, false);
};
MyExtension.onPageLoad = function(event)
{
// Get the document that loaded
var doc = event.originalTarget;
// Ignore frames that load
if (doc.defaultView != doc.defaultView.parent)
return;
// Ignore if this isn't the active tab
var browser = gBrowser.getBrowserForDocument(doc);
if (browser != gBrowser.selectedBrowser)
return;
alert("Page loaded in current tab: " + doc.defaultView.location.href);
};
// Wait for the browser window to finish loading before adding event listeners
window.addEventListener("load", MyExtension.init, false);
If you want to get notified earlier, when the address displayed in the URL bar changes, you can use a progress listener instead. You want to implement the method onLocationChange of the progress listener and leave the other methods empty. Note that this method is also called when the user switches to a different tab (this also causes a location bar change). Also: the parameter aURI passed to onLocationChange is an nsIURI instance. If you want the URL as a string you should look at aURI.spec.

Detecting the onload event of a window opened with window.open

window.popup = window.open($(this).attr('href'), 'Ad', 'left=20,top=20,width=500,height=500,toolbar=1,resizable=0');
$(window.popup).onload = function()
{
alert("Popup has loaded a page");
};
This doesn't work in any browser I've tried it with (IE, Firefox, Chrome). How can I detect when a page is loaded in the window (like an iframe onload)?
var myPopup = window.open(...);
myPopup.addEventListener('load', myFunction, false);
If you care about IE, use the following as the second line instead:
myPopup[myPopup.addEventListener ? 'addEventListener' : 'attachEvent'](
(myPopup.attachEvent ? 'on' : '') + 'load', myFunction, false
);
As you can see, supporting IE is quite cumbersome and should be avoided if possible. I mean, if you need to support IE because of your audience, by all means, do so.
If the pop-up's document is from a different domain, this is simply not possible.
Update April 2015: I was wrong about this: if you own both domains, you can use window.postMessage and the message event in pretty much all browsers that are relevant today.
If not, there's still no way you'll be able to make this work cross-browser without some help from the document being loaded into the pop-up. You need to be able to detect a change in the pop-up that occurs once it has loaded, which could be a variable that JavaScript in the pop-up page sets when it handles its own load event, or if you have some control of it you could add a call to a function in the opener.
As noted at Detecting the onload event of a window opened with window.open, the following solution is ideal:
/* Internet Explorer will throw an error on one of the two statements, Firefox on the other one of the two. */
(function(ow) {
ow.addEventListener("load", function() { alert("loaded"); }, false);
ow.attachEvent("onload", function() { alert("loaded"); }, false);
})(window.open(prompt("Where are you going today?", location.href), "snapDown"));
Other comments and answers perpetrate several erroneous misconceptions as explained below.
The following script demonstrates the fickleness of defining onload. Apply the script to a "fast loading" location for the window being opened, such as one with the file: scheme and compare this to a "slow" location to see the problem: it is possible to see either onload message or none at all (by reloading a loaded page all 3 variations can be seen). It is also assumed that the page being loaded itself does not define an onload event which would compound the problem.
The onload definitions are evidently not "inside pop-up document markup":
var popup = window.open(location.href, "snapDown");
popup.onload = function() { alert("message one"); };
alert("message 1 maybe too soon\n" + popup.onload);
popup.onload = function() { alert("message two"); };
alert("message 2 maybe too late\n" + popup.onload);
What you can do:
open a window with a "foreign" URL
on that window's address bar enter a javascript: URI -- the code will run with the same privileges as the domain of the "foreign" URL
The javascript: URI may need to be bookmarked if typing it in the address bar has no effect (may be the case with some browsers released around 2012)
Thus any page, well almost, irregardless of origin, can be modified like:
if(confirm("wipe out links & anchors?\n" + document.body.innerHTML))
void(document.body.innerHTML=document.body.innerHTML.replace(/<a /g,"< a "))
Well, almost:
jar:file:///usr/lib/firefox/omni.ja!/chrome/toolkit/content/global/aboutSupport.xhtml, Mozilla Firefox's troubleshooting page and other Jar archives are exceptions.
As another example, to routinely disable Google's usurping of target hits, change its rwt function with the following URI:
javascript: void(rwt = function(unusurpURL) { return unusurpURL; })
(Optionally Bookmark the above as e.g. "Spay Google" ("neutralize Google"?)
This bookmark is then clicked before any Google hits are clicked, so bookmarks of any of those hits are clean and not the mongrelized perverted aberrations that Google made of them.
Tests done with Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:11.0) Gecko/20100101 Firefox/11.0 UA string.
It should be noted that addEventListener in Firefox only has a non-standard fourth, boolean parameter, which if true allows untrusted content triggers to be instantiated for foreign pages.
Reference:
element.addEventListener | Document Object Model (DOM) | MDN:
Interaction between privileged and non-privileged pages | Code snippets | MDN:
This did the trick for me; full example:
HTML:
Click for my popup on same domain
Javascript:
(function(){
var doc = document;
jQuery('.import').click(function(e){
e.preventDefault();
window.popup = window.open(jQuery(this).attr('href'), 'importwindow', 'width=500, height=200, top=100, left=200, toolbar=1');
window.popup.onload = function() {
window.popup.onbeforeunload = function(){
doc.location.reload(true); //will refresh page after popup close
}
}
});
})();
onload event handler must be inside popup's HTML <body> markup.
First of all, when your first initial window is loaded, it is cached. Therefore, when creating a new window from the first window, the contents of the new window are not loaded from the server, but are loaded from the cache. Consequently, no onload event occurs when you create the new window.
However, in this case, an onpageshow event occurs. It always occurs after the onload event and even when the page is loaded from cache. Plus, it now supported by all major browsers.
window.popup = window.open($(this).attr('href'), 'Ad', 'left=20,top=20,width=500,height=500,toolbar=1,resizable=0');
$(window.popup).onpageshow = function() {
alert("Popup has loaded a page");
};
The w3school website elaborates more on this:
The onpageshow event is similar to the onload event, except that it occurs after the onload event when the page first loads. Also, the onpageshow event occurs every time the page is loaded, whereas the onload event does not occur when the page is loaded from the cache.
The core problem seems to be you are opening a window to show a page whose content is already cached in the browser. Therefore no loading happens and therefore no load-event happens.
One possibility could be to use the 'pageshow' -event instead, as described in:
https://support.microsoft.com/en-us/help/3011939/onload-event-does-not-occur-when-clicking-the-back-button-to-a-previou
Simple solution:
new_window = window.open(...);
new_window.document.write('<body onload="console.log(1);console.log(2);></body>');

Categories