mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 08:12:05 +00:00
Bug 993029: Create an add-on console actor that will be displayed in the console tab of the add-on debugger. r=msucan, r=Unfocused, r=past
This commit is contained in:
parent
eef1c9e982
commit
7f17ca3889
@ -1,21 +1,32 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const { interfaces: Ci, classes: Cc } = Components;
|
||||
const { interfaces: Ci, classes: Cc, utils: Cu } = Components;
|
||||
|
||||
function notify() {
|
||||
// Log objects so makeDebuggeeValue can get the global to use
|
||||
console.log({ msg: "Hello again" });
|
||||
}
|
||||
|
||||
function startup(aParams, aReason) {
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
let res = Services.io.getProtocolHandler("resource")
|
||||
.QueryInterface(Ci.nsIResProtocolHandler);
|
||||
res.setSubstitution("browser_dbg_addon4", aParams.resourceURI);
|
||||
|
||||
// Load a JS module
|
||||
Components.utils.import("resource://browser_dbg_addon4/test.jsm");
|
||||
Cu.import("resource://browser_dbg_addon4/test.jsm");
|
||||
// Log objects so makeDebuggeeValue can get the global to use
|
||||
console.log({ msg: "Hello from the test add-on" });
|
||||
|
||||
Services.obs.addObserver(notify, "addon-test-ping", false);
|
||||
}
|
||||
|
||||
function shutdown(aParams, aReason) {
|
||||
Services.obs.removeObserver(notify, "addon-test-ping");
|
||||
|
||||
// Unload the JS module
|
||||
Components.utils.unload("resource://browser_dbg_addon4/test.jsm");
|
||||
Cu.unload("resource://browser_dbg_addon4/test.jsm");
|
||||
|
||||
let res = Services.io.getProtocolHandler("resource")
|
||||
.QueryInterface(Ci.nsIResProtocolHandler);
|
||||
|
Binary file not shown.
@ -90,6 +90,7 @@ support-files =
|
||||
[browser_dbg_addon-modules.js]
|
||||
[browser_dbg_addon-modules-unpacked.js]
|
||||
[browser_dbg_addon-panels.js]
|
||||
[browser_dbg_addon-console.js]
|
||||
[browser_dbg_auto-pretty-print-01.js]
|
||||
[browser_dbg_auto-pretty-print-02.js]
|
||||
[browser_dbg_bfcache.js]
|
||||
|
44
browser/devtools/debugger/test/browser_dbg_addon-console.js
Normal file
44
browser/devtools/debugger/test/browser_dbg_addon-console.js
Normal file
@ -0,0 +1,44 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that the we can see console messages from the add-on
|
||||
|
||||
const ADDON_URL = EXAMPLE_URL + "addon4.xpi";
|
||||
|
||||
function getCachedMessages(webConsole) {
|
||||
let deferred = promise.defer();
|
||||
webConsole.getCachedMessages(["ConsoleAPI"], (aResponse) => {
|
||||
if (aResponse.error) {
|
||||
deferred.reject(aResponse.error);
|
||||
return;
|
||||
}
|
||||
deferred.resolve(aResponse.messages);
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function test() {
|
||||
Task.spawn(function () {
|
||||
let addon = yield addAddon(ADDON_URL);
|
||||
let addonDebugger = yield initAddonDebugger(ADDON_URL);
|
||||
|
||||
let webConsole = addonDebugger.webConsole;
|
||||
let messages = yield getCachedMessages(webConsole);
|
||||
is(messages.length, 1, "Should be one cached message");
|
||||
is(messages[0].arguments[0].type, "object", "Should have logged an object");
|
||||
is(messages[0].arguments[0].preview.ownProperties.msg.value, "Hello from the test add-on", "Should have got the right message");
|
||||
|
||||
let consolePromise = addonDebugger.once("console");
|
||||
|
||||
console.log("Bad message");
|
||||
Services.obs.notifyObservers(null, "addon-test-ping", "");
|
||||
|
||||
let messageGrip = yield consolePromise;
|
||||
is(messageGrip.arguments[0].type, "object", "Should have logged an object");
|
||||
is(messageGrip.arguments[0].preview.ownProperties.msg.value, "Hello again", "Should have got the right message");
|
||||
|
||||
yield addonDebugger.destroy();
|
||||
yield removeAddon(addon);
|
||||
finish();
|
||||
});
|
||||
}
|
@ -26,9 +26,9 @@ function test() {
|
||||
});
|
||||
|
||||
let tabs = addonDebugger.frame.contentDocument.getElementById("toolbox-tabs").children;
|
||||
let expectedTabs = ["options", "jsdebugger"];
|
||||
let expectedTabs = ["options", "webconsole", "jsdebugger", "scratchpad"];
|
||||
|
||||
is(tabs.length, 2, "displaying only 2 tabs in addon debugger");
|
||||
is(tabs.length, expectedTabs.length, "displaying only " + expectedTabs.length + " tabs in addon debugger");
|
||||
Array.forEach(tabs, (tab, i) => {
|
||||
let toolName = expectedTabs[i];
|
||||
is(tab.getAttribute("toolid"), toolName, "displaying " + toolName);
|
||||
|
@ -22,6 +22,7 @@ let { BrowserToolboxProcess } = Cu.import("resource:///modules/devtools/ToolboxP
|
||||
let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
|
||||
let { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
|
||||
let { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
|
||||
let EventEmitter = require("devtools/toolkit/event-emitter");
|
||||
const { promiseInvoke } = require("devtools/async-utils");
|
||||
let TargetFactory = devtools.TargetFactory;
|
||||
let Toolbox = devtools.Toolbox;
|
||||
@ -522,6 +523,8 @@ function initAddonDebugger(aUrl) {
|
||||
|
||||
function AddonDebugger() {
|
||||
this._onMessage = this._onMessage.bind(this);
|
||||
this._onConsoleAPICall = this._onConsoleAPICall.bind(this);
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
AddonDebugger.prototype = {
|
||||
@ -548,7 +551,11 @@ AddonDebugger.prototype = {
|
||||
let addonActor = yield getAddonActorForUrl(this.client, aUrl);
|
||||
|
||||
let targetOptions = {
|
||||
form: { addonActor: addonActor.actor, title: addonActor.name },
|
||||
form: {
|
||||
addonActor: addonActor.actor,
|
||||
consoleActor: addonActor.consoleActor,
|
||||
title: addonActor.name
|
||||
},
|
||||
client: this.client,
|
||||
chrome: true
|
||||
};
|
||||
@ -557,8 +564,8 @@ AddonDebugger.prototype = {
|
||||
customIframe: this.frame
|
||||
};
|
||||
|
||||
let target = devtools.TargetFactory.forTab(targetOptions);
|
||||
let toolbox = yield gDevTools.showToolbox(target, "jsdebugger", devtools.Toolbox.HostType.CUSTOM, toolboxOptions);
|
||||
this.target = devtools.TargetFactory.forTab(targetOptions);
|
||||
let toolbox = yield gDevTools.showToolbox(this.target, "jsdebugger", devtools.Toolbox.HostType.CUSTOM, toolboxOptions);
|
||||
|
||||
info("Addon debugger panel shown successfully.");
|
||||
|
||||
@ -567,6 +574,7 @@ AddonDebugger.prototype = {
|
||||
// Wait for the initial resume...
|
||||
yield waitForClientEvents(this.debuggerPanel, "resumed");
|
||||
yield prepareDebugger(this.debuggerPanel);
|
||||
yield this._attachConsole();
|
||||
}),
|
||||
|
||||
destroy: Task.async(function*() {
|
||||
@ -578,6 +586,27 @@ AddonDebugger.prototype = {
|
||||
window.removeEventListener("message", this._onMessage);
|
||||
}),
|
||||
|
||||
_attachConsole: function() {
|
||||
let deferred = promise.defer();
|
||||
this.client.attachConsole(this.target.form.consoleActor, ["ConsoleAPI"], (aResponse, aWebConsoleClient) => {
|
||||
if (aResponse.error) {
|
||||
deferred.reject(aResponse);
|
||||
}
|
||||
else {
|
||||
this.webConsole = aWebConsoleClient;
|
||||
this.client.addListener("consoleAPICall", this._onConsoleAPICall);
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
_onConsoleAPICall: function(aType, aPacket) {
|
||||
if (aPacket.from != this.webConsole.actor)
|
||||
return;
|
||||
this.emit("console", aPacket.message);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a list of the groups and sources in the UI. The returned array
|
||||
* contains objects for each group with properties name and sources. The
|
||||
|
@ -37,7 +37,11 @@ function connect() {
|
||||
if (addonID) {
|
||||
gClient.listAddons(({addons}) => {
|
||||
let addonActor = addons.filter(addon => addon.id === addonID).pop();
|
||||
openToolbox({ addonActor: addonActor.actor, title: addonActor.name });
|
||||
openToolbox({
|
||||
addonActor: addonActor.actor,
|
||||
consoleActor: addonActor.consoleActor,
|
||||
title: addonActor.name
|
||||
});
|
||||
});
|
||||
} else {
|
||||
gClient.listTabs(openToolbox);
|
||||
|
@ -105,7 +105,7 @@ Tools.webConsole = {
|
||||
},
|
||||
|
||||
isTargetSupported: function(target) {
|
||||
return !target.isAddon;
|
||||
return true;
|
||||
},
|
||||
build: function(iframeWindow, toolbox) {
|
||||
let panel = new WebConsolePanel(iframeWindow, toolbox);
|
||||
@ -314,7 +314,7 @@ Tools.scratchpad = {
|
||||
commands: "devtools/scratchpad/scratchpad-commands",
|
||||
|
||||
isTargetSupported: function(target) {
|
||||
return !target.isAddon && target.isRemote;
|
||||
return target.isRemote;
|
||||
},
|
||||
|
||||
build: function(iframeWindow, toolbox) {
|
||||
|
@ -526,6 +526,7 @@ function sendConsoleAPIMessage(aConsole, aLevel, aFrame, aArgs, aOptions = {})
|
||||
let consoleEvent = {
|
||||
ID: "jsm",
|
||||
innerID: aConsole.innerID || aFrame.filename,
|
||||
consoleID: aConsole.consoleID,
|
||||
level: aLevel,
|
||||
filename: aFrame.filename,
|
||||
lineNumber: aFrame.lineNumber,
|
||||
@ -582,6 +583,8 @@ function sendConsoleAPIMessage(aConsole, aLevel, aFrame, aArgs, aOptions = {})
|
||||
* written to stdout
|
||||
* - innerID {string}: An ID representing the source of the message.
|
||||
* Normally the inner ID of a DOM window.
|
||||
* - consoleID {string} : String identified for the console, this will
|
||||
* be passed through the console notifications
|
||||
* @return {object}
|
||||
* A console API instance object
|
||||
*/
|
||||
@ -592,6 +595,7 @@ function ConsoleAPI(aConsoleOptions = {}) {
|
||||
this.prefix = aConsoleOptions.prefix || "";
|
||||
this.maxLogLevel = aConsoleOptions.maxLogLevel || "all";
|
||||
this.innerID = aConsoleOptions.innerID || null;
|
||||
this.consoleID = aConsoleOptions.consoleID || "";
|
||||
|
||||
// Bind all the functions to this object.
|
||||
for (let prop in this) {
|
||||
|
@ -318,3 +318,23 @@ exports.dbg_assert = function dbg_assert(cond, e) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Utility function for updating an object with the properties of another
|
||||
* object.
|
||||
*
|
||||
* @param aTarget Object
|
||||
* The object being updated.
|
||||
* @param aNewAttrs Object
|
||||
* The new attributes being set on the target.
|
||||
*/
|
||||
exports.update = function update(aTarget, aNewAttrs) {
|
||||
for (let key in aNewAttrs) {
|
||||
let desc = Object.getOwnPropertyDescriptor(aNewAttrs, key);
|
||||
|
||||
if (desc) {
|
||||
Object.defineProperty(aTarget, key, desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ const { Cc, Ci, Cu, components } = require("chrome");
|
||||
const { ActorPool } = require("devtools/server/actors/common");
|
||||
const { DebuggerServer } = require("devtools/server/main");
|
||||
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
const { dbg_assert, dumpn } = DevToolsUtils;
|
||||
const { dbg_assert, dumpn, update } = DevToolsUtils;
|
||||
const { SourceMapConsumer, SourceMapGenerator } = require("source-map");
|
||||
const { all, defer, resolve } = promise;
|
||||
|
||||
@ -5357,25 +5357,6 @@ function getFrameLocation(aFrame) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function for updating an object with the properties of another
|
||||
* object.
|
||||
*
|
||||
* @param aTarget Object
|
||||
* The object being updated.
|
||||
* @param aNewAttrs Object
|
||||
* The new attributes being set on the target.
|
||||
*/
|
||||
function update(aTarget, aNewAttrs) {
|
||||
for (let key in aNewAttrs) {
|
||||
let desc = Object.getOwnPropertyDescriptor(aNewAttrs, key);
|
||||
|
||||
if (desc) {
|
||||
Object.defineProperty(aTarget, key, desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if its argument is not null.
|
||||
*/
|
||||
|
@ -1222,7 +1222,8 @@ BrowserAddonList.prototype.onUninstalled = function (aAddon) {
|
||||
function BrowserAddonActor(aConnection, aAddon) {
|
||||
this.conn = aConnection;
|
||||
this._addon = aAddon;
|
||||
this._contextPool = null;
|
||||
this._contextPool = new ActorPool(this.conn);
|
||||
this.conn.addActorPool(this._contextPool);
|
||||
this._threadActor = null;
|
||||
this._global = null;
|
||||
AddonManager.addAddonListener(this);
|
||||
@ -1253,6 +1254,11 @@ BrowserAddonActor.prototype = {
|
||||
|
||||
form: function BAA_form() {
|
||||
dbg_assert(this.actorID, "addon should have an actorID.");
|
||||
if (!this._consoleActor) {
|
||||
let {AddonConsoleActor} = require("devtools/server/actors/webconsole");
|
||||
this._consoleActor = new AddonConsoleActor(this._addon, this.conn, this);
|
||||
this._contextPool.addActor(this._consoleActor);
|
||||
}
|
||||
|
||||
return {
|
||||
actor: this.actorID,
|
||||
@ -1260,10 +1266,14 @@ BrowserAddonActor.prototype = {
|
||||
name: this._addon.name,
|
||||
url: this.url,
|
||||
debuggable: this._addon.isDebuggable,
|
||||
consoleActor: this._consoleActor.actorID,
|
||||
};
|
||||
},
|
||||
|
||||
disconnect: function BAA_disconnect() {
|
||||
this.conn.removeActorPool(this._contextPool);
|
||||
this._contextPool = null;
|
||||
this._consoleActor = null;
|
||||
this._addon = null;
|
||||
this._global = null;
|
||||
AddonManager.removeAddonListener(this);
|
||||
@ -1302,9 +1312,6 @@ BrowserAddonActor.prototype = {
|
||||
}
|
||||
|
||||
if (!this.attached) {
|
||||
this._contextPool = new ActorPool(this.conn);
|
||||
this.conn.addActorPool(this._contextPool);
|
||||
|
||||
this._threadActor = new AddonThreadActor(this.conn, this,
|
||||
this._addon.id);
|
||||
this._contextPool.addActor(this._threadActor);
|
||||
@ -1318,8 +1325,7 @@ BrowserAddonActor.prototype = {
|
||||
return { error: "wrongState" };
|
||||
}
|
||||
|
||||
this.conn.removeActorPool(this._contextPool);
|
||||
this._contextPool = null;
|
||||
this._contextPool.remoteActor(this._threadActor);
|
||||
|
||||
this._threadActor = null;
|
||||
|
||||
|
@ -10,6 +10,7 @@ const { Cc, Ci, Cu } = require("chrome");
|
||||
const Debugger = require("Debugger");
|
||||
const { DebuggerServer, ActorPool } = require("devtools/server/main");
|
||||
const { EnvironmentActor, LongStringActor, ObjectActor, ThreadActor } = require("devtools/server/actors/script");
|
||||
const { update } = require("devtools/toolkit/DevToolsUtils");
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
@ -46,7 +47,6 @@ for (let name of ["WebConsoleUtils", "ConsoleServiceListener",
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The WebConsoleActor implements capabilities needed for the Web Console
|
||||
* feature.
|
||||
@ -1315,6 +1315,7 @@ WebConsoleActor.prototype =
|
||||
delete result.wrappedJSObject;
|
||||
delete result.ID;
|
||||
delete result.innerID;
|
||||
delete result.consoleID;
|
||||
|
||||
result.arguments = Array.map(aMessage.arguments || [], (aObj) => {
|
||||
let dbgObj = this.makeDebuggeeValue(aObj, true);
|
||||
@ -1399,6 +1400,91 @@ WebConsoleActor.prototype.requestTypes =
|
||||
sendHTTPRequest: WebConsoleActor.prototype.onSendHTTPRequest
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The AddonConsoleActor implements capabilities needed for the add-on web
|
||||
* console feature.
|
||||
*
|
||||
* @constructor
|
||||
* @param object aAddon
|
||||
* The add-on that this console watches.
|
||||
* @param object aConnection
|
||||
* The connection to the client, DebuggerServerConnection.
|
||||
* @param object aParentActor
|
||||
* The parent BrowserAddonActor actor.
|
||||
*/
|
||||
function AddonConsoleActor(aAddon, aConnection, aParentActor)
|
||||
{
|
||||
this.addon = aAddon;
|
||||
WebConsoleActor.call(this, aConnection, aParentActor);
|
||||
}
|
||||
|
||||
AddonConsoleActor.prototype = Object.create(WebConsoleActor.prototype);
|
||||
|
||||
update(AddonConsoleActor.prototype, {
|
||||
constructor: AddonConsoleActor,
|
||||
|
||||
actorPrefix: "addonConsole",
|
||||
|
||||
/**
|
||||
* The add-on that this console watches.
|
||||
*/
|
||||
addon: null,
|
||||
|
||||
/**
|
||||
* The main add-on JS global
|
||||
*/
|
||||
get window() {
|
||||
return this.parentActor.global;
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy the current AddonConsoleActor instance.
|
||||
*/
|
||||
disconnect: function ACA_disconnect()
|
||||
{
|
||||
WebConsoleActor.prototype.disconnect.call(this);
|
||||
this.addon = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler for the "startListeners" request.
|
||||
*
|
||||
* @param object aRequest
|
||||
* The JSON request object received from the Web Console client.
|
||||
* @return object
|
||||
* The response object which holds the startedListeners array.
|
||||
*/
|
||||
onStartListeners: function ACA_onStartListeners(aRequest)
|
||||
{
|
||||
let startedListeners = [];
|
||||
|
||||
while (aRequest.listeners.length > 0) {
|
||||
let listener = aRequest.listeners.shift();
|
||||
switch (listener) {
|
||||
case "ConsoleAPI":
|
||||
if (!this.consoleAPIListener) {
|
||||
this.consoleAPIListener =
|
||||
new ConsoleAPIListener(null, this, "addon/" + this.addon.id);
|
||||
this.consoleAPIListener.init();
|
||||
}
|
||||
startedListeners.push(listener);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {
|
||||
startedListeners: startedListeners,
|
||||
nativeConsoleAPI: true,
|
||||
traits: this.traits,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
AddonConsoleActor.prototype.requestTypes = Object.create(WebConsoleActor.prototype.requestTypes);
|
||||
AddonConsoleActor.prototype.requestTypes.startListeners = AddonConsoleActor.prototype.onStartListeners;
|
||||
|
||||
exports.AddonConsoleActor = AddonConsoleActor;
|
||||
|
||||
/**
|
||||
* Creates an actor for a network event.
|
||||
*
|
||||
|
88
toolkit/devtools/tests/unit/test_consoleID.js
Normal file
88
toolkit/devtools/tests/unit/test_consoleID.js
Normal file
@ -0,0 +1,88 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const { console, ConsoleAPI } = Cu.import("resource://gre/modules/devtools/Console.jsm");
|
||||
|
||||
const { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
|
||||
const { ConsoleAPIListener } = require("devtools/toolkit/webconsole/utils");
|
||||
|
||||
var seenMessages = 0;
|
||||
var seenTypes = 0;
|
||||
|
||||
var callback = {
|
||||
onConsoleAPICall: function(aMessage) {
|
||||
switch (aMessage.consoleID) {
|
||||
case "foo":
|
||||
do_check_eq(aMessage.level, "warn");
|
||||
do_check_eq(aMessage.arguments[0], "Warning from foo");
|
||||
seenTypes |= 1;
|
||||
break;
|
||||
case "bar":
|
||||
do_check_eq(aMessage.level, "error");
|
||||
do_check_eq(aMessage.arguments[0], "Error from bar");
|
||||
seenTypes |= 2;
|
||||
break;
|
||||
default:
|
||||
do_check_eq(aMessage.level, "log");
|
||||
do_check_eq(aMessage.arguments[0], "Hello from default console");
|
||||
seenTypes |= 4;
|
||||
break;
|
||||
}
|
||||
seenMessages++;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Tests that the consoleID property of the ConsoleAPI options gets passed
|
||||
* through to console messages.
|
||||
*/
|
||||
function run_test() {
|
||||
let console1 = new ConsoleAPI({
|
||||
consoleID: "foo"
|
||||
});
|
||||
let console2 = new ConsoleAPI({
|
||||
consoleID: "bar"
|
||||
});
|
||||
|
||||
console.log("Hello from default console");
|
||||
console1.warn("Warning from foo");
|
||||
console2.error("Error from bar");
|
||||
|
||||
let listener = new ConsoleAPIListener(null, callback);
|
||||
listener.init();
|
||||
let messages = listener.getCachedMessages();
|
||||
|
||||
seenTypes = 0;
|
||||
seenMessages = 0;
|
||||
messages.forEach(callback.onConsoleAPICall);
|
||||
do_check_eq(seenMessages, 3);
|
||||
do_check_eq(seenTypes, 7);
|
||||
|
||||
seenTypes = 0;
|
||||
seenMessages = 0;
|
||||
console.log("Hello from default console");
|
||||
console1.warn("Warning from foo");
|
||||
console2.error("Error from bar");
|
||||
do_check_eq(seenMessages, 3);
|
||||
do_check_eq(seenTypes, 7);
|
||||
|
||||
listener.destroy();
|
||||
|
||||
listener = new ConsoleAPIListener(null, callback, "foo");
|
||||
listener.init();
|
||||
let messages = listener.getCachedMessages();
|
||||
|
||||
seenTypes = 0;
|
||||
seenMessages = 0;
|
||||
messages.forEach(callback.onConsoleAPICall);
|
||||
do_check_eq(seenMessages, 2);
|
||||
do_check_eq(seenTypes, 1);
|
||||
|
||||
seenTypes = 0;
|
||||
seenMessages = 0;
|
||||
console.log("Hello from default console");
|
||||
console1.warn("Warning from foo");
|
||||
console2.error("Error from bar");
|
||||
do_check_eq(seenMessages, 1);
|
||||
do_check_eq(seenTypes, 1);
|
||||
}
|
@ -7,4 +7,5 @@ tail =
|
||||
[test_safeErrorString.js]
|
||||
[test_defineLazyPrototypeGetter.js]
|
||||
[test_async-utils.js]
|
||||
[test_consoleID.js]
|
||||
[test_require_lazy.js]
|
@ -1291,11 +1291,14 @@ ConsoleServiceListener.prototype =
|
||||
* - onConsoleAPICall(). This method is invoked with one argument, the
|
||||
* Console API message that comes from the observer service, whenever
|
||||
* a relevant console API call is received.
|
||||
* @param string aConsoleID
|
||||
* Options - The consoleID that this listener should listen to
|
||||
*/
|
||||
function ConsoleAPIListener(aWindow, aOwner)
|
||||
function ConsoleAPIListener(aWindow, aOwner, aConsoleID)
|
||||
{
|
||||
this.window = aWindow;
|
||||
this.owner = aOwner;
|
||||
this.consoleID = aConsoleID;
|
||||
if (this.window) {
|
||||
this.layoutHelpers = new LayoutHelpers(this.window);
|
||||
}
|
||||
@ -1322,6 +1325,12 @@ ConsoleAPIListener.prototype =
|
||||
*/
|
||||
owner: null,
|
||||
|
||||
/**
|
||||
* The consoleID that we listen for. If not null then only messages from this
|
||||
* console will be returned.
|
||||
*/
|
||||
consoleID: null,
|
||||
|
||||
/**
|
||||
* Initialize the window.console API observer.
|
||||
*/
|
||||
@ -1355,6 +1364,9 @@ ConsoleAPIListener.prototype =
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this.consoleID && apiMessage.consoleID != this.consoleID) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.owner.onConsoleAPICall(apiMessage);
|
||||
},
|
||||
@ -1385,6 +1397,10 @@ ConsoleAPIListener.prototype =
|
||||
});
|
||||
}
|
||||
|
||||
if (this.consoleID) {
|
||||
messages = messages.filter((m) => m.consoleID == this.consoleID);
|
||||
}
|
||||
|
||||
if (aIncludePrivate) {
|
||||
return messages;
|
||||
}
|
||||
|
@ -37,6 +37,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "OS",
|
||||
"resource://gre/modules/osfile.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "BrowserToolboxProcess",
|
||||
"resource:///modules/devtools/ToolboxProcess.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ConsoleAPI",
|
||||
"resource://gre/modules/devtools/Console.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this,
|
||||
"ChromeRegistry",
|
||||
@ -4126,6 +4128,9 @@ this.XPIProvider = {
|
||||
for (let feature of features)
|
||||
this.bootstrapScopes[aId][feature] = gGlobalScope[feature];
|
||||
|
||||
// Define a console for the add-on
|
||||
this.bootstrapScopes[aId]["console"] = new ConsoleAPI({ consoleID: "addon/" + aId });
|
||||
|
||||
// As we don't want our caller to control the JS version used for the
|
||||
// bootstrap file, we run loadSubScript within the context of the
|
||||
// sandbox with the latest JS version set explicitly.
|
||||
|
29
toolkit/mozapps/extensions/test/addons/bootstrap_globals/bootstrap.js
vendored
Normal file
29
toolkit/mozapps/extensions/test/addons/bootstrap_globals/bootstrap.js
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
let seenGlobals = new Set();
|
||||
let scope = this;
|
||||
function checkGlobal(name, type) {
|
||||
if (scope[name] && typeof(scope[name]) == type)
|
||||
seenGlobals.add(name);
|
||||
}
|
||||
|
||||
let wrapped = {};
|
||||
Services.obs.notifyObservers({ wrappedJSObject: wrapped }, "bootstrap-request-globals", null);
|
||||
for (let [name, type] of wrapped.expectedGlobals) {
|
||||
checkGlobal(name, type);
|
||||
}
|
||||
|
||||
function install(data, reason) {
|
||||
}
|
||||
|
||||
function startup(data, reason) {
|
||||
Services.obs.notifyObservers({
|
||||
wrappedJSObject: seenGlobals
|
||||
}, "bootstrap-seen-globals", null);
|
||||
}
|
||||
|
||||
function shutdown(data, reason) {
|
||||
}
|
||||
|
||||
function uninstall(data, reason) {
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>bootstrap_globals@tests.mozilla.org</em:id>
|
||||
<em:version>1.0</em:version>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
|
||||
<!-- Front End MetaData -->
|
||||
<em:name>Test Bootstrap Globals</em:name>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>1</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
</Description>
|
||||
</RDF>
|
@ -0,0 +1,37 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// This verifies that bootstrap.js has the expected globals defined
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
|
||||
|
||||
const EXPECTED_GLOBALS = [
|
||||
["Worker", "function"],
|
||||
["ChromeWorker", "function"],
|
||||
["console", "object"]
|
||||
];
|
||||
|
||||
function run_test() {
|
||||
do_test_pending();
|
||||
startupManager();
|
||||
let sawGlobals = false;
|
||||
|
||||
Services.obs.addObserver(function(subject) {
|
||||
subject.wrappedJSObject.expectedGlobals = EXPECTED_GLOBALS;
|
||||
}, "bootstrap-request-globals", false);
|
||||
|
||||
Services.obs.addObserver(function({ wrappedJSObject: seenGlobals }) {
|
||||
for (let [name,] of EXPECTED_GLOBALS)
|
||||
do_check_true(seenGlobals.has(name));
|
||||
|
||||
sawGlobals = true;
|
||||
}, "bootstrap-seen-globals", false);
|
||||
|
||||
installAllFiles([do_get_addon("bootstrap_globals")], function() {
|
||||
do_check_true(sawGlobals);
|
||||
shutdownManager();
|
||||
do_test_finished();
|
||||
});
|
||||
}
|
@ -265,3 +265,4 @@ run-sequentially = Uses global XCurProcD dir.
|
||||
[test_overrideblocklist.js]
|
||||
run-sequentially = Uses global XCurProcD dir.
|
||||
[test_sourceURI.js]
|
||||
[test_bootstrap_globals.js]
|
||||
|
Loading…
Reference in New Issue
Block a user