Display a Popup

Add-ons using the techniques described in this document are considered a legacy technology in Firefox. Don't use these techniques to develop new add-ons. Use WebExtensions instead. If you maintain an add-on which uses the techniques described here, consider migrating it to use WebExtensions.

From Firefox 53 onwards, no new legacy add-ons will be accepted on addons.mozilla.org (AMO).

From Firefox 57 onwards, WebExtensions will be the only supported extension type, and Firefox will not load other types.

Even before Firefox 57, changes coming up in the Firefox platform will break many legacy extensions. These changes include multiprocess Firefox (e10s), sandboxing, and multiple content processes. Legacy extensions that are affected by these changes should migrate to WebExtensions if they can. See the "Compatibility Milestones" document for more.

A wiki page containing resources, migration paths, office hours, and more, is available to help developers transition to the new technologies.

To follow this tutorial you'll need to have learned the basics of jpm.

To display a popup dialog, use the panel module. A panel's content is defined using HTML. You can run content scripts in the panel: although the script running in the panel can't directly access your main add-on code, you can exchange messages between the panel script and the add-on code.

In this tutorial we'll create an add-on that adds an action button to the toolbar that 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 main add-on code logs the message to the console.

The add-on consists of seven files:

  • package.json: created when you run jpm init
  • index.js: the main add-on code, that creates the button and panel
  • get-text.js: the content script that interacts with the panel content
  • text-entry.html: the panel content itself, specified as HTML
  • icon-16.png, icon-32.png, and icon-64.png: icons for the button in three different sizes

The "index.js" looks like this:

var data = require("sdk/self").data;
// Construct a panel, loading its content from the "text-entry.html"
// file in the "data" directory, and loading the "get-text.js" script
// into it.
var text_entry = require("sdk/panel").Panel({
  contentURL: data.url("text-entry.html"),
  contentScriptFile: data.url("get-text.js")
});
// Create a button
require("sdk/ui/button/action").ActionButton({
  id: "show-panel",
  label: "Show Panel",
  icon: {
    "16": "./icon-16.png",
    "32": "./icon-32.png",
    "64": "./icon-64.png"
  },
  onClick: handleClick
});
// Show the panel when the user clicks the button.
function handleClick(state) {
  text_entry.show();
}
// When the panel is displayed it generated an event called
// "show": we will listen for that event and when it happens,
// send our own "show" event to the panel's script, so the
// script can prepare the panel for display.
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();
});

The content script "get-text.js" looks like this:

// When the user hits return, send the "text-entered"
// message to main.js.
// The message payload is the contents of the edit box.
var textArea = document.getElementById("edit-box");
textArea.addEventListener('keyup', function onkeyup(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 = '';
  }
}, false);
// Listen for the "show" event being sent from the
// main add-on code. It means that the panel's about
// to be shown.
//
// Set the focus to the text area so the user can
// just start typing.
self.port.on("show", function onShow() {
  textArea.focus();
});
 

Finally, the "text-entry.html" file defines the <textarea> element:

<html>
<head>
    <style type="text/css" media="all">
      textarea {
        margin: 10px;
      }
      body {
        background-color: gray;
      }
    </style>
  </head>
  <body>
    <textarea rows="13" cols="33" id="edit-box"></textarea>
  </body>
</html>
 

Finally, save these three icon files to the "data" directory:

icon-16.png
icon-32.png
icon-64.png

Try it out: "index.js" is saved in the top level, and the other five files go in your add-on's data directory:

my-addon/
         data/
              get-text.js
              icon-16.png
              icon-32.png
              icon-64.png
              text-entry.html
         index.js

Run the add-on, click the button, and you should see the panel. Type some text and press "return" and you should see the output in the console.

If you use a toggle button, you can attach the panel to the button.

Learning More

To learn more about the panel module, see the panel API reference.

To learn more about buttons, see the action button and toggle button API reference.

Document Tags and Contributors

Tags: 
 Contributors to this page: wbamberg, Makyen, tofutim, Canuckistani
 Last updated by: wbamberg,