As I'm writing a Firefox XUL Extension I find that I want to share some functionality (the business logic) across the whole extension. What would be the best place to store this?
Can I create some sort of library (javascript) file which always gets loaded first?
You most likely want to create a JavaScript code module. You can use Components.utils.import() to load it:
Components.utils.import("chrome://myaddon/content/utils.jsm");
And in utils.jsm you define which symbols should be imported by that statement, e.g.:
var EXPORTED_SYMBOLS = ["Utils"];
var Utils = {
};
The module will be loaded when it is first used and stay in memory after that - there will be only a single module instance no matter how many places on your extension use it. Note that I used a chrome:// URL to load the module, this is supported starting with Firefox 4. Documentation recommends using resource:// URLs which is cleaner because modules don't actually have anything to do with the user interface - still, using a chrome:// URL is often simpler.
Related
We have a rather old XUL extension which we want to make sure works with Electrolysis. We will eventually migrate it to the WebExtensions API, but for now we want to use the compatibility shims.
We can access content stuff (using window.content for example) in the some of our scripts (e.g, the overlay scripts). However, our extension also load some scripts using evalInSandbox. It look something like this:
var sandbox = Components.utils.Sandbox(Components.classes["#mozilla.org/systemprincipal;1"].createInstance(Components.interfaces.nsIPrincipal), {
sandboxPrototype: window,
wantXrays: false
});
// ...
Components.utils.evalInSandbox(script, sandbox, url, 0);
We don't seem to be able to access window.content on the scripts loaded in the sandbox. It seem like the shims don't work in this case. Is this a bug, or is it by design?
Thanks
Your sandboxPrototype is wrong, you are setting it to the nsIDOMWindow, set it to the aNSIDOMWindow.gBrowser.tabContainer.childNodes[tab_index_goes_here].contentWindow
see this example on mdn: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Language_Bindings/Components.utils.Sandbox#Example
also here is how to load in a script as file rather then doing evalInSandbox:
https://github.com/Noitidart/modtools
I'm trying to write a Firefox extension which hooks into WebAudio and adds a 'master-gain' to all AudioContexts.
I am trying to do this by overriding the AudioContext constructor in the window namespace. This way I can return a GainNode (which is internally connected to the destination) when the user tries to access destination. It's a hack, but I think it might be useful.
I'm currently struggling at exporting my newAudioContext constructor from the addon script(privileged scope) into the page script (less-privileged).
I tried...
unsafeWindow.AudioContext = cloneInto(newAudioContext,unsafeWindow);
But I still get the original AudioContext in the page script.
I also tried
exportFunction(newAudioContext, unsafeWindow, {defineAs: "AudioContext"});
but that exports it as a function and not as a constructor.
I understand that structured cloning has limitations, but are there any other ways I can override the window.AudioContext from an AddOn?
If you need to run more complex code (e.g. object construction) in the unprivileged context you can simply import a script into the target window (after waiving xrays) through the mozIJSSubScriptLoader.
Any function that needs to call into privileged code can be patched into its prototype from the chrome side after the script has been loaded.
You can do this before DOM parsing - and thus before any content script execution - by listening to the DOMWindowCreated event.
You will have to do this from a frame script, since the addon-sdk's page-mod sandboxes don't have enough privileges to access the script loader.
Keeping interaction with the unsafe window to a minimum, i.e. either running code wholly in the privileged environment with xrays or completely in the untrusted environment with the minimal amount of glue methods between those two seems like good security hygiene anyway.
Of course you should be aware that content code will be able to pick apart and modify any classes you create in content. It is untrusted after all.
my Firefox addon shall add a search engine, that
provides suggestions
gets its search template URL specified on runtime (i.e.: template URL depends on the preferences of the user)
And I don't see a way to do both at the same time.
I see two options to add a search engine:
addEngineWithDetails
addEngine
addEngineWithDetails() allows me to add a search engine with the template URL. But it does (apparently?) not allow to provide a suggestions URL.
addEngine() allows me to add a search engine that is specified in an XML file. But if have that file saved locally in my addon directory (e.g. chrome://example-engine/content/search.xml), how can I change the template URL on runtime? And using an online XML is an unsafe options since the internet connection could be broken or bad during the addon install.
First fo all, you're right, addEngineWithDetails does not support suggestions.
The way to go would be to use addEngine (and removeEngine).
As for the "dynamic" part of your question: While I didn't test it, the implementation seems to happily accept data: URIs. So you could:
Construct a data URI using whatever methods you like (even constructing a full XML DOM and serializing it).
Call addEngine with the data URI.
When the user changes a pref, remove the old engine, and construct a new one.
I have been asked to pass some html which is being sent from my server to my Firefox add-on through a function called parseHTML by a Mozilla add-on reviewer. The method in question is documented in this XUL school tutorial.
In my content script however, typeof parseHTML == "undefined". I believe this is because my add-on is built using the add-on SDK, not XUL.
It is correct that add-ons built with the SDK do not have access to this method? Is there an equivalent method in the SDK?
Of course this function is undefined - it isn't some globally defined helper but rather something you have to define in your code (as shown in the code example). It uses the nsIParserUtils interface that you would need to have access to. In an SDK-based extension you would use chrome authority for that:
var {Cc, Ci} = require("chrome");
var parser = Cc["#mozilla.org/parserutils;1"].getService(Ci.nsIParserUtils);
var sanitized = parser.sanitize(html, parser.SanitizerAllowStyle);
If you are in a content script then you cannot use that of course - you would need to send the HTML code to the add-on, sanitize it there and send it back to the content script. While this is an option, you might want to consider other possibilities like not using innerHTML in the first place (if all you need is setting some text on an element then textContent is a safe alternative).
Side-note: While XUL Tutorial is rather old, this particular page has only been imported into MDN a year ago - and updated regularly since that according to history. So it is current, just not meant explicitly for the Add-on SDK (like most MDN articles actually).
I have written a Firefox extension that catches when a particular URL is entered and does some stuff. My main app launches Firefox with this URL. The URL contains sensitive information so I don't want it being stored in the history.
I'm concerned about the case where the extension is not installed. If its not installed and Firefox gets launched with the sensitive URL, it will get stored in history and there's nothing I can do about it. So my idea is to use a bookmarklet.
I will launch Firefox with "javascript:window.location.href='pleaseinstallthisplugin.html'; sensitiveinfo='blahblah'".
If the extension is not installed they will get redirected to a page that tells them to install it and the sensitive info won't get stored in the history. If the extension IS installed it will grab the information in the sensitiveinfo variable and do its thing.
My question is, can the bookmarklet call a method in the extension to pass the sensitive info (and if so, how) or can the extension catch when javascript is being called in the bookmarklet?
How can a bookmarklet and Firefox extension communicate?
p.s. The alternative means of getting around this situation would be for my main app to launch Firefox and communicate with the extension using sockets but I am loath to do that because I've run into too many issues over the years with users with crazy firewalls blocking socket communication. I'd like to do everything without sockets if possible.
As far as I know, bookmarklets can never access chrome files (extensions).
Bookmarklets are executed in the scope of the current document, which is almost always a content document. However, if you are passing it in via the command line, it seems to work:
/Applications/Namoroka.app/Contents/MacOS/firefox-bin javascript:alert\(Components\)
Accessing Components would throw if it was not allowed, but the alert displays the proper object.
You could use unsafeWindow to inject a global. You can add a mere property so that your bookmarklet only needs to detect whether the global is defined or not, but you should know that, as far as I know, there is no way to prohibit sites in a non-bookmarklet context from also sniffing for this same global (since it may be a privacy concern to some that sites can detect whether they are using the extension). I have confirmed in my own add-on which injects a global in a manner similar to that below that it does work in a bookmarklet as well as regular site context.
If you register an nsIObserver, e.g., where content-document-global-created is the topic, and then unwrap the subject, you can inject your global (see this if you need to inject something more sophisticated like an object with methods).
Here is some (untested) code which should do the trick:
var observerService = Cc['#mozilla.org/observer-service;1'].getService(Ci.nsIObserverService);
observerService.addObserver({observe: function (subject, topic, data) {
var unsafeWindow = XPCNativeWrapper.unwrap(subject);
unsafeWindow.myGlobal = true;
}}, 'content-document-global-created', false);
See this and this if you want an apparently easier way in an SDK add-on (not sure whether SDK postMessage communication would work as an alternative but with the apparently same concern that this would be exposed to non-bookmarklet contexts (i.e., regular websites) as well).