зеркало из https://github.com/mozilla/gecko-dev.git
2340 строки
71 KiB
JavaScript
2340 строки
71 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
|
|
|
const BRAND_SHORT_NAME = Cc["@mozilla.org/intl/stringbundle;1"]
|
|
.getService(Ci.nsIStringBundleService)
|
|
.createBundle("chrome://branding/locale/brand.properties")
|
|
.GetStringFromName("brandShortName");
|
|
|
|
this.EXPORTED_SYMBOLS = [ "CmdAddonFlags", "CmdCommands", "DEFAULT_DEBUG_PORT", "connect" ];
|
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js").Promise;
|
|
Cu.import("resource://gre/modules/osfile.jsm");
|
|
|
|
Cu.import("resource://gre/modules/devtools/gcli.jsm");
|
|
Cu.import("resource:///modules/devtools/shared/event-emitter.js");
|
|
|
|
let devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
|
|
let Telemetry = devtools.require("devtools/shared/telemetry");
|
|
let telemetry = new Telemetry();
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
|
|
"resource:///modules/devtools/gDevTools.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
|
|
"resource:///modules/devtools/AppCacheUtils.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
|
|
"resource://gre/modules/Downloads.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
|
"resource://gre/modules/Task.jsm");
|
|
|
|
/* CmdAddon ---------------------------------------------------------------- */
|
|
|
|
(function(module) {
|
|
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
|
|
"resource://gre/modules/AddonManager.jsm");
|
|
|
|
// We need to use an object in which to store any flags because a primitive
|
|
// would remain undefined.
|
|
module.CmdAddonFlags = {
|
|
addonsLoaded: false
|
|
};
|
|
|
|
/**
|
|
* 'addon' command.
|
|
*/
|
|
gcli.addCommand({
|
|
name: "addon",
|
|
description: gcli.lookup("addonDesc")
|
|
});
|
|
|
|
/**
|
|
* 'addon list' command.
|
|
*/
|
|
gcli.addCommand({
|
|
name: "addon list",
|
|
description: gcli.lookup("addonListDesc"),
|
|
returnType: "addonsInfo",
|
|
params: [{
|
|
name: 'type',
|
|
type: {
|
|
name: 'selection',
|
|
data: ["dictionary", "extension", "locale", "plugin", "theme", "all"]
|
|
},
|
|
defaultValue: 'all',
|
|
description: gcli.lookup("addonListTypeDesc")
|
|
}],
|
|
exec: function(aArgs, context) {
|
|
let deferred = context.defer();
|
|
function pendingOperations(aAddon) {
|
|
let allOperations = ["PENDING_ENABLE",
|
|
"PENDING_DISABLE",
|
|
"PENDING_UNINSTALL",
|
|
"PENDING_INSTALL",
|
|
"PENDING_UPGRADE"];
|
|
return allOperations.reduce(function(operations, opName) {
|
|
return aAddon.pendingOperations & AddonManager[opName] ?
|
|
operations.concat(opName) :
|
|
operations;
|
|
}, []);
|
|
}
|
|
let types = aArgs.type === "all" ? null : [aArgs.type];
|
|
AddonManager.getAddonsByTypes(types, function(addons) {
|
|
deferred.resolve({
|
|
addons: addons.map(function(addon) {
|
|
return {
|
|
name: addon.name,
|
|
version: addon.version,
|
|
isActive: addon.isActive,
|
|
pendingOperations: pendingOperations(addon)
|
|
};
|
|
}),
|
|
type: aArgs.type
|
|
});
|
|
});
|
|
return deferred.promise;
|
|
}
|
|
});
|
|
|
|
gcli.addConverter({
|
|
from: "addonsInfo",
|
|
to: "view",
|
|
exec: function(addonsInfo, context) {
|
|
if (!addonsInfo.addons.length) {
|
|
return context.createView({
|
|
html: "<p>${message}</p>",
|
|
data: { message: gcli.lookup("addonNoneOfType") }
|
|
});
|
|
}
|
|
|
|
let headerLookups = {
|
|
"dictionary": "addonListDictionaryHeading",
|
|
"extension": "addonListExtensionHeading",
|
|
"locale": "addonListLocaleHeading",
|
|
"plugin": "addonListPluginHeading",
|
|
"theme": "addonListThemeHeading",
|
|
"all": "addonListAllHeading"
|
|
};
|
|
let header = gcli.lookup(headerLookups[addonsInfo.type] ||
|
|
"addonListUnknownHeading");
|
|
|
|
let operationLookups = {
|
|
"PENDING_ENABLE": "addonPendingEnable",
|
|
"PENDING_DISABLE": "addonPendingDisable",
|
|
"PENDING_UNINSTALL": "addonPendingUninstall",
|
|
"PENDING_INSTALL": "addonPendingInstall",
|
|
"PENDING_UPGRADE": "addonPendingUpgrade"
|
|
};
|
|
function lookupOperation(opName) {
|
|
let lookupName = operationLookups[opName];
|
|
return lookupName ? gcli.lookup(lookupName) : opName;
|
|
}
|
|
|
|
function arrangeAddons(addons) {
|
|
let enabledAddons = [];
|
|
let disabledAddons = [];
|
|
addons.forEach(function(aAddon) {
|
|
if (aAddon.isActive) {
|
|
enabledAddons.push(aAddon);
|
|
} else {
|
|
disabledAddons.push(aAddon);
|
|
}
|
|
});
|
|
|
|
function compareAddonNames(aNameA, aNameB) {
|
|
return String.localeCompare(aNameA.name, aNameB.name);
|
|
}
|
|
enabledAddons.sort(compareAddonNames);
|
|
disabledAddons.sort(compareAddonNames);
|
|
|
|
return enabledAddons.concat(disabledAddons);
|
|
}
|
|
|
|
function isActiveForToggle(addon) {
|
|
return (addon.isActive && ~~addon.pendingOperations.indexOf("PENDING_DISABLE"));
|
|
}
|
|
|
|
return context.createView({
|
|
html: addonsListHtml,
|
|
data: {
|
|
header: header,
|
|
addons: arrangeAddons(addonsInfo.addons).map(function(addon) {
|
|
return {
|
|
name: addon.name,
|
|
label: addon.name.replace(/\s/g, "_") +
|
|
(addon.version ? "_" + addon.version : ""),
|
|
status: addon.isActive ? "enabled" : "disabled",
|
|
version: addon.version,
|
|
pendingOperations: addon.pendingOperations.length ?
|
|
(" (" + gcli.lookup("addonPending") + ": "
|
|
+ addon.pendingOperations.map(lookupOperation).join(", ")
|
|
+ ")") :
|
|
"",
|
|
toggleActionName: isActiveForToggle(addon) ? "disable": "enable",
|
|
toggleActionMessage: isActiveForToggle(addon) ?
|
|
gcli.lookup("addonListOutDisable") :
|
|
gcli.lookup("addonListOutEnable")
|
|
};
|
|
}),
|
|
onclick: context.update,
|
|
ondblclick: context.updateExec
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
var addonsListHtml = "" +
|
|
"<table>" +
|
|
" <caption>${header}</caption>" +
|
|
" <tbody>" +
|
|
" <tr foreach='addon in ${addons}'" +
|
|
" class=\"gcli-addon-${addon.status}\">" +
|
|
" <td>${addon.name} ${addon.version}</td>" +
|
|
" <td>${addon.pendingOperations}</td>" +
|
|
" <td>" +
|
|
" <span class='gcli-out-shortcut'" +
|
|
" data-command='addon ${addon.toggleActionName} ${addon.label}'" +
|
|
" onclick='${onclick}'" +
|
|
" ondblclick='${ondblclick}'" +
|
|
" >${addon.toggleActionMessage}</span>" +
|
|
" </td>" +
|
|
" </tr>" +
|
|
" </tbody>" +
|
|
"</table>" +
|
|
"";
|
|
|
|
// We need a list of addon names for the enable and disable commands. Because
|
|
// getting the name list is async we do not add the commands until we have the
|
|
// list.
|
|
AddonManager.getAllAddons(function addonAsync(aAddons) {
|
|
// We listen for installs to keep our addon list up to date. There is no need
|
|
// to listen for uninstalls because uninstalled addons are simply disabled
|
|
// until restart (to enable undo functionality).
|
|
AddonManager.addAddonListener({
|
|
onInstalled: function(aAddon) {
|
|
addonNameCache.push({
|
|
name: representAddon(aAddon).replace(/\s/g, "_"),
|
|
value: aAddon.name
|
|
});
|
|
},
|
|
onUninstalled: function(aAddon) {
|
|
let name = representAddon(aAddon).replace(/\s/g, "_");
|
|
|
|
for (let i = 0; i < addonNameCache.length; i++) {
|
|
if(addonNameCache[i].name == name) {
|
|
addonNameCache.splice(i, 1);
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
});
|
|
|
|
/**
|
|
* Returns a string that represents the passed add-on.
|
|
*/
|
|
function representAddon(aAddon) {
|
|
let name = aAddon.name + " " + aAddon.version;
|
|
return name.trim();
|
|
}
|
|
|
|
let addonNameCache = [];
|
|
|
|
// The name parameter, used in "addon enable" and "addon disable."
|
|
let nameParameter = {
|
|
name: "name",
|
|
type: {
|
|
name: "selection",
|
|
lookup: addonNameCache
|
|
},
|
|
description: gcli.lookup("addonNameDesc")
|
|
};
|
|
|
|
for (let addon of aAddons) {
|
|
addonNameCache.push({
|
|
name: representAddon(addon).replace(/\s/g, "_"),
|
|
value: addon.name
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 'addon enable' command.
|
|
*/
|
|
gcli.addCommand({
|
|
name: "addon enable",
|
|
description: gcli.lookup("addonEnableDesc"),
|
|
params: [nameParameter],
|
|
exec: function(aArgs, context) {
|
|
/**
|
|
* Enables the addon in the passed list which has a name that matches
|
|
* according to the passed name comparer, and resolves the promise which
|
|
* is the scope (this) of this function to display the result of this
|
|
* enable attempt.
|
|
*/
|
|
function enable(aName, addons) {
|
|
// Find the add-on.
|
|
let addon = null;
|
|
addons.some(function(candidate) {
|
|
if (candidate.name == aName) {
|
|
addon = candidate;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
});
|
|
|
|
let name = representAddon(addon);
|
|
let message = "";
|
|
|
|
if (!addon.userDisabled) {
|
|
message = gcli.lookupFormat("addonAlreadyEnabled", [name]);
|
|
} else {
|
|
addon.userDisabled = false;
|
|
message = gcli.lookupFormat("addonEnabled", [name]);
|
|
}
|
|
this.resolve(message);
|
|
}
|
|
|
|
let deferred = context.defer();
|
|
// List the installed add-ons, enable one when done listing.
|
|
AddonManager.getAllAddons(enable.bind(deferred, aArgs.name));
|
|
return deferred.promise;
|
|
}
|
|
});
|
|
|
|
/**
|
|
* 'addon disable' command.
|
|
*/
|
|
gcli.addCommand({
|
|
name: "addon disable",
|
|
description: gcli.lookup("addonDisableDesc"),
|
|
params: [nameParameter],
|
|
exec: function(aArgs, context) {
|
|
/**
|
|
* Like enable, but ... you know ... the exact opposite.
|
|
*/
|
|
function disable(aName, addons) {
|
|
// Find the add-on.
|
|
let addon = null;
|
|
addons.some(function(candidate) {
|
|
if (candidate.name == aName) {
|
|
addon = candidate;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
});
|
|
|
|
let name = representAddon(addon);
|
|
let message = "";
|
|
|
|
// If the addon is not disabled or is set to "click to play" then
|
|
// disable it. Otherwise display the message "Add-on is already
|
|
// disabled."
|
|
if (!addon.userDisabled ||
|
|
addon.userDisabled === AddonManager.STATE_ASK_TO_ACTIVATE) {
|
|
addon.userDisabled = true;
|
|
message = gcli.lookupFormat("addonDisabled", [name]);
|
|
} else {
|
|
message = gcli.lookupFormat("addonAlreadyDisabled", [name]);
|
|
}
|
|
this.resolve(message);
|
|
}
|
|
|
|
let deferred = context.defer();
|
|
// List the installed add-ons, disable one when done listing.
|
|
AddonManager.getAllAddons(disable.bind(deferred, aArgs.name));
|
|
return deferred.promise;
|
|
}
|
|
});
|
|
module.CmdAddonFlags.addonsLoaded = true;
|
|
Services.obs.notifyObservers(null, "gcli_addon_commands_ready", null);
|
|
});
|
|
|
|
}(this));
|
|
|
|
/* CmdCalllog -------------------------------------------------------------- */
|
|
|
|
(function(module) {
|
|
XPCOMUtils.defineLazyGetter(this, "Debugger", function() {
|
|
let JsDebugger = {};
|
|
Components.utils.import("resource://gre/modules/jsdebugger.jsm", JsDebugger);
|
|
|
|
let global = Components.utils.getGlobalForObject({});
|
|
JsDebugger.addDebuggerToGlobal(global);
|
|
|
|
return global.Debugger;
|
|
});
|
|
|
|
let debuggers = [];
|
|
|
|
/**
|
|
* 'calllog' command
|
|
*/
|
|
gcli.addCommand({
|
|
name: "calllog",
|
|
description: gcli.lookup("calllogDesc")
|
|
})
|
|
|
|
/**
|
|
* 'calllog start' command
|
|
*/
|
|
gcli.addCommand({
|
|
name: "calllog start",
|
|
description: gcli.lookup("calllogStartDesc"),
|
|
|
|
exec: function(args, context) {
|
|
let contentWindow = context.environment.window;
|
|
|
|
let dbg = new Debugger(contentWindow);
|
|
dbg.onEnterFrame = function(frame) {
|
|
// BUG 773652 - Make the output from the GCLI calllog command nicer
|
|
contentWindow.console.log("Method call: " + this.callDescription(frame));
|
|
}.bind(this);
|
|
|
|
debuggers.push(dbg);
|
|
|
|
let gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
|
|
let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
|
|
gDevTools.showToolbox(target, "webconsole");
|
|
|
|
return gcli.lookup("calllogStartReply");
|
|
},
|
|
|
|
callDescription: function(frame) {
|
|
let name = "<anonymous>";
|
|
if (frame.callee.name) {
|
|
name = frame.callee.name;
|
|
}
|
|
else {
|
|
let desc = frame.callee.getOwnPropertyDescriptor("displayName");
|
|
if (desc && desc.value && typeof desc.value == "string") {
|
|
name = desc.value;
|
|
}
|
|
}
|
|
|
|
let args = frame.arguments.map(this.valueToString).join(", ");
|
|
return name + "(" + args + ")";
|
|
},
|
|
|
|
valueToString: function(value) {
|
|
if (typeof value !== "object" || value === null) {
|
|
return uneval(value);
|
|
}
|
|
return "[object " + value.class + "]";
|
|
}
|
|
});
|
|
|
|
/**
|
|
* 'calllog stop' command
|
|
*/
|
|
gcli.addCommand({
|
|
name: "calllog stop",
|
|
description: gcli.lookup("calllogStopDesc"),
|
|
|
|
exec: function(args, context) {
|
|
let numDebuggers = debuggers.length;
|
|
if (numDebuggers == 0) {
|
|
return gcli.lookup("calllogStopNoLogging");
|
|
}
|
|
|
|
for (let dbg of debuggers) {
|
|
dbg.onEnterFrame = undefined;
|
|
}
|
|
debuggers = [];
|
|
|
|
return gcli.lookupFormat("calllogStopReply", [ numDebuggers ]);
|
|
}
|
|
});
|
|
}(this));
|
|
|
|
/* CmdCalllogChrome -------------------------------------------------------- */
|
|
|
|
(function(module) {
|
|
XPCOMUtils.defineLazyGetter(this, "Debugger", function() {
|
|
let JsDebugger = {};
|
|
Cu.import("resource://gre/modules/jsdebugger.jsm", JsDebugger);
|
|
|
|
let global = Components.utils.getGlobalForObject({});
|
|
JsDebugger.addDebuggerToGlobal(global);
|
|
|
|
return global.Debugger;
|
|
});
|
|
|
|
let debuggers = [];
|
|
let sandboxes = [];
|
|
|
|
/**
|
|
* 'calllog chromestart' command
|
|
*/
|
|
gcli.addCommand({
|
|
name: "calllog chromestart",
|
|
description: gcli.lookup("calllogChromeStartDesc"),
|
|
get hidden() gcli.hiddenByChromePref(),
|
|
params: [
|
|
{
|
|
name: "sourceType",
|
|
type: {
|
|
name: "selection",
|
|
data: ["content-variable", "chrome-variable", "jsm", "javascript"]
|
|
}
|
|
},
|
|
{
|
|
name: "source",
|
|
type: "string",
|
|
description: gcli.lookup("calllogChromeSourceTypeDesc"),
|
|
manual: gcli.lookup("calllogChromeSourceTypeManual"),
|
|
}
|
|
],
|
|
exec: function(args, context) {
|
|
let globalObj;
|
|
let contentWindow = context.environment.window;
|
|
|
|
if (args.sourceType == "jsm") {
|
|
try {
|
|
globalObj = Cu.import(args.source);
|
|
}
|
|
catch (e) {
|
|
return gcli.lookup("callLogChromeInvalidJSM");
|
|
}
|
|
} else if (args.sourceType == "content-variable") {
|
|
if (args.source in contentWindow) {
|
|
globalObj = Cu.getGlobalForObject(contentWindow[args.source]);
|
|
} else {
|
|
throw new Error(gcli.lookup("callLogChromeVarNotFoundContent"));
|
|
}
|
|
} else if (args.sourceType == "chrome-variable") {
|
|
let chromeWin = context.environment.chromeDocument.defaultView;
|
|
if (args.source in chromeWin) {
|
|
globalObj = Cu.getGlobalForObject(chromeWin[args.source]);
|
|
} else {
|
|
return gcli.lookup("callLogChromeVarNotFoundChrome");
|
|
}
|
|
} else {
|
|
let chromeWin = context.environment.chromeDocument.defaultView;
|
|
let sandbox = new Cu.Sandbox(chromeWin,
|
|
{
|
|
sandboxPrototype: chromeWin,
|
|
wantXrays: false,
|
|
sandboxName: "gcli-cmd-calllog-chrome"
|
|
});
|
|
let returnVal;
|
|
try {
|
|
returnVal = Cu.evalInSandbox(args.source, sandbox, "ECMAv5");
|
|
sandboxes.push(sandbox);
|
|
} catch(e) {
|
|
// We need to save the message before cleaning up else e contains a dead
|
|
// object.
|
|
let msg = gcli.lookup("callLogChromeEvalException") + ": " + e;
|
|
Cu.nukeSandbox(sandbox);
|
|
return msg;
|
|
}
|
|
|
|
if (typeof returnVal == "undefined") {
|
|
return gcli.lookup("callLogChromeEvalNeedsObject");
|
|
}
|
|
|
|
globalObj = Cu.getGlobalForObject(returnVal);
|
|
}
|
|
|
|
let dbg = new Debugger(globalObj);
|
|
debuggers.push(dbg);
|
|
|
|
dbg.onEnterFrame = function(frame) {
|
|
// BUG 773652 - Make the output from the GCLI calllog command nicer
|
|
contentWindow.console.log(gcli.lookup("callLogChromeMethodCall") +
|
|
": " + this.callDescription(frame));
|
|
}.bind(this);
|
|
|
|
let gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
|
|
let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
|
|
gDevTools.showToolbox(target, "webconsole");
|
|
|
|
return gcli.lookup("calllogChromeStartReply");
|
|
},
|
|
|
|
valueToString: function(value) {
|
|
if (typeof value !== "object" || value === null)
|
|
return uneval(value);
|
|
return "[object " + value.class + "]";
|
|
},
|
|
|
|
callDescription: function(frame) {
|
|
let name = frame.callee.name || gcli.lookup("callLogChromeAnonFunction");
|
|
let args = frame.arguments.map(this.valueToString).join(", ");
|
|
return name + "(" + args + ")";
|
|
}
|
|
});
|
|
|
|
/**
|
|
* 'calllog chromestop' command
|
|
*/
|
|
gcli.addCommand({
|
|
name: "calllog chromestop",
|
|
description: gcli.lookup("calllogChromeStopDesc"),
|
|
get hidden() gcli.hiddenByChromePref(),
|
|
exec: function(args, context) {
|
|
let numDebuggers = debuggers.length;
|
|
if (numDebuggers == 0) {
|
|
return gcli.lookup("calllogChromeStopNoLogging");
|
|
}
|
|
|
|
for (let dbg of debuggers) {
|
|
dbg.onEnterFrame = undefined;
|
|
dbg.enabled = false;
|
|
}
|
|
for (let sandbox of sandboxes) {
|
|
Cu.nukeSandbox(sandbox);
|
|
}
|
|
debuggers = [];
|
|
sandboxes = [];
|
|
|
|
return gcli.lookupFormat("calllogChromeStopReply", [ numDebuggers ]);
|
|
}
|
|
});
|
|
}(this));
|
|
|
|
/* CmdCmd ------------------------------------------------------------------ */
|
|
|
|
(function(module) {
|
|
let prefSvc = "@mozilla.org/preferences-service;1";
|
|
XPCOMUtils.defineLazyGetter(this, "prefBranch", function() {
|
|
let prefService = Cc[prefSvc].getService(Ci.nsIPrefService);
|
|
return prefService.getBranch(null).QueryInterface(Ci.nsIPrefBranch2);
|
|
});
|
|
|
|
XPCOMUtils.defineLazyGetter(this, 'supportsString', function() {
|
|
return Cc["@mozilla.org/supports-string;1"]
|
|
.createInstance(Ci.nsISupportsString);
|
|
});
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
|
"resource://gre/modules/NetUtil.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "console",
|
|
"resource://gre/modules/devtools/Console.jsm");
|
|
|
|
const PREF_DIR = "devtools.commands.dir";
|
|
|
|
/**
|
|
* A place to store the names of the commands that we have added as a result of
|
|
* calling refreshAutoCommands(). Used by refreshAutoCommands to remove the
|
|
* added commands.
|
|
*/
|
|
let commands = [];
|
|
|
|
/**
|
|
* Exported API
|
|
*/
|
|
this.CmdCommands = {
|
|
/**
|
|
* Called to look in a directory pointed at by the devtools.commands.dir pref
|
|
* for *.mozcmd files which are then loaded.
|
|
* @param nsIPrincipal aSandboxPrincipal Scope object for the Sandbox in which
|
|
* we eval the script from the .mozcmd file. This should be a chrome window.
|
|
*/
|
|
refreshAutoCommands: function GC_refreshAutoCommands(aSandboxPrincipal) {
|
|
// First get rid of the last set of commands
|
|
commands.forEach(function(name) {
|
|
gcli.removeCommand(name);
|
|
});
|
|
|
|
let dirName = prefBranch.getComplexValue(PREF_DIR,
|
|
Ci.nsISupportsString).data.trim();
|
|
if (dirName == "") {
|
|
return;
|
|
}
|
|
|
|
// replaces ~ with the home directory path in unix and windows
|
|
if (dirName.indexOf("~") == 0) {
|
|
let dirService = Cc["@mozilla.org/file/directory_service;1"]
|
|
.getService(Ci.nsIProperties);
|
|
let homeDirFile = dirService.get("Home", Ci.nsIFile);
|
|
let homeDir = homeDirFile.path;
|
|
dirName = dirName.substr(1);
|
|
dirName = homeDir + dirName;
|
|
}
|
|
|
|
let statPromise = OS.File.stat(dirName);
|
|
statPromise = statPromise.then(
|
|
function onSuccess(stat) {
|
|
if (!stat.isDir) {
|
|
throw new Error('\'' + dirName + '\' is not a directory.');
|
|
} else {
|
|
return dirName;
|
|
}
|
|
},
|
|
function onFailure(reason) {
|
|
if (reason instanceof OS.File.Error && reason.becauseNoSuchFile) {
|
|
throw new Error('\'' + dirName + '\' does not exist.');
|
|
} else {
|
|
throw reason;
|
|
}
|
|
}
|
|
);
|
|
|
|
statPromise.then(
|
|
function onSuccess() {
|
|
let iterator = new OS.File.DirectoryIterator(dirName);
|
|
let iterPromise = iterator.forEach(
|
|
function onEntry(entry) {
|
|
if (entry.name.match(/.*\.mozcmd$/) && !entry.isDir) {
|
|
loadCommandFile(entry, aSandboxPrincipal);
|
|
}
|
|
}
|
|
);
|
|
|
|
iterPromise.then(
|
|
function onSuccess() {
|
|
iterator.close();
|
|
},
|
|
function onFailure(reason) {
|
|
iterator.close();
|
|
throw reason;
|
|
}
|
|
);
|
|
}
|
|
);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Load the commands from a single file
|
|
* @param OS.File.DirectoryIterator.Entry aFileEntry The DirectoryIterator
|
|
* Entry of the file containing the commands that we should read
|
|
* @param nsIPrincipal aSandboxPrincipal Scope object for the Sandbox in which
|
|
* we eval the script from the .mozcmd file. This should be a chrome window.
|
|
*/
|
|
function loadCommandFile(aFileEntry, aSandboxPrincipal) {
|
|
let readPromise = OS.File.read(aFileEntry.path);
|
|
readPromise = readPromise.then(
|
|
function onSuccess(array) {
|
|
let decoder = new TextDecoder();
|
|
let source = decoder.decode(array);
|
|
|
|
let sandbox = new Cu.Sandbox(aSandboxPrincipal, {
|
|
sandboxPrototype: aSandboxPrincipal,
|
|
wantXrays: false,
|
|
sandboxName: aFileEntry.path
|
|
});
|
|
let data = Cu.evalInSandbox(source, sandbox, "1.8", aFileEntry.name, 1);
|
|
|
|
if (!Array.isArray(data)) {
|
|
console.error("Command file '" + aFileEntry.name + "' does not have top level array.");
|
|
return;
|
|
}
|
|
|
|
data.forEach(function(commandSpec) {
|
|
gcli.addCommand(commandSpec);
|
|
commands.push(commandSpec.name);
|
|
});
|
|
},
|
|
function onError(reason) {
|
|
console.error("OS.File.read(" + aFileEntry.path + ") failed.");
|
|
throw reason;
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 'cmd' command
|
|
*/
|
|
gcli.addCommand({
|
|
name: "cmd",
|
|
get hidden() {
|
|
return !prefBranch.prefHasUserValue(PREF_DIR);
|
|
},
|
|
description: gcli.lookup("cmdDesc")
|
|
});
|
|
|
|
/**
|
|
* 'cmd refresh' command
|
|
*/
|
|
gcli.addCommand({
|
|
name: "cmd refresh",
|
|
description: gcli.lookup("cmdRefreshDesc"),
|
|
get hidden() {
|
|
return !prefBranch.prefHasUserValue(PREF_DIR);
|
|
},
|
|
exec: function(args, context) {
|
|
let chromeWindow = context.environment.chromeDocument.defaultView;
|
|
CmdCommands.refreshAutoCommands(chromeWindow);
|
|
|
|
let dirName = prefBranch.getComplexValue(PREF_DIR,
|
|
Ci.nsISupportsString).data.trim();
|
|
return gcli.lookupFormat("cmdStatus", [ commands.length, dirName ]);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* 'cmd setdir' command
|
|
*/
|
|
gcli.addCommand({
|
|
name: "cmd setdir",
|
|
description: gcli.lookup("cmdSetdirDesc"),
|
|
params: [
|
|
{
|
|
name: "directory",
|
|
description: gcli.lookup("cmdSetdirDirectoryDesc"),
|
|
type: {
|
|
name: "file",
|
|
filetype: "directory",
|
|
existing: "yes"
|
|
},
|
|
defaultValue: null
|
|
}
|
|
],
|
|
returnType: "string",
|
|
get hidden() {
|
|
return true; // !prefBranch.prefHasUserValue(PREF_DIR);
|
|
},
|
|
exec: function(args, context) {
|
|
supportsString.data = args.directory;
|
|
prefBranch.setComplexValue(PREF_DIR, Ci.nsISupportsString, supportsString);
|
|
|
|
let chromeWindow = context.environment.chromeDocument.defaultView;
|
|
CmdCommands.refreshAutoCommands(chromeWindow);
|
|
|
|
return gcli.lookupFormat("cmdStatus", [ commands.length, args.directory ]);
|
|
}
|
|
});
|
|
}(this));
|
|
|
|
/* CmdConsole -------------------------------------------------------------- */
|
|
|
|
(function(module) {
|
|
Object.defineProperty(this, "HUDService", {
|
|
get: function() {
|
|
return devtools.require("devtools/webconsole/hudservice");
|
|
},
|
|
configurable: true,
|
|
enumerable: true
|
|
});
|
|
|
|
/**
|
|
* 'console' command
|
|
*/
|
|
gcli.addCommand({
|
|
name: "console",
|
|
description: gcli.lookup("consoleDesc"),
|
|
manual: gcli.lookup("consoleManual")
|
|
});
|
|
|
|
/**
|
|
* 'console clear' command
|
|
*/
|
|
gcli.addCommand({
|
|
name: "console clear",
|
|
description: gcli.lookup("consoleclearDesc"),
|
|
exec: function Command_consoleClear(args, context) {
|
|
let hud = HUDService.getHudByWindow(context.environment.window);
|
|
// hud will be null if the web console has not been opened for this window
|
|
if (hud) {
|
|
hud.jsterm.clearOutput();
|
|
}
|
|
}
|
|
});
|
|
|
|
/**
|
|
* 'console close' command
|
|
*/
|
|
gcli.addCommand({
|
|
name: "console close",
|
|
description: gcli.lookup("consolecloseDesc"),
|
|
exec: function Command_consoleClose(args, context) {
|
|
let gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
|
|
let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
|
|
return gDevTools.closeToolbox(target);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* 'console open' command
|
|
*/
|
|
gcli.addCommand({
|
|
name: "console open",
|
|
description: gcli.lookup("consoleopenDesc"),
|
|
exec: function Command_consoleOpen(args, context) {
|
|
let gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
|
|
let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
|
|
return gDevTools.showToolbox(target, "webconsole");
|
|
}
|
|
});
|
|
}(this));
|
|
|
|
/* CmdCookie --------------------------------------------------------------- */
|
|
|
|
(function(module) {
|
|
XPCOMUtils.defineLazyModuleGetter(this, "console",
|
|
"resource://gre/modules/devtools/Console.jsm");
|
|
|
|
const cookieMgr = Cc["@mozilla.org/cookiemanager;1"]
|
|
.getService(Ci.nsICookieManager2);
|
|
|
|
/**
|
|
* The template for the 'cookie list' command.
|
|
*/
|
|
let cookieListHtml = "" +
|
|
"<ul class='gcli-cookielist-list'>" +
|
|
" <li foreach='cookie in ${cookies}'>" +
|
|
" <div>${cookie.name}=${cookie.value}</div>" +
|
|
" <table class='gcli-cookielist-detail'>" +
|
|
" <tr>" +
|
|
" <td>" + gcli.lookup("cookieListOutHost") + "</td>" +
|
|
" <td>${cookie.host}</td>" +
|
|
" </tr>" +
|
|
" <tr>" +
|
|
" <td>" + gcli.lookup("cookieListOutPath") + "</td>" +
|
|
" <td>${cookie.path}</td>" +
|
|
" </tr>" +
|
|
" <tr>" +
|
|
" <td>" + gcli.lookup("cookieListOutExpires") + "</td>" +
|
|
" <td>${cookie.expires}</td>" +
|
|
" </tr>" +
|
|
" <tr>" +
|
|
" <td>" + gcli.lookup("cookieListOutAttributes") + "</td>" +
|
|
" <td>${cookie.attrs}</td>" +
|
|
" </tr>" +
|
|
" <tr><td colspan='2'>" +
|
|
" <span class='gcli-out-shortcut' onclick='${onclick}'" +
|
|
" data-command='cookie set ${cookie.name} '" +
|
|
" >" + gcli.lookup("cookieListOutEdit") + "</span>" +
|
|
" <span class='gcli-out-shortcut'" +
|
|
" onclick='${onclick}' ondblclick='${ondblclick}'" +
|
|
" data-command='cookie remove ${cookie.name}'" +
|
|
" >" + gcli.lookup("cookieListOutRemove") + "</span>" +
|
|
" </td></tr>" +
|
|
" </table>" +
|
|
" </li>" +
|
|
"</ul>" +
|
|
"";
|
|
|
|
gcli.addConverter({
|
|
from: "cookies",
|
|
to: "view",
|
|
exec: function(cookies, context) {
|
|
if (cookies.length == 0) {
|
|
let host = context.environment.document.location.host;
|
|
let msg = gcli.lookupFormat("cookieListOutNoneHost", [ host ]);
|
|
return context.createView({ html: "<span>" + msg + "</span>" });
|
|
}
|
|
|
|
for (let cookie of cookies) {
|
|
cookie.expires = translateExpires(cookie.expires);
|
|
|
|
let noAttrs = !cookie.secure && !cookie.httpOnly && !cookie.sameDomain;
|
|
cookie.attrs = (cookie.secure ? 'secure' : ' ') +
|
|
(cookie.httpOnly ? 'httpOnly' : ' ') +
|
|
(cookie.sameDomain ? 'sameDomain' : ' ') +
|
|
(noAttrs ? gcli.lookup("cookieListOutNone") : ' ');
|
|
}
|
|
|
|
return context.createView({
|
|
html: cookieListHtml,
|
|
data: {
|
|
options: { allowEval: true },
|
|
cookies: cookies,
|
|
onclick: context.update,
|
|
ondblclick: context.updateExec
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* The cookie 'expires' value needs converting into something more readable
|
|
*/
|
|
function translateExpires(expires) {
|
|
if (expires == 0) {
|
|
return gcli.lookup("cookieListOutSession");
|
|
}
|
|
return new Date(expires).toLocaleString();
|
|
}
|
|
|
|
/**
|
|
* Check if a given cookie matches a given host
|
|
*/
|
|
function isCookieAtHost(cookie, host) {
|
|
if (cookie.host == null) {
|
|
return host == null;
|
|
}
|
|
if (cookie.host.startsWith(".")) {
|
|
return cookie.host === "." + host;
|
|
}
|
|
else {
|
|
return cookie.host == host;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 'cookie' command
|
|
*/
|
|
gcli.addCommand({
|
|
name: "cookie",
|
|
description: gcli.lookup("cookieDesc"),
|
|
manual: gcli.lookup("cookieManual")
|
|
});
|
|
|
|
/**
|
|
* 'cookie list' command
|
|
*/
|
|
gcli.addCommand({
|
|
name: "cookie list",
|
|
description: gcli.lookup("cookieListDesc"),
|
|
manual: gcli.lookup("cookieListManual"),
|
|
returnType: "cookies",
|
|
exec: function(args, context) {
|
|
let host = context.environment.document.location.host;
|
|
if (host == null || host == "") {
|
|
throw new Error(gcli.lookup("cookieListOutNonePage"));
|
|
}
|
|
|
|
let enm = cookieMgr.getCookiesFromHost(host);
|
|
|
|
let cookies = [];
|
|
while (enm.hasMoreElements()) {
|
|
let cookie = enm.getNext().QueryInterface(Ci.nsICookie);
|
|
if (isCookieAtHost(cookie, host)) {
|
|
cookies.push({
|
|
host: cookie.host,
|
|
name: cookie.name,
|
|
value: cookie.value,
|
|
path: cookie.path,
|
|
expires: cookie.expires,
|
|
secure: cookie.secure,
|
|
httpOnly: cookie.httpOnly,
|
|
sameDomain: cookie.sameDomain
|
|
});
|
|
}
|
|
}
|
|
|
|
return cookies;
|
|
}
|
|
});
|
|
|
|
/**
|
|
* 'cookie remove' command
|
|
*/
|
|
gcli.addCommand({
|
|
name: "cookie remove",
|
|
description: gcli.lookup("cookieRemoveDesc"),
|
|
manual: gcli.lookup("cookieRemoveManual"),
|
|
params: [
|
|
{
|
|
name: "name",
|
|
type: "string",
|
|
description: gcli.lookup("cookieRemoveKeyDesc"),
|
|
}
|
|
],
|
|
exec: function(args, context) {
|
|
let host = context.environment.document.location.host;
|
|
let enm = cookieMgr.getCookiesFromHost(host);
|
|
|
|
let cookies = [];
|
|
while (enm.hasMoreElements()) {
|
|
let cookie = enm.getNext().QueryInterface(Ci.nsICookie);
|
|
if (isCookieAtHost(cookie, host)) {
|
|
if (cookie.name == args.name) {
|
|
cookieMgr.remove(cookie.host, cookie.name, cookie.path, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
/**
|
|
* 'cookie set' command
|
|
*/
|
|
gcli.addCommand({
|
|
name: "cookie set",
|
|
description: gcli.lookup("cookieSetDesc"),
|
|
manual: gcli.lookup("cookieSetManual"),
|
|
params: [
|
|
{
|
|
name: "name",
|
|
type: "string",
|
|
description: gcli.lookup("cookieSetKeyDesc")
|
|
},
|
|
{
|
|
name: "value",
|
|
type: "string",
|
|
description: gcli.lookup("cookieSetValueDesc")
|
|
},
|
|
{
|
|
group: gcli.lookup("cookieSetOptionsDesc"),
|
|
params: [
|
|
{
|
|
name: "path",
|
|
type: { name: "string", allowBlank: true },
|
|
defaultValue: "/",
|
|
description: gcli.lookup("cookieSetPathDesc")
|
|
},
|
|
{
|
|
name: "domain",
|
|
type: "string",
|
|
defaultValue: null,
|
|
description: gcli.lookup("cookieSetDomainDesc")
|
|
},
|
|
{
|
|
name: "secure",
|
|
type: "boolean",
|
|
description: gcli.lookup("cookieSetSecureDesc")
|
|
},
|
|
{
|
|
name: "httpOnly",
|
|
type: "boolean",
|
|
description: gcli.lookup("cookieSetHttpOnlyDesc")
|
|
},
|
|
{
|
|
name: "session",
|
|
type: "boolean",
|
|
description: gcli.lookup("cookieSetSessionDesc")
|
|
},
|
|
{
|
|
name: "expires",
|
|
type: "string",
|
|
defaultValue: "Jan 17, 2038",
|
|
description: gcli.lookup("cookieSetExpiresDesc")
|
|
},
|
|
]
|
|
}
|
|
],
|
|
exec: function(args, context) {
|
|
let host = context.environment.document.location.host;
|
|
let time = Date.parse(args.expires) / 1000;
|
|
|
|
cookieMgr.add(args.domain ? "." + args.domain : host,
|
|
args.path ? args.path : "/",
|
|
args.name,
|
|
args.value,
|
|
args.secure,
|
|
args.httpOnly,
|
|
args.session,
|
|
time);
|
|
}
|
|
});
|
|
}(this));
|
|
|
|
/* CmdExport --------------------------------------------------------------- */
|
|
|
|
(function(module) {
|
|
/**
|
|
* 'export' command
|
|
*/
|
|
gcli.addCommand({
|
|
name: "export",
|
|
description: gcli.lookup("exportDesc"),
|
|
});
|
|
|
|
/**
|
|
* The 'export html' command. This command allows the user to export the page to
|
|
* HTML after they do DOM changes.
|
|
*/
|
|
gcli.addCommand({
|
|
name: "export html",
|
|
description: gcli.lookup("exportHtmlDesc"),
|
|
exec: function(args, context) {
|
|
let html = context.environment.document.documentElement.outerHTML;
|
|
let url = 'data:text/plain;charset=utf8,' + encodeURIComponent(html);
|
|
context.environment.window.open(url);
|
|
}
|
|
});
|
|
}(this));
|
|
|
|
/* CmdJsb ------------------------------------------------------------------ */
|
|
|
|
(function(module) {
|
|
const XMLHttpRequest =
|
|
Components.Constructor("@mozilla.org/xmlextras/xmlhttprequest;1");
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "js_beautify",
|
|
"resource:///modules/devtools/Jsbeautify.jsm");
|
|
|
|
/**
|
|
* jsb command.
|
|
*/
|
|
gcli.addCommand({
|
|
name: 'jsb',
|
|
description: gcli.lookup('jsbDesc'),
|
|
returnValue:'string',
|
|
params: [
|
|
{
|
|
name: 'url',
|
|
type: 'string',
|
|
description: gcli.lookup('jsbUrlDesc')
|
|
},
|
|
{
|
|
group: gcli.lookup("jsbOptionsDesc"),
|
|
params: [
|
|
{
|
|
name: 'indentSize',
|
|
type: 'number',
|
|
description: gcli.lookup('jsbIndentSizeDesc'),
|
|
manual: gcli.lookup('jsbIndentSizeManual'),
|
|
defaultValue: 2
|
|
},
|
|
{
|
|
name: 'indentChar',
|
|
type: {
|
|
name: 'selection',
|
|
lookup: [
|
|
{ name: "space", value: " " },
|
|
{ name: "tab", value: "\t" }
|
|
]
|
|
},
|
|
description: gcli.lookup('jsbIndentCharDesc'),
|
|
manual: gcli.lookup('jsbIndentCharManual'),
|
|
defaultValue: ' ',
|
|
},
|
|
{
|
|
name: 'doNotPreserveNewlines',
|
|
type: 'boolean',
|
|
description: gcli.lookup('jsbDoNotPreserveNewlinesDesc')
|
|
},
|
|
{
|
|
name: 'preserveMaxNewlines',
|
|
type: 'number',
|
|
description: gcli.lookup('jsbPreserveMaxNewlinesDesc'),
|
|
manual: gcli.lookup('jsbPreserveMaxNewlinesManual'),
|
|
defaultValue: -1
|
|
},
|
|
{
|
|
name: 'jslintHappy',
|
|
type: 'boolean',
|
|
description: gcli.lookup('jsbJslintHappyDesc'),
|
|
manual: gcli.lookup('jsbJslintHappyManual')
|
|
},
|
|
{
|
|
name: 'braceStyle',
|
|
type: {
|
|
name: 'selection',
|
|
data: ['collapse', 'expand', 'end-expand', 'expand-strict']
|
|
},
|
|
description: gcli.lookup('jsbBraceStyleDesc2'),
|
|
manual: gcli.lookup('jsbBraceStyleManual2'),
|
|
defaultValue: "collapse"
|
|
},
|
|
{
|
|
name: 'noSpaceBeforeConditional',
|
|
type: 'boolean',
|
|
description: gcli.lookup('jsbNoSpaceBeforeConditionalDesc')
|
|
},
|
|
{
|
|
name: 'unescapeStrings',
|
|
type: 'boolean',
|
|
description: gcli.lookup('jsbUnescapeStringsDesc'),
|
|
manual: gcli.lookup('jsbUnescapeStringsManual')
|
|
}
|
|
]
|
|
}
|
|
],
|
|
exec: function(args, context) {
|
|
let opts = {
|
|
indent_size: args.indentSize,
|
|
indent_char: args.indentChar,
|
|
preserve_newlines: !args.doNotPreserveNewlines,
|
|
max_preserve_newlines: args.preserveMaxNewlines == -1 ?
|
|
undefined : args.preserveMaxNewlines,
|
|
jslint_happy: args.jslintHappy,
|
|
brace_style: args.braceStyle,
|
|
space_before_conditional: !args.noSpaceBeforeConditional,
|
|
unescape_strings: args.unescapeStrings
|
|
};
|
|
|
|
let xhr = new XMLHttpRequest();
|
|
|
|
try {
|
|
xhr.open("GET", args.url, true);
|
|
} catch(e) {
|
|
return gcli.lookup('jsbInvalidURL');
|
|
}
|
|
|
|
let deferred = context.defer();
|
|
|
|
xhr.onreadystatechange = function(aEvt) {
|
|
if (xhr.readyState == 4) {
|
|
if (xhr.status == 200 || xhr.status == 0) {
|
|
let browserDoc = context.environment.chromeDocument;
|
|
let browserWindow = browserDoc.defaultView;
|
|
let gBrowser = browserWindow.gBrowser;
|
|
let result = js_beautify(xhr.responseText, opts);
|
|
|
|
browserWindow.Scratchpad.ScratchpadManager.openScratchpad({text: result});
|
|
|
|
deferred.resolve();
|
|
} else {
|
|
deferred.resolve("Unable to load page to beautify: " + args.url + " " +
|
|
xhr.status + " " + xhr.statusText);
|
|
}
|
|
};
|
|
}
|
|
xhr.send(null);
|
|
return deferred.promise;
|
|
}
|
|
});
|
|
}(this));
|
|
|
|
/* CmdPagemod -------------------------------------------------------------- */
|
|
|
|
(function(module) {
|
|
/**
|
|
* 'pagemod' command
|
|
*/
|
|
gcli.addCommand({
|
|
name: "pagemod",
|
|
description: gcli.lookup("pagemodDesc"),
|
|
});
|
|
|
|
/**
|
|
* The 'pagemod replace' command. This command allows the user to search and
|
|
* replace within text nodes and attributes.
|
|
*/
|
|
gcli.addCommand({
|
|
name: "pagemod replace",
|
|
description: gcli.lookup("pagemodReplaceDesc"),
|
|
params: [
|
|
{
|
|
name: "search",
|
|
type: "string",
|
|
description: gcli.lookup("pagemodReplaceSearchDesc"),
|
|
},
|
|
{
|
|
name: "replace",
|
|
type: "string",
|
|
description: gcli.lookup("pagemodReplaceReplaceDesc"),
|
|
},
|
|
{
|
|
name: "ignoreCase",
|
|
type: "boolean",
|
|
description: gcli.lookup("pagemodReplaceIgnoreCaseDesc"),
|
|
},
|
|
{
|
|
name: "selector",
|
|
type: "string",
|
|
description: gcli.lookup("pagemodReplaceSelectorDesc"),
|
|
defaultValue: "*:not(script):not(style):not(embed):not(object):not(frame):not(iframe):not(frameset)",
|
|
},
|
|
{
|
|
name: "root",
|
|
type: "node",
|
|
description: gcli.lookup("pagemodReplaceRootDesc"),
|
|
defaultValue: null,
|
|
},
|
|
{
|
|
name: "attrOnly",
|
|
type: "boolean",
|
|
description: gcli.lookup("pagemodReplaceAttrOnlyDesc"),
|
|
},
|
|
{
|
|
name: "contentOnly",
|
|
type: "boolean",
|
|
description: gcli.lookup("pagemodReplaceContentOnlyDesc"),
|
|
},
|
|
{
|
|
name: "attributes",
|
|
type: "string",
|
|
description: gcli.lookup("pagemodReplaceAttributesDesc"),
|
|
defaultValue: null,
|
|
},
|
|
],
|
|
exec: function(args, context) {
|
|
let searchTextNodes = !args.attrOnly;
|
|
let searchAttributes = !args.contentOnly;
|
|
let regexOptions = args.ignoreCase ? 'ig' : 'g';
|
|
let search = new RegExp(escapeRegex(args.search), regexOptions);
|
|
let attributeRegex = null;
|
|
if (args.attributes) {
|
|
attributeRegex = new RegExp(args.attributes, regexOptions);
|
|
}
|
|
|
|
let root = args.root || context.environment.document;
|
|
let elements = root.querySelectorAll(args.selector);
|
|
elements = Array.prototype.slice.call(elements);
|
|
|
|
let replacedTextNodes = 0;
|
|
let replacedAttributes = 0;
|
|
|
|
function replaceAttribute() {
|
|
replacedAttributes++;
|
|
return args.replace;
|
|
}
|
|
function replaceTextNode() {
|
|
replacedTextNodes++;
|
|
return args.replace;
|
|
}
|
|
|
|
for (let i = 0; i < elements.length; i++) {
|
|
let element = elements[i];
|
|
if (searchTextNodes) {
|
|
for (let y = 0; y < element.childNodes.length; y++) {
|
|
let node = element.childNodes[y];
|
|
if (node.nodeType == node.TEXT_NODE) {
|
|
node.textContent = node.textContent.replace(search, replaceTextNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (searchAttributes) {
|
|
if (!element.attributes) {
|
|
continue;
|
|
}
|
|
for (let y = 0; y < element.attributes.length; y++) {
|
|
let attr = element.attributes[y];
|
|
if (!attributeRegex || attributeRegex.test(attr.name)) {
|
|
attr.value = attr.value.replace(search, replaceAttribute);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return gcli.lookupFormat("pagemodReplaceResult",
|
|
[elements.length, replacedTextNodes,
|
|
replacedAttributes]);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* 'pagemod remove' command
|
|
*/
|
|
gcli.addCommand({
|
|
name: "pagemod remove",
|
|
description: gcli.lookup("pagemodRemoveDesc"),
|
|
});
|
|
|
|
|
|
/**
|
|
* The 'pagemod remove element' command.
|
|
*/
|
|
gcli.addCommand({
|
|
name: "pagemod remove element",
|
|
description: gcli.lookup("pagemodRemoveElementDesc"),
|
|
params: [
|
|
{
|
|
name: "search",
|
|
type: "string",
|
|
description: gcli.lookup("pagemodRemoveElementSearchDesc"),
|
|
},
|
|
{
|
|
name: "root",
|
|
type: "node",
|
|
description: gcli.lookup("pagemodRemoveElementRootDesc"),
|
|
defaultValue: null,
|
|
},
|
|
{
|
|
name: 'stripOnly',
|
|
type: 'boolean',
|
|
description: gcli.lookup("pagemodRemoveElementStripOnlyDesc"),
|
|
},
|
|
{
|
|
name: 'ifEmptyOnly',
|
|
type: 'boolean',
|
|
description: gcli.lookup("pagemodRemoveElementIfEmptyOnlyDesc"),
|
|
},
|
|
],
|
|
exec: function(args, context) {
|
|
let root = args.root || context.environment.document;
|
|
let elements = Array.prototype.slice.call(root.querySelectorAll(args.search));
|
|
|
|
let removed = 0;
|
|
for (let i = 0; i < elements.length; i++) {
|
|
let element = elements[i];
|
|
let parentNode = element.parentNode;
|
|
if (!parentNode || !element.removeChild) {
|
|
continue;
|
|
}
|
|
if (args.stripOnly) {
|
|
while (element.hasChildNodes()) {
|
|
parentNode.insertBefore(element.childNodes[0], element);
|
|
}
|
|
}
|
|
if (!args.ifEmptyOnly || !element.hasChildNodes()) {
|
|
element.parentNode.removeChild(element);
|
|
removed++;
|
|
}
|
|
}
|
|
|
|
return gcli.lookupFormat("pagemodRemoveElementResultMatchedAndRemovedElements",
|
|
[elements.length, removed]);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* The 'pagemod remove attribute' command.
|
|
*/
|
|
gcli.addCommand({
|
|
name: "pagemod remove attribute",
|
|
description: gcli.lookup("pagemodRemoveAttributeDesc"),
|
|
params: [
|
|
{
|
|
name: "searchAttributes",
|
|
type: "string",
|
|
description: gcli.lookup("pagemodRemoveAttributeSearchAttributesDesc"),
|
|
},
|
|
{
|
|
name: "searchElements",
|
|
type: "string",
|
|
description: gcli.lookup("pagemodRemoveAttributeSearchElementsDesc"),
|
|
},
|
|
{
|
|
name: "root",
|
|
type: "node",
|
|
description: gcli.lookup("pagemodRemoveAttributeRootDesc"),
|
|
defaultValue: null,
|
|
},
|
|
{
|
|
name: "ignoreCase",
|
|
type: "boolean",
|
|
description: gcli.lookup("pagemodRemoveAttributeIgnoreCaseDesc"),
|
|
},
|
|
],
|
|
exec: function(args, context) {
|
|
let root = args.root || context.environment.document;
|
|
let regexOptions = args.ignoreCase ? 'ig' : 'g';
|
|
let attributeRegex = new RegExp(args.searchAttributes, regexOptions);
|
|
let elements = root.querySelectorAll(args.searchElements);
|
|
elements = Array.prototype.slice.call(elements);
|
|
|
|
let removed = 0;
|
|
for (let i = 0; i < elements.length; i++) {
|
|
let element = elements[i];
|
|
if (!element.attributes) {
|
|
continue;
|
|
}
|
|
|
|
var attrs = Array.prototype.slice.call(element.attributes);
|
|
for (let y = 0; y < attrs.length; y++) {
|
|
let attr = attrs[y];
|
|
if (attributeRegex.test(attr.name)) {
|
|
element.removeAttribute(attr.name);
|
|
removed++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return gcli.lookupFormat("pagemodRemoveAttributeResult",
|
|
[elements.length, removed]);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Make a given string safe to use in a regular expression.
|
|
*
|
|
* @param string aString
|
|
* The string you want to use in a regex.
|
|
* @return string
|
|
* The equivalent of |aString| but safe to use in a regex.
|
|
*/
|
|
function escapeRegex(aString) {
|
|
return aString.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
}
|
|
}(this));
|
|
|
|
/* CmdTools -------------------------------------------------------------- */
|
|
|
|
(function(module) {
|
|
gcli.addCommand({
|
|
name: "tools",
|
|
description: gcli.lookupFormat("toolsDesc2", [BRAND_SHORT_NAME]),
|
|
manual: gcli.lookupFormat("toolsManual2", [BRAND_SHORT_NAME]),
|
|
get hidden() gcli.hiddenByChromePref(),
|
|
});
|
|
|
|
gcli.addCommand({
|
|
name: "tools srcdir",
|
|
description: gcli.lookup("toolsSrcdirDesc"),
|
|
manual: gcli.lookupFormat("toolsSrcdirManual2", [BRAND_SHORT_NAME]),
|
|
get hidden() gcli.hiddenByChromePref(),
|
|
params: [
|
|
{
|
|
name: "srcdir",
|
|
type: "string" /* {
|
|
name: "file",
|
|
filetype: "directory",
|
|
existing: "yes"
|
|
} */,
|
|
description: gcli.lookup("toolsSrcdirDir")
|
|
}
|
|
],
|
|
returnType: "string",
|
|
exec: function(args, context) {
|
|
let clobber = OS.Path.join(args.srcdir, "CLOBBER");
|
|
return OS.File.exists(clobber).then(function(exists) {
|
|
if (exists) {
|
|
let str = Cc["@mozilla.org/supports-string;1"]
|
|
.createInstance(Ci.nsISupportsString);
|
|
str.data = args.srcdir;
|
|
Services.prefs.setComplexValue("devtools.loader.srcdir",
|
|
Ci.nsISupportsString, str);
|
|
devtools.reload();
|
|
|
|
let msg = gcli.lookupFormat("toolsSrcdirReloaded", [args.srcdir]);
|
|
throw new Error(msg);
|
|
}
|
|
|
|
return gcli.lookupFormat("toolsSrcdirNotFound", [args.srcdir]);
|
|
});
|
|
}
|
|
});
|
|
|
|
gcli.addCommand({
|
|
name: "tools builtin",
|
|
description: gcli.lookup("toolsBuiltinDesc"),
|
|
manual: gcli.lookup("toolsBuiltinManual"),
|
|
get hidden() gcli.hiddenByChromePref(),
|
|
returnType: "string",
|
|
exec: function(args, context) {
|
|
Services.prefs.clearUserPref("devtools.loader.srcdir");
|
|
devtools.reload();
|
|
return gcli.lookup("toolsBuiltinReloaded");
|
|
}
|
|
});
|
|
|
|
gcli.addCommand({
|
|
name: "tools reload",
|
|
description: gcli.lookup("toolsReloadDesc"),
|
|
get hidden() gcli.hiddenByChromePref() || !Services.prefs.prefHasUserValue("devtools.loader.srcdir"),
|
|
|
|
returnType: "string",
|
|
exec: function(args, context) {
|
|
devtools.reload();
|
|
return gcli.lookup("toolsReloaded2");
|
|
}
|
|
});
|
|
}(this));
|
|
|
|
/* CmdRestart -------------------------------------------------------------- */
|
|
|
|
(function(module) {
|
|
/**
|
|
* Restart command
|
|
*
|
|
* @param boolean nocache
|
|
* Disables loading content from cache upon restart.
|
|
*
|
|
* Examples :
|
|
* >> restart
|
|
* - restarts browser immediately
|
|
* >> restart --nocache
|
|
* - restarts immediately and starts Firefox without using cache
|
|
*/
|
|
gcli.addCommand({
|
|
name: "restart",
|
|
description: gcli.lookupFormat("restartBrowserDesc", [BRAND_SHORT_NAME]),
|
|
params: [
|
|
{
|
|
name: "nocache",
|
|
type: "boolean",
|
|
description: gcli.lookup("restartBrowserNocacheDesc")
|
|
}
|
|
],
|
|
returnType: "string",
|
|
exec: function Restart(args, context) {
|
|
let canceled = Cc["@mozilla.org/supports-PRBool;1"]
|
|
.createInstance(Ci.nsISupportsPRBool);
|
|
Services.obs.notifyObservers(canceled, "quit-application-requested", "restart");
|
|
if (canceled.data) {
|
|
return gcli.lookup("restartBrowserRequestCancelled");
|
|
}
|
|
|
|
// disable loading content from cache.
|
|
if (args.nocache) {
|
|
Services.appinfo.invalidateCachesOnRestart();
|
|
}
|
|
|
|
// restart
|
|
Cc['@mozilla.org/toolkit/app-startup;1']
|
|
.getService(Ci.nsIAppStartup)
|
|
.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
|
|
return gcli.lookupFormat("restartBrowserRestarting", [BRAND_SHORT_NAME]);
|
|
}
|
|
});
|
|
}(this));
|
|
|
|
/* CmdScreenshot ----------------------------------------------------------- */
|
|
|
|
(function(module) {
|
|
XPCOMUtils.defineLazyModuleGetter(this, "LayoutHelpers",
|
|
"resource://gre/modules/devtools/LayoutHelpers.jsm");
|
|
|
|
// String used as an indication to generate default file name in the following
|
|
// format: "Screen Shot yyyy-mm-dd at HH.MM.SS.png"
|
|
const FILENAME_DEFAULT_VALUE = " ";
|
|
|
|
/**
|
|
* 'screenshot' command
|
|
*/
|
|
gcli.addCommand({
|
|
name: "screenshot",
|
|
description: gcli.lookup("screenshotDesc"),
|
|
manual: gcli.lookup("screenshotManual"),
|
|
returnType: "dom",
|
|
params: [
|
|
{
|
|
name: "filename",
|
|
type: "string",
|
|
defaultValue: FILENAME_DEFAULT_VALUE,
|
|
description: gcli.lookup("screenshotFilenameDesc"),
|
|
manual: gcli.lookup("screenshotFilenameManual")
|
|
},
|
|
{
|
|
group: gcli.lookup("screenshotGroupOptions"),
|
|
params: [
|
|
{
|
|
name: "clipboard",
|
|
type: "boolean",
|
|
description: gcli.lookup("screenshotClipboardDesc"),
|
|
manual: gcli.lookup("screenshotClipboardManual")
|
|
},
|
|
{
|
|
name: "chrome",
|
|
type: "boolean",
|
|
description: gcli.lookupFormat("screenshotChromeDesc2", [BRAND_SHORT_NAME]),
|
|
manual: gcli.lookupFormat("screenshotChromeManual2", [BRAND_SHORT_NAME])
|
|
},
|
|
{
|
|
name: "delay",
|
|
type: { name: "number", min: 0 },
|
|
defaultValue: 0,
|
|
description: gcli.lookup("screenshotDelayDesc"),
|
|
manual: gcli.lookup("screenshotDelayManual")
|
|
},
|
|
{
|
|
name: "fullpage",
|
|
type: "boolean",
|
|
description: gcli.lookup("screenshotFullPageDesc"),
|
|
manual: gcli.lookup("screenshotFullPageManual")
|
|
},
|
|
{
|
|
name: "selector",
|
|
type: "node",
|
|
defaultValue: null,
|
|
description: gcli.lookup("inspectNodeDesc"),
|
|
manual: gcli.lookup("inspectNodeManual")
|
|
}
|
|
]
|
|
}
|
|
],
|
|
exec: function Command_screenshot(args, context) {
|
|
if (args.chrome && args.selector) {
|
|
// Node screenshot with chrome option does not work as inteded
|
|
// Refer https://bugzilla.mozilla.org/show_bug.cgi?id=659268#c7
|
|
// throwing for now.
|
|
throw new Error(gcli.lookup("screenshotSelectorChromeConflict"));
|
|
}
|
|
var document = args.chrome? context.environment.chromeDocument
|
|
: context.environment.document;
|
|
var deferred = context.defer();
|
|
if (args.delay > 0) {
|
|
document.defaultView.setTimeout(function Command_screenshotDelay() {
|
|
let promise = this.grabScreen(document, args.filename, args.clipboard,
|
|
args.fullpage);
|
|
promise.then(deferred.resolve, deferred.reject);
|
|
}.bind(this), args.delay * 1000);
|
|
}
|
|
else {
|
|
let promise = this.grabScreen(document, args.filename, args.clipboard,
|
|
args.fullpage, args.selector);
|
|
promise.then(deferred.resolve, deferred.reject);
|
|
}
|
|
return deferred.promise;
|
|
},
|
|
grabScreen: function(document, filename, clipboard, fullpage, node) {
|
|
return Task.spawn(function() {
|
|
let window = document.defaultView;
|
|
let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
|
let left = 0;
|
|
let top = 0;
|
|
let width;
|
|
let height;
|
|
let div = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
|
|
|
|
if (!fullpage) {
|
|
if (!node) {
|
|
left = window.scrollX;
|
|
top = window.scrollY;
|
|
width = window.innerWidth;
|
|
height = window.innerHeight;
|
|
} else {
|
|
let lh = new LayoutHelpers(window);
|
|
let rect = lh.getRect(node, window);
|
|
top = rect.top;
|
|
left = rect.left;
|
|
width = rect.width;
|
|
height = rect.height;
|
|
}
|
|
} else {
|
|
width = window.innerWidth + window.scrollMaxX;
|
|
height = window.innerHeight + window.scrollMaxY;
|
|
}
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
|
|
let ctx = canvas.getContext("2d");
|
|
ctx.drawWindow(window, left, top, width, height, "#fff");
|
|
let data = canvas.toDataURL("image/png", "");
|
|
|
|
let loadContext = document.defaultView
|
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIWebNavigation)
|
|
.QueryInterface(Ci.nsILoadContext);
|
|
|
|
if (clipboard) {
|
|
try {
|
|
let io = Cc["@mozilla.org/network/io-service;1"]
|
|
.getService(Ci.nsIIOService);
|
|
let channel = io.newChannel(data, null, null);
|
|
let input = channel.open();
|
|
let imgTools = Cc["@mozilla.org/image/tools;1"]
|
|
.getService(Ci.imgITools);
|
|
|
|
let container = {};
|
|
imgTools.decodeImageData(input, channel.contentType, container);
|
|
|
|
let wrapped = Cc["@mozilla.org/supports-interface-pointer;1"]
|
|
.createInstance(Ci.nsISupportsInterfacePointer);
|
|
wrapped.data = container.value;
|
|
|
|
let trans = Cc["@mozilla.org/widget/transferable;1"]
|
|
.createInstance(Ci.nsITransferable);
|
|
trans.init(loadContext);
|
|
trans.addDataFlavor(channel.contentType);
|
|
trans.setTransferData(channel.contentType, wrapped, -1);
|
|
|
|
let clipid = Ci.nsIClipboard;
|
|
let clip = Cc["@mozilla.org/widget/clipboard;1"].getService(clipid);
|
|
clip.setData(trans, null, clipid.kGlobalClipboard);
|
|
div.textContent = gcli.lookup("screenshotCopied");
|
|
}
|
|
catch (ex) {
|
|
div.textContent = gcli.lookup("screenshotErrorCopying");
|
|
}
|
|
throw new Task.Result(div);
|
|
}
|
|
|
|
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
|
|
|
|
// Create a name for the file if not present
|
|
if (filename == FILENAME_DEFAULT_VALUE) {
|
|
let date = new Date();
|
|
let dateString = date.getFullYear() + "-" + (date.getMonth() + 1) +
|
|
"-" + date.getDate();
|
|
dateString = dateString.split("-").map(function(part) {
|
|
if (part.length == 1) {
|
|
part = "0" + part;
|
|
}
|
|
return part;
|
|
}).join("-");
|
|
let timeString = date.toTimeString().replace(/:/g, ".").split(" ")[0];
|
|
filename = gcli.lookupFormat("screenshotGeneratedFilename",
|
|
[dateString, timeString]) + ".png";
|
|
}
|
|
// Check there is a .png extension to filename
|
|
else if (!filename.match(/.png$/i)) {
|
|
filename += ".png";
|
|
}
|
|
// If the filename is relative, tack it onto the download directory
|
|
if (!filename.match(/[\\\/]/)) {
|
|
let preferredDir = yield Downloads.getPreferredDownloadsDirectory();
|
|
filename = OS.Path.join(preferredDir, filename);
|
|
}
|
|
|
|
try {
|
|
file.initWithPath(filename);
|
|
} catch (ex) {
|
|
div.textContent = gcli.lookup("screenshotErrorSavingToFile") + " " + filename;
|
|
throw new Task.Result(div);
|
|
}
|
|
|
|
let ioService = Cc["@mozilla.org/network/io-service;1"]
|
|
.getService(Ci.nsIIOService);
|
|
|
|
let Persist = Ci.nsIWebBrowserPersist;
|
|
let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
|
|
.createInstance(Persist);
|
|
persist.persistFlags = Persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
|
|
Persist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
|
|
|
|
let source = ioService.newURI(data, "UTF8", null);
|
|
persist.saveURI(source, null, null, null, null, file, loadContext);
|
|
|
|
div.textContent = gcli.lookup("screenshotSavedToFile") + " \"" + filename +
|
|
"\"";
|
|
div.addEventListener("click", function openFile() {
|
|
div.removeEventListener("click", openFile);
|
|
file.reveal();
|
|
});
|
|
div.style.cursor = "pointer";
|
|
let image = document.createElement("div");
|
|
let previewHeight = parseInt(256*height/width);
|
|
image.setAttribute("style",
|
|
"width:256px; height:" + previewHeight + "px;" +
|
|
"max-height: 256px;" +
|
|
"background-image: url('" + data + "');" +
|
|
"background-size: 256px " + previewHeight + "px;" +
|
|
"margin: 4px; display: block");
|
|
div.appendChild(image);
|
|
throw new Task.Result(div);
|
|
});
|
|
}
|
|
});
|
|
}(this));
|
|
|
|
|
|
/* Remoting ----------------------------------------------------------- */
|
|
|
|
const { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
|
|
|
|
/**
|
|
* 'listen' command
|
|
*/
|
|
gcli.addCommand({
|
|
name: "listen",
|
|
description: gcli.lookup("listenDesc"),
|
|
manual: gcli.lookupFormat("listenManual2", [BRAND_SHORT_NAME]),
|
|
params: [
|
|
{
|
|
name: "port",
|
|
type: "number",
|
|
get defaultValue() {
|
|
return Services.prefs.getIntPref("devtools.debugger.chrome-debugging-port");
|
|
},
|
|
description: gcli.lookup("listenPortDesc"),
|
|
}
|
|
],
|
|
exec: function Command_screenshot(args, context) {
|
|
if (!DebuggerServer.initialized) {
|
|
DebuggerServer.init();
|
|
DebuggerServer.addBrowserActors();
|
|
}
|
|
var reply = DebuggerServer.openListener(args.port);
|
|
if (!reply) {
|
|
throw new Error(gcli.lookup("listenDisabledOutput"));
|
|
}
|
|
|
|
if (DebuggerServer.initialized) {
|
|
return gcli.lookupFormat("listenInitOutput", [ '' + args.port ]);
|
|
}
|
|
|
|
return gcli.lookup("listenNoInitOutput");
|
|
},
|
|
});
|
|
|
|
|
|
/* CmdPaintFlashing ------------------------------------------------------- */
|
|
|
|
(function(module) {
|
|
/**
|
|
* 'paintflashing' command
|
|
*/
|
|
gcli.addCommand({
|
|
name: 'paintflashing',
|
|
description: gcli.lookup('paintflashingDesc')
|
|
});
|
|
|
|
gcli.addCommand({
|
|
name: 'paintflashing on',
|
|
description: gcli.lookup('paintflashingOnDesc'),
|
|
manual: gcli.lookup('paintflashingManual'),
|
|
params: [{
|
|
group: "options",
|
|
params: [
|
|
{
|
|
type: "boolean",
|
|
name: "chrome",
|
|
get hidden() gcli.hiddenByChromePref(),
|
|
description: gcli.lookup("paintflashingChromeDesc"),
|
|
}
|
|
]
|
|
}],
|
|
exec: function(args, context) {
|
|
var window = args.chrome ?
|
|
context.environment.chromeWindow :
|
|
context.environment.window;
|
|
|
|
window.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIDOMWindowUtils)
|
|
.paintFlashing = true;
|
|
onPaintFlashingChanged(context);
|
|
}
|
|
});
|
|
|
|
gcli.addCommand({
|
|
name: 'paintflashing off',
|
|
description: gcli.lookup('paintflashingOffDesc'),
|
|
manual: gcli.lookup('paintflashingManual'),
|
|
params: [{
|
|
group: "options",
|
|
params: [
|
|
{
|
|
type: "boolean",
|
|
name: "chrome",
|
|
get hidden() gcli.hiddenByChromePref(),
|
|
description: gcli.lookup("paintflashingChromeDesc"),
|
|
}
|
|
]
|
|
}],
|
|
exec: function(args, context) {
|
|
var window = args.chrome ?
|
|
context.environment.chromeWindow :
|
|
context.environment.window;
|
|
|
|
window.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIDOMWindowUtils)
|
|
.paintFlashing = false;
|
|
onPaintFlashingChanged(context);
|
|
}
|
|
});
|
|
|
|
gcli.addCommand({
|
|
name: 'paintflashing toggle',
|
|
hidden: true,
|
|
buttonId: "command-button-paintflashing",
|
|
buttonClass: "command-button",
|
|
state: {
|
|
isChecked: function(aTarget) {
|
|
if (aTarget.isLocalTab) {
|
|
let window = aTarget.tab.linkedBrowser.contentWindow;
|
|
let wUtils = window.QueryInterface(Ci.nsIInterfaceRequestor).
|
|
getInterface(Ci.nsIDOMWindowUtils);
|
|
return wUtils.paintFlashing;
|
|
} else {
|
|
throw new Error("Unsupported target");
|
|
}
|
|
},
|
|
onChange: function(aTarget, aChangeHandler) {
|
|
eventEmitter.on("changed", aChangeHandler);
|
|
},
|
|
offChange: function(aTarget, aChangeHandler) {
|
|
eventEmitter.off("changed", aChangeHandler);
|
|
},
|
|
},
|
|
tooltipText: gcli.lookup("paintflashingTooltip"),
|
|
description: gcli.lookup('paintflashingToggleDesc'),
|
|
manual: gcli.lookup('paintflashingManual'),
|
|
exec: function(args, context) {
|
|
var window = context.environment.window;
|
|
var wUtils = window.QueryInterface(Ci.nsIInterfaceRequestor).
|
|
getInterface(Ci.nsIDOMWindowUtils);
|
|
wUtils.paintFlashing = !wUtils.paintFlashing;
|
|
onPaintFlashingChanged(context);
|
|
}
|
|
});
|
|
|
|
let eventEmitter = new EventEmitter();
|
|
function onPaintFlashingChanged(context) {
|
|
var gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
|
|
var tab = gBrowser.selectedTab;
|
|
eventEmitter.emit("changed", tab);
|
|
function fireChange() {
|
|
eventEmitter.emit("changed", tab);
|
|
}
|
|
var target = devtools.TargetFactory.forTab(tab);
|
|
target.off("navigate", fireChange);
|
|
target.once("navigate", fireChange);
|
|
|
|
var window = context.environment.window;
|
|
var wUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIDOMWindowUtils);
|
|
if (wUtils.paintFlashing) {
|
|
telemetry.toolOpened("paintflashing");
|
|
} else {
|
|
telemetry.toolClosed("paintflashing");
|
|
}
|
|
}
|
|
}(this));
|
|
|
|
/* CmdAppCache ------------------------------------------------------- */
|
|
|
|
(function(module) {
|
|
/**
|
|
* 'appcache' command
|
|
*/
|
|
|
|
gcli.addCommand({
|
|
name: 'appcache',
|
|
description: gcli.lookup('appCacheDesc')
|
|
});
|
|
|
|
gcli.addConverter({
|
|
from: "appcacheerrors",
|
|
to: "view",
|
|
exec: function([errors, manifestURI], context) {
|
|
if (errors.length == 0) {
|
|
return context.createView({
|
|
html: "<span>" + gcli.lookup("appCacheValidatedSuccessfully") + "</span>"
|
|
});
|
|
}
|
|
|
|
let appcacheValidateHtml =
|
|
"<h4>Manifest URI: ${manifestURI}</h4>" +
|
|
"<ol>" +
|
|
" <li foreach='error in ${errors}'>" +
|
|
" ${error.msg}" +
|
|
" </li>" +
|
|
"</ol>";
|
|
|
|
return context.createView({
|
|
html: "<div>" + appcacheValidateHtml + "</div>",
|
|
data: {
|
|
errors: errors,
|
|
manifestURI: manifestURI
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
gcli.addCommand({
|
|
name: 'appcache validate',
|
|
description: gcli.lookup('appCacheValidateDesc'),
|
|
manual: gcli.lookup('appCacheValidateManual'),
|
|
returnType: 'appcacheerrors',
|
|
params: [{
|
|
group: "options",
|
|
params: [
|
|
{
|
|
type: "string",
|
|
name: "uri",
|
|
description: gcli.lookup("appCacheValidateUriDesc"),
|
|
defaultValue: null,
|
|
}
|
|
]
|
|
}],
|
|
exec: function(args, context) {
|
|
let utils;
|
|
let deferred = context.defer();
|
|
|
|
if (args.uri) {
|
|
utils = new AppCacheUtils(args.uri);
|
|
} else {
|
|
utils = new AppCacheUtils(context.environment.document);
|
|
}
|
|
|
|
utils.validateManifest().then(function(errors) {
|
|
deferred.resolve([errors, utils.manifestURI || "-"]);
|
|
});
|
|
|
|
return deferred.promise;
|
|
}
|
|
});
|
|
|
|
gcli.addCommand({
|
|
name: 'appcache clear',
|
|
description: gcli.lookup('appCacheClearDesc'),
|
|
manual: gcli.lookup('appCacheClearManual'),
|
|
exec: function(args, context) {
|
|
let utils = new AppCacheUtils(args.uri);
|
|
utils.clearAll();
|
|
|
|
return gcli.lookup("appCacheClearCleared");
|
|
}
|
|
});
|
|
|
|
let appcacheListEntries = "" +
|
|
"<ul class='gcli-appcache-list'>" +
|
|
" <li foreach='entry in ${entries}'>" +
|
|
" <table class='gcli-appcache-detail'>" +
|
|
" <tr>" +
|
|
" <td>" + gcli.lookup("appCacheListKey") + "</td>" +
|
|
" <td>${entry.key}</td>" +
|
|
" </tr>" +
|
|
" <tr>" +
|
|
" <td>" + gcli.lookup("appCacheListFetchCount") + "</td>" +
|
|
" <td>${entry.fetchCount}</td>" +
|
|
" </tr>" +
|
|
" <tr>" +
|
|
" <td>" + gcli.lookup("appCacheListLastFetched") + "</td>" +
|
|
" <td>${entry.lastFetched}</td>" +
|
|
" </tr>" +
|
|
" <tr>" +
|
|
" <td>" + gcli.lookup("appCacheListLastModified") + "</td>" +
|
|
" <td>${entry.lastModified}</td>" +
|
|
" </tr>" +
|
|
" <tr>" +
|
|
" <td>" + gcli.lookup("appCacheListExpirationTime") + "</td>" +
|
|
" <td>${entry.expirationTime}</td>" +
|
|
" </tr>" +
|
|
" <tr>" +
|
|
" <td>" + gcli.lookup("appCacheListDataSize") + "</td>" +
|
|
" <td>${entry.dataSize}</td>" +
|
|
" </tr>" +
|
|
" <tr>" +
|
|
" <td>" + gcli.lookup("appCacheListDeviceID") + "</td>" +
|
|
" <td>${entry.deviceID} <span class='gcli-out-shortcut' " +
|
|
"onclick='${onclick}' ondblclick='${ondblclick}' " +
|
|
"data-command='appcache viewentry ${entry.key}'" +
|
|
">" + gcli.lookup("appCacheListViewEntry") + "</span>" +
|
|
" </td>" +
|
|
" </tr>" +
|
|
" </table>" +
|
|
" </li>" +
|
|
"</ul>";
|
|
|
|
gcli.addConverter({
|
|
from: "appcacheentries",
|
|
to: "view",
|
|
exec: function(entries, context) {
|
|
return context.createView({
|
|
html: appcacheListEntries,
|
|
data: {
|
|
entries: entries,
|
|
onclick: context.update,
|
|
ondblclick: context.updateExec
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
gcli.addCommand({
|
|
name: 'appcache list',
|
|
description: gcli.lookup('appCacheListDesc'),
|
|
manual: gcli.lookup('appCacheListManual'),
|
|
returnType: "appcacheentries",
|
|
params: [{
|
|
group: "options",
|
|
params: [
|
|
{
|
|
type: "string",
|
|
name: "search",
|
|
description: gcli.lookup("appCacheListSearchDesc"),
|
|
defaultValue: null,
|
|
},
|
|
]
|
|
}],
|
|
exec: function(args, context) {
|
|
let utils = new AppCacheUtils();
|
|
return utils.listEntries(args.search);
|
|
}
|
|
});
|
|
|
|
gcli.addCommand({
|
|
name: 'appcache viewentry',
|
|
description: gcli.lookup('appCacheViewEntryDesc'),
|
|
manual: gcli.lookup('appCacheViewEntryManual'),
|
|
params: [
|
|
{
|
|
type: "string",
|
|
name: "key",
|
|
description: gcli.lookup("appCacheViewEntryKey"),
|
|
defaultValue: null,
|
|
}
|
|
],
|
|
exec: function(args, context) {
|
|
let utils = new AppCacheUtils();
|
|
return utils.viewEntry(args.key);
|
|
}
|
|
});
|
|
}(this));
|
|
|
|
/* CmdMedia ------------------------------------------------------- */
|
|
|
|
(function(module) {
|
|
/**
|
|
* 'media' command
|
|
*/
|
|
|
|
gcli.addCommand({
|
|
name: "media",
|
|
description: gcli.lookup("mediaDesc")
|
|
});
|
|
|
|
gcli.addCommand({
|
|
name: "media emulate",
|
|
description: gcli.lookup("mediaEmulateDesc"),
|
|
manual: gcli.lookup("mediaEmulateManual"),
|
|
params: [
|
|
{
|
|
name: "type",
|
|
description: gcli.lookup("mediaEmulateType"),
|
|
type: {
|
|
name: "selection",
|
|
data: ["braille", "embossed", "handheld", "print", "projection",
|
|
"screen", "speech", "tty", "tv"]
|
|
}
|
|
}
|
|
],
|
|
exec: function(args, context) {
|
|
let markupDocumentViewer = context.environment.chromeWindow
|
|
.gBrowser.markupDocumentViewer;
|
|
markupDocumentViewer.emulateMedium(args.type);
|
|
}
|
|
});
|
|
|
|
gcli.addCommand({
|
|
name: "media reset",
|
|
description: gcli.lookup("mediaResetDesc"),
|
|
manual: gcli.lookup("mediaEmulateManual"),
|
|
exec: function(args, context) {
|
|
let markupDocumentViewer = context.environment.chromeWindow
|
|
.gBrowser.markupDocumentViewer;
|
|
markupDocumentViewer.stopEmulatingMedium();
|
|
}
|
|
});
|
|
}(this));
|
|
|
|
/* CmdSplitConsole ------------------------------------------------------- */
|
|
|
|
(function(module) {
|
|
/**
|
|
* 'splitconsole' command (hidden)
|
|
*/
|
|
|
|
gcli.addCommand({
|
|
name: 'splitconsole',
|
|
hidden: true,
|
|
buttonId: "command-button-splitconsole",
|
|
buttonClass: "command-button",
|
|
tooltipText: gcli.lookup("splitconsoleTooltip"),
|
|
state: {
|
|
isChecked: function(aTarget) {
|
|
let toolbox = gDevTools.getToolbox(aTarget);
|
|
return toolbox &&
|
|
toolbox.splitConsole;
|
|
},
|
|
onChange: function(aTarget, aChangeHandler) {
|
|
eventEmitter.on("changed", aChangeHandler);
|
|
},
|
|
offChange: function(aTarget, aChangeHandler) {
|
|
eventEmitter.off("changed", aChangeHandler);
|
|
},
|
|
},
|
|
exec: function(args, context) {
|
|
toggleSplitConsole(context);
|
|
}
|
|
});
|
|
|
|
function toggleSplitConsole(context) {
|
|
let gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
|
|
let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
|
|
let toolbox = gDevTools.getToolbox(target);
|
|
|
|
if (!toolbox) {
|
|
gDevTools.showToolbox(target, "inspector").then((toolbox) => {
|
|
toolbox.toggleSplitConsole();
|
|
});
|
|
} else {
|
|
toolbox.toggleSplitConsole();
|
|
}
|
|
}
|
|
|
|
let eventEmitter = new EventEmitter();
|
|
function fireChange(tab) {
|
|
eventEmitter.emit("changed", tab);
|
|
}
|
|
|
|
gDevTools.on("toolbox-ready", (e, toolbox) => {
|
|
if (!toolbox.target) {
|
|
return;
|
|
}
|
|
let fireChangeForTab = fireChange.bind(this, toolbox.target.tab);
|
|
toolbox.on("split-console", fireChangeForTab);
|
|
toolbox.on("select", fireChangeForTab);
|
|
toolbox.once("destroyed", () => {
|
|
toolbox.off("split-console", fireChangeForTab);
|
|
toolbox.off("select", fireChangeForTab);
|
|
});
|
|
});
|
|
|
|
}(this));
|