Firefox extension, storing variable value until firefox is restarted - javascript

Building a Firefox extension and I require to save a value after an action has been performed by the user. I need this value to persist until Firefox is restarted. I`m testing with this code.
Components.utils.import("chrome://***/content/symbols.jsm");
window.addEventListener("load", function() { myExtension.init() }, false);
var myExtension = {
init: function() {
document.addEventListener("DOMContentLoaded", this.onPageLoad, false);
},
onPageLoad: function() {
if (blocked == 0) {
alert("OFF");
}
else {
alert("ON");
}
blocked = 1;
}
}
symbols.jsm
var EXPORTED_SYMBOLS = ["blocked"];
var blocked = 0;
With this code Firefox is started and "OFF" is shown because variable has not been set yet.( as intended) Navigating to a different page and even opening a new tab will show "ON" how ever as soon as a new window is opened the variable is lost and "OFF" is shown. How can I make the variable value persist until all Firefox windows are closed(restart).
I do not want to set this in a preference in about:config as this can be easily changed by the user.

You can use JavaScript code modules https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Using
Your module should export functions instead of just variables e.g.
var EXPORTED_SYMBOLS = ["getBlocked", "setBlocked"];
var blocked = 0;
function getBlocked() {
return blocked;
}
function setBlocked(value) {
blocked = value;
}
and then use the functions instead of the variable name

Related

How do I handle a custom protocol not being installed on Safari still triggering the onblur event?

I have a custom protocol checker that checks if a protocol is installed.
For Safari (same as chrome) it focuses on an element, fires the protocol and listens for onblur.
However in Safari if the protocol is not installed the browser throws an alert esc popup saying: "Safari cannot open the page because the address is invalid." which in turns triggers the onblur event.
Has anyone found a better way of managing this? It can be a Safari specific solution if needs be.
//Chrome (and default for other browsers)
function checkChrome(){
bodyElement.append("<input type='text' id='focusInput' style='background: transparent;border: none;height: 0px;width: 0px;' />");
var focusBodyElement = $('#focusInput')[0], temporaryResult = false;
focusBodyElement.focus();
focusBodyElement.onblur = function () {
updateResult(true);
return;
};
//will trigger onblur
location.href = protocolStr;
//Note: timeout could vary as per the browser version, have a higher value
setTimeout(function () {
focusBodyElement.onblur = null;
if (protocolSupport[protocolStr]===null) {
updateResult(false)
}
}, 1000);
}
I've used custom-protocol-detection in the past here, though my target was all browsers.
That being said - in digging through their source, it seems their strategy is to embed the content in a hidden frame with the javascript creates.
function openUriWithHiddenFrame(uri, failCb, successCb) {
var timeout = setTimeout(function () {
failCb();
handler.remove();
}, 1000);
var iframe = document.querySelector("#hiddenIframe");
if (!iframe) {
iframe = _createHiddenIframe(document.body, "about:blank");
}
var handler = _registerEvent(window, "blur", onBlur);
function onBlur() {
clearTimeout(timeout);
handler.remove();
successCb();
}
iframe.contentWindow.location.href = uri;
}
source
The source also contains strategies for all browsers.

Issues with removeEventListener() not removing an event, testing under Chrome Externsion

I'm calling the following method to set and then remove an input event listener on a DOM element from my Google Chrome extension's content script:
//From the global scope
gl = {
setEventIfNotDoneAlready: function(elmt)
{
if(elmt.doneAlready)
return true;
if(!elmt.myEventSet)
{
elmt.addEventListener('input', gl.onMyInputEvent, true);
elmt.myEventSet = true;
}
return false;
},
onMyInputEvent: function(elmt)
{
elmt.target.doneAlready = true;
elmt.target.removeEventListener('input', gl.onMyInputEvent, true);
console.log(">>Input fired!");
}
};
and then the method above is called as such:
var objAll = document.getElementsByTagName("*");
for(var i = 0; i < objAll.length; i++)
{
if(objAll[i].isContentEditable)
{
if(gl.setEventIfNotDoneAlready(objAll[i]))
{
//And so on...
}
}
}
I need to point out that the code above is injected into arbitrary pages that the Chrome browser user navigates to. (This is done from a Chrome Extension.)
This approach seems to work fine on most pages, except in some cases the removeEventListener doesn't do anything and my onMyInputEvent continues to be called like if nothing was done.
Any idea why?

'load' event not firing when iframe is loaded in Chrome

I am trying to display a 'mask' on my client while a file is dynamically generated server side. Seems like the recommend work around for this (since its not ajax) is to use an iframe and listen from the onload or done event to determine when the file has actually shipped to the client from the server.
here is my angular code:
var url = // url to my api
var e = angular.element("<iframe style='display:none' src=" + url + "></iframe>");
e.load(function() {
$scope.$apply(function() {
$scope.exporting = false; // this will remove the mask/spinner
});
});
angular.element('body').append(e);
This works great in Firefox but no luck in Chrome. I have also tried to use the onload function:
e.onload = function() { //unmask here }
But I did not have any luck there either.
Ideas?
Unfortunately it is not possible to use an iframe's onload event in Chrome if the content is an attachment. This answer may provide you with an idea of how you can work around it.
I hate this, but I couldn't find any other way than checking whether it is still loading or not except by checking at intervals.
var timer = setInterval(function () {
iframe = document.getElementById('iframedownload');
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
// Check if loading is complete
if (iframeDoc.readyState == 'complete' || iframeDoc.readyState == 'interactive') {
loadingOff();
clearInterval(timer);
return;
}
}, 4000);
You can do it in another way:
In the main document:
function iframeLoaded() {
$scope.$apply(function() {
$scope.exporting = false; // this will remove the mask/spinner
});
}
var url = // url to my api
var e = angular.element("<iframe style='display:none' src=" + url + "></iframe>");
angular.element('body').append(e);
In the iframe document (this is, inside the html of the page referenced by url)
window.onload = function() {
parent.iframeLoaded();
}
This will work if the main page, and the page inside the iframe are in the same domain.
Actually, you can access the parent through:
window.parent
parent
//and, if the parent is the top-level document, and not inside another frame
top
window.top
It's safer to use window.parent since the variables parent and top could be overwritten (usually not intended).
you have to consider 2 points:
1- first of all, if your url has different domain name, it is not possible to do this except when you have access to the other domain to add the Access-Control-Allow-Origin: * header, to fix this go to this link.
2- but if it has the same domain or you have added Access-Control-Allow-Origin: * to the headers of your domain, you can do what you want like this:
var url = // url to my api
var e = angular.element("<iframe style='display:none' src=" + url + "></iframe>");
angular.element(document.body).append(e);
e[0].contentWindow.onload = function() {
$scope.$apply(function() {
$scope.exporting = false; // this will remove the mask/spinner
});
};
I have done this in all kinds of browsers.
I had problems with the iframe taking too long to load. The iframe registered as loaded while the request wasn't handled. I came up with the following solution:
JS
Function:
function iframeReloaded(iframe, callback) {
let state = iframe.contentDocument.readyState;
let checkLoad = setInterval(() => {
if (state !== iframe.contentDocument.readyState) {
if (iframe.contentDocument.readyState === 'complete') {
clearInterval(checkLoad);
callback();
}
state = iframe.contentDocument.readyState;
}
}, 200)
}
Usage:
iframeReloaded(iframe[0], function () {
console.log('Reloaded');
})
JQuery
Function:
$.fn.iframeReloaded = function (callback) {
if (!this.is('iframe')) {
throw new Error('The element is not an iFrame, please provide the correct element');
}
let iframe = this[0];
let state = iframe.contentDocument.readyState;
let checkLoad = setInterval(() => {
if (state !== iframe.contentDocument.readyState) {
if (iframe.contentDocument.readyState === 'complete') {
clearInterval(checkLoad);
callback();
}
state = iframe.contentDocument.readyState;
}
}, 200)
}
Usage:
iframe.iframeReloaded(function () {
console.log('Reloaded');
})
I've just noticed that Chrome is not always firing the load event for the main page so this could have an effect on iframes too as they are basically treated the same way.
Use Dev Tools or the Performance api to check if the load event is being fired at all.
I just checked http://ee.co.uk/ and if you open the console and enter window.performance.timing you'll find the entries for domComplete, loadEventStart and loadEventEnd are 0 - at least at this current time:)
Looks like there is a problem with Chrome here - I've checked it on 2 PCs using the latest version 31.0.1650.63.
Update: checked ee again and load event fired but not on subsequent reloads so this is intermittent and may possibly be related to loading errors on their site. But the load event should fire whatever.
This problem has occurred on 5 or 6 sites for me now in the last day since I noticed my own site monitoring occasionally failed. Only just pinpointed the cause to this. I need some beauty sleep then I'll investigate further when I'm more awake.

Firefox extension: run when page loaded

I'm trying to write firefox extension which will run when a (specific) page is loaded (it will be same key words replace).
I write code like:
window.addEventListener("load", function() maApp.init(); }, false);
var maApp= {
init: function() {
var appcontent = document.getElementById("appcontent"); // browser
if(appcontent)
appcontent.addEventListener("DOMContentLoaded", maApp.onPageLoad, true);
var messagepane = document.getElementById("messagepane"); // mail
if(messagepane)
messagepane.addEventListener("load", function(event) { maApp.onPageLoad(event); }, true);
},
onPageLoad: function() {
alert("test);
doSomething();
}
};
But onPageLoad is never run... no alert... Can somebody please tell me what I'm doing wrong?
First some words on getting the browser element. In Firefox this element has ID content, not appcontent. Still, the recommended way of getting it is the window.gBrowser variable. In Thunderbird 5 the ID of the browser element changed so your code will stop working - rather than going by ID you should use window.messageContent variable which will work both in the current and future versions. Together you get:
var browser = null;
if ("gBrowser" in window)
browser = window.gBrowser; // Firefox and SeaMonkey Browser
else if ("messageContent" in window)
browser = window.messageContent; // Thunderbird
else if ("getMessageBrowser" in window)
browser = window.getMessageBrowser(); // SeaMonkey Mail
if (browser)
...
Now about listening to page loads, the recommended approach here is progress listeners - see https://developer.mozilla.org/en/Code_snippets/Progress_Listeners. You attach a progress listener to the browser and look for state changes:
onStateChange: function(aWebProgress, aRequest, aFlag, aStatus)
{
if ((aFlag & Components.interfaces.nsIWebProgressListener.STATE_STOP) &&
(aFlag & Components.interfaces.nsIWebProgressListener.STATE_IS_WINDOW))
{
// A window finished loading
doSomething(aWebProgress.DOMWindow);
}
}
The accepted answer is outdated. The solution using the WebExtensions API would be
browser.tabs.onUpdated.addListener(function(tabId, changeInfo) {
if (changeInfo.status == "complete") {
//add your script
}
})
browser.tabs.onUpdated.addListener Listens to events within the Tab (Reference)
(changeInfo.status == "complete") filters the right event and executes the commands within the if statement.

FireFox Toolbar Prefwindow unload/acceptdialog Event to Update the toolbar

I'm trying to develop a firefox toolbar ;)
so my structure is
In the options.xul is an PrefWindow which i'm opening over an
<toolbarbutton oncommand="esbTb_OpenPreferences()"/>
function esbTb_OpenPreferences() {
window.openDialog("chrome://Toolbar/content/options.xul", "einstellungen", "chrome,titlebar,toolbar,centerscreen,modal", this);}
so in my preferences i can set some checkboxes which indicates what links are presented in my toolbar. So when the preferences window is Closed or the "Ok" button is hitted I want to raise an event or an function which updates via DOM my toolbar.
So this is the function which is called when the toolbar is loaded. It sets the links visibility of the toolbar.
function esbTB_LoadMenue() {
var MenuItemNews = document.getElementById("esbTb_rss_reader");
var MenuItemEservice = document.getElementById("esbTb_estv");
if (!(prefManager.getBoolPref("extensions.esbtoolbar.ShowNews"))) {
MenuItemNews.style.display = 'none';
}
if (!(prefManager.getBoolPref("extensions.esbtoolbar.ShowEservice"))) {
MenuItemEservice.style.display = 'none';
}
}
So I tried some thinks like adding an eventlistener to the dialog which doesn't work... in the way I tried...
And i also tried to hand over the window object from the root window( the toolbar) as an argument of the opendialog function changed the function to this.
function esbTB_LoadMenue(RootWindow) {
var MenuItemNews = RootWindow.getElementById("esbTb_rss_reader");
var MenuItemEservice = RootWindow.getElementById("esbTb_estv");}
And then tried to Access the elements over the handover object, but this also not changed my toolbar at runtime.
So what i'm trying to do is to change the visibile links in my toolbar during the runtime and I don't get it how I should do that...
thanks in advance
-------edit-------
var prefManager = {
prefs: null,
start: function()
{
this.prefs = Components.classes["#mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService)
.getBranch("extensions.esbtoolbar.");
this.prefs.QueryInterface(Components.interfaces.nsIPrefBranch2);
this.prefs.addObserver("", this, false);
},
end: function()
{
this.prefs.removeObserver("", this);
},
observe: function(subject, topic, data)
{
if (topic != "nsPref:changed")
{
return;
}
//Stuff what is done when Prefs have changed
esbTB_LoadMenue();
},
SetBoolPref: function(pref,value)
{
this.prefs.setBoolPref(pref,value);
},
GetBoolPref: function(pref)
{
this.prefs.getBoolPref(pref);
}
}
So this is my implementation.
The trick is to listen to preference changes. That way your toolbar updates whenever the prefs change -- regardless if it happened through your PrefWindow, about:config or some other mechanism.
In Toolbar.js you do the following
var esbTB_observe = function(subject, topic, data) {
if (topic != "nsPref:changed") {
return;
}
// find out which pref changed and do stuff
}
var esbTB_init = function() {
prefs =
Components.classes["#mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService)
.getBranch("extensions.esbtoolbar.");
prefs.QueryInterface(Components.interfaces.nsIPrefBranch2);
prefs.addObserver("", esbTB_observe, false);
}
// Init addin after window loaded
window.addEventListener("load", esbTB_init, false);
Now, when the window loads, the esbTB_init() function is called in which the observer to the pref branch "extensions.esbtoolbar." is added. Later, when a pref in the branch is changed, the esbTB_observe() function is automatically called.
In esbTB_observe() you have to read the values of your prefs and adjust the toolbar.

Categories

Resources