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).
Related
I want to create a straightforward extension for Firefox.
User hovers over some word on any page
Pull the dictionary definition of that word from a file inside the extension
Display it while still hovered
I am new to Firefox add-ons and WebExtensions, so here's what I'm wondering:
I want the dictionary file(s) used by the extension to be local, instead of referring to some online website each time.
Any given user might be interested in a different part of the entire dictionary (it contains entries in different languages, users might only want their own 1 or 2 languages) so I want to avoid forcing every user to download the entire dictionary base.
The way I have seen similar add-ons handle that before Firefox 5.* is that they offer the search-and-display add-on separately from the dictionary files which are each available as add-ons in their own right, only actually doing stuff if you have the master add-on installed.
However, none of those examples seem to have been updated for the WebExtensions API and do not support more recent versions of Firefox.
I have also been unable to find how to communicate between web extensions so far.
My question being, how can I share information between 2 or more coorperating extensions to achieve what I described.
And actually, if this seems really stupid for some reason I'm unaware of, do point out any more reasonable alternatives that allow me to handle the dictionary files separately from the main extension.
Possibly related questions I found:
Communicating between 2 Firefox Add-Ons (Cross-Extension Communication)
This one is from 2010 however, thus out of date as far as I could tell.
Mozilla Addon Development - Communicating between windows with different domains
Kind of a similar situation, but they want to pull the definitions from an online source, rather than a local one.
The closest thing I found on the Mozilla browser extension website is communicating between add-on and some native applications, not quite what I need I think.
Communicating between add-ons is a normal part of the functionality of runtime.sendMessage(), runtime.connect(), runtime.onMessage, and runtime.onConnect.
Both runtime.sendMessage() and runtime.connect() have as their optional first parameter:
extensionId
For runtime.sendMessage(), this is:
string. The ID of the extension to send the message to. Include this to send the message to a different extension. If the intended recipient has set an ID explicitly using the applications key in manifest.json, then extensionId should have that value. Otherwise it should have the ID that was generated for the intended recipient.
If extensionId is omitted, the message will be sent to your own extension.
For runtime.connect(), this is:
string. The ID of the extension to connect to. If the target has set an ID explicitly using the applications key in manifest.json, then extensionId should have that value. Otherwise it should be have the ID that was generated for the target.
Both runtime.onMessage, and runtime.onConnect provide a sender property or parameter, either with the message, or as part of the port. This parameter/property is a runtime.MessageSender which includes an id property which is:
id
string. The ID of the extension that sent the message, if the message was sent by an extension. If the sender set an ID explicitly using the applications key in manifest.json, then id will have this value. Otherwise it will have the ID that was generated for the sender.
Note that in Firefox, before version 54, this value was the extension's internal ID (that is, the UUID that appears in the extension's URL).
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.
I'm building an add-on for FireFox that simulates a website, but running from a local library. (If you want to know more, look here)
I'm looking for a way to get a hold of the user-agent string that FireFox would send if it were doing plain http. I'm doing the nsIProtocolHandler myself and serve my own implementation of nsIHttpChannel, so if I have a peek at the source, it looks like I'll have to do all the work myself.
Unless there's a contract/object-id on nsHttpHandler I could use to create an instance just for a brief moment to get the UserAgent? (Though I notice I'll need to call Init() because it does InitUserAgentComponents() and hope it'll get to there... And I guess the http protocol handler does the channels and handlers so there won't be a contract to nsHttpHandler directly.)
If I have a little peek over the wall I notice this globally available call ObtainUserAgentString which does just this in that parallel dimension...
Apparently Firefox changed how this was done in version 4. Have you tried:
alert(window.navigator.userAgent);
You can get it via XPCOM like this:
var httpHandler = Cc["#mozilla.org/network/protocol;1?name=http"].
getService(Ci.nsIHttpProtocolHandler);
var userAgent = httpHandler.userAgent;
If for some reason you actaully do need to use NPAPI like you suggest in your tags, you can use NPN_UserAgent to get it; however, I would be shocked if you actually needed to do that just for an extension. Most likely Anthony's answer is more what you're looking for.
The problem: toggle javascript support without restarting firefox (nor resorting to different driver) during cucumber test run.
If Firefox's prefutils were exposed to javascript in a web page, that would make it possible. But it is not the case.
So, is there a plugin that does it? Or is there another way to solve the problem? Or is there a good tutorial (that highlights the exposing bit) on how to make such a plugin?
Edit
On a second thought, how would javascript be of any help once it is disabled? Probably the whole idea is a bit screwed.
I assume that your tests run with normal web content privileges. In that case, they aren't going to be able to affect browser settings such as whether JavaScript is enabled (I assume that's what you mean by "toggle JavaScript support").
I'd implement a simple XPCOM component with a method to turn JS support on and off (by setting the appropriate pref). You can expose it as a JavaScript global property so that your tests can access it. See Expose an XPCOM component to javascript in a web page for more details. Package your component in an extension and make sure it is installed in the Firefox instance where your tests are running.
If you want to access the preferences API directly from your content script, you can add the following prefs to Firefox, either in about:config or by adding the following lines to prefs.js in your profile directory:
user_pref("capability.principal.codebase.p1.granted", "UniversalXPConnect UniversalBrowserRead UniversalBrowserWrite UniversalPreferencesRead UniversalPreferencesWrite UniversalFileRead");
user_pref("capability.principal.codebase.p1.id", "http://www.example.com");
user_pref("capability.principal.codebase.p1.subjectName", "");`
user_pref("signed.applets.codebase_principal_support", true);
Replace www.example.com with the domain that you want to grant the privileges to. Also add this line to your JS code before you call the preferences API:
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
A local file (something loaded from file:///) is allowed to request additional privileges. Normally you would get a prompt asking whether you want to allow access - you can "auto-accept" the prompt by adding the following lines to prefs.js in the Firefox profile:
user_pref("capability.principal.codebase.p0.granted", "UniversalXPConnect");
user_pref("capability.principal.codebase.p0.id", "file://");
user_pref("capability.principal.codebase.p0.subjectName", "");
You page can then do:
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var branch = Components.classes["#mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
branch.setBoolPref("javascript.enabled", false);
This will definitely work if your page is a local file. Judging by the error message however, you are currently running code from about:blank. It might be that changing capability.principal.codebase.p0.id into about:blank or into moz-safe-about:blank will allow that page to get extended privileges as well but I am not sure.
However, none of this will really help if JavaScript is already disabled and you need to enable it. This can only be solved by writing an extension and adding it to the test profile. JavaScript in Firefox extensions works regardless of this setting.
That means you need Javascript to toggle enabling or disabling Javascript.
function setJavascriptPref(bool) {
prefs = Components.classes["#mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
prefs.setBoolPref("javascript.enabled", bool);
}
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).