Firefox Addon SDK - How to create an about: page - firefox-addon

I need to create an about: page, to display addon options. I have seen ti done before, but there seems to be no option in the SDK that allows you to do that.
Is there another way I could let users type about:pagename and get to my page?
I would prefer not to redirect all tabs with a URL of about:pagename to another options page.
Thanks in advance

This is the index.js file for a restartless add-on developed using jpm:
const { Cc, Ci, Cr, Cu, Cm, components } = require("chrome");
Cm.QueryInterface(Ci.nsIComponentRegistrar);
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
// globals
var factory;
const aboutPage_description = 'This is my custom about page';
const aboutPage_id = '6c098a80-9e13-11e5-a837-0800200c9a66'; // make sure you generate a unique id from https://www.famkruithof.net/uuid/uuidgen
const aboutPage_word = 'foobar';
const aboutPage_page = Services.io.newChannel('data:text/html,hi this is the page that is shown when navigate to about:foobar', null, null);
function AboutCustom() {};
AboutCustom.prototype = Object.freeze({
classDescription: aboutPage_description,
contractID: '#mozilla.org/network/protocol/about;1?what=' + aboutPage_word,
classID: components.ID('{' + aboutPage_id + '}'),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]),
getURIFlags: function(aURI) {
return Ci.nsIAboutModule.ALLOW_SCRIPT;
},
newChannel: function(aURI) {
let channel = aboutPage_page;
channel.originalURI = aURI;
return channel;
}
});
function Factory(component) {
this.createInstance = function(outer, iid) {
if (outer) {
throw Cr.NS_ERROR_NO_AGGREGATION;
}
return new component();
};
this.register = function() {
Cm.registerFactory(component.prototype.classID, component.prototype.classDescription, component.prototype.contractID, this);
};
this.unregister = function() {
Cm.unregisterFactory(component.prototype.classID, this);
}
Object.freeze(this);
this.register();
}
exports.main = function() {
factory = new Factory(AboutCustom);
};
exports.onUnload = function(reason) {
factory.unregister();
};
Basically it registers a custom about page that will be loaded when you access about:foobar. The loaded page is just a line of text.
This is how it looks like:
You can see a working example here: https://github.com/matagus/about-foobar-addon

I think this is a better solution if you are using the addons-sdk:
Credit goes here:
https://stackoverflow.com/a/9196046/1038866
var pageMod = require("page-mod");
pageMod.PageMod({
include: data.url("options.html"),
...
});
var tabs = require("tabs");
tabs.open(data.url("options.html"));
But there are other ways. You could take a look at the Scroll to Top addon which implements this: https://addons.mozilla.org/firefox/addon/402816

Related

how to print an element with jspdf / Angular

I found below code and trying to modify it to print an specific div element. Below code opens print dialog but shows empty screen. I dont know where to put my id of div element in the code.
print(){
var doc = new jsPDF("portrait", "mm", "a4");
doc.autoPrint();
const hiddFrame = document.createElement('iframe');
hiddFrame.style.position = 'fixed';
// "visibility: hidden" would trigger safety rules in some browsers like safariļ¼Œ
// in which the iframe display in a pretty small size instead of hidden.
// here is some little hack ~
hiddFrame.style.width = '1px';
hiddFrame.style.height = '1px';
hiddFrame.style.opacity = '0.01';
const isSafari = /^((?!chrome|android).)*safari/i.test(window.navigator.userAgent);
if (isSafari) {
// fallback in safari
hiddFrame.onload = () => {
try {
hiddFrame.contentWindow.document.execCommand('print', false, null);
} catch (e) {
hiddFrame.contentWindow.print();
}
};
}
var blob = doc.output("blob");
window.open(URL.createObjectURL(blob), '_blank');
}

How to create multiple instances of IIFE Javascript module?

I'm dealing with a huge javascript codebase that I'm trying to reorganize. I'm not really an expert and I just started studying good javascript coding practices. So, one thing I'm trying to do is to divide all the code in modules. In this particular case I'm trying to create a module that would help me to optimize video embeds. I would like to pass the module an id and receive some html code or an image out of it.
I'm not putting the whole code here, but it's enough for the example:
var videoIframe = (function($) {
'use strict';
var id,
setVideoId = function(videoId) {
id = videoId;
console.log(id);
},
getVideoThumbnail = function(videoId) {
setVideoId(videoId);
},
test = function() {
console.log(id)
},
getVideoEmbedCode = function() {
};
return {
test: test,
getVideoThumbnail: getVideoThumbnail
};
})(jQuery);
In another module I assign it to two variables:
var video1 = videoIframe;
var video2 = videoIframe;
video1.getVideoThumbnail(123);
video2.getVideoThumbnail(456);
video1.test();
video2.test();
And, of course, I'm not getting what I expected. After the second getVideoThumbnail call, it always prints 456.
Doing some research I understood that I'm creating a singleton, a single instance, and I'm only changing values inside that instance. I think I need a constructor for my module, but I'm not sure how to create it in combination with the IIFE pattern. And is it the right approach?
And is it the right approach?
No. IIFEs are for things you want to do exactly once.
If you want to do something multiple times, then use a regular function and call it multiple times.
var videoIframe = (function($) {
function videoIframe() {
'use strict';
var id,
setVideoId = function(videoId) {
id = videoId;
console.log(id);
},
getVideoThumbnail = function(videoId) {
setVideoId(videoId);
},
test = function() {
console.log(id)
},
getVideoEmbedCode = function() {
};
return {
test: test,
getVideoThumbnail: getVideoThumbnail
};
}
return videoIframe;
})(jQuery);
var video1 = videoIframe();
var video2 = videoIframe();
video1.getVideoThumbnail(123);
video2.getVideoThumbnail(456);
video1.test();
video2.test();
The problem is that you are initializing object when assigning to videoIframe variable:
var videoIframe = (function($) {
// ...
})(jQuery);
You can try with:
var videoIframe = (function($) {
// ...
});
var video1 = videoIframe(jQuery);
var video2 = videoIframe(jQuery);
Just keep videoIframe as a function that returns your IIFE, instead of reusing the singleton. I kept the IIFE so the id and the inner functions keep being encapsulated and hence, not reachable if they're not in the returned interface object. If you are planning to create alot of these, it might be more efficient to just use a constructor and a prototype, so the inner functions don't get recreated for every instance.
var videoIframe = function() {
'use strict';
return (function() {
var id,
setVideoId = function(videoId) {
id = videoId;
console.log(id);
},
getVideoThumbnail = function(videoId) {
setVideoId(videoId);
},
test = function() {
console.log(id)
},
getVideoEmbedCode = function() {
};
return {
test: test,
getVideoThumbnail: getVideoThumbnail
};
}());
};
I did some minor modification. Hope it will be helpful
var videoIframe = (function($) {
'use strict';
var id;
function _setVideoId(videoId) {
id = videoId;
alert(id);
};
function _getVideoThumbnail(videoId) {
_setVideoId(videoId);
};
function _test(){
console.log(id)
}
function _getVideoEmbedCode() {
};
return {
test: _test,
getVideoThumbnail: _getVideoThumbnail
};
})(jQuery);
Now you can call like this
videoIframe.getVideoThumbnail(123);
videoIframe.getVideoThumbnail(561);
jsfiddle

How to manipulate window prototype in a <browser> element?

What I'm doing right now is:
When <browser src="..." /> loads, I attach data into its .contentWindow:
frame.addEventListener("load",function(){
this.contentWindow.someMethod = function(){};
},true);
Now I want to know if there is a way to do this earlier, into the <browser>'s window prototype, or any Window prototype, as for example I can do in the "current" window:
// [W]indow is the constructor
Window.prototype.test = function(){ alert("hello"); };
// [w]indow is the instance
window.test();
There are currently two ways to inject properties into a window before any JavaScript code runs. Usually, content-document-global-created notification is simpler. The other is implementing nsIDOMGlobalPropertyInitializer interface. Both allow you to get notified when a new window loads and before that window runs JavaScript code.
Here is the approximate code for doing it with the observer notification:
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var myObserver =
{
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
observe: function(subject, topic, data)
{
if (topic == "content-document-global-created" &&
subject instanceof Ci.nsIDOMWindow &&
subject.location.hostname == "example.com")
{
XPCNativeWrapper.unwrap(subject).someMethod = function() {};
}
}
};
var observerService = Cc["#mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
observerService.addObserver(myObserver, "content-document-global-created", true);

Firefox extension observing response

I am trying using code
// This is an active module of the goelvivek (8) Add-on
exports.main = function() {
var httpRequestObserver =
{
observe: function(subject, topic, data)
{
if (topic == "http-on-examine-response") {
if(console)
console.log(data);
}
}
};
var {Cc, Ci, Cr} = require("chrome");
var observer = require("observer-service");
observerService = Components.classes["#mozilla.org/observer-service;1"].
getService(Components.interfaces.nsIObserverService);
observerService.addObserver(httpRequestObserver, "http-on-examine-response", false);
};
but line console.log(data); is not printing any thing in console log. why ?
In addition to the issue noted by Nickolay, an observer needs to implement a QueryInterface() function (typically by means of XPCOMUtils.generateQI()). Here is how one would do it with the Add-on SDK:
var {Cc, Ci, Cr, Cu} = require("chrome");
var {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var httpRequestObserver =
{
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
...
};
However, since you already require observer-service package, it would be easier to use it:
var observer = require("observer-service");
observer.add("http-on-examine-response", onHttpRequest);
function onHttpRequest(subject, data)
{
...
}
The downside of this approach is that observer-service is an internal package and its API might change in future Add-on SDK versions.
Is it the real snippet? You should see an error about Components being undefined in the Error Console. Either get it from require('chrome') or use the object from require("observer-service").

The document.body.innerHTML.replace() replaces the url in the address bar

I am trying to make an extension as part of which i want certain words in the web pages to be highlighted. The document.body.innerHTML.replace() replaces the url in the address bar as well. So the moment this code gets exwecuted the page doesnt get loaded properly..
Is there a way around this problem?
onPageLoad: function(aEvent) {
var doc = aEvent.originalTarget;
var str="the";
var regex;
var regex = new RegExp(str, "g");
doc.body.innerHTML = doc.body.innerHTML.replace(regex,'<b>'+str+'</b>');
}
The listener is registered as follows in a browser.xul overlay:
window.addEventListener("load", function() {
myExtension.init();
}, false);
var myExtension = {
init: function() {
var appcontent = document.getElementById("appcontent"); // browser
if(appcontent)
appcontent.addEventListener("DOMContentLoaded", myExtension.onPageLoad, false);
},
onPageLoad: function(aEvent) {
if(aEvent.originalTarget.nodeName=="#document"){
var doc = aEvent.originalTarget;
var str="the";
var regex;
var regex = new RegExp(str, "g");
doc.body.innerHTML = doc.body.innerHTML.replace(regex,'<b>'+str+'</b>');
}
}
The if condition in the function happens to do the trick..
:)

Resources