mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-04 02:57:38 +00:00
Bug 1143497 - Offer a way to extend WebConsole commands. r=bgrins
This commit is contained in:
parent
a2ecc81eb0
commit
5337ffc494
@ -32,8 +32,8 @@ XPCOMUtils.defineLazyGetter(this, "events", () => {
|
||||
});
|
||||
|
||||
for (let name of ["WebConsoleUtils", "ConsoleServiceListener",
|
||||
"ConsoleAPIListener", "JSTermHelpers", "JSPropertyProvider",
|
||||
"ConsoleReflowListener"]) {
|
||||
"ConsoleAPIListener", "addWebConsoleCommands", "JSPropertyProvider",
|
||||
"ConsoleReflowListener"]) {
|
||||
Object.defineProperty(this, name, {
|
||||
get: function(prop) {
|
||||
if (prop == "WebConsoleUtils") {
|
||||
@ -295,11 +295,11 @@ WebConsoleActor.prototype =
|
||||
consoleReflowListener: null,
|
||||
|
||||
/**
|
||||
* The JSTerm Helpers names cache.
|
||||
* The Web Console Commands names cache.
|
||||
* @private
|
||||
* @type array
|
||||
*/
|
||||
_jstermHelpersCache: null,
|
||||
_webConsoleCommandsCache: null,
|
||||
|
||||
actorPrefix: "console",
|
||||
|
||||
@ -358,7 +358,7 @@ WebConsoleActor.prototype =
|
||||
}
|
||||
this._actorPool = null;
|
||||
|
||||
this._jstermHelpersCache = null;
|
||||
this._webConsoleCommandsCache = null;
|
||||
this._lastConsoleInputEvaluation = null;
|
||||
this._evalWindow = null;
|
||||
this._netEvents.clear();
|
||||
@ -882,14 +882,16 @@ WebConsoleActor.prototype =
|
||||
// helper functions.
|
||||
let lastNonAlphaIsDot = /[.][a-zA-Z0-9$]*$/.test(reqText);
|
||||
if (!lastNonAlphaIsDot) {
|
||||
if (!this._jstermHelpersCache) {
|
||||
if (!this._webConsoleCommandsCache) {
|
||||
let helpers = {
|
||||
sandbox: Object.create(null)
|
||||
};
|
||||
JSTermHelpers(helpers);
|
||||
this._jstermHelpersCache = Object.getOwnPropertyNames(helpers.sandbox);
|
||||
addWebConsoleCommands(helpers);
|
||||
this._webConsoleCommandsCache =
|
||||
Object.getOwnPropertyNames(helpers.sandbox);
|
||||
}
|
||||
matches = matches.concat(this._jstermHelpersCache.filter(n => n.startsWith(result.matchProp)));
|
||||
matches = matches.concat(this._webConsoleCommandsCache
|
||||
.filter(n => n.startsWith(result.matchProp)));
|
||||
}
|
||||
|
||||
return {
|
||||
@ -966,13 +968,13 @@ WebConsoleActor.prototype =
|
||||
* @private
|
||||
* @param object aDebuggerGlobal
|
||||
* A Debugger.Object that wraps a content global. This is used for the
|
||||
* JSTerm helpers.
|
||||
* Web Console Commands.
|
||||
* @return object
|
||||
* The same object as |this|, but with an added |sandbox| property.
|
||||
* The sandbox holds methods and properties that can be used as
|
||||
* bindings during JS evaluation.
|
||||
*/
|
||||
_getJSTermHelpers: function WCA__getJSTermHelpers(aDebuggerGlobal)
|
||||
_getWebConsoleCommands: function(aDebuggerGlobal)
|
||||
{
|
||||
let helpers = {
|
||||
window: this.evalWindow,
|
||||
@ -983,7 +985,7 @@ WebConsoleActor.prototype =
|
||||
helperResult: null,
|
||||
consoleActor: this,
|
||||
};
|
||||
JSTermHelpers(helpers);
|
||||
addWebConsoleCommands(helpers);
|
||||
|
||||
let evalWindow = this.evalWindow;
|
||||
function maybeExport(obj, name) {
|
||||
@ -1037,9 +1039,9 @@ WebConsoleActor.prototype =
|
||||
*
|
||||
* The Debugger.Frame comes from the jsdebugger's Debugger instance, which
|
||||
* is different from the Web Console's Debugger instance. This means that
|
||||
* for evaluation to work, we need to create a new instance for the jsterm
|
||||
* helpers - they need to be Debugger.Objects coming from the jsdebugger's
|
||||
* Debugger instance.
|
||||
* for evaluation to work, we need to create a new instance for the Web
|
||||
* Console Commands helpers - they need to be Debugger.Objects coming from the
|
||||
* jsdebugger's Debugger instance.
|
||||
*
|
||||
* When |bindObjectActor| is used objects can come from different iframes,
|
||||
* from different domains. To avoid permission-related errors when objects
|
||||
@ -1069,7 +1071,8 @@ WebConsoleActor.prototype =
|
||||
* - window: the Debugger.Object for the global where the string was
|
||||
* evaluated.
|
||||
* - result: the result of the evaluation.
|
||||
* - helperResult: any result coming from a JSTerm helper function.
|
||||
* - helperResult: any result coming from a Web Console commands
|
||||
* function.
|
||||
* - url: the url to evaluate the script as. Defaults to
|
||||
* "debugger eval code".
|
||||
*/
|
||||
@ -1125,8 +1128,8 @@ WebConsoleActor.prototype =
|
||||
}
|
||||
}
|
||||
|
||||
// Get the JSTerm helpers for the given debugger window.
|
||||
let helpers = this._getJSTermHelpers(dbgWindow);
|
||||
// Get the Web Console commands for the given debugger window.
|
||||
let helpers = this._getWebConsoleCommands(dbgWindow);
|
||||
let bindings = helpers.sandbox;
|
||||
if (bindSelf) {
|
||||
bindings._self = bindSelf;
|
||||
@ -1140,7 +1143,8 @@ WebConsoleActor.prototype =
|
||||
}
|
||||
|
||||
// Check if the Debugger.Frame or Debugger.Object for the global include
|
||||
// $ or $$. We will not overwrite these functions with the jsterm helpers.
|
||||
// $ or $$. We will not overwrite these functions with the Web Console
|
||||
// commands.
|
||||
let found$ = false, found$$ = false;
|
||||
if (frame) {
|
||||
let env = frame.environment;
|
||||
|
@ -11,6 +11,7 @@ support-files =
|
||||
[test_basics.html]
|
||||
[test_bug819670_getter_throws.html]
|
||||
[test_cached_messages.html]
|
||||
[test_commands_registration.html]
|
||||
[test_consoleapi.html]
|
||||
[test_consoleapi_innerID.html]
|
||||
[test_file_uri.html]
|
||||
|
159
toolkit/devtools/webconsole/test/test_commands_registration.html
Normal file
159
toolkit/devtools/webconsole/test/test_commands_registration.html
Normal file
@ -0,0 +1,159 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf8">
|
||||
<title>Test for Web Console commands registration.</title>
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript;version=1.8" src="common.js"></script>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
</head>
|
||||
<body>
|
||||
<p>Test for Web Console commands registration.</p>
|
||||
<p id="quack"></p>
|
||||
|
||||
<script class="testbody" type="text/javascript;version=1.8">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let gState;
|
||||
let tests;
|
||||
|
||||
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
let {WebConsoleCommands} = devtools.require("devtools/toolkit/webconsole/utils");
|
||||
|
||||
function evaluateJS(input) {
|
||||
return new Promise((resolve) => gState.client.evaluateJS(input, resolve));
|
||||
}
|
||||
|
||||
function startTest()
|
||||
{
|
||||
removeEventListener("load", startTest);
|
||||
|
||||
attachConsole(["PageError"], onAttach, true);
|
||||
}
|
||||
|
||||
function onAttach(aState, aResponse)
|
||||
{
|
||||
gState = aState;
|
||||
|
||||
runTests(tests, testEnd);
|
||||
}
|
||||
|
||||
tests = [
|
||||
Task.async(function* registerNewCommand() {
|
||||
let win;
|
||||
WebConsoleCommands.register("setFoo", (owner, value) => {
|
||||
owner.window.foo = value;
|
||||
return "ok";
|
||||
});
|
||||
|
||||
ok(WebConsoleCommands.hasCommand("setFoo"),
|
||||
"The command should be registered");
|
||||
|
||||
let command = "setFoo('bar')";
|
||||
let response = yield evaluateJS(command);
|
||||
|
||||
checkObject(response, {
|
||||
from: gState.actor,
|
||||
input: command,
|
||||
result: "ok"
|
||||
});
|
||||
is(top.foo, "bar", "top.foo should equal to 'bar'");
|
||||
nextTest();
|
||||
}),
|
||||
|
||||
Task.async(function* wrapCommand() {
|
||||
let origKeys = WebConsoleCommands.getCommand("keys");
|
||||
|
||||
let newKeys = (...args) => {
|
||||
let [owner, arg0] = args;
|
||||
if (arg0 === ">o_/") {
|
||||
return "bang!";
|
||||
}
|
||||
else {
|
||||
return origKeys(...args);
|
||||
}
|
||||
};
|
||||
|
||||
WebConsoleCommands.register("keys", newKeys);
|
||||
is(WebConsoleCommands.getCommand("keys"), newKeys,
|
||||
"the keys() command should have been replaced");
|
||||
|
||||
let response = yield evaluateJS("keys('>o_/')");
|
||||
checkObject(response, {
|
||||
from: gState.actor,
|
||||
result: "bang!"
|
||||
});
|
||||
|
||||
response = yield evaluateJS("keys({foo: 'bar'})");
|
||||
checkObject(response, {
|
||||
from: gState.actor,
|
||||
result: {
|
||||
class: "Array",
|
||||
preview: {
|
||||
items: ["foo"]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
WebConsoleCommands.register("keys", origKeys);
|
||||
is(WebConsoleCommands.getCommand("keys"), origKeys,
|
||||
"the keys() command should be restored");
|
||||
nextTest();
|
||||
}),
|
||||
|
||||
Task.async(function* unregisterCommand() {
|
||||
WebConsoleCommands.unregister("setFoo");
|
||||
|
||||
let response = yield evaluateJS("setFoo");
|
||||
|
||||
checkObject(response, {
|
||||
from: gState.actor,
|
||||
input: "setFoo",
|
||||
result: {
|
||||
type: "undefined"
|
||||
},
|
||||
exceptionMessage: /setFoo is not defined/
|
||||
});
|
||||
nextTest();
|
||||
}),
|
||||
|
||||
Task.async(function* registerAccessor() {
|
||||
WebConsoleCommands.register("$foo", {
|
||||
get(owner) {
|
||||
let foo = owner.window.frames[0].window.document.getElementById("quack");
|
||||
return owner.makeDebuggeeValue(foo);
|
||||
}
|
||||
});
|
||||
let command = "$foo.textContent = '>o_/'";
|
||||
let response = yield evaluateJS(command);
|
||||
|
||||
checkObject(response, {
|
||||
from: gState.actor,
|
||||
input: command,
|
||||
result: ">o_/"
|
||||
});
|
||||
is(document.getElementById("quack").textContent, ">o_/",
|
||||
"#foo textContent should equal to \">o_/\"");
|
||||
WebConsoleCommands.unregister("$foo");
|
||||
ok(!WebConsoleCommands.hasCommand("$foo"), "$foo should be unregistered");
|
||||
nextTest();
|
||||
})
|
||||
];
|
||||
|
||||
function testEnd()
|
||||
{
|
||||
// If this is the first run, reload the page and do it again.
|
||||
// Otherwise, end the test.
|
||||
delete top.foo;
|
||||
closeDebugger(gState, function() {
|
||||
gState = null;
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
addEventListener("load", startTest);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -14,7 +14,7 @@ loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
|
||||
loader.lazyImporter(this, "LayoutHelpers", "resource://gre/modules/devtools/LayoutHelpers.jsm");
|
||||
|
||||
// TODO: Bug 842672 - toolkit/ imports modules from browser/.
|
||||
// Note that these are only used in JSTermHelpers, see $0 and pprint().
|
||||
// Note that these are only used in WebConsoleCommands, see $0 and pprint().
|
||||
loader.lazyImporter(this, "gDevTools", "resource:///modules/devtools/gDevTools.jsm");
|
||||
loader.lazyImporter(this, "devtools", "resource://gre/modules/devtools/Loader.jsm");
|
||||
loader.lazyImporter(this, "VariablesView", "resource:///modules/devtools/VariablesView.jsm");
|
||||
@ -32,7 +32,7 @@ const REGEX_MATCH_FUNCTION_NAME = /^\(?function\s+([^(\s]+)\s*\(/;
|
||||
const REGEX_MATCH_FUNCTION_ARGS = /^\(?function\s*[^\s(]*\s*\((.+?)\)/;
|
||||
|
||||
// Number of terminal entries for the self-xss prevention to go away
|
||||
const CONSOLE_ENTRY_THRESHOLD = 5
|
||||
const CONSOLE_ENTRY_THRESHOLD = 5;
|
||||
|
||||
// Provide an easy way to bail out of even attempting an autocompletion
|
||||
// if an object has way too many properties. Protects against large objects
|
||||
@ -550,7 +550,7 @@ let WebConsoleUtils = {
|
||||
_usageCount: 0,
|
||||
get usageCount() {
|
||||
if (WebConsoleUtils._usageCount < CONSOLE_ENTRY_THRESHOLD) {
|
||||
WebConsoleUtils._usageCount = Services.prefs.getIntPref("devtools.selfxss.count")
|
||||
WebConsoleUtils._usageCount = Services.prefs.getIntPref("devtools.selfxss.count");
|
||||
if (Services.prefs.getBoolPref("devtools.chrome.enabled")) {
|
||||
WebConsoleUtils.usageCount = CONSOLE_ENTRY_THRESHOLD;
|
||||
}
|
||||
@ -912,7 +912,7 @@ function JSPropertyProvider(aDbgObject, anEnvironment, aInputValue, aCursor)
|
||||
return null;
|
||||
}
|
||||
|
||||
if (/\[\d+\]$/.test(prop)) {
|
||||
if (/\[\d+\]$/.test(prop)) {
|
||||
// The property to autocomplete is a member of array. For example
|
||||
// list[i][j]..[n]. Traverse the array to get the actual element.
|
||||
obj = getArrayMemberProperty(obj, prop);
|
||||
@ -1507,307 +1507,406 @@ ConsoleAPIListener.prototype =
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* WebConsole commands manager.
|
||||
*
|
||||
* Defines a set of functions /variables ("commands") that are available from
|
||||
* the Web Console but not from the web page.
|
||||
*
|
||||
*/
|
||||
let WebConsoleCommands = {
|
||||
_registeredCommands: new Map(),
|
||||
|
||||
/**
|
||||
* Register a new command.
|
||||
* @param {string} name The command name (exemple: "$")
|
||||
* @param {(function|object)} command The command to register.
|
||||
* It can be a function so the command is a function (like "$()"),
|
||||
* or it can also be a property descriptor to describe a getter / value (like
|
||||
* "$0").
|
||||
*
|
||||
* The command function or the command getter are passed a owner object as
|
||||
* their first parameter (see the example below).
|
||||
*
|
||||
* Note that setters don't work currently and "enumerable" and "configurable"
|
||||
* are forced to true.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* WebConsoleCommands.register("$", function JSTH_$(aOwner, aSelector)
|
||||
* {
|
||||
* return aOwner.window.document.querySelector(aSelector);
|
||||
* });
|
||||
*
|
||||
* WebConsoleCommands.register("$0", {
|
||||
* get: function(aOwner) {
|
||||
* return aOwner.makeDebuggeeValue(aOwner.selectedNode);
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
register: function(name, command) {
|
||||
this._registeredCommands.set(name, command);
|
||||
},
|
||||
|
||||
/**
|
||||
* Unregister a command.
|
||||
*
|
||||
* @param {string} name The name of the command
|
||||
*/
|
||||
unregister: function(name) {
|
||||
this._registeredCommands.delete(name);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a command by its name.
|
||||
*
|
||||
* @param {string} name The name of the command.
|
||||
*
|
||||
* @return {(function|object)} The command.
|
||||
*/
|
||||
getCommand: function(name) {
|
||||
return this._registeredCommands.get(name);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if a command is registered with the given name.
|
||||
*
|
||||
* @param {string} name The name of the command.
|
||||
*
|
||||
* @return {boolean} True if the command is registered.
|
||||
*/
|
||||
hasCommand: function(name) {
|
||||
return this._registeredCommands.has(name);
|
||||
},
|
||||
};
|
||||
|
||||
exports.WebConsoleCommands = WebConsoleCommands;
|
||||
|
||||
|
||||
/*
|
||||
* Built-in commands.
|
||||
*
|
||||
* A list of helper functions used by Firebug can be found here:
|
||||
* http://getfirebug.com/wiki/index.php/Command_Line_API
|
||||
*/
|
||||
|
||||
/**
|
||||
* Find a node by ID.
|
||||
*
|
||||
* @param string aId
|
||||
* The ID of the element you want.
|
||||
* @return nsIDOMNode or null
|
||||
* The result of calling document.querySelector(aSelector).
|
||||
*/
|
||||
WebConsoleCommands.register("$", function JSTH_$(aOwner, aSelector)
|
||||
{
|
||||
return aOwner.window.document.querySelector(aSelector);
|
||||
});
|
||||
|
||||
/**
|
||||
* Find the nodes matching a CSS selector.
|
||||
*
|
||||
* @param string aSelector
|
||||
* A string that is passed to window.document.querySelectorAll.
|
||||
* @return nsIDOMNodeList
|
||||
* Returns the result of document.querySelectorAll(aSelector).
|
||||
*/
|
||||
WebConsoleCommands.register("$$", function JSTH_$$(aOwner, aSelector)
|
||||
{
|
||||
return aOwner.window.document.querySelectorAll(aSelector);
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns the result of the last console input evaluation
|
||||
*
|
||||
* @return object|undefined
|
||||
* Returns last console evaluation or undefined
|
||||
*/
|
||||
WebConsoleCommands.register("$_", {
|
||||
get: function(aOwner) {
|
||||
return aOwner.consoleActor.getLastConsoleInputEvaluation();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* JSTerm helper functions.
|
||||
* Runs an xPath query and returns all matched nodes.
|
||||
*
|
||||
* Defines a set of functions ("helper functions") that are available from the
|
||||
* Web Console but not from the web page.
|
||||
*
|
||||
* A list of helper functions used by Firebug can be found here:
|
||||
* http://getfirebug.com/wiki/index.php/Command_Line_API
|
||||
*
|
||||
* @param object aOwner
|
||||
* The owning object.
|
||||
* @param string aXPath
|
||||
* xPath search query to execute.
|
||||
* @param [optional] nsIDOMNode aContext
|
||||
* Context to run the xPath query on. Uses window.document if not set.
|
||||
* @return array of nsIDOMNode
|
||||
*/
|
||||
function JSTermHelpers(aOwner)
|
||||
WebConsoleCommands.register("$x", function JSTH_$x(aOwner, aXPath, aContext)
|
||||
{
|
||||
/**
|
||||
* Find a node by ID.
|
||||
*
|
||||
* @param string aId
|
||||
* The ID of the element you want.
|
||||
* @return nsIDOMNode or null
|
||||
* The result of calling document.querySelector(aSelector).
|
||||
*/
|
||||
aOwner.sandbox.$ = function JSTH_$(aSelector)
|
||||
{
|
||||
return aOwner.window.document.querySelector(aSelector);
|
||||
let nodes = new aOwner.window.wrappedJSObject.Array();
|
||||
let doc = aOwner.window.document;
|
||||
aContext = aContext || doc;
|
||||
|
||||
let results = doc.evaluate(aXPath, aContext, null,
|
||||
Ci.nsIDOMXPathResult.ANY_TYPE, null);
|
||||
let node;
|
||||
while ((node = results.iterateNext())) {
|
||||
nodes.push(node);
|
||||
}
|
||||
|
||||
return nodes;
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns the currently selected object in the highlighter.
|
||||
*
|
||||
* @return Object representing the current selection in the
|
||||
* Inspector, or null if no selection exists.
|
||||
*/
|
||||
WebConsoleCommands.register("$0", {
|
||||
get: function(aOwner) {
|
||||
return aOwner.makeDebuggeeValue(aOwner.selectedNode);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Clears the output of the WebConsole.
|
||||
*/
|
||||
WebConsoleCommands.register("clear", function JSTH_clear(aOwner)
|
||||
{
|
||||
aOwner.helperResult = {
|
||||
type: "clearOutput",
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Find the nodes matching a CSS selector.
|
||||
*
|
||||
* @param string aSelector
|
||||
* A string that is passed to window.document.querySelectorAll.
|
||||
* @return nsIDOMNodeList
|
||||
* Returns the result of document.querySelectorAll(aSelector).
|
||||
*/
|
||||
aOwner.sandbox.$$ = function JSTH_$$(aSelector)
|
||||
{
|
||||
return aOwner.window.document.querySelectorAll(aSelector);
|
||||
/**
|
||||
* Clears the input history of the WebConsole.
|
||||
*/
|
||||
WebConsoleCommands.register("clearHistory", function JSTH_clearHistory(aOwner)
|
||||
{
|
||||
aOwner.helperResult = {
|
||||
type: "clearHistory",
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns the result of the last console input evaluation
|
||||
*
|
||||
* @return object|undefined
|
||||
* Returns last console evaluation or undefined
|
||||
*/
|
||||
Object.defineProperty(aOwner.sandbox, "$_", {
|
||||
get: function() {
|
||||
return aOwner.consoleActor.getLastConsoleInputEvaluation();
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
/**
|
||||
* Returns the result of Object.keys(aObject).
|
||||
*
|
||||
* @param object aObject
|
||||
* Object to return the property names from.
|
||||
* @return array of strings
|
||||
*/
|
||||
WebConsoleCommands.register("keys", function JSTH_keys(aOwner, aObject)
|
||||
{
|
||||
return aOwner.window.wrappedJSObject.Object.keys(WebConsoleUtils.unwrap(aObject));
|
||||
});
|
||||
|
||||
/**
|
||||
* Runs an xPath query and returns all matched nodes.
|
||||
*
|
||||
* @param string aXPath
|
||||
* xPath search query to execute.
|
||||
* @param [optional] nsIDOMNode aContext
|
||||
* Context to run the xPath query on. Uses window.document if not set.
|
||||
* @return array of nsIDOMNode
|
||||
*/
|
||||
aOwner.sandbox.$x = function JSTH_$x(aXPath, aContext)
|
||||
{
|
||||
let nodes = new aOwner.window.wrappedJSObject.Array();
|
||||
let doc = aOwner.window.document;
|
||||
aContext = aContext || doc;
|
||||
/**
|
||||
* Returns the values of all properties on aObject.
|
||||
*
|
||||
* @param object aObject
|
||||
* Object to display the values from.
|
||||
* @return array of string
|
||||
*/
|
||||
WebConsoleCommands.register("values", function JSTH_values(aOwner, aObject)
|
||||
{
|
||||
let arrValues = new aOwner.window.wrappedJSObject.Array();
|
||||
let obj = WebConsoleUtils.unwrap(aObject);
|
||||
|
||||
let results = doc.evaluate(aXPath, aContext, null,
|
||||
Ci.nsIDOMXPathResult.ANY_TYPE, null);
|
||||
let node;
|
||||
while ((node = results.iterateNext())) {
|
||||
nodes.push(node);
|
||||
}
|
||||
for (let prop in obj) {
|
||||
arrValues.push(obj[prop]);
|
||||
}
|
||||
|
||||
return nodes;
|
||||
};
|
||||
return arrValues;
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns the currently selected object in the highlighter.
|
||||
*
|
||||
* @return Object representing the current selection in the
|
||||
* Inspector, or null if no selection exists.
|
||||
*/
|
||||
Object.defineProperty(aOwner.sandbox, "$0", {
|
||||
get: function() {
|
||||
return aOwner.makeDebuggeeValue(aOwner.selectedNode)
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
/**
|
||||
* Opens a help window in MDN.
|
||||
*/
|
||||
WebConsoleCommands.register("help", function JSTH_help(aOwner)
|
||||
{
|
||||
aOwner.helperResult = { type: "help" };
|
||||
});
|
||||
|
||||
/**
|
||||
* Clears the output of the JSTerm.
|
||||
*/
|
||||
aOwner.sandbox.clear = function JSTH_clear()
|
||||
{
|
||||
aOwner.helperResult = {
|
||||
type: "clearOutput",
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears the input history of the JSTerm.
|
||||
*/
|
||||
aOwner.sandbox.clearHistory = function JSTH_clearHistory()
|
||||
{
|
||||
aOwner.helperResult = {
|
||||
type: "clearHistory",
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the result of Object.keys(aObject).
|
||||
*
|
||||
* @param object aObject
|
||||
* Object to return the property names from.
|
||||
* @return array of strings
|
||||
*/
|
||||
aOwner.sandbox.keys = function JSTH_keys(aObject)
|
||||
{
|
||||
return aOwner.window.wrappedJSObject.Object.keys(WebConsoleUtils.unwrap(aObject));
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the values of all properties on aObject.
|
||||
*
|
||||
* @param object aObject
|
||||
* Object to display the values from.
|
||||
* @return array of string
|
||||
*/
|
||||
aOwner.sandbox.values = function JSTH_values(aObject)
|
||||
{
|
||||
let arrValues = new aOwner.window.wrappedJSObject.Array();
|
||||
let obj = WebConsoleUtils.unwrap(aObject);
|
||||
|
||||
for (let prop in obj) {
|
||||
arrValues.push(obj[prop]);
|
||||
}
|
||||
|
||||
return arrValues;
|
||||
};
|
||||
|
||||
/**
|
||||
* Opens a help window in MDN.
|
||||
*/
|
||||
aOwner.sandbox.help = function JSTH_help()
|
||||
{
|
||||
aOwner.helperResult = { type: "help" };
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the JS evaluation scope.
|
||||
*
|
||||
* @param DOMElement|string|window aWindow
|
||||
* The window object to use for eval scope. This can be a string that
|
||||
* is used to perform document.querySelector(), to find the iframe that
|
||||
* you want to cd() to. A DOMElement can be given as well, the
|
||||
* .contentWindow property is used. Lastly, you can directly pass
|
||||
* a window object. If you call cd() with no arguments, the current
|
||||
* eval scope is cleared back to its default (the top window).
|
||||
*/
|
||||
aOwner.sandbox.cd = function JSTH_cd(aWindow)
|
||||
{
|
||||
if (!aWindow) {
|
||||
aOwner.consoleActor.evalWindow = null;
|
||||
aOwner.helperResult = { type: "cd" };
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof aWindow == "string") {
|
||||
aWindow = aOwner.window.document.querySelector(aWindow);
|
||||
}
|
||||
if (aWindow instanceof Ci.nsIDOMElement && aWindow.contentWindow) {
|
||||
aWindow = aWindow.contentWindow;
|
||||
}
|
||||
if (!(aWindow instanceof Ci.nsIDOMWindow)) {
|
||||
aOwner.helperResult = { type: "error", message: "cdFunctionInvalidArgument" };
|
||||
return;
|
||||
}
|
||||
|
||||
aOwner.consoleActor.evalWindow = aWindow;
|
||||
/**
|
||||
* Change the JS evaluation scope.
|
||||
*
|
||||
* @param DOMElement|string|window aWindow
|
||||
* The window object to use for eval scope. This can be a string that
|
||||
* is used to perform document.querySelector(), to find the iframe that
|
||||
* you want to cd() to. A DOMElement can be given as well, the
|
||||
* .contentWindow property is used. Lastly, you can directly pass
|
||||
* a window object. If you call cd() with no arguments, the current
|
||||
* eval scope is cleared back to its default (the top window).
|
||||
*/
|
||||
WebConsoleCommands.register("cd", function JSTH_cd(aOwner, aWindow)
|
||||
{
|
||||
if (!aWindow) {
|
||||
aOwner.consoleActor.evalWindow = null;
|
||||
aOwner.helperResult = { type: "cd" };
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspects the passed aObject. This is done by opening the PropertyPanel.
|
||||
*
|
||||
* @param object aObject
|
||||
* Object to inspect.
|
||||
*/
|
||||
aOwner.sandbox.inspect = function JSTH_inspect(aObject)
|
||||
{
|
||||
let dbgObj = aOwner.makeDebuggeeValue(aObject);
|
||||
let grip = aOwner.createValueGrip(dbgObj);
|
||||
if (typeof aWindow == "string") {
|
||||
aWindow = aOwner.window.document.querySelector(aWindow);
|
||||
}
|
||||
if (aWindow instanceof Ci.nsIDOMElement && aWindow.contentWindow) {
|
||||
aWindow = aWindow.contentWindow;
|
||||
}
|
||||
if (!(aWindow instanceof Ci.nsIDOMWindow)) {
|
||||
aOwner.helperResult = { type: "error", message: "cdFunctionInvalidArgument" };
|
||||
return;
|
||||
}
|
||||
|
||||
aOwner.consoleActor.evalWindow = aWindow;
|
||||
aOwner.helperResult = { type: "cd" };
|
||||
});
|
||||
|
||||
/**
|
||||
* Inspects the passed aObject. This is done by opening the PropertyPanel.
|
||||
*
|
||||
* @param object aObject
|
||||
* Object to inspect.
|
||||
*/
|
||||
WebConsoleCommands.register("inspect", function JSTH_inspect(aOwner, aObject)
|
||||
{
|
||||
let dbgObj = aOwner.makeDebuggeeValue(aObject);
|
||||
let grip = aOwner.createValueGrip(dbgObj);
|
||||
aOwner.helperResult = {
|
||||
type: "inspectObject",
|
||||
input: aOwner.evalInput,
|
||||
object: grip,
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Prints aObject to the output.
|
||||
*
|
||||
* @param object aObject
|
||||
* Object to print to the output.
|
||||
* @return string
|
||||
*/
|
||||
WebConsoleCommands.register("pprint", function JSTH_pprint(aOwner, aObject)
|
||||
{
|
||||
if (aObject === null || aObject === undefined || aObject === true ||
|
||||
aObject === false) {
|
||||
aOwner.helperResult = {
|
||||
type: "inspectObject",
|
||||
input: aOwner.evalInput,
|
||||
object: grip,
|
||||
type: "error",
|
||||
message: "helperFuncUnsupportedTypeError",
|
||||
};
|
||||
return null;
|
||||
}
|
||||
|
||||
aOwner.helperResult = { rawOutput: true };
|
||||
|
||||
if (typeof aObject == "function") {
|
||||
return aObject + "\n";
|
||||
}
|
||||
|
||||
let output = [];
|
||||
|
||||
let obj = WebConsoleUtils.unwrap(aObject);
|
||||
for (let name in obj) {
|
||||
let desc = WebConsoleUtils.getPropertyDescriptor(obj, name) || {};
|
||||
if (desc.get || desc.set) {
|
||||
// TODO: Bug 842672 - toolkit/ imports modules from browser/.
|
||||
let getGrip = VariablesView.getGrip(desc.get);
|
||||
let setGrip = VariablesView.getGrip(desc.set);
|
||||
let getString = VariablesView.getString(getGrip);
|
||||
let setString = VariablesView.getString(setGrip);
|
||||
output.push(name + ":", " get: " + getString, " set: " + setString);
|
||||
}
|
||||
else {
|
||||
let valueGrip = VariablesView.getGrip(obj[name]);
|
||||
let valueString = VariablesView.getString(valueGrip);
|
||||
output.push(name + ": " + valueString);
|
||||
}
|
||||
}
|
||||
|
||||
return " " + output.join("\n ");
|
||||
});
|
||||
|
||||
/**
|
||||
* Print the String representation of a value to the output, as-is.
|
||||
*
|
||||
* @param any aValue
|
||||
* A value you want to output as a string.
|
||||
* @return void
|
||||
*/
|
||||
WebConsoleCommands.register("print", function JSTH_print(aOwner, aValue)
|
||||
{
|
||||
aOwner.helperResult = { rawOutput: true };
|
||||
if (typeof aValue === "symbol") {
|
||||
return Symbol.prototype.toString.call(aValue);
|
||||
}
|
||||
// Waiving Xrays here allows us to see a closer representation of the
|
||||
// underlying object. This may execute arbitrary content code, but that
|
||||
// code will run with content privileges, and the result will be rendered
|
||||
// inert by coercing it to a String.
|
||||
return String(Cu.waiveXrays(aValue));
|
||||
});
|
||||
|
||||
/**
|
||||
* Copy the String representation of a value to the clipboard.
|
||||
*
|
||||
* @param any aValue
|
||||
* A value you want to copy as a string.
|
||||
* @return void
|
||||
*/
|
||||
WebConsoleCommands.register("copy", function JSTH_copy(aOwner, aValue)
|
||||
{
|
||||
let payload;
|
||||
try {
|
||||
if (aValue instanceof Ci.nsIDOMElement) {
|
||||
payload = aValue.outerHTML;
|
||||
} else if (typeof aValue == "string") {
|
||||
payload = aValue;
|
||||
} else {
|
||||
payload = JSON.stringify(aValue, null, " ");
|
||||
}
|
||||
} catch (ex) {
|
||||
payload = "/* " + ex + " */";
|
||||
}
|
||||
aOwner.helperResult = {
|
||||
type: "copyValueToClipboard",
|
||||
value: payload,
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Prints aObject to the output.
|
||||
*
|
||||
* @param object aObject
|
||||
* Object to print to the output.
|
||||
* @return string
|
||||
*/
|
||||
aOwner.sandbox.pprint = function JSTH_pprint(aObject)
|
||||
{
|
||||
if (aObject === null || aObject === undefined || aObject === true ||
|
||||
aObject === false) {
|
||||
aOwner.helperResult = {
|
||||
type: "error",
|
||||
message: "helperFuncUnsupportedTypeError",
|
||||
};
|
||||
return null;
|
||||
|
||||
/**
|
||||
* (Internal only) Add the bindings to |owner.sandbox|.
|
||||
* This is intended to be used by the WebConsole actor only.
|
||||
*
|
||||
* @param object aOwner
|
||||
* The owning object.
|
||||
*/
|
||||
function addWebConsoleCommands(owner) {
|
||||
if (!owner) {
|
||||
throw new Error("The owner is required");
|
||||
}
|
||||
for (let [name, command] of WebConsoleCommands._registeredCommands) {
|
||||
if (typeof command === "function") {
|
||||
owner.sandbox[name] = command.bind(undefined, owner);
|
||||
}
|
||||
else if (typeof command === "object") {
|
||||
let clone = Object.assign({}, command, {
|
||||
// We force the enumerability and the configurability (so the
|
||||
// WebConsoleActor can reconfigure the property).
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
aOwner.helperResult = { rawOutput: true };
|
||||
|
||||
if (typeof aObject == "function") {
|
||||
return aObject + "\n";
|
||||
}
|
||||
|
||||
let output = [];
|
||||
|
||||
let obj = WebConsoleUtils.unwrap(aObject);
|
||||
for (let name in obj) {
|
||||
let desc = WebConsoleUtils.getPropertyDescriptor(obj, name) || {};
|
||||
if (desc.get || desc.set) {
|
||||
// TODO: Bug 842672 - toolkit/ imports modules from browser/.
|
||||
let getGrip = VariablesView.getGrip(desc.get);
|
||||
let setGrip = VariablesView.getGrip(desc.set);
|
||||
let getString = VariablesView.getString(getGrip);
|
||||
let setString = VariablesView.getString(setGrip);
|
||||
output.push(name + ":", " get: " + getString, " set: " + setString);
|
||||
if (typeof command.get === "function") {
|
||||
clone.get = command.get.bind(undefined, owner);
|
||||
}
|
||||
else {
|
||||
let valueGrip = VariablesView.getGrip(obj[name]);
|
||||
let valueString = VariablesView.getString(valueGrip);
|
||||
output.push(name + ": " + valueString);
|
||||
if (typeof command.set === "function") {
|
||||
clone.set = command.set.bind(undefined, owner);
|
||||
}
|
||||
}
|
||||
|
||||
return " " + output.join("\n ");
|
||||
};
|
||||
|
||||
/**
|
||||
* Print the String representation of a value to the output, as-is.
|
||||
*
|
||||
* @param any aValue
|
||||
* A value you want to output as a string.
|
||||
* @return void
|
||||
*/
|
||||
aOwner.sandbox.print = function JSTH_print(aValue)
|
||||
{
|
||||
aOwner.helperResult = { rawOutput: true };
|
||||
if (typeof aValue === "symbol") {
|
||||
return Symbol.prototype.toString.call(aValue);
|
||||
Object.defineProperty(owner.sandbox, name, clone);
|
||||
}
|
||||
// Waiving Xrays here allows us to see a closer representation of the
|
||||
// underlying object. This may execute arbitrary content code, but that
|
||||
// code will run with content privileges, and the result will be rendered
|
||||
// inert by coercing it to a String.
|
||||
return String(Cu.waiveXrays(aValue));
|
||||
};
|
||||
|
||||
/**
|
||||
* Copy the String representation of a value to the clipboard.
|
||||
*
|
||||
* @param any aValue
|
||||
* A value you want to copy as a string.
|
||||
* @return void
|
||||
*/
|
||||
aOwner.sandbox.copy = function JSTH_copy(aValue)
|
||||
{
|
||||
let payload;
|
||||
try {
|
||||
if (aValue instanceof Ci.nsIDOMElement) {
|
||||
payload = aValue.outerHTML;
|
||||
} else if (typeof aValue == "string") {
|
||||
payload = aValue;
|
||||
} else {
|
||||
payload = JSON.stringify(aValue, null, " ");
|
||||
}
|
||||
} catch (ex) {
|
||||
payload = "/* " + ex + " */";
|
||||
}
|
||||
aOwner.helperResult = {
|
||||
type: "copyValueToClipboard",
|
||||
value: payload,
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.JSTermHelpers = JSTermHelpers;
|
||||
|
||||
exports.addWebConsoleCommands = addWebConsoleCommands;
|
||||
|
||||
/**
|
||||
* A ReflowObserver that listens for reflow events from the page.
|
||||
|
Loading…
Reference in New Issue
Block a user