merge m-c to fx-team

This commit is contained in:
Tim Taubert 2013-03-25 09:45:45 +01:00
commit bbaac6bbbf
26 changed files with 1280 additions and 725 deletions

View File

@ -601,6 +601,16 @@ XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
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 promise = OS.File.stat(dirName);
promise = promise.then(
function onSuccess(stat) {
@ -770,69 +780,71 @@ XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
XPCOMUtils.defineLazyModuleGetter(this, "console",
"resource://gre/modules/devtools/Console.jsm");
// We should really be using nsICookieManager so we can read more than just the
// key/value of cookies. The difficulty is filtering the cookies that are
// relevant to the current page. See
// https://github.com/firebug/firebug/blob/master/extension/content/firebug/cookies/cookieObserver.js#L123
// For details on how this is done with Firebug
const cookieMgr = Cc["@mozilla.org/cookiemanager;1"]
.getService(Ci.nsICookieManager2);
/**
* 'cookie' command
*/
gcli.addCommand({
name: "cookie",
description: gcli.lookup("cookieDesc"),
manual: gcli.lookup("cookieManual")
});
/**
* The template for the 'cookie list' command.
*/
var cookieListHtml = "" +
"<table>" +
" <tr>" +
" <th>" + gcli.lookup("cookieListOutKey") + "</th>" +
" <th>" + gcli.lookup("cookieListOutValue") + "</th>" +
" <th>" + gcli.lookup("cookieListOutActions") + "</th>" +
" </tr>" +
" <tr foreach='cookie in ${cookies}'>" +
" <td>${cookie.key}</td>" +
" <td>${cookie.value}</td>" +
" <td>" +
" <span class='gcli-out-shortcut' onclick='${onclick}'" +
" data-command='cookie set ${cookie.key} '" +
" >" + gcli.lookup("cookieListOutEdit") + "</span>" +
" <span class='gcli-out-shortcut'" +
" onclick='${onclick}' ondblclick='${ondblclick}'" +
" data-command='cookie remove ${cookie.key}'" +
" >" + gcli.lookup("cookieListOutRemove") + "</span>" +
" </td>" +
" </tr>" +
"</table>" +
* 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>" +
"";
/**
* 'cookie list' command
*/
gcli.addCommand({
name: "cookie list",
description: gcli.lookup("cookieListDesc"),
manual: gcli.lookup("cookieListManual"),
returnType: "string",
exec: function Command_cookieList(args, context) {
// Parse out an array of { key:..., value:... } objects for each cookie
var doc = context.environment.contentDocument;
var cookies = doc.cookie.split("; ").map(function(cookieStr) {
var equalsPos = cookieStr.indexOf("=");
return {
key: cookieStr.substring(0, equalsPos),
value: cookieStr.substring(equalsPos + 1)
};
});
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: createUpdateHandler(context),
ondblclick: createExecuteHandler(context),
@ -842,37 +854,116 @@ XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
});
/**
* 'cookie remove' command
*/
* 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 Command_cookieList(args, context) {
let host = context.environment.document.location.host;
if (host == null) {
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: "key",
name: "name",
type: "string",
description: gcli.lookup("cookieRemoveKeyDesc"),
}
],
exec: function Command_cookieRemove(args, context) {
let document = context.environment.contentDocument;
let expDate = new Date();
expDate.setDate(expDate.getDate() - 1);
document.cookie = escape(args.key) + "=; expires=" + expDate.toGMTString();
let host = context.environment.document.location.host;
let enm = cookieMgr.getCookiesFromHost(host);
let cookies = [];
while (enm.hasMoreElements()) {
let cookie = enm.getNext().QueryInterface(Components.interfaces.nsICookie);
if (isCookieAtHost(cookie, host)) {
if (cookie.name == args.name) {
cookieMgr.remove(cookie.host, cookie.name, cookie.path, false);
}
}
}
}
});
/**
* 'cookie set' command
*/
* 'cookie set' command
*/
gcli.addCommand({
name: "cookie set",
description: gcli.lookup("cookieSetDesc"),
manual: gcli.lookup("cookieSetManual"),
params: [
{
name: "key",
name: "name",
type: "string",
description: gcli.lookup("cookieSetKeyDesc")
},
@ -900,26 +991,47 @@ XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
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 Command_cookieSet(args, context) {
let document = context.environment.contentDocument;
let host = context.environment.document.location.host;
let time = Date.parse(args.expires) / 1000;
document.cookie = escape(args.key) + "=" + escape(args.value) +
(args.domain ? "; domain=" + args.domain : "") +
(args.path ? "; path=" + args.path : "") +
(args.secure ? "; secure" : "");
cookieMgr.add(args.domain ? "." + args.domain : host,
args.path ? args.path : "/",
args.name,
args.value,
args.secure,
args.httpOnly,
args.session,
time);
}
});
/**
* Helper to find the 'data-command' attribute and call some action on it.
* @see |updateCommand()| and |executeCommand()|
*/
* Helper to find the 'data-command' attribute and call some action on it.
* @see |updateCommand()| and |executeCommand()|
*/
function withCommand(element, action) {
var command = element.getAttribute("data-command");
let command = element.getAttribute("data-command");
if (!command) {
command = element.querySelector("*[data-command]")
.getAttribute("data-command");
@ -934,11 +1046,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
}
/**
* Create a handler to update the requisition to contain the text held in the
* first matching data-command attribute under the currentTarget of the event.
* @param context Either a Requisition or an ExecutionContext or another object
* that contains an |update()| function that follows a similar contract.
*/
* Create a handler to update the requisition to contain the text held in the
* first matching data-command attribute under the currentTarget of the event.
* @param context Either a Requisition or an ExecutionContext or another object
* that contains an |update()| function that follows a similar contract.
*/
function createUpdateHandler(context) {
return function(ev) {
withCommand(ev.currentTarget, function(command) {
@ -948,11 +1060,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
}
/**
* Create a handler to execute the text held in the data-command attribute
* under the currentTarget of the event.
* @param context Either a Requisition or an ExecutionContext or another object
* that contains an |update()| function that follows a similar contract.
*/
* Create a handler to execute the text held in the data-command attribute
* under the currentTarget of the event.
* @param context Either a Requisition or an ExecutionContext or another object
* that contains an |update()| function that follows a similar contract.
*/
function createExecuteHandler(context) {
return function(ev) {
withCommand(ev.currentTarget, function(command) {

View File

@ -34,6 +34,16 @@
direction: ltr;
}
.gcli-cookielist-list {
list-style-type: none;
padding-left: 0;
}
.gcli-cookielist-detail {
padding-left: 20px;
padding-bottom: 10px;
}
.gcli-row-out .nowrap {
white-space: nowrap;
}

File diff suppressed because it is too large Load Diff

View File

@ -39,7 +39,7 @@ function test() {
setup: 'cookie remove',
check: {
input: 'cookie remove',
hints: ' <key>',
hints: ' <name>',
markup: 'VVVVVVVVVVVVV',
status: 'ERROR'
},
@ -48,7 +48,7 @@ function test() {
setup: 'cookie set',
check: {
input: 'cookie set',
hints: ' <key> <value> [options]',
hints: ' <name> <value> [options]',
markup: 'VVVVVVVVVV',
status: 'ERROR'
},
@ -70,17 +70,23 @@ function test() {
markup: 'VVVVVVVVVVVVVVVVVVVV',
status: 'VALID',
args: {
key: { value: 'fruit' },
name: { value: 'fruit' },
value: { value: 'ban' },
secure: { value: false },
}
},
},
{
setup: "cookie list",
exec: {
output: 'No cookies found for host'
}
},
{
setup: "cookie set fruit banana",
check: {
args: {
key: { value: 'fruit' },
name: { value: 'fruit' },
value: { value: 'banana' },
}
},
@ -91,20 +97,26 @@ function test() {
{
setup: "cookie list",
exec: {
output: /Key/
output: [ /fruit=banana/, /Expires:/, /Edit/ ]
}
},
{
setup: "cookie remove fruit",
check: {
args: {
key: { value: 'fruit' },
name: { value: 'fruit' },
}
},
exec: {
output: ""
}
},
{
setup: "cookie list",
exec: {
output: 'No cookies found for host'
}
},
]);
}).then(finish);
}

View File

@ -199,5 +199,4 @@ exports.testAddRemove3 = function(options) {
canon.onCanonChange.remove(canonChange);
};
// });

View File

@ -179,7 +179,7 @@ exports.testTsv = function(options) {
cursor: 4,
current: 'optionType',
status: 'ERROR',
predictions: [ 'option1', 'option2' ],
predictions: [ 'option1', 'option2', 'option3' ],
unassigned: [ ],
tooltipState: 'true:importantFieldFlag',
args: {
@ -219,7 +219,7 @@ exports.testTsv = function(options) {
cursor: 5,
current: 'optionType',
status: 'ERROR',
predictions: [ 'option1', 'option2' ],
predictions: [ 'option1', 'option2', 'option3' ],
unassigned: [ ],
tooltipState: 'true:importantFieldFlag',
args: {
@ -248,7 +248,7 @@ exports.testTsv = function(options) {
cursor: 10,
current: 'optionType',
status: 'ERROR',
predictions: [ 'option1', 'option2' ],
predictions: [ 'option1', 'option2', 'option3' ],
unassigned: [ ],
tooltipState: 'true:importantFieldFlag',
args: {

View File

@ -35,8 +35,10 @@ function test() {
'use strict';
// var mockCommands = require('gclitest/mockCommands');
var nodetype = require('gcli/types/node');
var canon = require('gcli/canon');
// var assert = require('test/assert');
// var mockCommands = require('gclitest/mockCommands');
// var helpers = require('gclitest/helpers');
@ -71,6 +73,16 @@ var mockDoc = {
}
};
exports.testParamGroup = function(options) {
var tsg = canon.getCommand('tsg');
assert.is(tsg.params[0].groupName, null, 'tsg param 0 group null');
assert.is(tsg.params[1].groupName, 'First', 'tsg param 1 group First');
assert.is(tsg.params[2].groupName, 'First', 'tsg param 2 group First');
assert.is(tsg.params[3].groupName, 'Second', 'tsg param 3 group Second');
assert.is(tsg.params[4].groupName, 'Second', 'tsg param 4 group Second');
};
exports.testWithHelpers = function(options) {
return helpers.audit(options, [
{

View File

@ -142,11 +142,13 @@ function checkPrediction(res, prediction) {
var name = prediction.name;
var value = prediction.value;
return res.parseString(name).then(function(conversion) {
// resources don't need context so cheat and pass in null
var context = null;
return res.parseString(name, context).then(function(conversion) {
assert.is(conversion.getStatus(), Status.VALID, 'status VALID for ' + name);
assert.is(conversion.value, value, 'value for ' + name);
var strung = res.stringify(value);
var strung = res.stringify(value, context);
assert.is(strung, name, 'stringify for ' + name);
assert.is(typeof value.loadContents, 'function', 'resource for ' + name);

View File

@ -90,7 +90,7 @@ exports.testDefault = function(options) {
exports.testNullDefault = function(options) {
forEachType(options, { defaultValue: null }, function(type) {
assert.is(type.stringify(null), '', 'stringify(null) for ' + type.name);
assert.is(type.stringify(null, null), '', 'stringify(null) for ' + type.name);
});
};

View File

@ -31,6 +31,8 @@ let assert = { ok: ok, is: is, log: info };
var util = require('util/util');
var converters = require('gcli/converters');
/**
* Warning: For use with Firefox Mochitests only.
*
@ -380,7 +382,7 @@ helpers._createDebugCheck = function(options) {
output += ']);';
return output;
}.bind(this), console.error);
}.bind(this), util.errorHandler);
};
/**
@ -723,32 +725,36 @@ helpers._exec = function(options, name, expected) {
var checkOutput = function() {
var div = options.window.document.createElement('div');
output.toDom(div);
var actualOutput = div.textContent.trim();
var nodePromise = converters.convert(output.data, output.type, 'dom',
options.display.requisition.context);
nodePromise.then(function(node) {
div.appendChild(node);
var actualOutput = div.textContent.trim();
var doTest = function(match, against) {
if (!match.test(against)) {
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
log('Actual textContent');
log(against);
var doTest = function(match, against) {
if (!match.test(against)) {
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
log('Actual textContent');
log(against);
}
};
if (typeof expected.output === 'string') {
assert.is(actualOutput,
expected.output,
'html output for ' + name);
}
else if (Array.isArray(expected.output)) {
expected.output.forEach(function(match) {
doTest(match, actualOutput);
});
}
else {
doTest(expected.output, actualOutput);
}
};
if (typeof expected.output === 'string') {
assert.is(actualOutput,
expected.output,
'html output for ' + name);
}
else if (Array.isArray(expected.output)) {
expected.output.forEach(function(match) {
doTest(match, actualOutput);
});
}
else {
doTest(expected.output, actualOutput);
}
deferred.resolve();
deferred.resolve();
});
};
if (output.completed !== false) {

View File

@ -111,50 +111,34 @@ mockCommands.shutdown = function(opts) {
mockCommands.option1 = { type: types.getType('string') };
mockCommands.option2 = { type: types.getType('number') };
var lastOption = undefined;
var debug = false;
mockCommands.option3 = { type: types.getType({
name: 'selection',
lookup: [
{ name: 'one', value: 1 },
{ name: 'two', value: 2 },
{ name: 'three', value: 3 }
]
})};
mockCommands.optionType = new SelectionType({
name: 'optionType',
lookup: [
{ name: 'option1', value: mockCommands.option1 },
{ name: 'option2', value: mockCommands.option2 }
],
noMatch: function() {
lastOption = undefined;
if (debug) {
console.log('optionType.noMatch: lastOption = undefined');
}
},
stringify: function(option) {
lastOption = option;
if (debug) {
console.log('optionType.stringify: lastOption = ', lastOption);
}
return SelectionType.prototype.stringify.call(this, option);
},
parse: function(arg) {
var promise = SelectionType.prototype.parse.call(this, arg);
promise.then(function(conversion) {
lastOption = conversion.value;
if (debug) {
console.log('optionType.parse: lastOption = ', lastOption);
}
});
return promise;
}
{ name: 'option2', value: mockCommands.option2 },
{ name: 'option3', value: mockCommands.option3 }
]
});
mockCommands.optionValue = new DelegateType({
name: 'optionValue',
delegateType: function() {
if (lastOption && lastOption.type) {
return lastOption.type;
}
else {
return types.getType('blank');
delegateType: function(context) {
if (context != null) {
var option = context.getArgsObject().optionType;
if (option != null) {
return option.type;
}
}
return types.getType('blank');
}
});
@ -370,21 +354,18 @@ mockCommands.tsg = {
]
},
{
group: 'Second',
params: [
{
name: 'txt2',
type: 'string',
defaultValue: 'd',
description: 'txt2 param'
},
{
name: 'num',
type: { name: 'number', min: 40 },
defaultValue: 42,
description: 'num param'
}
]
name: 'txt2',
type: 'string',
defaultValue: 'd',
description: 'txt2 param',
option: 'Second'
},
{
name: 'num',
type: { name: 'number', min: 40 },
defaultValue: 42,
description: 'num param',
option: 'Second'
}
],
exec: createExec('tsg')

View File

@ -31,6 +31,8 @@ let assert = { ok: ok, is: is, log: info };
var util = require('util/util');
var converters = require('gcli/converters');
/**
* Warning: For use with Firefox Mochitests only.
*
@ -380,7 +382,7 @@ helpers._createDebugCheck = function(options) {
output += ']);';
return output;
}.bind(this), console.error);
}.bind(this), util.errorHandler);
};
/**
@ -723,32 +725,36 @@ helpers._exec = function(options, name, expected) {
var checkOutput = function() {
var div = options.window.document.createElement('div');
output.toDom(div);
var actualOutput = div.textContent.trim();
var nodePromise = converters.convert(output.data, output.type, 'dom',
options.display.requisition.context);
nodePromise.then(function(node) {
div.appendChild(node);
var actualOutput = div.textContent.trim();
var doTest = function(match, against) {
if (!match.test(against)) {
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
log('Actual textContent');
log(against);
var doTest = function(match, against) {
if (!match.test(against)) {
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
log('Actual textContent');
log(against);
}
};
if (typeof expected.output === 'string') {
assert.is(actualOutput,
expected.output,
'html output for ' + name);
}
else if (Array.isArray(expected.output)) {
expected.output.forEach(function(match) {
doTest(match, actualOutput);
});
}
else {
doTest(expected.output, actualOutput);
}
};
if (typeof expected.output === 'string') {
assert.is(actualOutput,
expected.output,
'html output for ' + name);
}
else if (Array.isArray(expected.output)) {
expected.output.forEach(function(match) {
doTest(match, actualOutput);
});
}
else {
doTest(expected.output, actualOutput);
}
deferred.resolve();
deferred.resolve();
});
};
if (output.completed !== false) {

View File

@ -500,7 +500,8 @@ Toolbox.prototype = {
let boundLoad = function() {
iframe.removeEventListener("DOMContentLoaded", boundLoad, true);
definition.build(iframe.contentWindow, this).then(function(panel) {
let built = definition.build(iframe.contentWindow, this);
Promise.resolve(built).then(function(panel) {
this._toolPanels.set(id, panel);
this.emit(id + "-ready", panel);

View File

@ -31,6 +31,8 @@ let assert = { ok: ok, is: is, log: info };
var util = require('util/util');
var converters = require('gcli/converters');
/**
* Warning: For use with Firefox Mochitests only.
*
@ -380,7 +382,7 @@ helpers._createDebugCheck = function(options) {
output += ']);';
return output;
}.bind(this), console.error);
}.bind(this), util.errorHandler);
};
/**
@ -723,32 +725,36 @@ helpers._exec = function(options, name, expected) {
var checkOutput = function() {
var div = options.window.document.createElement('div');
output.toDom(div);
var actualOutput = div.textContent.trim();
var nodePromise = converters.convert(output.data, output.type, 'dom',
options.display.requisition.context);
nodePromise.then(function(node) {
div.appendChild(node);
var actualOutput = div.textContent.trim();
var doTest = function(match, against) {
if (!match.test(against)) {
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
log('Actual textContent');
log(against);
var doTest = function(match, against) {
if (!match.test(against)) {
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
log('Actual textContent');
log(against);
}
};
if (typeof expected.output === 'string') {
assert.is(actualOutput,
expected.output,
'html output for ' + name);
}
else if (Array.isArray(expected.output)) {
expected.output.forEach(function(match) {
doTest(match, actualOutput);
});
}
else {
doTest(expected.output, actualOutput);
}
};
if (typeof expected.output === 'string') {
assert.is(actualOutput,
expected.output,
'html output for ' + name);
}
else if (Array.isArray(expected.output)) {
expected.output.forEach(function(match) {
doTest(match, actualOutput);
});
}
else {
doTest(expected.output, actualOutput);
}
deferred.resolve();
deferred.resolve();
});
};
if (output.completed !== false) {

View File

@ -18,16 +18,39 @@ XPCOMUtils.defineLazyGetter(this, "DebuggerServer", function () {
return DebuggerServer;
});
/**
* Makes a structure representing an individual profile.
*/
function makeProfile(name) {
return {
name: name,
timeStarted: null,
timeEnded: null
};
}
/**
* Object acting as a mediator between the ProfilerController and
* DebuggerServer.
*/
function ProfilerConnection(client) {
this.client = client;
this.startTime = 0;
}
ProfilerConnection.prototype = {
actor: null,
startTime: null,
/**
* Returns how many milliseconds have passed since the connection
* was started (start time is specificed by the startTime property).
*
* @return number
*/
get currentTime() {
return (new Date()).getTime() - this.startTime;
},
/**
* Connects to a debugee and executes a callback when ready.
@ -71,7 +94,13 @@ ProfilerConnection.prototype = {
interval: 1,
features: ["js"],
};
this.client.request(message, aCallback);
this.client.request(message, function () {
// Record the current time so we could split profiler data
// in chunks later.
this.startTime = (new Date()).getTime();
aCallback.apply(null, Array.slice(arguments));
}.bind(this));
},
/**
@ -113,9 +142,12 @@ ProfilerConnection.prototype = {
*/
function ProfilerController(target) {
this.profiler = new ProfilerConnection(target.client);
// Chrome debugging targets have already obtained a reference to the profiler
// actor.
this.pool = {};
// Chrome debugging targets have already obtained a reference to the
// profiler actor.
this._connected = !!target.chrome;
if (target.chrome) {
this.profiler.actor = target.form.profilerActor;
}
@ -157,44 +189,118 @@ ProfilerController.prototype = {
},
/**
* Starts the profiler.
* Checks whether the profile is currently recording.
*
* @param function aCallback
* @param object profile
* An object made by calling makeProfile function.
* @return boolean
*/
isProfileRecording: function PC_isProfileRecording(profile) {
return profile.timeStarted !== null && profile.timeEnded === null;
},
/**
* Creates a new profile and starts the profiler, if needed.
*
* @param string name
* Name of the profile.
* @param function cb
* Function to be called once the profiler is started
* or we get an error. It will be called with a single
* argument: an error object (may be null).
*/
start: function PC_start(aCallback) {
this.profiler.startProfiler(function onStart(aResponse) {
aCallback(aResponse.error);
start: function PC_start(name, cb) {
if (this.pool[name]) {
return;
}
let profile = this.pool[name] = makeProfile(name);
let profiler = this.profiler;
// If profile is already running, no need to do anything.
if (this.isProfileRecording(profile)) {
return void cb();
}
this.isActive(function (err, isActive) {
if (isActive) {
profile.timeStarted = profiler.currentTime;
return void cb();
}
profiler.startProfiler(function onStart(aResponse) {
if (aResponse.error) {
return void cb(aResponse.error);
}
profile.timeStarted = profiler.currentTime;
cb();
});
});
},
/**
* Stops the profiler.
*
* @param function aCallback
* @param string name
* Name of the profile that needs to be stopped.
* @param function cb
* Function to be called once the profiler is stopped
* or we get an error. It will be called with a single
* argument: an error object (may be null).
*/
stop: function PC_stop(aCallback) {
this.profiler.getProfileData(function onData(aResponse) {
let data = aResponse.profile;
if (aResponse.error) {
Cu.reportError("Failed to fetch profile data before stopping the profiler.");
stop: function PC_stop(name, cb) {
let profiler = this.profiler;
let profile = this.pool[name];
if (!profile || !this.isProfileRecording(profile)) {
return;
}
let isRecording = function () {
for (let name in this.pool) {
if (this.isProfileRecording(this.pool[name])) {
return true;
}
}
this.profiler.stopProfiler(function onStop(aResponse) {
aCallback(aResponse.error, data);
return false;
}.bind(this);
let onStop = function (data) {
if (isRecording()) {
return void cb(null, data);
}
profiler.stopProfiler(function onStopProfiler(response) {
cb(response.error, data);
});
}.bind(this));
}.bind(this);
profiler.getProfileData(function onData(aResponse) {
if (aResponse.error) {
Cu.reportError("Failed to fetch profile data before stopping the profiler.");
return void cb(aResponse.error, null);
}
let data = aResponse.profile;
profile.timeEnded = profiler.currentTime;
data.threads = data.threads.map(function (thread) {
let samples = thread.samples.filter(function (sample) {
return sample.time >= profile.timeStarted;
});
return { samples: samples };
});
onStop(data);
});
},
/**
* Cleanup.
*/
destroy: function PC_destroy(aCallback) {
destroy: function PC_destroy() {
this.profiler.destroy();
this.profiler = null;
}

View File

@ -71,6 +71,8 @@ function ProfileUI(uid, panel) {
}
let label = doc.querySelector("li#profile-" + this.uid + " > h1");
let name = label.textContent.replace(/\s\*$/, "");
switch (event.data.status) {
case "loaded":
if (this.panel._runningUid !== null) {
@ -89,9 +91,10 @@ function ProfileUI(uid, panel) {
// so that it could update the UI. Also, once started, we add a
// star to the profile name to indicate which profile is currently
// running.
this.panel.startProfiling(function onStart() {
this.panel.startProfiling(name, function onStart() {
label.textContent = name + " *";
this.panel.broadcast(this.uid, {task: "onStarted"});
label.textContent = label.textContent + " *";
this.emit("started");
}.bind(this));
break;
@ -99,9 +102,10 @@ function ProfileUI(uid, panel) {
// Stop profiling and, once stopped, notify the underlying page so
// that it could update the UI and remove a star from the profile
// name.
this.panel.stopProfiling(function onStop() {
this.panel.stopProfiling(name, function onStop() {
label.textContent = name;
this.panel.broadcast(this.uid, {task: "onStopped"});
label.textContent = label.textContent.replace(/\s\*$/, "");
this.emit("stopped");
}.bind(this));
break;
case "disabled":
@ -372,8 +376,8 @@ ProfilerPanel.prototype = {
* A function to call once we get the message
* that profiling had been successfuly started.
*/
startProfiling: function PP_startProfiling(onStart) {
this.controller.start(function (err) {
startProfiling: function PP_startProfiling(name, onStart) {
this.controller.start(name, function (err) {
if (err) {
Cu.reportError("ProfilerController.start: " + err.message);
return;
@ -392,7 +396,7 @@ ProfilerPanel.prototype = {
* A function to call once we get the message
* that profiling had been successfuly stopped.
*/
stopProfiling: function PP_stopProfiling(onStop) {
stopProfiling: function PP_stopProfiling(name, onStop) {
this.controller.isActive(function (err, isActive) {
if (err) {
Cu.reportError("ProfilerController.isActive: " + err.message);
@ -403,18 +407,19 @@ ProfilerPanel.prototype = {
return;
}
this.controller.stop(function (err, data) {
this.controller.stop(name, function (err, data) {
if (err) {
Cu.reportError("ProfilerController.stop: " + err.message);
return;
}
this.activeProfile.data = data;
this.activeProfile.parse(data, function onParsed() {
this.emit("parsed");
}.bind(this));
onStop();
this.emit("stopped");
this.emit("stopped", data);
}.bind(this));
}.bind(this));
},

View File

@ -54,31 +54,20 @@ function onParentMessage(event) {
var profilerMessage = document.getElementById("profilerMessage");
var msg = JSON.parse(event.data);
if (msg.task !== "receiveProfileData" && !msg.isCurrent) {
return;
}
switch (msg.task) {
case "onStarted":
if (msg.isCurrent) {
start.style.display = "none";
start.querySelector("button").removeAttribute("disabled");
stop.style.display = "inline";
} else {
start.querySelector("button").setAttribute("disabled", true);
var text = gStrings.getFormatStr("profiler.alreadyRunning", [msg.uid]);
profilerMessage.textContent = text;
profilerMessage.style.display = "block";
notifyParent("disabled");
}
start.style.display = "none";
start.querySelector("button").removeAttribute("disabled");
stop.style.display = "inline";
break;
case "onStopped":
if (msg.isCurrent) {
stop.style.display = "none";
stop.querySelector("button").removeAttribute("disabled");
start.style.display = "inline";
} else {
start.querySelector("button").removeAttribute("disabled");
profilerMessage.textContent = "";
profilerMessage.style.display = "none";
notifyParent("enabled");
}
stop.style.display = "none";
stop.querySelector("button").removeAttribute("disabled");
start.style.display = "inline";
break;
case "receiveProfileData":
loadProfile(JSON.stringify(msg.rawProfile));
@ -267,4 +256,4 @@ function enterProgressUI() {
Parser.updateLogSetting();
return reporter;
}
}

View File

@ -33,43 +33,44 @@ function getCleoControls(doc) {
];
}
function sendFromActiveProfile(msg) {
let [win, doc] = getProfileInternals();
win.parent.postMessage({
uid: gPanel.activeProfile.uid,
status: msg
}, "*");
function sendFromProfile(uid, msg) {
let [win, doc] = getProfileInternals(uid);
win.parent.postMessage({ uid: uid, status: msg }, "*");
}
function startProfiling() {
gPanel.profiles.get(gUid).once("disabled", stopProfiling);
sendFromActiveProfile("start");
gPanel.profiles.get(gPanel.activeProfile.uid).once("started", function () {
setTimeout(function () {
sendFromProfile(2, "start");
gPanel.profiles.get(2).once("started", function () setTimeout(stopProfiling, 50));
}, 50);
});
sendFromProfile(gPanel.activeProfile.uid, "start");
}
function stopProfiling() {
let [win, doc] = getProfileInternals(gUid);
let [btn, msg] = getCleoControls(doc);
ok(msg.textContent.match("Profile 1") !== null, "Message is visible");
ok(btn.hasAttribute("disabled"), "Button is disabled");
is(gPanel.document.querySelector("li#profile-1 > h1").textContent,
"Profile 1 *", "Profile 1 has a star next to it.");
is(gPanel.document.querySelector("li#profile-2 > h1").textContent,
"Profile 2", "Profile 2 doesn't have a star next to it.");
"Profile 2 *", "Profile 2 has a star next to it.");
gPanel.profiles.get(gUid).once("enabled", confirmAndFinish);
sendFromActiveProfile("stop");
gPanel.profiles.get(gPanel.activeProfile.uid).once("stopped", function () {
is(gPanel.document.querySelector("li#profile-1 > h1").textContent,
"Profile 1", "Profile 1 doesn't have a star next to it anymore.");
sendFromProfile(2, "stop");
gPanel.profiles.get(2).once("stopped", confirmAndFinish);
});
sendFromProfile(gPanel.activeProfile.uid, "stop");
}
function confirmAndFinish() {
function confirmAndFinish(ev, data) {
let [win, doc] = getProfileInternals(gUid);
let [btn, msg] = getCleoControls(doc);
ok(msg.style.display === "none", "Message is hidden");
ok(!btn.hasAttribute("disabled"), "Button is enabled");
is(gPanel.document.querySelector("li#profile-1 > h1").textContent,
"Profile 1", "Profile 1 doesn't have a star next to it.");
is(gPanel.document.querySelector("li#profile-2 > h1").textContent,
@ -80,4 +81,4 @@ function confirmAndFinish() {
gTab = null;
gUid = null;
});
}
}

View File

@ -12,7 +12,7 @@ function test() {
gTab = tab;
gPanel = panel;
testInactive(testStart);
testInactive(startFirstProfile);
});
}
@ -32,23 +32,33 @@ function testActive(next=function(){}) {
});
}
function testStart() {
gPanel.controller.start(function (err) {
ok(!err, "Profiler started without errors");
testActive(testStop);
function startFirstProfile() {
gPanel.controller.start("Profile 1", function (err) {
ok(!err, "Profile 1 started without errors");
testActive(startSecondProfile);
});
}
function testStop() {
gPanel.controller.stop(function (err, data) {
ok(!err, "Profiler stopped without errors");
function startSecondProfile() {
gPanel.controller.start("Profile 2", function (err) {
ok(!err, "Profile 2 started without errors");
testActive(stopFirstProfile);
});
}
function stopFirstProfile() {
gPanel.controller.stop("Profile 1", function (err, data) {
ok(!err, "Profile 1 stopped without errors");
ok(data, "Profiler returned some data");
testInactive(function () {
tearDown(gTab, function () {
gTab = null;
gPanel = null;
});
});
testActive(stopSecondProfile);
});
}
function stopSecondProfile() {
gPanel.controller.stop("Profile 2", function (err, data) {
ok(!err, "Profile 2 stopped without errors");
ok(data, "Profiler returned some data");
testInactive(tearDown.call(null, gTab, function () gTab = gPanel = null));
});
}

View File

@ -80,4 +80,4 @@ function tearDown(tab, callback=function(){}) {
finish();
});
}
}

View File

@ -31,6 +31,8 @@ let assert = { ok: ok, is: is, log: info };
var util = require('util/util');
var converters = require('gcli/converters');
/**
* Warning: For use with Firefox Mochitests only.
*
@ -380,7 +382,7 @@ helpers._createDebugCheck = function(options) {
output += ']);';
return output;
}.bind(this), console.error);
}.bind(this), util.errorHandler);
};
/**
@ -723,32 +725,36 @@ helpers._exec = function(options, name, expected) {
var checkOutput = function() {
var div = options.window.document.createElement('div');
output.toDom(div);
var actualOutput = div.textContent.trim();
var nodePromise = converters.convert(output.data, output.type, 'dom',
options.display.requisition.context);
nodePromise.then(function(node) {
div.appendChild(node);
var actualOutput = div.textContent.trim();
var doTest = function(match, against) {
if (!match.test(against)) {
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
log('Actual textContent');
log(against);
var doTest = function(match, against) {
if (!match.test(against)) {
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
log('Actual textContent');
log(against);
}
};
if (typeof expected.output === 'string') {
assert.is(actualOutput,
expected.output,
'html output for ' + name);
}
else if (Array.isArray(expected.output)) {
expected.output.forEach(function(match) {
doTest(match, actualOutput);
});
}
else {
doTest(expected.output, actualOutput);
}
};
if (typeof expected.output === 'string') {
assert.is(actualOutput,
expected.output,
'html output for ' + name);
}
else if (Array.isArray(expected.output)) {
expected.output.forEach(function(match) {
doTest(match, actualOutput);
});
}
else {
doTest(expected.output, actualOutput);
}
deferred.resolve();
deferred.resolve();
});
};
if (output.completed !== false) {

View File

@ -33,6 +33,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
"resource:///modules/devtools/Target.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "require",
"resource://gre/modules/devtools/Require.jsm");
XPCOMUtils.defineLazyGetter(this, "prefBranch", function() {
let prefService = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService);
@ -44,6 +47,8 @@ XPCOMUtils.defineLazyGetter(this, "toolboxStrings", function () {
return Services.strings.createBundle("chrome://browser/locale/devtools/toolbox.properties");
});
const converters = require("gcli/converters");
/**
* A collection of utilities to help working with commands
*/
@ -338,7 +343,7 @@ DeveloperToolbar.prototype.show = function DT_show(aFocus, aCallback)
this._input = this._doc.querySelector(".gclitoolbar-input-node");
this.tooltipPanel = new TooltipPanel(this._doc, this._input, checkLoad);
this.outputPanel = new OutputPanel(this._doc, this._input, checkLoad);
this.outputPanel = new OutputPanel(this, checkLoad);
};
/**
@ -369,16 +374,19 @@ DeveloperToolbar.prototype._onload = function DT_onload(aFocus)
this.display.focusManager.addMonitoredElement(this.outputPanel._frame);
this.display.focusManager.addMonitoredElement(this._element);
this.display.onVisibilityChange.add(this.outputPanel._visibilityChanged, this.outputPanel);
this.display.onVisibilityChange.add(this.tooltipPanel._visibilityChanged, this.tooltipPanel);
this.display.onVisibilityChange.add(this.outputPanel._visibilityChanged,
this.outputPanel);
this.display.onVisibilityChange.add(this.tooltipPanel._visibilityChanged,
this.tooltipPanel);
this.display.onOutput.add(this.outputPanel._outputChanged, this.outputPanel);
this._chromeWindow.getBrowser().tabContainer.addEventListener("TabSelect", this, false);
this._chromeWindow.getBrowser().tabContainer.addEventListener("TabClose", this, false);
this._chromeWindow.getBrowser().addEventListener("load", this, true);
this._chromeWindow.getBrowser().addEventListener("beforeunload", this, true);
let tabbrowser = this._chromeWindow.getBrowser();
tabbrowser.tabContainer.addEventListener("TabSelect", this, false);
tabbrowser.tabContainer.addEventListener("TabClose", this, false);
tabbrowser.addEventListener("load", this, true);
tabbrowser.addEventListener("beforeunload", this, true);
this._initErrorsCount(this._chromeWindow.getBrowser().selectedTab);
this._initErrorsCount(tabbrowser.selectedTab);
this._element.hidden = false;
@ -495,13 +503,13 @@ DeveloperToolbar.prototype.destroy = function DT_destroy()
return;
}
this._chromeWindow.getBrowser().tabContainer.removeEventListener("TabSelect", this, false);
this._chromeWindow.getBrowser().tabContainer.removeEventListener("TabClose", this, false);
this._chromeWindow.getBrowser().removeEventListener("load", this, true);
this._chromeWindow.getBrowser().removeEventListener("beforeunload", this, true);
let tabbrowser = this._chromeWindow.getBrowser();
tabbrowser.tabContainer.removeEventListener("TabSelect", this, false);
tabbrowser.tabContainer.removeEventListener("TabClose", this, false);
tabbrowser.removeEventListener("load", this, true);
tabbrowser.removeEventListener("beforeunload", this, true);
let tabs = this._chromeWindow.getBrowser().tabs;
Array.prototype.forEach.call(tabs, this._stopErrorsCount, this);
Array.prototype.forEach.call(tabbrowser.tabs, this._stopErrorsCount, this);
this.display.focusManager.removeMonitoredElement(this.outputPanel._frame);
this.display.focusManager.removeMonitoredElement(this._element);
@ -700,10 +708,11 @@ function DT_resetErrorsCount(aTab)
* @param aInput the input element that should get focus.
* @param aLoadCallback called when the panel is loaded properly.
*/
function OutputPanel(aChromeDoc, aInput, aLoadCallback)
function OutputPanel(aDevToolbar, aLoadCallback)
{
this._input = aInput;
this._toolbar = aChromeDoc.getElementById("developer-toolbar");
this._devtoolbar = aDevToolbar;
this._input = this._devtoolbar._input;
this._toolbar = this._devtoolbar._doc.getElementById("developer-toolbar");
this._loadCallback = aLoadCallback;
@ -721,7 +730,7 @@ function OutputPanel(aChromeDoc, aInput, aLoadCallback)
// TODO: Switch back from tooltip to panel when metacity focus issue is fixed:
// https://bugzilla.mozilla.org/show_bug.cgi?id=780102
this._panel = aChromeDoc.createElement(isLinux ? "tooltip" : "panel");
this._panel = this._devtoolbar._doc.createElement(isLinux ? "tooltip" : "panel");
this._panel.id = "gcli-output";
this._panel.classList.add("gcli-panel");
@ -743,7 +752,7 @@ function OutputPanel(aChromeDoc, aInput, aLoadCallback)
this._toolbar.parentElement.insertBefore(this._panel, this._toolbar);
this._frame = aChromeDoc.createElementNS(NS_XHTML, "iframe");
this._frame = this._devtoolbar._doc.createElementNS(NS_XHTML, "iframe");
this._frame.id = "gcli-output-frame";
this._frame.setAttribute("src", "chrome://browser/content/devtools/commandlineoutput.xhtml");
this._frame.setAttribute("sandbox", "allow-same-origin");
@ -909,12 +918,28 @@ OutputPanel.prototype._outputChanged = function OP_outputChanged(aEvent)
*/
OutputPanel.prototype.update = function OP_update()
{
if (this.displayedOutput.data == null) {
while (this._div.hasChildNodes()) {
this._div.removeChild(this._div.firstChild);
}
} else {
this.displayedOutput.toDom(this._div);
// Empty this._div
while (this._div.hasChildNodes()) {
this._div.removeChild(this._div.firstChild);
}
if (this.displayedOutput.data != null) {
let requisition = this._devtoolbar.display.requisition;
let nodePromise = converters.convert(this.displayedOutput.data,
this.displayedOutput.type, 'dom',
requisition.context);
nodePromise.then(function(node) {
while (this._div.hasChildNodes()) {
this._div.removeChild(this._div.firstChild);
}
var links = node.ownerDocument.querySelectorAll('*[href]');
for (var i = 0; i < links.length; i++) {
links[i].setAttribute('target', '_blank');
}
this._div.appendChild(node);
}.bind(this));
this.show();
}
};
@ -951,6 +976,7 @@ OutputPanel.prototype.destroy = function OP_destroy()
this._panel.removeChild(this._frame);
this._toolbar.parentElement.removeChild(this._panel);
delete this._devtoolbar;
delete this._input;
delete this._toolbar;
delete this._onload;

View File

@ -31,6 +31,8 @@ let assert = { ok: ok, is: is, log: info };
var util = require('util/util');
var converters = require('gcli/converters');
/**
* Warning: For use with Firefox Mochitests only.
*
@ -380,7 +382,7 @@ helpers._createDebugCheck = function(options) {
output += ']);';
return output;
}.bind(this), console.error);
}.bind(this), util.errorHandler);
};
/**
@ -723,32 +725,36 @@ helpers._exec = function(options, name, expected) {
var checkOutput = function() {
var div = options.window.document.createElement('div');
output.toDom(div);
var actualOutput = div.textContent.trim();
var nodePromise = converters.convert(output.data, output.type, 'dom',
options.display.requisition.context);
nodePromise.then(function(node) {
div.appendChild(node);
var actualOutput = div.textContent.trim();
var doTest = function(match, against) {
if (!match.test(against)) {
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
log('Actual textContent');
log(against);
var doTest = function(match, against) {
if (!match.test(against)) {
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
log('Actual textContent');
log(against);
}
};
if (typeof expected.output === 'string') {
assert.is(actualOutput,
expected.output,
'html output for ' + name);
}
else if (Array.isArray(expected.output)) {
expected.output.forEach(function(match) {
doTest(match, actualOutput);
});
}
else {
doTest(expected.output, actualOutput);
}
};
if (typeof expected.output === 'string') {
assert.is(actualOutput,
expected.output,
'html output for ' + name);
}
else if (Array.isArray(expected.output)) {
expected.output.forEach(function(match) {
doTest(match, actualOutput);
});
}
else {
doTest(expected.output, actualOutput);
}
deferred.resolve();
deferred.resolve();
});
};
if (output.completed !== false) {

View File

@ -15,6 +15,10 @@
# or command parameter when no description has been provided.
canonDescNone=(No description)
# LOCALIZATION NOTE (canonDefaultGroupName): The default name for a group of
# parameters.
canonDefaultGroupName=Options
# LOCALIZATION NOTE (cliEvalJavascript): The special '{' command allows entry
# of JavaScript like traditional developer tool command lines. This describes
# the '{' command.
@ -239,9 +243,9 @@ prefShowSettingDesc=Setting to display
# for help on what it does.
prefShowSettingManual=The name of the setting to display
# LOCALIZATION NOTE (prefShowSettingValue): This is used to show the preference
# name and the associated preference value. %1$S is replaced with the preference
# name and %2$S is replaced with the preference value.
# LOCALIZATION NOTE (prefShowSettingValue): This is used to show the
# preference name and the associated preference value. %1$S is replaced with
# the preference name and %2$S is replaced with the preference value.
prefShowSettingValue=%1$S: %2$S
# LOCALIZATION NOTE (prefSetDesc): A very short description of the 'pref set'

View File

@ -736,17 +736,32 @@ cookieListDesc=Display cookies
# command, displayed when the user asks for help on what it does.
cookieListManual=Display a list of the cookies relevant to the current page.
# LOCALIZATION NOTE (cookieListOutKey) A heading used in the output from the
# 'cookie list' command above a list of cookie keys
cookieListOutKey=Key
# LOCALIZATION NOTE (cookieListOutHost,cookieListOutPath,cookieListOutExpires,cookieListOutAttributes):
# The 'cookie list' command has a number of headings for cookie properties.
# Particular care should be taken in translating these strings as they have
# references to names in the cookies spec.
cookieListOutHost=Host:
cookieListOutPath=Path:
cookieListOutExpires=Expires:
cookieListOutAttributes=Attributes:
# LOCALIZATION NOTE (cookieListOutValue) A heading used in the output from the
# 'cookie list' command above a list of cookie values
cookieListOutValue=Value
# LOCALIZATION NOTE (cookieListOutNone) The output of the 'cookie list' command
# uses this string when no cookie attributes (like httpOnly, secure, etc) apply
cookieListOutNone=None
# LOCALIZATION NOTE (cookieListOutActions) A heading used in the output from the
# 'cookie list' command above a list of actions to take on cookies
cookieListOutActions=Actions
# LOCALIZATION NOTE (cookieListOutSession) The output of the 'cookie list'
# command uses this string to describe a cookie with an expiry value of '0'
# that is to say it is a session cookie
cookieListOutSession=At browser exit (session)
# LOCALIZATION NOTE (cookieListOutNonePage) The output of the 'cookie list'
# command uses this string for pages like 'about:blank' which can't contain
# cookies
cookieListOutNonePage=No cookies found for this page
# LOCALIZATION NOTE (cookieListOutNoneHost) The output of the 'cookie list'
# command uses this string when there are no cookies on a given web page
cookieListOutNoneHost=No cookies found for host %1$S
# LOCALIZATION NOTE (cookieListOutEdit) A title used in the output from the
# 'cookie list' command on a button which can be used to edit cookie values
@ -808,6 +823,21 @@ cookieSetDomainDesc=The domain of the cookie to set
# when the user is using this command.
cookieSetSecureDesc=Only transmitted over https
# LOCALIZATION NOTE (cookieSetHttpOnlyDesc) A very short string to describe the
# 'httpOnly' parameter to the 'cookie set' command, which is displayed in a dialog
# when the user is using this command.
cookieSetHttpOnlyDesc=Not accessible from client side script
# LOCALIZATION NOTE (cookieSetSessionDesc) A very short string to describe the
# 'session' parameter to the 'cookie set' command, which is displayed in a dialog
# when the user is using this command.
cookieSetSessionDesc=Only valid for the lifetime of the browser session
# LOCALIZATION NOTE (cookieSetExpiresDesc) A very short string to describe the
# 'expires' parameter to the 'cookie set' command, which is displayed in a dialog
# when the user is using this command.
cookieSetExpiresDesc=The expiry date of the cookie (quoted RFC2822 or ISO 8601 date)
# LOCALIZATION NOTE (jsbDesc) A very short description of the
# 'jsb' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.

View File

@ -74,6 +74,12 @@ function fmt(aStr, aMaxLen, aMinLen, aOptions) {
* The constructor name
*/
function getCtorName(aObj) {
if (aObj === null) {
return "null";
}
if (aObj === undefined) {
return "undefined";
}
if (aObj.constructor && aObj.constructor.name) {
return aObj.constructor.name;
}