Labs/Jetpack/Release Notes/1.4

From MozillaWiki
Jump to: navigation, search

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>

Bug 667817

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.

Bug 684047

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.

Bug 701760

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.

Bug 700733

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.

Bug 689215

The tab array is not reordered to match the on-screen order of the tabs after a tab move event takes place.

Bug 697215

Cannot scroll panel on OS X. This is a platform bug that might be fixed in Firefox 10.

Bug 638742

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.

Bug 663480

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.

Bug 663048

The SDK will give an error if your package name contains spaces or Unicode characters.

Bug 627432

If your add-on stores data using the simple-storage API, the data is not cleaned up when your add-on is uninstalled.

Bug 656037

Some core JavaScript functions, such as btoa(), are not available to add-on code. This is now being tracked in a Feature Page.

Bug 571049

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.

Bug 660862

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.

Bug 660857

A widget containing HTML content gets no icon in the Customize Toolbar Window.

Bug 660860

When a user removes a widget from the toolbar its detach event does not get sent.

Bug 661884

Selection events for a page are not sent if the page did not fully load (for example, because the user stopped the page loading).

Bug 556562

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.