Custom output in the Web Console

This document describes how you can extend and customize the output of the Web Console and of the Browser Console. Here you can also learn how to extend the object previews we generate in the consoles and in the Object Inspector.

This document is mainly intended for add-on developers who want to extend the Firefox developer tools.

Output styling

The main style sheet loaded in the console output is webconsole.css. You will want to add any styles to this file in your theme. Use the Browser Toolbox or the DOM Inspector addon to find the structure of the console output markup, the elements and classes used.

In your extension you can add styles and custom behavior to the webconsole.xul.

Learn more about theming Firefox.

Add messages

To add a message to the Web Console you need to first determine which API you want to use and which tab the message belongs to. You have several options, as described below.

Use the nsIConsoleService

Create an nsIScriptError instance and set the innerWindowID of the message to be the same as the inner window ID of the top level window of the tab where you want the message to be displayed. You can also use the inner window ID of any iframe contained within the tab.

Any message arriving from the nsiConsoleService is put into one of the CSS, Security or JavaScript categories. Make sure you set the correct category and severity for the script error.

If the script error does not have any inner window ID, or if the ID is incorrect, the message will not be shown in the Web Console of the tab you want. You should see all of these messages in the Browser Console (introduced in Firefox 24).

Example code which uses nsIScriptError:

const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
const WARN_FLAG = Ci.nsIScriptError.warningFlag;
const ERROR_FLAG = Ci.nsIScriptError.ERROR_FLAG;
Cu.import("resource://gre/modules/Services.jsm");
// Log a message to the Web Console using an nsIScriptError.
function logMessage({flags, message, category, url,
                     lineText, line, column, windowId}) {
  let scriptError = Cc["@mozilla.org/scripterror;1"]
                    .createInstance(Ci.nsIScriptError);
  if (windowId) {
    scriptError.initWithWindowID(message, url, lineText, line,
                                 column, flags, category, windowId);
  } else {
    scriptError.init(message, url, lineText, line, column, flags, category);
  }
  Services.console.logMessage(scriptError);
}
// Get the innerWindowID of the given window.
function getInnerWindowID(win) {
  try {
     let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
                 .getInterface(Ci.nsIDOMWindowUtils);
     return utils.currentInnerWindowID;
  } catch (ex) {  }
  return null;
}
// Log a "hello world" message for a given window |win|.
logMessage({
  flags: WARN_FLAG,
  message: "hello world",
  url: "http://example.com/script.js",
  line: 10,
  windowId: getInnerWindowID(win),
});

You can also use Cu.reportError() to log script errors. This method is particularly useful with exceptions.

To log a string to the Browser Console you can use nsIConsoleService.logStringMessage().

Use the console API

For any window object you can use the console API to add messages to the Web Console. Do note that these messages are only associated to the "page logging" category and this should be avoided whenever possible. Users will be confused if they see messages that do not come from their scripts.

Chrome windows will have their messages shown in the Browser Console. This is useful for addons.

In JavaScript Modules you can do the following:

const {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
console.log("hello world", foo);

Then any console API call will show in your terminal (string-only, if dump() is enabled) and in the Browser Console. Soon you will not need to import Console.jsm - once all patches from bug 965860 land.

Use the Web Console output API

Requires Gecko 29.0(Firefox 29.0 / Thunderbird 29.0 / SeaMonkey 2.26)

First you need to get the WebConsole object instance for the tab you want. For this you need to use the DevTools API.

const {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
function getWebConsole(tab) {
  // |tab| is the XUL tab for the page you want.
  let target = devtools.TargetFactory.forTab(tab);
  let toolbox = gDevTools.getToolbox(target);
  let panel = toolbox ? toolbox.getPanel("webconsole") : null;
  return panel ? panel.hud : null;
}

Once you have the WebConsole object you can add a message like this:

// Get the Messages object which holds the main classes of Messages
// used by the Web Console.
const {Messages} = devtools.require("devtools/webconsole/console-output");
// Create the simplest message we can.
let msg = new Messages.Simple("hello world", {
  category: "js",
  severity: "error",
});
// Add it to the output.
let hud = getWebConsole(myTab);
hud.ui.output.addMessage(msg);

HUD (Heads-Up Display) is the old term we used for referring to the Web Console. We continue to use it for convenience.

Output a message in the Browser Console

If you want to use the Web Console API to output a message to the Browser Console you can do this:

// Get HUDService - the Web/Browser Consoles manager.
const hudservice = devtools.require("devtools/webconsole/hudservice");
// Get the Browser Console - there can only be one.
// This method returns null if the Browser Console is not open.
let browserConsole = hudservice.getBrowserConsole();
// Add a message.
browserConsole.ui.output.addMessage(msgObject);

Messages API

Requires Gecko 29.0(Firefox 29.0 / Thunderbird 29.0 / SeaMonkey 2.26)

In the following table you see a list of message classes you can use. To create a new message you can do:

let msg = new Messages.ConstructorName();
List of Message classes
Constructor Description
BaseMessage() The BaseMessage object is used for all types of messages. Every kind of message should use this object as its base. No functionality is provided by this class.
NavigationMarker(url, time)

The NavigationMarker is used to show a page load event. Arguments:

url (string):
the URL to display.
time (number):
the message timestamp (milliseconds since the Unix epoch).
Simple(msg[, options]) The Simple message is used to show any basic message in the Web Console. See details about the constructor arguments.
Extended(msg[, options]) This class adds support for displaying messages with multiple links inline, objects and custom fragment rendering. See details about the constructor arguments.
JavaScriptEvalOutput(evalResponse[, errorMessage])

This is used by the Web Console to display the JS evaluation results. Arguments:

evalResponse (object):
the JS evaluation response packet received from the server.
errorMessage (optional, string):
evaluation error message.
ConsoleGeneric(packet) Displays a console API method call as described by a consoleAPICall packet received from the server through the remote debugging protocol. This message class is only used for the log(), info(), warn(), error(), exception(), debug(), count() and assert() methods.
ConsoleTrace(packet) Displays a console.trace() method call.

In this section we do not attempt to provide an exhaustive list of all kinds of messages, methods and properties. You should always check console-output.js.

Messages.BaseMessage

The BaseMessage object is used for all types of messages. Every kind of message should use this object as its base. No functionality is provided by this class.

BaseMessage methods and properties
Method/property Description
object output ConsoleOutput object instance.
HTMLElement element The message DOM element. This is available only after the message is rendered.
document document The DOM document where the message exists.
string textContent The text-only message representation.
object init(output[, parent])

Message initialization method. Arguments:

output (object):
the ConsoleOutput instance that will own the message.
parent (optional, object):
parent message. Currently unused.

Return value: the message instance itself.

string getRepeatID() Non-unique ID for this message object used for tracking duplicate messages. Different message kinds can identify themselves based their own criteria.
object render()

Render the message. After this method is invoked the message.element property will point to the DOM element of this message.

Return value: the message instance itself.

void _addLinkCallback(element[, callback]) Add a click callback to the given DOM element. If the callback is not given, a generic anchor click handler is used for opening anchor links in new tabs. Note that the callback is invoked only for single left clicks, not for additional click events (no invocation for right/middle/double clicks). The default event handler is prevented before the callback is invoked.
void destroy() Invoked when the message is removed.

Messages.Simple

The Simple message is used to show any basic message in the Web Console. This class extends BaseMessage. Constructor arguments:

message (string|element|function):
the message to display. If a DOM element is given the element will be inserted into the message body. If a function is given, then the function is invoked with one argument, the message object, and the result is expected to be a DOM element which is then inserted into the message body.
options (optional, object):
options for this message:
category (string):
the category that this message belongs to. Defaults to no category.
severity (string):
severity of the message. Defaults to no severity.
timestamp (number):
date and time when the message was recorded. Defaults to Date.now().
link (string):
if provided, the message will be wrapped in an anchor pointing to the given URL here.
linkCallback (function):
if provided, the message will be wrapped in an anchor. The linkCallback function will be added as click event handler.
location (object):
tells the message source: url, line, column and lineText.
className (string):
additional element class names for styling purposes.
private (boolean):
mark this as a private message.
filterDuplicates (boolean):
true if you want this message to be filtered as a potential duplicate message, false otherwise.

The following table describes a part of the Messages.Simple API:

Messages.Simple methods and properties
Method/property Description
string category Message category.
string severity Message severity.
object|null location Message location: url, line, column and lineText.
number timestamp Time in milliseconds since the Unix epoch.
boolean private Tells if this message was logged in a private window/tab or not.
element _renderBody() Render the message body DOM element.
element _renderRepeatNode() Render the DOM element used to display the number of repeats.
element _renderLocation() Render the message source location DOM element.

The Simple message class should cover most common needs for message output. Extensions can override the _render*() methods to change the styling or behavior of these types of messages, based on any criteria (category, severity, location, etc).

Examples:

const {Messages} = devtools.require("devtools/webconsole/console-output");
// Add a link.
let msg = new Messages.Simple("this is a link", {
  link: "http://example.com/page",
});
hud.output.addMessage(msg);
// Add a link with a callback function.
msg = new Messages.Simple("click me", {
  linkCallback: function() {
    msg.element.style.background = "red";
  },
});
hud.output.addMessage(msg);

Do you find the hud and Messages objects surprising? Learn how to use the Web Console output API.

Messages.Extended

The Extended message class is used whenever the Web Console needs to display object actors or messages with multiple elements inline. This class extends the Simple class. Constructor arguments:

messagePieces (array):
the message to display given as an array of elements. Each array element can be a DOM element, function, ObjectActor, LongString or a string.
options (optional, object):
same options as Messages.Simple, plus the following:
quoteStrings (boolean):
tells if you want strings to be wrapped in quotes or not.
Messages.Extended methods and properties
Method/property Description
element _renderBodyPieceSeparator() Render the separator between the pieces of the message.
element _renderBodyPiece(piece) Render an element from messagePieces array.
element _renderValueGrip(grip[, options])

Render a grip that represents a value received from the server. This method picks the appropriate widget to render the value with. A grip can represent primitive values like strings and booleans, but it can also represent objects. In that case the grip is an ObjectActor grip.

Arguments:

grip:
the value grip received from the server.
options (optional, object):
options for displaying the value:
noStringQuotes (boolean):
tells the renderer to not use quotes around strings.
concise (boolean):
tells the renderer to compactly display the grip. This is typically set to true when the object needs to be displayed in an array preview, or as a property value in object previews, etc.

This method is invoked by:

  • _renderBodyPiece() if the given piece is not a DOM element nor a function.
  • any other message method or widget method that needs to display inline a value grip. For example, previewers for arrays and objects invoke this method to show array elements or object property values.
element _renderObjectActor(actor[, options]) Display an object actor with the appropriate renderer. This method takes the same options as _renderValueGrip(). Learn more about how object preview works.

Example use:

const {Messages} = devtools.require("devtools/webconsole/console-output");
const XHTML_NS = "http://www.w3.org/1999/xhtml";
// Create a DOM element to be added in this message.
let file = hud.output.document.createElementNS(XHTML_NS, "a");
file.textContent = "foobar.js";
file.href = "http://example.com/foobar.js";
// Use a function to display an interactive button.
// This is invoked when the message is rendered.
function button(msg) {
  let clicks = 0;
  let elem = msg.document.createElementNS(XHTML_NS, "button");
  elem.textContent = "click me " + Date.now();
  elem.onclick = function() {
    elem.textContent = "clicks " + (++clicks);
  };
  return elem;
}
// Create the message object.
let pieces = ["there was an error in ", file, button];
let msg = new Messages.Extended(pieces, {
  category: "js",
  severity: "error",
});
// Make sure a click on the file name opens the anchor in a new tab.
msg._addLinkCallback(file);
// Add the new message.
hud.output.addMessage(msg);

Do you find the hud and Messages objects surprising? Learn how to use the Web Console output API.

Add new types of messages

If you want to create complex kinds of messages in the Web Console you may want to create a reusable Message class. Simply extend any of the existing message classes:

const {Messages} = devtools.require("devtools/webconsole/console-output");
const Heritage = require("sdk/core/heritage");
Messages.RandomPrefix = function(msg) {
  this._onClick = this._onClick.bind(this);
  let prefix = this._getPrefix();
  Messages.Simple.call(this, prefix + msg, {
    linkCallback: this._onClick,
  });
};
Messages.RandomPrefix.prototype = Heritage.extend(Messages.Simple.prototype,
{
  _onClick: function() {
    let body = this.element.querySelector(".body");
    let newPrefix = this._getPrefix();
    body.textContent = body.textContent.replace(/^\[[\d.]+\]/, newPrefix);
  },
  _getPrefix: function() {
    return "[" + Math.random() + "] ";
  },
};
// Later you can use this message class.
let msg = new Messages.RandomPrefix("oh la la");
hud.output.addMessage(msg);

That's all you need for custom messages.

Customize previews for objects

The Web Console does not have direct access to the JavaScript objects from the content page. The console only receives a JSON packet that describes the JS object from the server - these are provided by the Object Actor. You can customize the output for such objects in console client code, and you can also extend the server to add anything you need to the JSON packet.

For the purpose of this document we describe here the following object actor properties:

type (string):
the result of typeof for the object.
class (string):
The ECMAScript Class of the object to which the instance refers, as a string. This can be anything from RegExp, Number, Date, Array, Set, Map, Object to Window, HTMLDocument, HTMLBodyElement, etc.
displayString (optional, string):
the server can include a suggested string for displaying the object.
preview (optional, object):
the server can include an object with additional presentational information. For example, for arrays the preview object includes the first 10 elements and the array length. For each object type the preview object can be different.
kind (optional, string):
most commonly preview objects include the kind property to indicate the kind of object preview is expected. Different object classes and types can share the same preview kind. For example arrays, typed arrays, sets, DOMTokenList and other object classes share the ArrayLike preview format.

You should set devtools.debugger.log to true in about:config to see the raw JSON packets sent and received by the debugger server.

String-only output

Requires Gecko 29.0(Firefox 29.0 / Thunderbird 29.0 / SeaMonkey 2.26)

String-only output is used by the object inspector in the Web Console and in the JS Debugger. The web console uses this approach when rich output renderers are not available for any given object.

String-only previews are generated by VariablesView.getString() - see VariablesView.jsm. You can add stringifiers, functions that are given the value grip from the server.

Stringifiers are grouped as follows:

List of VariablesView stringifier categories
Object Description
VariablesView.stringifiers.byType Here you can add a stringifier for the object type you want. For example VariablesView.stringifiers.byType.string is used for displaying strings. In this object you can also find stringifiers for longString and object.
VariablesView.stringifiers.byObjectClass Stringifiers for native object classes. For example, here you can find functions for displaying Number. Date, RegExp and more.
VariablesView.stringifiers.byObjectKind Stringifiers for object previewer "kinds". Here you can see the stringifiers used for ArrayLike, MapLike, DOMNode and more.

Each of the objects in the table above holds a map of types, object classes or object kinds to functions. The function is invoked with the following arguments:

valueGrip:
the value grip as defined in the remote debugger protocol.
options (object, optional):
Options for string display:
concise (boolean):
tells if a concisely formatted string should be generated. This is used when the object preview is part of the preview of a parent object. For example, concise is set to true when previews for array elements are generated.
noStringQuotes (boolean):
tells to not quote strings.
noEllipsis (boolean):
tells to not add an ellipsis after the initial text of a longString.

The stringifiers take the same arguments as VariablesView.getString(). This is the description of the getString() arguments.

Return value: each stringifier is expected to return the string to be displayed. If the stringifier returns null, then a more generic stringifier is used. You can use this return value to explicitly notify you cannot provide a preview for the given value grip.

Here is an example stringifier used for displaying functions:

const {VariablesView} = Cu.import("resource:///modules/devtools/VariablesView.jsm", {});
VariablesView.stringifiers.byObjectClass.Function = function(grip, {concise}) {
  let name = grip.userDisplayName || grip.displayName || grip.name || "";
  name = VariablesView.getString(name, { noStringQuotes: true });
  let params = grip.parameterNames || "";
  if (!concise) {
    return "function " + name + "(" + params + ")";
  }
  return (name || "function ") + "(" + params + ")";
};

Rich output

Requires Gecko 30.0(Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27)

The Web Console Message objects use Widgets to display object actors, stack frames and other well-contained output elements that can be reused between different messages.

Here is an example Message class that uses the Stacktrace widget:

// Get the Widgets object - this is where we can find all of the
// console output widgets.
const {Messages, Widgets} = devtools.require("devtools/webconsole/console-output");
const Heritage = require("sdk/core/heritage");
// The stacktrace argument is expected to use the same format as that
// of the console.trace() calls provided over the remote debugging protocol.
Messages.StalkyMuch = function(stacktrace) {
  Messages.BaseMessage.call(this);
  this._stacktrace = stacktrace;
};
Messages.StalkyMuch.prototype = Heritage.extend(Messages.BaseMessage.prototype,
{
  render: function() {
    let stack = new Widgets.Stacktrace(this, this._stacktrace).render();
    let render = Messages.BaseMessage.prototype.render.bind(this);
    render().element.appendChild(stack.element);
    return this;
  },
};
// Later you can do this:
let msg = new Messages.StalkyMuch(stackFromServer);
hud.output.addMessage(msg);

Tip 1: You can see the actual Messages.ConsoleTrace and Widgets.Stacktrace implementations in console-output.js.

Tip 2: Do you find the hud and Messages objects surprising? Learn how to use the Web Console output API.

Widgets API

The table below is an overview of the available widgets you can use.

List of Widget classes
Constructor Description
BaseWidget(message) This is the base class for all widgets. The message argument needs to be the parent message instance.
MessageTimestamp(message, timestamp) Widget used to display the timestamp for each message. The timestamp argument must be the number of milliseconds since the UNIX epoch.
JSObject(message, objectActor[, options]) This is used to display object actors for which we do not have any more specific object renderer. This is also the base class for all of the ObjectRenderers.
LongString(message, longStringActor) This is used to display long strings.
Stacktrace(message, stacktrace) This is used to display stack traces.

The BaseWidget API:

The BaseWidget methods and properties
Method/property Description
object message The owning message object.
element element The DOM element of the widget. This is available after render() is invoked.
document document The DOM document where the widget is rendered.
object output The ConsoleOutput instance that owns this widget.
object render()

Renders the widget's DOM element. This method must make the this.element property available for use.

Return value: the widget instance itself.

void destroy() Destroy the widget instance - remove any event listeners to avoid memory leaks.
element el(tagName[, attributes[, textContent]])
element el(tagName[, textContent])

Helper for creating DOM elements. Arguments:

tagName (string):
the tag name for the new DOM element. Optionally you can add #id at the end and .class.names.you.need, these will be added as proper class names and the element ID will also be set.
attributes (optional, object):
a map of attribute names and values to set for the new DOM element.
textContent (optional, string):
the text content to add to the element.

Usage:

// Create element "tag" with ID "id"
// and two class names: "class" and "names".
this.el("tag#id.class.names");
// A <span attr1=value1> element
this.el("span", { attr1: "value1"}) 
// A <p attr1=value1>text content<p> element.
this.el("p", { attr1: "value1", ... }, "text content");
// A <p>text content</p> element.
this.el("p", "text content");

As an example, here is the MessageTimestamp widget from console-output.js:

const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const {Widgets} = devtools.require("devtools/webconsole/console-output");
const Heritage = require("sdk/core/heritage");
const WebConsoleUtils = require("devtools/toolkit/webconsole/utils").Utils;
const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
const l10n = new WebConsoleUtils.l10n(STRINGS_URI);
const XHTML_NS = "http://www.w3.org/1999/xhtml";
Widgets.MessageTimestamp = function(message, timestamp) {
  Widgets.BaseWidget.call(this, message);
  this.timestamp = timestamp;
};
Widgets.MessageTimestamp.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
{
  timestamp: 0,
  render: function() {
    if (this.element) {
      return this;
    }
    this.element = this.document.createElementNS(XHTML_NS, "span");
    this.element.className = "timestamp devtools-monospace";
    this.element.textContent = l10n.timestampString(this.timestamp) + " ";
    return this;
  },
});

Object renderers API

For rich output of objects we use widgets and we keep them grouped in the Widgets.ObjectRenderers object. Most often Widgets.JSObject is the base class of all these object renderers, but that is not a requirement - it's just for convenience.

List of ObjectRenderer categories
Object Description
Widgets.ObjectRenderers.byClass Widgets for specific object classes. Here you can find the widgets used to display objects like Date and Function.
Widgets.ObjectRenderers.byKind Widgets for specific preview kinds. Here you can find the widgets used to display ArrayLike, MapLike, DOMEvent, DOMNode objects and more.

Each widget constructor from ObjectRenderers is invoked with the same arguments as the _renderObjectActor(objectActor[, options]) method from Messages.Extended:

objectActor (object):
the ObjectActor grip received from the server.
options (optional, object):
options for displaying the value:
noStringQuotes (boolean):
tells the renderer to not use quotes around strings.
concise (boolean):
tells the renderer to compactly display the grip. This is typically set to true when the object needs to be displayed in an array preview, or as a property value in object previews, etc.

The ObjectRenderer widgets can also have a static method named canRender(objectActor). This is invoked, if available, before the widget is constructed. This method is given only the ObjectActor grip. If true is returned, the widget is used to display the object, otherwise a more generic renderer is used.

Add an object renderer

We have a helper function for adding object renderers, this is Widgets.ObjectRenders.add(descriptor). Properties:

byClass (optional, string):
this renderer will be used for the given object class.
byKind (optional, string):
this renderer will be used for the given object kind. One of the byClass or byKind properties must be provided.
extends (optional, object):
the renderer object extends the given object. Default: Widgets.JSObject.
boolean canRender(objectActor) (optional, function):
this method is invoked when a candidate object needs to be displayed. The function is invoked as a static method, as such, none of the properties of the renderer object will be available. One argument is passed to the function: the object actor grip received from the server. If the method returns true, then this renderer is used for displaying the object, otherwise not.
void initialize(ownerMessage, objectActor[, options]) (optional, function):
the constructor of the renderer widget. This function is invoked with the following arguments:
  • the owner message object instance,
  • the object actor grip to display, and
  • an options object. Read the previous section for details about this object.
void render() (required, function):
the method that displays the given object actor. This method must make the this.element property available for use, as explained in the previous section.

Any other methods and properties in the descriptor will be set on the prototype of the new Widget object that is added to Widgets.ObjectRenderers.

As an example here is the object renderer used for Date:

const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const {Widgets} = devtools.require("devtools/webconsole/console-output");
Widgets.ObjectRenderers.add({
  byClass: "Date",
  render: function() {
    let {preview} = this.objectActor;
    this.element = this.el("span.class-" + this.objectActor.class);
    let anchorText = this.objectActor.class;
    let anchorClass = "cm-variable";
    if ("timestamp" in preview && typeof preview.timestamp != "number") {
      anchorText = new Date(preview.timestamp).toString(); // invalid date
      anchorClass = "";
    }
    this._anchor(anchorText, { className: anchorClass });
    if (!("timestamp" in preview) || typeof preview.timestamp != "number") {
      return;
    }
    this._text(" ");
    let elem = this.el("span.cm-string-2", new Date(preview.timestamp).toISOString());
    this.element.appendChild(elem);
  },
});

Add object previewers to the server

Requires Gecko 29.0(Firefox 29.0 / Thunderbird 29.0 / SeaMonkey 2.26)
The debugger server sends Object Actor grips to the client. The JSON packets are generated in script.js. To add more details to object actors you need to add your function to the DebuggerServer.ObjectActorPreviewers object. This object maps native object class names to arrays of functions, see the class property of Debugger.Object.

 

When the debugger server is asked for an object actor with class k it goes through each function of DebuggerServer.ObjectActorPreviewers[k]. If the function returns true the search for an object previewer stops. If none of the functions return true then the search starts again in the generic "Object" class, the DebuggerServer.ObjectActorPreviewers.Object array. Here you can add functions that add preview details based on different criteria than the object class.

Your function is invoked with the following arguments:

objectActor (object):
the ObjectActor instance for which an object preview is needed.
grip (object):
the actor grip, the object which will be sent to the client through the remote debugging protocol.
rawObject (optional, object):
this is the raw JavaScript object from the content page without any content script changes. For example, given a DOM element you will not find properties added by the content scripts. This argument is optional because we cannot always provide an unmodified JS object.

The previewer function needs to modify the grip object to add any additional properties needed in the client. The convention is to use a grip.preview object where you add anything you see fit. Do note that these previewers are invoked often - please exercise care and pay attention to performance.

Things to note:

  • the objectActor argument holds the obj property which is a Debugger.Object which wraps the JS object we work with.
  • you will need to use the aforementioned Debugger.Object API to read properties from obj and/or execute functions in the content page.
  • the rawObj argument is subject to XPConnect wrapping.
  • before you can send an object to the client you need to: (1) make it a Debuggee value, using the makeDebuggeeValue() method of Debugger.Object (if it is not already a debugger object), then (2) call the createValueGrip() method of the ThreadActor - it is available through objectActor.threadActor.
  • rule of thumb: never trust that the object you work with has the expected properties. This is why you should always use the Debugger API to work with such objects.
  • you will most-likely want to look at script.js.

Examples from the codebase:

// Import the DebuggerServer.
const {DebuggerServer} = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
const {DevToolsUtils} = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
// Add the previewer for CSSStyleRules in the generic Object class.
// Use unshift() to add elements in front of other previewers.
DebuggerServer.ObjectActorPreviewers.Object
  .unshift(function CSSStyleRule({obj, threadActor}, aGrip, aRawObj) {
    // Check if this is the type of object we can provide previews for.
    if (!aRawObj || !(aRawObj instanceof Ci.nsIDOMCSSStyleRule)) {
      return false;
    }
    aGrip.preview = {
      kind: "ObjectWithText",
      text: threadActor.createValueGrip(aRawObj.selectorText),
    };
    return true;
  },
  // The previewer we use for Errors.
  function Error({obj, threadActor}, aGrip) {
    switch (obj.class) {
      case "Error":
      case "EvalError":
      case "RangeError":
      case "ReferenceError":
      case "SyntaxError":
      case "TypeError":
      case "URIError":
        let name = DevToolsUtils.getProperty(obj, "name");
        let msg = DevToolsUtils.getProperty(obj, "message");
        let stack = DevToolsUtils.getProperty(obj, "stack");
        let fileName = DevToolsUtils.getProperty(obj, "fileName");
        let lineNumber = DevToolsUtils.getProperty(obj, "lineNumber");
        let columnNumber = DevToolsUtils.getProperty(obj, "columnNumber");
        aGrip.preview = {
          kind: "Error",
          name: threadActor.createValueGrip(name),
          message: threadActor.createValueGrip(msg),
          stack: threadActor.createValueGrip(stack),
          fileName: threadActor.createValueGrip(fileName),
          lineNumber: threadActor.createValueGrip(lineNumber),
          columnNumber: threadActor.createValueGrip(columnNumber),
        };
        return true;
      default:
        return false;
    }
  });

This is all! Thank you for reading and extending our developer tools! Do you have questions? Contact the developer tools team.

Document Tags and Contributors

 Contributors to this page: wbamberg, Lexx3000, cold sun, mihaisucan, Delapouite
 Last updated by: wbamberg,