Loading script after page fully loaded - firefox-addon

I am building a firefox addon that loads javascript at every page load. I'm using progress listener function I found on this page: https://developer.mozilla.org/en/Code_snippets/Progress_Listeners
My problem is that the code seems to execute to early before the page is fully loaded which causes my script to not run. Here is my code.
var PageLoad = {
browser: null,
domain: null,
oldURL: null,
init: function() {
gBrowser.addProgressListener(urlBarListener,Components.interfaces.nsIWebProgress.NOTIFY_LOCATION);
},
uninit: function() {
gBrowser.removeProgressListener(urlBarListener);
},
processNewURL: function(aURI) {
//if (aURI.spec == this.oldURL)
//return;
MyObject.function();
this.oldURL = aURI.spec;
}
};
var urlBarListener = {
locChange: false,
QueryInterface: function(aIID) {
if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
aIID.equals(Components.interfaces.nsISupports))
return this;
throw Components.results.NS_NOINTERFACE;
},
onLocationChange: function(aProgress, aRequest, aURI) {
PageLoad.processNewURL(aURI);
},
onStateChange: function(aWebProgress, aRequest, aFlag, aStatus) {},
onProgressChange: function(a, b, c, d, e, f) {},
onStatusChange: function(a, b, c, d) {},
onSecurityChange: function(a, b, c) {}
};
window.addEventListener("load",
function() {
PageLoad.init()
}, false);
var MyObject = {
function : function() {
var script = PageLoad.browser.createElement('script');
script.src = 'url_to_script.js';
PageLoad.browser.getElementsByTagName('head')[0].appendChild(script);
}
};
With this code I get this error message on the console:
PageLoad.browser.getElementByTagName("head")[0] is undefined
If I add a timeout then the script does work intermittently but if the page loads slow I get the same error, here's what works sometimes setTimeout(MyObject.function, 1000);
I need a more reliable way of making sure it's executing the script after the page is loaded.
Unrelated, and it doesn't seem to cause any problems but I also see this error message:
gBrowser.addProgressListener was called with a second argument, which is not supported. See bug 608628.

If you want to load javascript at every page load - the best way is subscribing to DOMContentLoaded event:
var MyObject = {
processDOMContentLoaded: function(doc) {
var script = doc.createElement('script');
script.src = 'url_to_script.js';
script.type = 'text/javascript';
doc.getElementsByTagName('head')[0].appendChild(script);
}
};
window.addEventListener('load', function() {
var appcontent = document.getElementById('appcontent');
if(appcontent != null) {
appcontent.addEventListener('DOMContentLoaded', function(event) {
var doc = event.originalTarget;
if(doc instanceof HTMLDocument) {
MyObject.processDOMContentLoaded(doc);
}
}, true);
}
}, false);
Have not tested, but should work.

You are using onLocationChange method - but if you look at how the browser behaves, the location in the address bar changes as soon as a connection with the server is established. You should look at state changes instead:
onStateChange: function(aWebProgress, aRequest, aFlag, aStatus)
{
if ((aFlag & Components.interfaces.nsIWebProgressListener.STATE_STOP) &&
(aFlag & Components.interfaces.nsIWebProgressListener.STATE_IS_WINDOW))
{
// A window finished loading
PageLoad.windowLoaded(aWebProgress.DOMWindow);
}
},
Note that the window that finished loading is explicitly passed to PageLoad.windowLoaded() - you will be receiving notifications from different tabs and you cannot assume that the notification comes from the foreground tab.
As to the warning you are getting, just leave out the second parameter in the call to gBrowser.addProgressListener():
gBrowser.addProgressListener(urlBarListener);
tabbrowser.addProgressListener() only accepts one parameter, unlike nsIWebProgress.addProgressListener() which has a second parameter.

Actually its a great question.
You should use event listener, but carefully, because if you trigger for every page load its can trigger you more than one time (in the worst case about dozens of times).
So how you can do that?
window.addEventListener("load", function load(event){
window.removeEventListener("load", load, false); //remove listener, no longer needed
myExtension.init();
},false);
var myExtension = {
init: function() {
var appcontent = document.getElementById("appcontent"); // browser
if(appcontent){
appcontent.addEventListener("DOMContentLoaded", myExtension.onPageLoad, true);
}
},
onPageLoad: function(aEvent) {
var doc = aEvent.originalTarget; // doc is document that triggered the event
var win = doc.defaultView; // win is the window for the doc
if (doc.nodeName != "#document") return;
if (win != win.top) return;
if (win.frameElement) return;
alert("the main page has been loaded");
},
};
get notice that we check for the type of the trigger every pageload triggering to prevent multi load.

The answers that were provided were acceptable but I found yet another solution that works perfectly.
var PageLoad = {
init: function() {
if(gBrowser) gBrowser.addEventListener("DOMContentLoaded", this.onPageLoad, false);
},
onPageLoad: function(aEvent) {
var doc = aEvent.originalTarget; // doc is document that triggered the event
var win = doc.defaultView; // win is the window for the doc
if (doc.nodeName != "#document") return;
if (win != win.top) return;
if (win.frameElement) return;
MyAddon.function();
}
}
window.addEventListener("load", function load(event){
window.removeEventListener("load", load, false); //remove listener, no longer needed
PageLoad.init();
},false);

Related

I need to manipulate focus shifting

This is the scenario I want:
I'm checking for the presence of an application (using protocol detection), if it's not present the exe file automatically starts downloading. The webpage continuously checks in regular interval whether the app has been successfully downloaded and when the installation is complete, the browser prompts to open the app.
What is working:
The download is starting automatically. The webpage is checking for the presence of the app in regular interval.
What is NOT working:
After the exe is downloaded, when I start the download, the focus shifts and the webpage thinks that the app has been launched, whereas the app actually hasn't yet been installed even.
I need a focus checking mechanism which detects when the specific app has been launched and focus shifts and not respond to any other focus shifts.
This is my code:
_registerEvent = function(target, eventType, cb) {
if (target.addEventListener) {
target.addEventListener(eventType, cb);
return {
remove: function() {
target.removeEventListener(eventType, cb);
}
};
} else {
target.attachEvent(eventType, cb);
return {
remove: function() {
target.detachEvent(eventType, cb);
}
};
}
},
checkIfHasProtocol = function($el) {
openUriWithTimeoutHack(url);
},
openUriWithTimeoutHack = function(protocol) {
var timeout = setTimeout(function() {
handler.remove();
checkIfHasProtocol(protocol);
}, 5000);
var target = window;
while (target != target.parent) {
target = target.parent;
}
var handler = _registerEvent(window, "blur", onBlur);
function onBlur() {
if (tabVisible() && isBlurred) {
clearTimeout(timeout);
handler.remove();
//App has been launched
}
}
}
window.location = protocol;
};
tabVisible(function() {
if (tabVisible() && inCheck) {
inCheck = false;
}
});
window.addEventListener("blur", function() {
isBlurred = true;
});
window.addEventListener("focus", function() {
isBlurred = false;
inCheck = false;
});

Firefox addon which refreshes the tap and auto resend data

I'm trying make a firefox add-on, which reloads a page automatically under some conditions.
First I add a contentScript to a tab to get some information on the page in my addon
tab.attach({
contentScript:self.port.emit...
I have it working to the point of the refresh with
tab.reload();
but then an alert pops up every time "if it should resend data".
I want to resend the data automatically.
How and where do I do it? In the add-on or in the contentScript?
Has it to do with the Load Flags constant?
This clicks the accept button when the prompt opens. But you see the prompt window for like a couple hundred ms.
i had to use setTimeout with 0 wait time other wise the aDOMWindow.args property and aDOMWindow.Dialog and a bunch of other stuff would be undefined or null so weird. But this works:
var stringBundle = Services.strings.createBundle('chrome://browser/locale/appstrings.properties');
try {
windowListener.unregister();
} catch (ignore) {}
var windowListener = {
//DO NOT EDIT HERE
onOpenWindow: function(aXULWindow) {
// Wait for the window to finish loading
let aDOMWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
aDOMWindow.addEventListener('load', function() {
aDOMWindow.removeEventListener('load', arguments.callee, false);
windowListener.loadIntoWindow(aDOMWindow, aXULWindow);
}, false);
},
onCloseWindow: function(aXULWindow) {},
onWindowTitleChange: function(aXULWindow, aNewTitle) {},
register: function() {
// Load into any existing windows
let XULWindows = Services.wm.getXULWindowEnumerator(null);
while (XULWindows.hasMoreElements()) {
let aXULWindow = XULWindows.getNext();
let aDOMWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
windowListener.loadIntoWindow(aDOMWindow, aXULWindow);
}
// Listen to new windows
Services.wm.addListener(windowListener);
},
unregister: function() {
// Unload from any existing windows
let XULWindows = Services.wm.getXULWindowEnumerator(null);
while (XULWindows.hasMoreElements()) {
let aXULWindow = XULWindows.getNext();
let aDOMWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
windowListener.unloadFromWindow(aDOMWindow, aXULWindow);
}
//Stop listening so future added windows dont get this attached
Services.wm.removeListener(windowListener);
},
//END - DO NOT EDIT HERE
loadIntoWindow: function(aDOMWindow, aXULWindow) {
if (!aDOMWindow) {
return;
}
if (aDOMWindow.location == 'chrome://global/content/commonDialog.xul') {
var repostString = stringBundle.GetStringFromName('confirmRepostPrompt');
var repostStringFormatted = stringBundle.formatStringFromName('confirmRepostPrompt', [aDOMWindow.Application.name], 1);
aDOMWindow.setTimeout(function() {
console.log('setimeout val 00:', aDOMWindow.args)
//aDOMWindow.args and aDOMWindow.Dialog is not available till after setTimeout of 0 so weird
if (aDOMWindow.args.text == repostString || aDOMWindow.args.text == repostStringFormatted) {
console.log('this is resend prompt so accept it');
//aDOMWindow.Dialog.ui.button0.click(); //doesnt work
//aDOMWindow.Dialog.onButton0(); // doesnt work
//aDOMWindow.ondialogaccept(); //doesnt work
var dialog = aDOMWindow.document.getElementById('commonDialog');
var btnAccept = aDOMWindow.document.getAnonymousElementByAttribute(dialog, 'dlgtype', 'accept');
btnAccept.click();
console.log('clicked');
}
}, 0);
}
},
unloadFromWindow: function(aDOMWindow, aXULWindow) {
if (!aDOMWindow) {
return;
}
}
};
windowListener.register();
open scratchpad. set environemnt to browser. run the code. refresh a page to get prompt you'll see it get clicked.

Dynamic publishing swfobject external interface callback method undefined

Could some genius help on this issue to sort it out!!! It will be much appreciated
Actually I have flash music player loaded by means of config file named config.xml
Flash player Action script example
**myUltimateMp3Player.loadConfig("config.xml");**
import flash.external.ExternalInterface;
stop();
// ExternalInterface receive
if (ExternalInterface.available) {
ExternalInterface.addCallback("sendTextFromHtml", null, function(val:String):Void {
myUltimateMp3Player.loadConfig(val); });
}
I'm tried to embed the flash dynamically using Swf object,so when a click event handled from a list from html, the corresponding config.xml should be loaded on the flash player.
Sample HTML code:
$( document ).on( "click", "#Musiclist li", function() {
var c = "youtube-playlist";
$("#media-" + c).remove();
createMedia(c);
var id=this.id;
var idxml=id+".xml";
var swfPanel="media-" + c;
var flashvars = {};
var params = {};
params.swliveconnect = "true";
params.allowfullscreen = "true";
params.allowscriptaccess = "always";
var attributes = {};
attributes.id = "flashobj";
attributes.name = "flashobj";
swfobject.embedSWF("source.swf", "flashcontent", "100%", "100%", "9.0.0", "swf/expressInstall.swf", flashvars, params, attributes,callbackFn);
});
callback function
//This function is invoked by SWFObject once the <object> has been created
var callbackFn = function (e){
//Only execute if SWFObject embed was successful
if(!e.success || !e.ref){ return false; }
swfLoadEvent(function(){
var obj=e.ref;
obj.sendTextFromHtml("config.xml");
alert("The SWF has finished loading!");
});
};
Function to hold timer for SWF's PercentLoaded value and waits until it hits "100"
function swfLoadEvent(fn){
//Ensure fn is a valid function
if(typeof fn !== "function"){ return false; }
//This timeout ensures we don't try to access PercentLoaded too soon
var initialTimeout = setTimeout(function (){
//Ensure Flash Player's PercentLoaded method is available and returns a value
if(typeof e.ref.PercentLoaded !== "undefined" && e.ref.PercentLoaded()){
//Set up a timer to periodically check value of PercentLoaded
var loadCheckInterval = setInterval(function (){
//Once value == 100 (fully loaded) we can do whatever we want
if(e.ref.PercentLoaded() === 100){
//Execute function
fn();
//Clear timer
clearInterval(loadCheckInterval);
}
}, 1500);
}
}, 200);}
So I cant figure it out what I'm doing wrong,
The error getting was,
Uncaught ReferenceError: e is not defined
If I'm not using the swfLoadEvent Timer,
The error getting was,
Uncaught TypeError: Object # has no method 'sendTextFromHtml'
e is not a global variable. try modifying your swfLoadEvent function to pass a reference to the swf as an argument:
function swfLoadEvent(swf, fn){
swf.sendTextFromHtml("config.xml");
...
}
which then gets invoked as
swfLoadEvent(e.ref, function(){
...
});

jquery mobile plugin creation

I am trying to create a plugin for JQuery Mobile. Does anyone have a template or examples to help? Currently, I have the following defined in myplugin.js
(function ($) {
$.fn.myPlugin = function (options) {
var defaults = { e: 0 },
settings = $.extend({}, defaults, options);
var h= $.myPlugin.getHtml(options.e);
alert("here 1");
if ((h != null) && (h != undefined) && (h.length > 0)) {
alert("here 2");
this.html(h);
}
};
$.myPlugin = {
getHtml: function (e) {
var s = "";
return s;
}
};
})(jQuery);
I am trying to initialize an instance of this plugin like such:
$("#pluginInstance", "#myPage").myPlugin({ e: 0 });
Oddly, neither alert dialog appears. There aren't any errors in the console either. What am I doing wrong?
How about this or this?
If you look how JQM is designed, the syntax is pretty similar. I'm also sticking with it, when working on plugins I'm doing.

Inserting html on the fly and tabs

I'm building an add-on that uses a browser overlay.xul, which has included js files. The easiest way I found to modify HTML on a webpage was this bit of code I found on the mdn I believe.
const STATE_START = Components.interfaces.nsIWebProgressListener.STATE_START;
const STATE_STOP = Components.interfaces.nsIWebProgressListener.STATE_STOP;
var myListener = {
QueryInterface: function(aIID) {
if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
aIID.equals(Components.interfaces.nsISupports))
return this;
throw Components.results.NS_NOINTERFACE;
},
onStateChange: function(aWebProgress, aRequest, aFlag, aStatus) { },
onLocationChange: function(aProgress, aRequest, aURI) {
PageLoad.initialzed = false;
PageLoad.init();
},
onProgressChange: function(aWebProgress, aRequest, curSelf, maxSelf, curTot, maxTot) { },
onStatusChange: function(aWebProgress, aRequest, aStatus, aMessage) { },
onSecurityChange: function(aWebProgress, aRequest, aState) { }
};
gBrowser.addProgressListener(myListener);
var PageLoad = {
initialzed : false,
browser : null,
domain : null,
appcontent : null,
init : function() {
PageLoad.appcontent = document.getElementById("appcontent");
if (PageLoad.appcontent) {
PageLoad.appcontent.addEventListener("DOMContentLoaded", PageLoad.load, true);
}
},
load : function() {
//I do this so it only calls the function once
if (PageLoad.initialzed == false) {
PageLoad.initialzed = true;
PageLoad.browser = gBrowser.contentDocument;
PageLoad.domain = (PageLoad.browser.location.host.match(/([^.]+)\.\w{2,3}(?:\.\w{2})?$/) || [])[1];
myFunctiontoInsertHTML();
}
}
};
Then I can use PageLoad.browser.createElement('div') and so on in my function.. This works good so far but I have come across a problem that with multiple tabs open, my PageLoad.domain variable contains the domain of the last loaded tab or web page (which in turn causes errors inserting html on 2 or more different pages/tabs). So what I want to change is everytime the user clicks on a different tab or reloads the page I want to call the function but I have run into a dead end.
gBrowser.contentDocument is the document of the page loaded in the current browser tab - as soon as the user switches tabs or navigates to a different page its value changes and you shouldn't keep the old value (it is a memory leak). You are observing the progress of all browser tabs, you should look at the one that you got called for. Meaning in your case: aProgress.DOMWindow (see nsIWebProgress.DOMWindow). That's also where you should attach your DOMContentLoaded event listener if you are interested in the load progress of a single tab.

Resources