Labs/Jetpack/Release Notes/1.4
Contents
About
The Add-on SDK is a software development kit that provides a set of tools and APIs for building, testing, and packaging Firefox add-ons.
The 1.4 release features a rewritten loader, a simplified XPI layout, and two new modules.
Note that this release also includes an issue which you need to know about if you are hardcoding resource://
URLs to files in your add-on's "data" directory.
Installation
Obtain the SDK in your favorite compression format:
Then unpack the archive, open the addon-sdk-1.4/README.txt file, and follow its instructions.
Major Changes Since 1.3
New stuff
Simple-Preferences Module
We've landed the first cut of a preferences module. At this stage it's still experimental. See its documentation page.
Note that the preferences are associated with your profile: since cfx run
by default creates a new profile each time it's executed, then changes to preferences won't persist across instances of cfx run
. To see changes persist, either build the add-on into an XPI file and install it, or instruct cfx
to use a existing instance using the --profiledir
option.
Bypass Content Scripts for Trusted Content
For page-worker, panel, and widget, we've enabled you to bypass content scripts for trusted content.
This means that if the HTML content for these objects is packaged along with your add-on in its "data" directory, and loaded using self.data.url("my-file.html")
, then instead of using content scripts to interact with this content, you can use normal page scripts.
Communications between the page script and the main add-on code use the same message-passing mechanism as content scripts (that is, using port
or postMessage
).
The key difference is that the page script accesses port
and postMessage
using the global addon
object, rather than the global self
object. We're still deciding what the best name is for this object, and as a result of that, this feature is experimental in 1.4.
For example, suppose you've got an add-on that adds a widget which displays a panel when clicked. The panel just contains a <textarea>
element: when the user presses the return key, the contents of the <textarea>
is sent to the main add-on code.
The add-on consists of three files:
main.js: the main add-on code, that creates the widget and panel get-text.js: the content script that interacts with the panel content text-entry.html: the panel content itself, specified as HTML
"main.js" is saved in your add-on's lib directory, and the other two files go in your add-on's data directory.
The "main.js" looks like this:
var data = require("self").data; // Create a panel whose content is defined in "text-entry.html". // Attach a content script called "get-text.js". var text_entry = require("panel").Panel({ width: 212, height: 200, contentURL: data.url("text-entry.html"), contentScriptFile: data.url("get-text.js") }); // Send the content script a message called "show" when // the panel is shown. text_entry.on("show", function() { text_entry.port.emit("show"); }); // Listen for messages called "text-entered" coming from // the content script. The message payload is the text the user // entered. // In this implementation we'll just log the text to the console. text_entry.port.on("text-entered", function (text) { console.log(text); text_entry.hide(); }); // Create a widget, and attach the panel to it, so the panel is // shown when the user clicks the widget. require("widget").Widget({ label: "Text entry", id: "text-entry", contentURL: "http://www.mozilla.org/favicon.ico", panel: text_entry });
The content script "get-text.js" looks like this:
self.port.on("show", function (arg) { var textArea = document.getElementById('edit-box'); textArea.focus(); // When the user hits return, send a message to main.js. // The message payload is the contents of the edit box. textArea.onkeyup = function(event) { if (event.keyCode == 13) { // Remove the newline. text = textArea.value.replace(/(\r\n|\n|\r)/gm,""); self.port.emit("text-entered", text); textArea.value = ''; } }; });
Finally, the "text-entry.html" file defines the <textarea>
element:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <style type="text/css" media="all"> textarea { margin: 10px; } </style> </head> <body> <textarea rows="10" cols="20" id="edit-box"></textarea> </body> </html>
To rewrite this code to use normal page scripts instead of content scripts, we need to make three changes:
(1) don't attach a content script to the panel in "main.js":
var data = require("self").data; // Create a panel whose content is defined in "text-entry.html". var text_entry = require("panel").Panel({ width: 212, height: 200, contentURL: data.url("text-entry.html"), }); // Send the page script a message called "show" when // the panel is shown. text_entry.on("show", function() { text_entry.port.emit("show"); }); // Listen for messages called "text-entered" coming from // the page script. The message payload is the text the user // entered. // In this implementation we'll just log the text to the console. text_entry.port.on("text-entered", function (text) { console.log(text); text_entry.hide(); }); // Create a widget, and attach the panel to it, so the panel is // shown when the user clicks the widget. require("widget").Widget({ label: "Text entry", id: "text-entry", contentURL: "http://www.mozilla.org/favicon.ico", panel: text_entry });
(2) in "get-text.js", rename self
to addon
:
addon.port.on("show", function (arg) { var textArea = document.getElementById('edit-box'); textArea.focus(); // When the user hits return, send a message to main.js. // The message payload is the contents of the edit box. textArea.onkeyup = function(event) { if (event.keyCode == 13) { // Remove the newline. text = textArea.value.replace(/(\r\n|\n|\r)/gm,""); addon.port.emit("text-entered", text); textArea.value = ''; } }; });
(3) in the HTML file "text-entry.html", reference "get-text.js" in a <script
tag:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <style type="text/css" media="all"> textarea { margin: 10px; } </style> <script src="get-text.js"></script> </head> <body> <textarea rows="10" cols="20" id="edit-box"></textarea> </body> </html>
Rewritten loader
We've rewritten the loader, making it simpler, smaller and faster. See bug 674492 and pull request 247.
HTTPD Module
We've added a new module to the internal api-utils package. It's a basic HTTP server which can be used for unit tests. See its documentation page.
Simplified XPI Layout
We've simplified the structure of the XPI files generated by the SDK. See bug 660629.
Known Issues
See the complete list of known issues and requests for enhancement. We've listed some of the issues you're most likely to encounter separately below.
Change in resource://
URLs
In the SDK you store files such as HTML files, image files and content scripts in your add-on's "data" directory, and can then reference them using something like:
require("self").data.url("my-file.png");
This function actually returns a resource://
URL which looks something like:
resource://jid1-294x0ji6mesjag-at-jetpack-example-data/my-file.png
We've changed the internal structure of the XPI files that the SDK generates, and as a result, this URL has changed.
How to Tell Whether You're Affected
If your code hardcodes one of these resource://
URLs, it will be broken by this change.
For example, the following HTML file references an image stored in the "data" directory using a hardcoded resource://
URL, and will be broken by the change:
<html> <body> <img src="resource://jid1-294x0ji6mesjag-at-jetpack-example-data/my-file.png"</img> </body> </html>
What the Impact Is
Because we're not repacking add-ons hosted on AMO, this change won't - yet - break any add-ons on AMO. However, if we need to repack AMO-hosted add-ons when we release SDK 1.5, because of a compatibility change in Firefox, then any affected AMO-hosted add-ons will be broken at that time. So it's a good idea to update your add-on anyway.
If you manually rebuild your add-on using the 1.4 SDK, it'll be broken.
How to Fix It
If the hardcoded reference is in the main add-on code, so you have access to the self
module, then use it to generate the URL. So instead of:
var myPanel = require("panel").Panel({ contentURL: "resource://jid1-294x0ji6mesjag-at-jetpack-example-data/my-file.html" });
use:
var myPanel = require("panel").Panel({ contentURL: require("self").data.url("my-file.html") });
If the hardcoded reference is in a content script, HTML file, or some other file which is itself stored in "data", then just use a relative path instead. So instead of:
<html> <body> <img src="resource://jid1-294x0ji6mesjag-at-jetpack-example-data/my-file.png"</img> </body> </html>
use:
<html> <body> <img src="my-file.png"</img> </body> </html>
If you change location in a content script using:
window.location="http://mozilla.org";
Nothing happens, instead, `location` attribute is just replaced by a string.
If a page-mod matches a page, its content scripts get attached to every iframe in the page, and this can have a bad effect on performance if the script is large and the page contains many iframes.
Although this behaviour is intentional, there should be an option to make content scripts only run on the topmost frame of a page.
When an html select element (e.g. a drop-down menu) in web content inside a panel has focus, clicking *outside* the panel does not close the panel.
If you create a context menu item for the selection type, and right-click a form button, the context menu does not appear and an error is logged. It's not yet clear whether this is specific to form buttons or not.
The tab array is not reordered to match the on-screen order of the tabs after a tab move event takes place.
Cannot scroll panel on OS X. This is a platform bug that might be fixed in Firefox 10.
If your add-on has a long name, and the path to your Firefox profile is long enough, then the XPI installation process will try to create an intermediate file with a name longer than the maximum supported length on some Windows systems. If that happens you may get an error like:
"<your add-on> could not be installed because Firefox cannot modify the needed file"
The main fix for this will be bug 638742, which is to stop unpacking the XPI completely. When that is done, none of the pathnames will matter: they'll all stay safely trapped inside the zipfile. At that point, the name of the XPI file and the length of the profile directory will be the only issues.
Until then, the best advice is to use shorter package names or install Firefox higher up the directory tree so the profile directory's absolute path is shorter.
The SDK automatically includes a dependency on the packages supplied with the SDK such as addon-kit
and api-utils
. This means that you can require()
modules in the SDK such as widget
without having to specify addon-kit
as a dependency.
You can also use modules from other packages, such as the packages listed here, by including a reference to the package in your package.json
file.
However, if you do add any dependencies to package.json
, then the SDK will no longer add addon-kit
or api-utils
automatically, and you must add them manually if you want to include their modules.
The SDK will give an error if your package name contains spaces or Unicode characters.
If your add-on stores data using the simple-storage API, the data is not cleaned up when your add-on is uninstalled.
Some core JavaScript functions, such as btoa()
, are not available to add-on code. This is now being tracked in a Feature Page.
If an add-on is uninstalled while it's disabled, it's not notified of uninstall. If the add-on needs to do some special cleanup on uninstall, like removing persistent storage such as a file, this won't be possible if it has been disabled.
By default, widgets are placed on the add-on bar, and given a height to match the height of that bar. If the user moves the widget to a different toolbar, and that toolbar has a different height, the widget's height is not updated accordingly.
A widget containing HTML content gets no icon in the Customize Toolbar Window.
When a user removes a widget from the toolbar its detach
event does not get sent.
Selection events for a page are not sent if the page did not fully load (for example, because the user stopped the page loading).
If you have copy of mozrunner installed on your system, the cfx test
command may not work correctly.
There is a workaround for this.
Feedback and Bug Reports
We'd love to hear any feedback you have regarding this release! You can post it to the discussion group or report a bug.