Merge m-c to inbound.

This commit is contained in:
Ryan VanderMeulen 2012-11-02 16:11:46 -04:00
commit a7d45f8d16
40 changed files with 1150 additions and 502 deletions

View File

@ -130,7 +130,9 @@ function openBrowserWindowIntl()
"_blank", params,
gBrowserContext.startURL);
addA11yLoadEvent(startBrowserTests, browserWindow());
whenDelayedStartupFinished(browserWindow(), function () {
addA11yLoadEvent(startBrowserTests, browserWindow());
});
}
function startBrowserTests()
@ -140,3 +142,12 @@ function startBrowserTests()
else
gBrowserContext.testFunc();
}
function whenDelayedStartupFinished(aWindow, aCallback) {
Services.obs.addObserver(function observer(aSubject, aTopic) {
if (aWindow == aSubject) {
Services.obs.removeObserver(observer, aTopic);
setTimeout(aCallback, 0);
}
}, "browser-delayed-startup-finished", false);
}

View File

@ -1027,7 +1027,7 @@ pref("devtools.responsiveUI.enabled", true);
// Enable the Debugger
pref("devtools.debugger.enabled", true);
pref("devtools.debugger.chrome-enabled", false);
pref("devtools.debugger.chrome-enabled", true);
pref("devtools.debugger.remote-host", "localhost");
pref("devtools.debugger.remote-autoconnect", false);
pref("devtools.debugger.remote-connection-retries", 3);

View File

@ -1008,7 +1008,6 @@ var gBrowserInit = {
if ("arguments" in window && window.arguments[0])
var uriToLoad = window.arguments[0];
var isLoadingBlank = isBlankPageURL(uriToLoad);
var mustLoadSidebar = false;
gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver, false);
@ -1095,44 +1094,6 @@ var gBrowserInit = {
// setup simple gestures support
gGestureSupport.init(true);
if (uriToLoad && uriToLoad != "about:blank") {
if (uriToLoad instanceof Ci.nsISupportsArray) {
let count = uriToLoad.Count();
let specs = [];
for (let i = 0; i < count; i++) {
let urisstring = uriToLoad.GetElementAt(i).QueryInterface(Ci.nsISupportsString);
specs.push(urisstring.data);
}
// This function throws for certain malformed URIs, so use exception handling
// so that we don't disrupt startup
try {
gBrowser.loadTabs(specs, false, true);
} catch (e) {}
}
else if (uriToLoad instanceof XULElement) {
// swap the given tab with the default about:blank tab and then close
// the original tab in the other window.
// Stop the about:blank load
gBrowser.stop();
// make sure it has a docshell
gBrowser.docShell;
gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, uriToLoad);
}
else if (window.arguments.length >= 3) {
loadURI(uriToLoad, window.arguments[2], window.arguments[3] || null,
window.arguments[4] || false);
window.focus();
}
// Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3.
// Such callers expect that window.arguments[0] is handled as a single URI.
else
loadOneOrMoreURIs(uriToLoad);
}
if (window.opener && !window.opener.closed) {
let openerSidebarBox = window.opener.document.getElementById("sidebar-box");
// If the opener had a sidebar, open the same sidebar in our window.
@ -1242,7 +1203,7 @@ var gBrowserInit = {
retrieveToolbarIconsizesFromTheme();
// Wait until chrome is painted before executing code not critical to making the window visible
this._boundDelayedStartup = this._delayedStartup.bind(this, isLoadingBlank, mustLoadSidebar);
this._boundDelayedStartup = this._delayedStartup.bind(this, uriToLoad, mustLoadSidebar);
window.addEventListener("MozAfterPaint", this._boundDelayedStartup);
gStartupRan = true;
@ -1253,7 +1214,7 @@ var gBrowserInit = {
this._boundDelayedStartup = null;
},
_delayedStartup: function(isLoadingBlank, mustLoadSidebar) {
_delayedStartup: function(uriToLoad, mustLoadSidebar) {
let tmp = {};
Cu.import("resource:///modules/TelemetryTimestamps.jsm", tmp);
let TelemetryTimestamps = tmp.TelemetryTimestamps;
@ -1261,6 +1222,45 @@ var gBrowserInit = {
this._cancelDelayedStartup();
var isLoadingBlank = isBlankPageURL(uriToLoad);
if (uriToLoad && uriToLoad != "about:blank") {
if (uriToLoad instanceof Ci.nsISupportsArray) {
let count = uriToLoad.Count();
let specs = [];
for (let i = 0; i < count; i++) {
let urisstring = uriToLoad.GetElementAt(i).QueryInterface(Ci.nsISupportsString);
specs.push(urisstring.data);
}
// This function throws for certain malformed URIs, so use exception handling
// so that we don't disrupt startup
try {
gBrowser.loadTabs(specs, false, true);
} catch (e) {}
}
else if (uriToLoad instanceof XULElement) {
// swap the given tab with the default about:blank tab and then close
// the original tab in the other window.
// Stop the about:blank load
gBrowser.stop();
// make sure it has a docshell
gBrowser.docShell;
gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, uriToLoad);
}
else if (window.arguments.length >= 3) {
loadURI(uriToLoad, window.arguments[2], window.arguments[3] || null,
window.arguments[4] || false);
window.focus();
}
// Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3.
// Such callers expect that window.arguments[0] is handled as a single URI.
else
loadOneOrMoreURIs(uriToLoad);
}
#ifdef MOZ_SAFE_BROWSING
// Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008.
setTimeout(function() { SafeBrowsing.init(); }, 2000);

View File

@ -1172,6 +1172,8 @@ Argument.prototype.merge = function(following) {
* - prefixPostSpace: Should the prefix be altered to end with a space?
* - suffixSpace: Should the suffix be altered to end with a space?
* - type: Constructor to use in creating new instances. Default: Argument
* - dontQuote: Should we avoid adding prefix/suffix quotes when the text value
* has a space? Needed when we're completing a sub-command.
*/
Argument.prototype.beget = function(options) {
var text = this.text;
@ -1182,10 +1184,13 @@ Argument.prototype.beget = function(options) {
text = options.text;
// We need to add quotes when the replacement string has spaces or is empty
var needsQuote = text.indexOf(' ') >= 0 || text.length == 0;
if (needsQuote && /['"]/.test(prefix)) {
prefix = prefix + '\'';
suffix = '\'' + suffix;
if (!options.dontQuote) {
var needsQuote = text.indexOf(' ') >= 0 || text.length == 0;
var hasQuote = /['"]$/.test(prefix);
if (needsQuote && !hasQuote) {
prefix = prefix + '\'';
suffix = '\'' + suffix;
}
}
}
@ -1590,9 +1595,9 @@ NamedArgument.prototype.beget = function(options) {
options.type = NamedArgument;
var begotten = Argument.prototype.beget.call(this, options);
// Cut the prefix into |whitespace|non-whitespace|whitespace| so we can
// Cut the prefix into |whitespace|non-whitespace|whitespace+quote so we can
// rebuild nameArg and valueArg from the parts
var matches = /^([\s]*)([^\s]*)([\s]*)$/.exec(begotten.prefix);
var matches = /^([\s]*)([^\s]*)([\s]*['"]?)$/.exec(begotten.prefix);
if (this.valueArg == null && begotten.text === '') {
begotten.nameArg = new Argument(matches[2], matches[1], matches[3]);
@ -3038,13 +3043,23 @@ function hash(str) {
return hash;
}
for (var i = 0; i < str.length; i++) {
var char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
var character = str.charCodeAt(i);
hash = ((hash << 5) - hash) + character;
hash = hash & hash; // Convert to 32bit integer
}
return hash;
}
/**
* Shortcut for clearElement/createTextNode/appendChild to make up for the lack
* of standards around textContent/innerText
*/
exports.setTextContent = function(elem, text) {
exports.clearElement(elem);
var child = elem.ownerDocument.createTextNode(text);
elem.appendChild(child);
};
/**
* There are problems with innerHTML on XML documents, so we need to do a dance
* using document.createRange().createContextualFragment() when in XML mode
@ -5411,7 +5426,7 @@ function UnassignedAssignment(requisition, arg) {
name: 'param',
requisition: requisition,
isIncompleteName: (arg.text.charAt(0) === '-')
},
}
});
this.paramIndex = -1;
this.onAssignmentChange = util.createEvent('UnassignedAssignment.onAssignmentChange');
@ -5812,7 +5827,10 @@ Requisition.prototype.complete = function(cursor, predictionChoice) {
}
else {
// Mutate this argument to hold the completion
var arg = assignment.arg.beget({ text: prediction.name });
var arg = assignment.arg.beget({
text: prediction.name,
dontQuote: (assignment === this.commandAssignment)
});
this.setAssignment(assignment, arg, { argUpdate: true });
if (!prediction.incomplete) {
@ -5905,7 +5923,7 @@ Requisition.prototype.toCanonicalString = function() {
* to display this typed input. It's a bit like toString on steroids.
* <p>
* The returned object has the following members:<ul>
* <li>char: The character to which this arg trace refers.
* <li>character: The character to which this arg trace refers.
* <li>arg: The Argument to which this character is assigned.
* <li>part: One of ['prefix'|'text'|suffix'] - how was this char understood
* </ul>
@ -5930,13 +5948,13 @@ Requisition.prototype.createInputArgTrace = function() {
var i;
this._args.forEach(function(arg) {
for (i = 0; i < arg.prefix.length; i++) {
args.push({ arg: arg, char: arg.prefix[i], part: 'prefix' });
args.push({ arg: arg, character: arg.prefix[i], part: 'prefix' });
}
for (i = 0; i < arg.text.length; i++) {
args.push({ arg: arg, char: arg.text[i], part: 'text' });
args.push({ arg: arg, character: arg.text[i], part: 'text' });
}
for (i = 0; i < arg.suffix.length; i++) {
args.push({ arg: arg, char: arg.suffix[i], part: 'suffix' });
args.push({ arg: arg, character: arg.suffix[i], part: 'suffix' });
}
});
@ -6024,7 +6042,7 @@ Requisition.prototype.getInputStatusMarkup = function(cursor) {
}
}
markup.push({ status: status, string: argTrace.char });
markup.push({ status: status, string: argTrace.character });
}
// De-dupe: merge entries where 2 adjacent have same status
@ -6796,7 +6814,12 @@ Output.prototype.toDom = function(element) {
node = util.createElement(document, 'p');
}
util.setContents(node, output.toString());
if (this.command.returnType === 'string') {
node.textContent = output;
}
else {
util.setContents(node, output.toString());
}
}
// Make sure that links open in a new window.
@ -6937,7 +6960,7 @@ var eagerHelperSettingSpec = {
lookup: [
{ name: 'never', value: Eagerness.NEVER },
{ name: 'sometimes', value: Eagerness.SOMETIMES },
{ name: 'always', value: Eagerness.ALWAYS },
{ name: 'always', value: Eagerness.ALWAYS }
]
},
defaultValue: Eagerness.SOMETIMES,
@ -7345,7 +7368,6 @@ var TrueNamedArgument = require('gcli/argument').TrueNamedArgument;
var FalseNamedArgument = require('gcli/argument').FalseNamedArgument;
var ArrayArgument = require('gcli/argument').ArrayArgument;
var Conversion = require('gcli/types').Conversion;
var ArrayConversion = require('gcli/types').ArrayConversion;
var StringType = require('gcli/types/basic').StringType;
@ -7608,7 +7630,7 @@ function ArrayField(type, options) {
this.addButton = util.createElement(this.document, 'button');
this.addButton.classList.add('gcli-array-member-add');
this.addButton.addEventListener('click', this._onAdd, false);
this.addButton.innerHTML = l10n.lookup('fieldArrayAdd');
this.addButton.textContent = l10n.lookup('fieldArrayAdd');
this.element.appendChild(this.addButton);
// <div class=gcliArrayMbrs save="${mbrElement}">
@ -7676,7 +7698,7 @@ ArrayField.prototype._onAdd = function(ev, subConversion) {
var delButton = util.createElement(this.document, 'button');
delButton.classList.add('gcli-array-member-del');
delButton.addEventListener('click', this._onDel, false);
delButton.innerHTML = l10n.lookup('fieldArrayDel');
delButton.textContent = l10n.lookup('fieldArrayDel');
element.appendChild(delButton);
var member = {
@ -7790,10 +7812,7 @@ Field.prototype.setMessageElement = function(element) {
*/
Field.prototype.setMessage = function(message) {
if (this.messageElement) {
if (message == null) {
message = '';
}
util.setContents(this.messageElement, message);
util.setTextContent(this.messageElement, message || '');
}
};
@ -8453,7 +8472,7 @@ SelectionField.prototype._addOption = function(item) {
this.items.push(item);
var option = util.createElement(this.document, 'option');
option.innerHTML = item.name;
option.textContent = item.name;
option.value = item.index;
this.element.appendChild(option);
};
@ -8594,7 +8613,7 @@ var helpCommandSpec = {
name: 'search',
type: 'string',
description: l10n.lookup('helpSearchDesc'),
manual: l10n.lookup('helpSearchManual2'),
manual: l10n.lookup('helpSearchManual3'),
defaultValue: null
}
],
@ -8679,7 +8698,7 @@ function getListTemplateData(args, context) {
ondblclick: function(ev) {
util.executeCommand(ev.currentTarget, context);
},
}
};
}
@ -8699,11 +8718,8 @@ function getManTemplateData(command, context) {
util.executeCommand(ev.currentTarget, context);
},
describe: function(item, element) {
var text = item.manual || item.description;
var parent = element.ownerDocument.createElement('div');
util.setContents(parent, text);
return parent.childNodes;
describe: function(item) {
return item.manual || item.description;
},
getTypeDescription: function(param) {
@ -8755,7 +8771,7 @@ define("text!gcli/commands/help_man.html", [], "\n" +
"\n" +
" <h4 class=\"gcli-help-header\">${l10n.helpManDescription}:</h4>\n" +
"\n" +
" <p class=\"gcli-help-description\">${describe(command, __element)}</p>\n" +
" <p class=\"gcli-help-description\">${describe(command)}</p>\n" +
"\n" +
" <div if=\"${command.exec}\">\n" +
" <h4 class=\"gcli-help-header\">${l10n.helpManParameters}:</h4>\n" +
@ -8765,7 +8781,7 @@ define("text!gcli/commands/help_man.html", [], "\n" +
" <li foreach=\"param in ${command.params}\">\n" +
" ${param.name} <em>${getTypeDescription(param)}</em>\n" +
" <br/>\n" +
" ${describe(param, __element)}\n" +
" ${describe(param)}\n" +
" </li>\n" +
" </ul>\n" +
" </div>\n" +
@ -8904,7 +8920,7 @@ var prefSetCmdSpec = {
activate: function() {
context.exec('pref set ' + exports.allowSet.name + ' true');
}
},
}
});
}
args.setting.value = args.value;
@ -9975,9 +9991,7 @@ Completer.prototype.resized = function(ev) {
* Bring the completion element up to date with what the requisition says
*/
Completer.prototype.update = function(ev) {
if (ev && ev.choice != null) {
this.choice = ev.choice;
}
this.choice = (ev && ev.choice != null) ? ev.choice : 0;
var data = this._getCompleterTemplateData();
var template = this.template.cloneNode(true);
@ -10140,14 +10154,15 @@ exports.Completer = Completer;
});
define("text!gcli/ui/completer.html", [], "\n" +
"<description>\n" +
"<description\n" +
" xmlns=\"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul\">\n" +
" <loop foreach=\"member in ${statusMarkup}\">\n" +
" <label class=\"${member.className}\">${member.string}</label>\n" +
" <label class=\"${member.className}\" value=\"${member.string}\"></label>\n" +
" </loop>\n" +
" <label class=\"gcli-in-ontab\">${directTabText}</label>\n" +
" <label class=\"gcli-in-todo\" foreach=\"param in ${emptyParameters}\">${param}</label>\n" +
" <label class=\"gcli-in-ontab\">${arrowTabText}</label>\n" +
" <label class=\"gcli-in-closebrace\" if=\"${unclosedJs}\">}</label>\n" +
" <label class=\"gcli-in-ontab\" value=\"${directTabText}\"/>\n" +
" <label class=\"gcli-in-todo\" foreach=\"param in ${emptyParameters}\" value=\"${param}\"/>\n" +
" <label class=\"gcli-in-ontab\" value=\"${arrowTabText}\"/>\n" +
" <label class=\"gcli-in-closebrace\" if=\"${unclosedJs}\" value=\"}\"/>\n" +
"</description>\n" +
"");
@ -10375,7 +10390,7 @@ Tooltip.prototype.assignmentContentsChanged = function(ev) {
}
this.field.setConversion(ev.conversion);
util.setContents(this.descriptionEle, this.description);
util.setTextContent(this.descriptionEle, this.description);
this._updatePosition();
};
@ -10429,19 +10444,7 @@ Object.defineProperty(Tooltip.prototype, 'description', {
return '';
}
var output = this.assignment.param.manual;
if (output) {
var wrapper = this.document.createElement('span');
util.setContents(wrapper, output);
if (!this.assignment.param.isDataRequired) {
var optional = this.document.createElement('span');
optional.appendChild(this.document.createTextNode(' (Optional)'));
wrapper.appendChild(optional);
}
return wrapper;
}
return this.assignment.param.description;
return this.assignment.param.manual || this.assignment.param.description;
},
enumerable: true
});

View File

@ -407,5 +407,53 @@ exports.testCompleteIntoOptional = function(options) {
});
};
exports.testSpaceComplete = function(options) {
helpers.setInput('tslong --sel2 wit');
helpers.check({
input: 'tslong --sel2 wit',
hints: 'h space <msg> [options]',
markup: 'VVVVVVVIIIIIIVIII',
cursor: 17,
current: 'sel2',
status: 'ERROR',
tooltipState: 'true:importantFieldFlag',
args: {
command: { name: 'tslong' },
msg: { status: 'INCOMPLETE', message: '' },
num: { status: 'VALID' },
sel: { status: 'VALID' },
bool: { value: false, status: 'VALID' },
num2: { status: 'VALID' },
bool2: { value: false, status: 'VALID' },
sel2: { arg: ' --sel2 wit', status: 'INCOMPLETE' }
}
});
helpers.pressTab();
helpers.check({
input: 'tslong --sel2 \'with space\' ',
hints: '<msg> [options]',
markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVV',
cursor: 27,
current: 'sel2',
status: 'ERROR',
tooltipState: 'true:importantFieldFlag',
args: {
command: { name: 'tslong' },
msg: { status: 'INCOMPLETE', message: '' },
num: { status: 'VALID' },
sel: { status: 'VALID' },
bool: { value: false,status: 'VALID' },
num2: { status: 'VALID' },
bool2: { value: false,status: 'VALID' },
sel2: {
value: 'with space',
arg: ' --sel2 \'with space\' ',
status: 'VALID'
}
}
});
};
// });

View File

@ -472,7 +472,7 @@ mockCommands.tslong = {
name: 'sel2',
type: {
name: 'selection',
data: ['collapse', 'expand', 'end-expand', 'expand-strict']
data: [ 'collapse', 'basic', 'with space', 'with two spaces' ]
},
description: 'sel2 Desc',
defaultValue: "collapse"

View File

@ -261,8 +261,7 @@ DebuggerPane.prototype = {
*/
_initServer: function DP__initServer() {
if (!DebuggerServer.initialized) {
// Always allow connections from nsIPipe transports.
DebuggerServer.init(function() true);
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
},

View File

@ -51,6 +51,7 @@ let DebuggerController = {
window.removeEventListener("load", this._startupDebugger, true);
DebuggerView.initialize(function() {
DebuggerView._isInitialized = true;
window.dispatchEvent("Debugger:Loaded");
this._connect();
}.bind(this));
@ -67,6 +68,7 @@ let DebuggerController = {
window.removeEventListener("unload", this._shutdownDebugger, true);
DebuggerView.destroy(function() {
DebuggerView._isDestroyed = true;
this.SourceScripts.disconnect();
this.StackFrames.disconnect();
this.ThreadState.disconnect();
@ -155,8 +157,13 @@ let DebuggerController = {
client.connect(function(aType, aTraits) {
client.listTabs(function(aResponse) {
let tab = aResponse.tabs[aResponse.selected];
this._startDebuggingTab(client, tab);
if (window._isChromeDebugger) {
let dbg = aResponse.chromeDebugger;
this._startChromeDebugging(client, dbg);
} else {
let tab = aResponse.tabs[aResponse.selected];
this._startDebuggingTab(client, tab);
}
window.dispatchEvent("Debugger:Connected");
}.bind(this));
}.bind(this));
@ -234,6 +241,36 @@ let DebuggerController = {
}.bind(this));
},
/**
* Sets up a chrome debugging session.
*
* @param DebuggerClient aClient
* The debugger client.
* @param object aChromeDebugger
* The remote protocol grip of the chrome debugger.
*/
_startChromeDebugging: function DC__startChromeDebugging(aClient, aChromeDebugger) {
if (!aClient) {
Cu.reportError("No client found!");
return;
}
this.client = aClient;
aClient.attachThread(aChromeDebugger, function(aResponse, aThreadClient) {
if (!aThreadClient) {
Cu.reportError("Couldn't attach to thread: " + aResponse.error);
return;
}
this.activeThread = aThreadClient;
this.ThreadState.connect();
this.StackFrames.connect();
this.SourceScripts.connect();
aThreadClient.resume();
}.bind(this));
},
/**
* Attempts to quit the current process if allowed.
*/
@ -685,6 +722,7 @@ StackFrames.prototype = {
*/
function SourceScripts() {
this._onNewScript = this._onNewScript.bind(this);
this._onNewGlobal = this._onNewGlobal.bind(this);
this._onScriptsAdded = this._onScriptsAdded.bind(this);
}
@ -697,6 +735,7 @@ SourceScripts.prototype = {
*/
connect: function SS_connect() {
this.debuggerClient.addListener("newScript", this._onNewScript);
this.debuggerClient.addListener("newGlobal", this._onNewGlobal);
this._handleTabNavigation();
},
@ -708,6 +747,7 @@ SourceScripts.prototype = {
return;
}
this.debuggerClient.removeListener("newScript", this._onNewScript);
this.debuggerClient.removeListener("newGlobal", this._onNewGlobal);
},
/**
@ -769,6 +809,14 @@ SourceScripts.prototype = {
window.dispatchEvent("Debugger:AfterNewScript");
},
/**
* Handler for the debugger client's unsolicited newGlobal notification.
*/
_onNewGlobal: function SS__onNewGlobal(aNotification, aPacket) {
// TODO: bug 806775, update the globals list using aPacket.hostAnnotations
// from bug 801084.
},
/**
* Callback for the debugger's active thread getScripts() method.
*/

View File

@ -56,7 +56,8 @@ ToolbarView.prototype = {
this._stepOutButton.setAttribute("tooltiptext", this._stepOutTooltip);
this.toggleCloseButton(!window._isRemoteDebugger && !window._isChromeDebugger);
this.toggleChromeGlobalsContainer(window._isChromeDebugger);
// TODO: bug 806775
// this.toggleChromeGlobalsContainer(window._isChromeDebugger);
},
/**
@ -584,11 +585,12 @@ FilterView.prototype = {
this._lineOperatorLabel.setAttribute("value",
L10N.getFormatStr("searchPanelLine", [this._lineSearchKey]));
if (window._isChromeDebugger) {
this.target = DebuggerView.ChromeGlobals;
} else {
this.target = DebuggerView.Sources;
}
// TODO: bug 806775
// if (window._isChromeDebugger) {
// this.target = DebuggerView.ChromeGlobals;
// } else {
this.target = DebuggerView.Sources;
// }
},
/**

View File

@ -45,7 +45,6 @@ let DebuggerView = {
this._initializePanes();
this._initializeEditor(aCallback)
this._isInitialized = true;
},
/**
@ -157,8 +156,11 @@ let DebuggerView = {
* The script url.
* @param string aContentType [optional]
* The script content type.
* @param string aTextContent [optional]
* The script text content.
*/
setEditorMode: function DV_setEditorMode(aUrl, aContentType) {
setEditorMode:
function DV_setEditorMode(aUrl, aContentType = "", aTextContent = "") {
if (!this.editor) {
return;
}
@ -171,12 +173,16 @@ let DebuggerView = {
} else {
this.editor.setMode(SourceEditor.MODES.HTML);
}
} else if (aTextContent.match(/^\s*</)) {
// Use HTML mode for files in which the first non whitespace character is
// &lt;, regardless of extension.
this.editor.setMode(SourceEditor.MODES.HTML);
} else {
// Use JS mode for files with .js and .jsm extensions.
if (/\.jsm?$/.test(SourceUtils.trimUrlQuery(aUrl))) {
this.editor.setMode(SourceEditor.MODES.JAVASCRIPT);
} else {
this.editor.setMode(SourceEditor.MODES.HTML);
this.editor.setMode(SourceEditor.MODES.TEXT);
}
}
},
@ -216,7 +222,9 @@ let DebuggerView = {
// If the source is already loaded, display it immediately.
else {
if (aSource.text.length < SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE) {
this.setEditorMode(aSource.url, aSource.contentType);
this.setEditorMode(aSource.url, aSource.contentType, aSource.text);
} else {
this.editor.setMode(SourceEditor.MODES.TEXT);
}
this.editor.setText(aSource.text);
this.editor.resetUndo();
@ -419,6 +427,7 @@ let DebuggerView = {
_stackframesAndBreakpoints: null,
_variables: null,
_isInitialized: false,
_isDestroyed: false
};
/**
@ -752,7 +761,7 @@ MenuContainer.prototype = {
* @param string aLabel
*/
set selectedLabel(aLabel) {
let item = this._itemsByLabel.get(aValue);
let item = this._itemsByLabel.get(aLabel);
if (item) {
this._container.selectedItem = item.target;
}

View File

@ -73,6 +73,8 @@ MOCHITEST_BROWSER_TESTS = \
browser_dbg_bfcache.js \
browser_dbg_breakpoint-new-script.js \
browser_dbg_bug737803_editor_actual_location.js \
browser_dbg_progress-listener-bug.js \
browser_dbg_chrome-debugging.js \
head.js \
$(NULL)

View File

@ -0,0 +1,71 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Tests that chrome debugging works.
var gClient = null;
var gTab = null;
var gThreadClient = null;
var gNewGlobal = false;
var gAttached = false;
var gChromeScript = false;
const DEBUGGER_TAB_URL = EXAMPLE_URL + "browser_dbg_debuggerstatement.html";
function test()
{
// Make sure there is enough time for findAllGlobals.
requestLongerTimeout(3);
let transport = DebuggerServer.connectPipe();
gClient = new DebuggerClient(transport);
gClient.connect(function(aType, aTraits) {
gTab = addTab(DEBUGGER_TAB_URL, function() {
gClient.listTabs(function(aResponse) {
let dbg = aResponse.chromeDebugger;
ok(dbg, "Found a chrome debugging actor.");
gClient.addOneTimeListener("newGlobal", function() gNewGlobal = true);
gClient.addListener("newScript", onNewScript);
gClient.attachThread(dbg, function(aResponse, aThreadClient) {
gThreadClient = aThreadClient;
ok(!aResponse.error, "Attached to the chrome debugger.");
gAttached = true;
// Ensure that a new global will be created.
let frame = content.document.createElement("iframe");
content.document.querySelector("body").appendChild(frame);
finish_test();
});
});
});
});
}
function onNewScript(aEvent, aScript)
{
if (aScript.url.startsWith("chrome:")) {
gChromeScript = true;
}
finish_test();
}
function finish_test()
{
if (!gAttached || !gChromeScript) {
return;
}
gClient.removeListener("newScript", onNewScript);
gThreadClient.resume(function(aResponse) {
removeTab(gTab);
gClient.close(function() {
ok(gNewGlobal, "Received newGlobal event.");
ok(gChromeScript, "Received newScript event for a chrome: script.");
finish();
});
});
}

View File

@ -83,10 +83,17 @@ function test() {
});
let iframe = gTab.linkedBrowser.contentWindow.wrappedJSObject.frames[0];
is(iframe.document.title, "Browser Debugger Test Tab", "Found the iframe");
iframe.runDebuggerStatement();
function handler() {
if (iframe.document.readyState != "complete") {
return;
}
iframe.window.removeEventListener("load", handler, false);
executeSoon(iframe.runDebuggerStatement);
};
iframe.window.addEventListener("load", handler, false);
handler();
},
function beforeTabAdded() {
if (!DebuggerServer.initialized) {

View File

@ -47,10 +47,17 @@ function test() {
});
let iframe = gTab.linkedBrowser.contentWindow.wrappedJSObject.frames[0];
is(iframe.document.title, "Browser Debugger Test Tab", "Found the iframe");
iframe.runDebuggerStatement();
function handler() {
if (iframe.document.readyState != "complete") {
return;
}
iframe.window.removeEventListener("load", handler, false);
executeSoon(iframe.runDebuggerStatement);
};
iframe.window.addEventListener("load", handler, false);
handler();
});
}

View File

@ -13,43 +13,61 @@ var gDebugger = null;
function test()
{
let scriptShown = false;
let framesAdded = false;
debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
gTab = aTab;
gDebuggee = aDebuggee;
gPane = aPane;
gDebugger = gPane.contentWindow;
testSimpleCall();
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
framesAdded = true;
runTest();
});
gDebuggee.simpleCall();
});
window.addEventListener("Debugger:SourceShown", function _onEvent(aEvent) {
let url = aEvent.detail.url;
if (url.indexOf("browser_dbg_stack") != -1) {
scriptShown = true;
window.removeEventListener(aEvent.type, _onEvent);
runTest();
}
});
function runTest()
{
if (scriptShown && framesAdded) {
Services.tm.currentThread.dispatch({ run: testSimpleCall }, 0);
}
}
}
function testSimpleCall() {
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
Services.tm.currentThread.dispatch({
run: function() {
var frames = gDebugger.DebuggerView.StackFrames._container._list,
childNodes = frames.childNodes;
var frames = gDebugger.DebuggerView.StackFrames._container._list,
childNodes = frames.childNodes;
is(gDebugger.DebuggerController.activeThread.state, "paused",
"Should only be getting stack frames while paused.");
is(gDebugger.DebuggerController.activeThread.state, "paused",
"Should only be getting stack frames while paused.");
is(frames.querySelectorAll(".dbg-stackframe").length, 1,
"Should have only one frame.");
is(frames.querySelectorAll(".dbg-stackframe").length, 1,
"Should have only one frame.");
is(childNodes.length, frames.querySelectorAll(".dbg-stackframe").length,
"All children should be frames.");
is(childNodes.length, frames.querySelectorAll(".dbg-stackframe").length,
"All children should be frames.");
isnot(gDebugger.DebuggerView.Sources.selectedValue, null,
"There should be a selected script.");
isnot(gDebugger.editor.getText().length, 0,
"The source editor should have some text displayed.");
isnot(gDebugger.DebuggerView.Sources.selectedValue, null,
"There should be a selected script.");
isnot(gDebugger.editor.getText().length, 0,
"The source editor should have some text displayed.");
isnot(gDebugger.editor.getText(), gDebugger.L10N.getStr("loadingText"),
"The source editor text should not be 'Loading...'");
testLocationChange();
}
}, 0);
});
gDebuggee.simpleCall();
testLocationChange();
}
function testLocationChange()

View File

@ -13,43 +13,61 @@ var gDebugger = null;
function test()
{
let scriptShown = false;
let framesAdded = false;
debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
gTab = aTab;
gDebuggee = aDebuggee;
gPane = aPane;
gDebugger = gPane.contentWindow;
testSimpleCall();
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
framesAdded = true;
runTest();
});
gDebuggee.simpleCall();
});
window.addEventListener("Debugger:SourceShown", function _onEvent(aEvent) {
let url = aEvent.detail.url;
if (url.indexOf("browser_dbg_stack") != -1) {
scriptShown = true;
window.removeEventListener(aEvent.type, _onEvent);
runTest();
}
});
function runTest()
{
if (scriptShown && framesAdded) {
Services.tm.currentThread.dispatch({ run: testSimpleCall }, 0);
}
}
}
function testSimpleCall() {
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
Services.tm.currentThread.dispatch({
run: function() {
var frames = gDebugger.DebuggerView.StackFrames._container._list,
childNodes = frames.childNodes;
var frames = gDebugger.DebuggerView.StackFrames._container._list,
childNodes = frames.childNodes;
is(gDebugger.DebuggerController.activeThread.state, "paused",
"Should only be getting stack frames while paused.");
is(gDebugger.DebuggerController.activeThread.state, "paused",
"Should only be getting stack frames while paused.");
is(frames.querySelectorAll(".dbg-stackframe").length, 1,
"Should have only one frame.");
is(frames.querySelectorAll(".dbg-stackframe").length, 1,
"Should have only one frame.");
is(childNodes.length, frames.querySelectorAll(".dbg-stackframe").length,
"All children should be frames.");
is(childNodes.length, frames.querySelectorAll(".dbg-stackframe").length,
"All children should be frames.");
isnot(gDebugger.DebuggerView.Sources.selectedValue, null,
"There should be a selected script.");
isnot(gDebugger.editor.getText().length, 0,
"The source editor should have some text displayed.");
isnot(gDebugger.DebuggerView.Sources.selectedValue, null,
"There should be a selected script.");
isnot(gDebugger.editor.getText().length, 0,
"The source editor should have some text displayed.");
isnot(gDebugger.editor.getText(), gDebugger.L10N.getStr("loadingText"),
"The source editor text should not be 'Loading...'");
testLocationChange();
}
}, 0);
});
gDebuggee.simpleCall();
testLocationChange();
}
function testLocationChange()

View File

@ -0,0 +1,70 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Tests that the debugger does show up even if a progress listener reads the
// WebProgress argument's DOMWindow property in onStateChange() (bug 771655).
var gPane = null;
var gTab = null;
var gOldListener = null;
const TEST_URL = EXAMPLE_URL + "browser_dbg_script-switching.html";
function test() {
installListener();
debug_tab_pane(TEST_URL, function(aTab, aDebuggee, aPane) {
gTab = aTab;
gPane = aPane;
let gDebugger = gPane.contentWindow;
is(gDebugger.DebuggerController._isInitialized, true,
"Controller should be initialized after debug_tab_pane.");
is(gDebugger.DebuggerView._isInitialized, true,
"View should be initialized after debug_tab_pane.");
closeDebuggerAndFinish();
});
}
// This is taken almost verbatim from bug 771655.
function installListener() {
if ("_testPL" in window) {
gOldListener = _testPL;
Cc['@mozilla.org/docloaderservice;1'].getService(Ci.nsIWebProgress)
.removeProgressListener(_testPL);
}
window._testPL = {
START_DOC: Ci.nsIWebProgressListener.STATE_START |
Ci.nsIWebProgressListener.STATE_IS_DOCUMENT,
onStateChange: function(wp, req, stateFlags, status) {
if ((stateFlags & this.START_DOC) === this.START_DOC) {
// This DOMWindow access triggers the unload event.
wp.DOMWindow;
}
},
QueryInterface: function(iid) {
if (iid.equals(Ci.nsISupportsWeakReference) ||
iid.equals(Ci.nsIWebProgressListener))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
}
}
Cc['@mozilla.org/docloaderservice;1'].getService(Ci.nsIWebProgress)
.addProgressListener(_testPL, Ci.nsIWebProgress.NOTIFY_STATE_REQUEST);
}
registerCleanupFunction(function() {
if (gOldListener) {
window._testPL = gOldListener;
} else {
delete window._testPL;
}
removeTab(gTab);
gPane = null;
gTab = null;
});

View File

@ -7,6 +7,9 @@
http://creativecommons.org/publicdomain/zero/1.0/ -->
<script type="text/javascript" src="test-script-switching-01.js?q=a"></script>
<script type="text/javascript" src="test-editor-mode?a=b"></script>
<script type="text/javascript">
function banana() { }
</script>
</head>
<body>
</body>

View File

@ -30,6 +30,7 @@ function test()
gDebuggee = aDebuggee;
gPane = aPane;
gDebugger = gPane.contentWindow;
gScripts = gDebugger.DebuggerView.Sources._container;
resumed = true;
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
@ -60,14 +61,13 @@ function test()
}
function testScriptsDisplay() {
gScripts = gDebugger.DebuggerView.Sources._container;
is(gDebugger.DebuggerController.activeThread.state, "paused",
"Should only be getting stack frames while paused.");
is(gScripts.itemCount, 2, "Found the expected number of scripts.");
is(gScripts.itemCount, 3,
"Found the expected number of scripts.");
is(gDebugger.editor.getMode(), SourceEditor.MODES.HTML,
is(gDebugger.editor.getMode(), SourceEditor.MODES.TEXT,
"Found the expected editor mode.");
ok(gDebugger.editor.getText().search(/debugger/) != -1,
@ -77,7 +77,7 @@ function testScriptsDisplay() {
let url = aEvent.detail.url;
if (url.indexOf("switching-01.js") != -1) {
window.removeEventListener(aEvent.type, _onEvent);
testSwitchPaused();
testSwitchPaused1();
}
});
@ -85,8 +85,14 @@ function testScriptsDisplay() {
gDebugger.DebuggerView.Sources.selectedValue = url;
}
function testSwitchPaused()
function testSwitchPaused1()
{
is(gDebugger.DebuggerController.activeThread.state, "paused",
"Should only be getting stack frames while paused.");
is(gScripts.itemCount, 3,
"Found the expected number of scripts.");
ok(gDebugger.editor.getText().search(/debugger/) == -1,
"The second script is no longer displayed.");
@ -96,6 +102,38 @@ function testSwitchPaused()
is(gDebugger.editor.getMode(), SourceEditor.MODES.JAVASCRIPT,
"Found the expected editor mode.");
window.addEventListener("Debugger:SourceShown", function _onEvent(aEvent) {
let url = aEvent.detail.url;
if (url.indexOf("update-editor-mode") != -1) {
window.removeEventListener(aEvent.type, _onEvent);
testSwitchPaused2();
}
});
let label = "browser_dbg_update-editor-mode.html";
gDebugger.DebuggerView.Sources.selectedLabel = label;
}
function testSwitchPaused2()
{
is(gDebugger.DebuggerController.activeThread.state, "paused",
"Should only be getting stack frames while paused.");
is(gScripts.itemCount, 3,
"Found the expected number of scripts.");
ok(gDebugger.editor.getText().search(/firstCall/) == -1,
"The first script is no longer displayed.");
ok(gDebugger.editor.getText().search(/debugger/) == -1,
"The second script is no longer displayed.");
ok(gDebugger.editor.getText().search(/banana/) != -1,
"The third script is displayed.");
is(gDebugger.editor.getMode(), SourceEditor.MODES.HTML,
"Found the expected editor mode.");
gDebugger.DebuggerController.activeThread.resume(function() {
closeDebuggerAndFinish();
});

View File

@ -552,7 +552,7 @@ function OutputPanel(aChromeDoc, aInput, aLoadCallback)
<html:iframe xmlns:html="http://www.w3.org/1999/xhtml"
id="gcli-output-frame"
src="chrome://browser/content/devtools/commandlineoutput.xhtml"
flex="1"/>
sandbox="allow-same-origin"/>
</tooltip|panel>
*/
@ -583,6 +583,7 @@ function OutputPanel(aChromeDoc, aInput, aLoadCallback)
this._frame = aChromeDoc.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");
this._panel.appendChild(this._frame);
this.displayedOutput = undefined;
@ -850,7 +851,8 @@ function TooltipPanel(aChromeDoc, aInput, aLoadCallback)
<html:iframe xmlns:html="http://www.w3.org/1999/xhtml"
id="gcli-tooltip-frame"
src="chrome://browser/content/devtools/commandlinetooltip.xhtml"
flex="1"/>
flex="1"
sandbox="allow-same-origin"/>
</tooltip|panel>
*/
@ -882,6 +884,7 @@ function TooltipPanel(aChromeDoc, aInput, aLoadCallback)
this._frame.id = "gcli-tooltip-frame";
this._frame.setAttribute("src", "chrome://browser/content/devtools/commandlinetooltip.xhtml");
this._frame.setAttribute("flex", "1");
this._frame.setAttribute("sandbox", "allow-same-origin");
this._panel.appendChild(this._frame);
this._frame.addEventListener("load", this._onload, true);

View File

@ -360,9 +360,8 @@ Function .onGUIEnd
${AndIf} $CheckboxSendPing == 1
System::Int64Op $DownloadedAmount / 1024
Pop $DownloadedAmount
InetBgDL::Get "${BaseURLStubPing}${Channel}/${AB_CD}/$ExitCode/" \
"$FirefoxLaunch/$SecondsToDownload/$DownloadedAmount/" \
"$ExistingProfile/$ExistingInstall/" "$PLUGINSDIR\_temp" /END
InetBgDL::Get "${BaseURLStubPing}${Channel}/${AB_CD}/$ExitCode/$FirefoxLaunch/$SecondsToDownload/$DownloadedAmount/$ExistingProfile/$ExistingInstall/" \
"$PLUGINSDIR\_temp" /END
${EndIf}
${UnloadUAC}

View File

@ -123,16 +123,15 @@ helpDesc=Get help on the available commands
helpManual=Provide help either on a specific command (if a search string is provided and an exact match is found) or on the available commands (if a search string is not provided, or if no exact match is found).
# LOCALIZATION NOTE (helpSearchDesc): A very short description of the 'search'
# parameter to the 'help' command. See helpSearchManual2 for a fuller
# parameter to the 'help' command. See helpSearchManual3 for a fuller
# description of what it does. This string is designed to be shown in a dialog
# with restricted space, which is why it should be as short as possible.
helpSearchDesc=Search string
# LOCALIZATION NOTE (helpSearchManual2): A fuller description of the 'search'
# LOCALIZATION NOTE (helpSearchManual3): A fuller description of the 'search'
# parameter to the 'help' command. Displayed when the user asks for help on
# what it does. Inline HTML (e.g. <strong>) can be used to emphasize the core
# concept.
helpSearchManual2=<strong>search string</strong> to use in narrowing down the displayed commands. Regular expressions not supported.
# what it does.
helpSearchManual3=search string to use in narrowing down the displayed commands. Regular expressions not supported.
# LOCALIZATION NOTE (helpManSynopsis): A heading shown at the top of a help
# page for a command in the console It labels a summary of the parameters to

View File

@ -10,6 +10,7 @@ extern "C" {
#include <libgnomevfs/gnome-vfs-mime-utils.h>
}
#include "NSPRFormatTime.h" // must be before anything that includes prtime.h
#include "nsServiceManagerUtils.h"
#include "nsComponentManagerUtils.h"
#include "mozilla/ModuleUtils.h"

View File

@ -0,0 +1,48 @@
// removeAllDebuggees removes all the debuggees.
var dbg = new Debugger;
// If we eval in a debuggee, log which debuggee it was.
var log;
dbg.onEnterFrame = function (frame) {
log += 'e';
log += frame.environment.object.label;
};
var g1 = newGlobal();
log = '';
g1.eval('Math');
assertEq(log, ''); // not yet a debuggee
var g1w = dbg.addDebuggee(g1);
assertEq(g1w instanceof Debugger.Object, true);
g1w.label = 'g1';
log = '';
g1.eval('Math'); // now a debuggee
assertEq(log, 'eg1');
var g2 = newGlobal();
log = '';
g1.eval('Math'); // debuggee
g2.eval('Math'); // not a debuggee
assertEq(log, 'eg1');
var g2w = dbg.addDebuggee(g2);
assertEq(g2w instanceof Debugger.Object, true);
g2w.label = 'g2';
log = '';
g1.eval('Math'); // debuggee
g2.eval('this'); // debuggee
assertEq(log, 'eg1eg2');
var a1 = dbg.getDebuggees();
assertEq(a1.length, 2);
assertEq(dbg.removeAllDebuggees(), undefined);
var a2 = dbg.getDebuggees();
assertEq(a2.length, 0);
log = '';
g1.eval('Math'); // no longer a debuggee
g2.eval('this'); // no longer a debuggee
assertEq(log, '');

View File

@ -1869,6 +1869,16 @@ Debugger::removeDebuggee(JSContext *cx, unsigned argc, Value *vp)
return true;
}
JSBool
Debugger::removeAllDebuggees(JSContext *cx, unsigned argc, Value *vp)
{
THIS_DEBUGGER(cx, argc, vp, "removeAllDebuggees", args, dbg);
for (GlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
dbg->removeDebuggeeGlobal(cx->runtime->defaultFreeOp(), e.front(), NULL, &e);
args.rval().setUndefined();
return true;
}
JSBool
Debugger::hasDebuggee(JSContext *cx, unsigned argc, Value *vp)
{
@ -2494,6 +2504,7 @@ JSPropertySpec Debugger::properties[] = {
JSFunctionSpec Debugger::methods[] = {
JS_FN("addDebuggee", Debugger::addDebuggee, 1, 0),
JS_FN("removeDebuggee", Debugger::removeDebuggee, 1, 0),
JS_FN("removeAllDebuggees", Debugger::removeAllDebuggees, 0, 0),
JS_FN("hasDebuggee", Debugger::hasDebuggee, 1, 0),
JS_FN("getDebuggees", Debugger::getDebuggees, 0, 0),
JS_FN("getNewestFrame", Debugger::getNewestFrame, 0, 0),

View File

@ -176,6 +176,7 @@ class Debugger {
static JSBool setUncaughtExceptionHook(JSContext *cx, unsigned argc, Value *vp);
static JSBool addDebuggee(JSContext *cx, unsigned argc, Value *vp);
static JSBool removeDebuggee(JSContext *cx, unsigned argc, Value *vp);
static JSBool removeAllDebuggees(JSContext *cx, unsigned argc, Value *vp);
static JSBool hasDebuggee(JSContext *cx, unsigned argc, Value *vp);
static JSBool getDebuggees(JSContext *cx, unsigned argc, Value *vp);
static JSBool getNewestFrame(JSContext *cx, unsigned argc, Value *vp);

View File

@ -52,6 +52,9 @@ public class AnnouncementsFetchResourceDelegate extends SyncResourceDelegate {
request.addHeader("Accept-Language", delegate.getLocale().toString());
request.addHeader("Accept", ACCEPT_HEADER);
// We never want to keep connections alive.
request.addHeader("Connection", "close");
// Set If-Modified-Since to avoid re-fetching content.
final long ifModifiedSince = delegate.getLastFetch();
if (ifModifiedSince > 0) {

View File

@ -33,6 +33,7 @@ REPOSITORY_PATHS = [
'mozboot/bootstrap.py',
'mozboot/centos.py',
'mozboot/fedora.py',
'mozboot/gentoo.py',
'mozboot/mint.py',
'mozboot/openbsd.py',
'mozboot/osx.py',

View File

@ -10,6 +10,7 @@ import sys
from mozboot.centos import CentOSBootstrapper
from mozboot.fedora import FedoraBootstrapper
from mozboot.gentoo import GentooBootstrapper
from mozboot.mint import MintBootstrapper
from mozboot.osx import OSXBootstrapper
from mozboot.openbsd import OpenBSDBootstrapper
@ -42,6 +43,8 @@ class Bootstrapper(object):
cls = CentOSBootstrapper
elif distro == 'Fedora':
cls = FedoraBootstrapper
elif distro == 'Gentoo Base System':
cls = GentooBootstrapper
elif distro == 'Mint':
cls = MintBootstrapper
elif distro == 'Ubuntu':

View File

@ -0,0 +1,19 @@
# 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/.
import os
from mozboot.base import BaseBootstrapper
class GentooBootstrapper(BaseBootstrapper):
def __init__(self, version, dist_id):
BaseBootstrapper.__init__(self)
self.version = version
self.dist_id = dist_id
def install_system_packages(self):
self.run_as_root(['emerge', '--onlydeps', '--quiet', 'firefox'])
self.run_as_root(['emerge', '--quiet', 'git', 'mercurial'])

View File

@ -13,7 +13,6 @@ const HTTP_PORT = 8888;
let prefs = new Preferences();
Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
prefs.set("extensions.getAddons.get.url", "http://localhost:8888/search/guid:%IDS%");
loadAddonTestFunctions();
startupManager();
@ -77,110 +76,6 @@ function run_test() {
run_next_test();
}
add_test(function test_get_all_ids() {
_("Ensures that getAllIDs() returns an appropriate set.");
engine._refreshReconcilerState();
let addon1 = installAddon("test_install1");
let addon2 = installAddon("test_bootstrap1_1");
let ids = store.getAllIDs();
do_check_eq("object", typeof(ids));
do_check_eq(2, Object.keys(ids).length);
do_check_true(addon1.syncGUID in ids);
do_check_true(addon2.syncGUID in ids);
addon1.install.cancel();
uninstallAddon(addon2);
run_next_test();
});
add_test(function test_change_item_id() {
_("Ensures that changeItemID() works properly.");
let addon = installAddon("test_bootstrap1_1");
let oldID = addon.syncGUID;
let newID = Utils.makeGUID();
store.changeItemID(oldID, newID);
let newAddon = getAddonFromAddonManagerByID(addon.id);
do_check_neq(null, newAddon);
do_check_eq(newID, newAddon.syncGUID);
uninstallAddon(newAddon);
run_next_test();
});
add_test(function test_create() {
_("Ensure creating/installing an add-on from a record works.");
let server = createAndStartHTTPServer(HTTP_PORT);
let addon = installAddon("test_bootstrap1_1");
let id = addon.id;
uninstallAddon(addon);
let guid = Utils.makeGUID();
let record = createRecordForThisApp(guid, id, true, false);
let failed = store.applyIncomingBatch([record]);
do_check_eq(0, failed.length);
let newAddon = getAddonFromAddonManagerByID(id);
do_check_neq(null, newAddon);
do_check_eq(guid, newAddon.syncGUID);
do_check_false(newAddon.userDisabled);
uninstallAddon(newAddon);
server.stop(run_next_test);
});
add_test(function test_create_missing_search() {
_("Ensures that failed add-on searches are handled gracefully.");
let server = createAndStartHTTPServer(HTTP_PORT);
// The handler for this ID is not installed, so a search should 404.
const id = "missing@tests.mozilla.org";
let guid = Utils.makeGUID();
let record = createRecordForThisApp(guid, id, true, false);
let failed = store.applyIncomingBatch([record]);
do_check_eq(1, failed.length);
do_check_eq(guid, failed[0]);
let addon = getAddonFromAddonManagerByID(id);
do_check_eq(null, addon);
server.stop(run_next_test);
});
add_test(function test_create_bad_install() {
_("Ensures that add-ons without a valid install are handled gracefully.");
let server = createAndStartHTTPServer(HTTP_PORT);
// The handler returns a search result but the XPI will 404.
const id = "missing-xpi@tests.mozilla.org";
let guid = Utils.makeGUID();
let record = createRecordForThisApp(guid, id, true, false);
let failed = store.applyIncomingBatch([record]);
do_check_eq(1, failed.length);
do_check_eq(guid, failed[0]);
let addon = getAddonFromAddonManagerByID(id);
do_check_eq(null, addon);
server.stop(run_next_test);
});
add_test(function test_remove() {
_("Ensure removing add-ons from deleted records works.");
@ -406,6 +301,122 @@ add_test(function test_ignore_hotfixes() {
run_next_test();
});
add_test(function test_get_all_ids() {
_("Ensures that getAllIDs() returns an appropriate set.");
Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
_("Installing two addons.");
let addon1 = installAddon("test_install1");
let addon2 = installAddon("test_bootstrap1_1");
_("Ensure they're syncable.");
do_check_true(store.isAddonSyncable(addon1));
do_check_true(store.isAddonSyncable(addon2));
let ids = store.getAllIDs();
do_check_eq("object", typeof(ids));
do_check_eq(2, Object.keys(ids).length);
do_check_true(addon1.syncGUID in ids);
do_check_true(addon2.syncGUID in ids);
addon1.install.cancel();
uninstallAddon(addon2);
Svc.Prefs.reset("addons.ignoreRepositoryChecking");
run_next_test();
});
add_test(function test_change_item_id() {
_("Ensures that changeItemID() works properly.");
let addon = installAddon("test_bootstrap1_1");
let oldID = addon.syncGUID;
let newID = Utils.makeGUID();
store.changeItemID(oldID, newID);
let newAddon = getAddonFromAddonManagerByID(addon.id);
do_check_neq(null, newAddon);
do_check_eq(newID, newAddon.syncGUID);
uninstallAddon(newAddon);
run_next_test();
});
add_test(function test_create() {
_("Ensure creating/installing an add-on from a record works.");
// Set this so that getInstallFromSearchResult doesn't end up
// failing the install due to an insecure source URI scheme.
Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
let server = createAndStartHTTPServer(HTTP_PORT);
let addon = installAddon("test_bootstrap1_1");
let id = addon.id;
uninstallAddon(addon);
let guid = Utils.makeGUID();
let record = createRecordForThisApp(guid, id, true, false);
let failed = store.applyIncomingBatch([record]);
do_check_eq(0, failed.length);
let newAddon = getAddonFromAddonManagerByID(id);
do_check_neq(null, newAddon);
do_check_eq(guid, newAddon.syncGUID);
do_check_false(newAddon.userDisabled);
uninstallAddon(newAddon);
Svc.Prefs.reset("addons.ignoreRepositoryChecking");
server.stop(run_next_test);
});
add_test(function test_create_missing_search() {
_("Ensures that failed add-on searches are handled gracefully.");
let server = createAndStartHTTPServer(HTTP_PORT);
// The handler for this ID is not installed, so a search should 404.
const id = "missing@tests.mozilla.org";
let guid = Utils.makeGUID();
let record = createRecordForThisApp(guid, id, true, false);
let failed = store.applyIncomingBatch([record]);
do_check_eq(1, failed.length);
do_check_eq(guid, failed[0]);
let addon = getAddonFromAddonManagerByID(id);
do_check_eq(null, addon);
server.stop(run_next_test);
});
add_test(function test_create_bad_install() {
_("Ensures that add-ons without a valid install are handled gracefully.");
let server = createAndStartHTTPServer(HTTP_PORT);
// The handler returns a search result but the XPI will 404.
const id = "missing-xpi@tests.mozilla.org";
let guid = Utils.makeGUID();
let record = createRecordForThisApp(guid, id, true, false);
let failed = store.applyIncomingBatch([record]);
do_check_eq(1, failed.length);
do_check_eq(guid, failed[0]);
let addon = getAddonFromAddonManagerByID(id);
do_check_eq(null, addon);
server.stop(run_next_test);
});
add_test(function test_wipe() {
_("Ensures that wiping causes add-ons to be uninstalled.");

View File

@ -10,7 +10,7 @@ const logsdir = FileUtils.getDir("ProfD", ["weave", "logs"], true);
const LOG_PREFIX_SUCCESS = "success-";
const LOG_PREFIX_ERROR = "error-";
const CLEANUP_DELAY = 1000; // delay to age files for cleanup (ms)
const DELAY_BUFFER = 50; // buffer for timers on different OS platforms
const DELAY_BUFFER = 500; // buffer for timers on different OS platforms
const PROLONGED_ERROR_DURATION =
(Svc.Prefs.get('errorhandler.networkFailureReportTimeout') * 2) * 1000;
@ -248,8 +248,8 @@ add_test(function test_login_error_logOnError_true() {
// Check that error log files are deleted above an age threshold.
add_test(function test_logErrorCleanup_age() {
let maxAge = CLEANUP_DELAY/1000;
let firstlog_name;
_("Beginning test_logErrorCleanup_age.");
let maxAge = CLEANUP_DELAY / 1000;
let oldLogs = [];
let numLogs = 10;
let errString = "some error log\n";
@ -257,13 +257,15 @@ add_test(function test_logErrorCleanup_age() {
Svc.Prefs.set("log.appender.file.logOnError", true);
Svc.Prefs.set("log.appender.file.maxErrorAge", maxAge);
// Make some files.
_("Making some files.");
for (let i = 0; i < numLogs; i++) {
let filename = LOG_PREFIX_ERROR + Date.now() + i + ".txt";
let now = Date.now();
let filename = LOG_PREFIX_ERROR + now + "" + i + ".txt";
let newLog = FileUtils.getFile("ProfD", ["weave", "logs", filename]);
let foStream = FileUtils.openFileOutputStream(newLog);
foStream.write(errString, errString.length);
foStream.close();
_(" > Created " + filename);
oldLogs.push(newLog.leafName);
}
@ -291,7 +293,10 @@ add_test(function test_logErrorCleanup_age() {
run_next_test();
});
let delay = CLEANUP_DELAY + DELAY_BUFFER;
_("Cleaning up logs after " + delay + "msec.");
CommonUtils.namedTimer(function onTimer() {
Svc.Obs.notify("weave:service:sync:error");
}, CLEANUP_DELAY + DELAY_BUFFER, this, "cleanup-timer");
}, delay, this, "cleanup-timer");
});

View File

@ -96,8 +96,11 @@ Tester.prototype = {
Services.obs.addObserver(this, "chrome-document-global-created", false);
Services.obs.addObserver(this, "content-document-global-created", false);
this._globalProperties = Object.keys(window);
this._globalPropertyWhitelist = ["navigator", "constructor", "Application",
"__SS_tabsToRestore", "__SSi", "webConsoleCommandController",
this._globalPropertyWhitelist = [
"navigator", "constructor", "top",
"Application",
"__SS_tabsToRestore", "__SSi",
"webConsoleCommandController",
];
if (this.tests.length)

View File

@ -186,6 +186,8 @@ let snapshotFormatters = {
userJSFile.append("user.js");
$("prefs-user-js-link").href = Services.io.newFileURI(userJSFile).spec;
$("prefs-user-js-section").style.display = "";
// Clear the no-copy class
$("prefs-user-js-section").className = "";
},
};

View File

@ -209,7 +209,7 @@
</tbody>
</table>
<section id="prefs-user-js-section" style="display:none">
<section id="prefs-user-js-section" class="no-copy" style="display:none">
<h3>&aboutSupport.userJSTitle;</h3>
<p>&aboutSupport.userJSDescription;</p>
</section>

View File

@ -173,6 +173,7 @@ const UnsolicitedNotifications = {
"locationChange": "locationChange",
"networkEvent": "networkEvent",
"networkEventUpdate": "networkEventUpdate",
"newGlobal": "newGlobal",
"newScript": "newScript",
"tabDetached": "tabDetached",
"tabNavigated": "tabNavigated",

View File

@ -72,18 +72,26 @@ BrowserRootActor.prototype = {
},
/**
* Handles the listTabs request. Builds a list of actors
* for the tabs running in the process. The actors will survive
* until at least the next listTabs request.
* Handles the listTabs request. Builds a list of actors for the tabs running
* in the process. The actors will survive until at least the next listTabs
* request.
*/
onListTabs: function BRA_onListTabs() {
// Get actors for all the currently-running tabs (reusing
// existing actors where applicable), and store them in
// an ActorPool.
// Get actors for all the currently-running tabs (reusing existing actors
// where applicable), and store them in an ActorPool.
let actorPool = new ActorPool(this.conn);
let tabActorList = [];
// Get the chrome debugger actor.
let actor = this._chromeDebugger;
if (!actor) {
actor = new ChromeDebuggerActor(this);
actor.parentID = this.actorID;
this._chromeDebugger = actor;
actorPool.addActor(actor);
}
// Walk over open browser windows.
let e = windowMediator.getEnumerator("navigator:browser");
let top = windowMediator.getMostRecentWindow("navigator:browser");
@ -91,12 +99,12 @@ BrowserRootActor.prototype = {
while (e.hasMoreElements()) {
let win = e.getNext();
// Watch the window for tab closes so we can invalidate
// actors as needed.
// Watch the window for tab closes so we can invalidate actors as needed.
this.watchWindow(win);
// List the tabs in this browser.
let selectedBrowser = win.getBrowser().selectedBrowser;
let browsers = win.getBrowser().browsers;
for each (let browser in browsers) {
if (browser == selectedBrowser && win == top) {
@ -115,9 +123,8 @@ BrowserRootActor.prototype = {
this._createExtraActors(DebuggerServer.globalActorFactories, actorPool);
// Now drop the old actorID -> actor map. Actors that still
// mattered were added to the new map, others will go
// away.
// Now drop the old actorID -> actor map. Actors that still mattered were
// added to the new map, others will go away.
if (this._tabActorPool) {
this.conn.removeActorPool(this._tabActorPool);
}
@ -127,7 +134,8 @@ BrowserRootActor.prototype = {
let response = {
"from": "root",
"selected": selected,
"tabs": [actor.grip() for (actor of tabActorList)]
"tabs": [actor.grip() for (actor of tabActorList)],
"chromeDebugger": this._chromeDebugger.actorID
};
this._appendExtraActors(response);
return response;
@ -204,7 +212,58 @@ BrowserRootActor.prototype = {
}
},
// nsIWindowMediatorListener
// ChromeDebuggerActor hooks.
/**
* Add the specified actor to the default actor pool connection, in order to
* keep it alive as long as the server is. This is used by breakpoints in the
* thread and chrome debugger actors.
*
* @param actor aActor
* The actor object.
*/
addToParentPool: function BRA_addToParentPool(aActor) {
this.conn.addActor(aActor);
},
/**
* Remove the specified actor from the default actor pool.
*
* @param BreakpointActor aActor
* The actor object.
*/
removeFromParentPool: function BRA_removeFromParentPool(aActor) {
this.conn.removeActor(aActor);
},
/**
* Prepare to enter a nested event loop by disabling debuggee events.
*/
preNest: function BRA_preNest() {
let top = windowMediator.getMostRecentWindow("navigator:browser");
let browser = top.gBrowser.selectedBrowser;
let windowUtils = browser.contentWindow
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
windowUtils.suppressEventHandling(true);
windowUtils.suspendTimeouts();
},
/**
* Prepare to exit a nested event loop by enabling debuggee events.
*/
postNest: function BRA_postNest(aNestData) {
let top = windowMediator.getMostRecentWindow("navigator:browser");
let browser = top.gBrowser.selectedBrowser;
let windowUtils = browser.contentWindow
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
windowUtils.resumeTimeouts();
windowUtils.suppressEventHandling(false);
},
// nsIWindowMediatorListener.
onWindowTitleChange: function BRA_onWindowTitleChange(aWindow, aTitle) { },
onOpenWindow: function BRA_onOpenWindow(aWindow) { },
onCloseWindow: function BRA_onCloseWindow(aWindow) {
@ -267,23 +326,24 @@ BrowserTabActor.prototype = {
_pendingNavigation: null,
/**
* Add the specified breakpoint to the default actor pool connection, in order
* to be alive as long as the server is.
* Add the specified actor to the default actor pool connection, in order to
* keep it alive as long as the server is. This is used by breakpoints in the
* thread actor.
*
* @param BreakpointActor aActor
* @param actor aActor
* The actor object.
*/
addToBreakpointPool: function BTA_addToBreakpointPool(aActor) {
addToParentPool: function BTA_addToParentPool(aActor) {
this.conn.addActor(aActor);
},
/**
* Remove the specified breakpint from the default actor pool.
* Remove the specified actor from the default actor pool.
*
* @param BreakpointActor aActor
* The actor object.
*/
removeFromBreakpointPool: function BTA_removeFromBreakpointPool(aActor) {
removeFromParentPool: function BTA_removeFromParentPool(aActor) {
this.conn.removeActor(aActor);
},
@ -296,9 +356,17 @@ BrowserTabActor.prototype = {
dbg_assert(this.actorID,
"tab should have an actorID.");
let title = this.browser.contentTitle;
// If contentTitle is empty (e.g. on a not-yet-restored tab), but there is a
// tabbrowser (i.e. desktop Firefox, but not Fennec), we can use the label
// as the title.
if (!title && this._tabbrowser) {
title = this._tabbrowser
._getTabForContentWindow(this.browser.contentWindow).label;
}
let response = {
actor: this.actorID,
title: this.browser.contentTitle,
title: title,
url: this.browser.currentURI.spec
};
@ -384,21 +452,9 @@ BrowserTabActor.prototype = {
this.conn.addActorPool(this._contextPool);
this.threadActor = new ThreadActor(this);
this._addDebuggees(this.browser.contentWindow.wrappedJSObject);
this._contextPool.addActor(this.threadActor);
},
/**
* Add the provided window and all windows in its frame tree as debuggees.
*/
_addDebuggees: function BTA__addDebuggees(aWindow) {
this.threadActor.addDebuggee(aWindow);
let frames = aWindow.frames;
for (let i = 0; i < frames.length; i++) {
this._addDebuggees(frames[i]);
}
},
/**
* Exits the current thread actor and removes the context-lifetime actor pool.
* The content window is no longer being debugged after this call.
@ -509,7 +565,9 @@ BrowserTabActor.prototype = {
}
if (this._attached) {
this.threadActor.clearDebuggees();
this.threadActor.dbg.enabled = true;
if (this.threadActor.dbg) {
this.threadActor.dbg.enabled = true;
}
if (this._progressListener) {
delete this._progressListener._needsTabNavigated;
}
@ -519,7 +577,10 @@ BrowserTabActor.prototype = {
}
if (this._attached) {
this._addDebuggees(evt.target.defaultView.wrappedJSObject);
this.threadActor.global = evt.target.defaultView.wrappedJSObject;
if (this.threadActor.attached) {
this.threadActor.findGlobals();
}
}
}
};
@ -591,107 +652,10 @@ DebuggerProgressListener.prototype = {
* Destroy the progress listener instance.
*/
destroy: function DPL_destroy() {
this._tabActor._tabbrowser.removeProgressListener(this);
if (this._tabActor._tabbrowser.removeProgressListener) {
this._tabActor._tabbrowser.removeProgressListener(this);
}
this._tabActor._progressListener = null;
this._tabActor = null;
}
};
// DebuggerServer extension API.
/**
* Registers handlers for new tab-scoped request types defined dynamically.
* This is used for example by add-ons to augment the functionality of the tab
* actor.
* TODO: remove this API in the next release after bug 753401 lands, once all
* our experimental add-ons have been converted to the new API.
*
* @param aName string
* The name of the new request type.
* @param aFunction function
* The handler for this request type.
*/
DebuggerServer.addTabRequest = function DS_addTabRequest(aName, aFunction) {
BrowserTabActor.prototype.requestTypes[aName] = function(aRequest) {
if (!this.attached) {
return { error: "wrongState" };
}
return aFunction(this, aRequest);
}
};
/**
* Registers handlers for new tab-scoped request types defined dynamically.
* This is used for example by add-ons to augment the functionality of the tab
* actor.
*
* @param aFunction function
* The constructor function for this request type.
* @param aName string [optional]
* The name of the new request type. If this is not present, the
* actorPrefix property of the constructor prototype is used.
*/
DebuggerServer.addTabActor = function DS_addTabActor(aFunction, aName) {
let name = aName ? aName : aFunction.prototype.actorPrefix;
if (["title", "url", "actor"].indexOf(name) != -1) {
throw Error(name + " is not allowed");
}
if (DebuggerServer.tabActorFactories.hasOwnProperty(name)) {
throw Error(name + " already exists");
}
DebuggerServer.tabActorFactories[name] = aFunction;
};
/**
* Unregisters the handler for the specified tab-scoped request type.
* This may be used for example by add-ons when shutting down or upgrading.
*
* @param aFunction function
* The constructor function for this request type.
*/
DebuggerServer.removeTabActor = function DS_removeTabActor(aFunction) {
for (let name in DebuggerServer.tabActorFactories) {
let handler = DebuggerServer.tabActorFactories[name];
if (handler.name == aFunction.name) {
delete DebuggerServer.tabActorFactories[name];
}
}
};
/**
* Registers handlers for new browser-scoped request types defined dynamically.
* This is used for example by add-ons to augment the functionality of the root
* actor.
*
* @param aFunction function
* The constructor function for this request type.
* @param aName string [optional]
* The name of the new request type. If this is not present, the
* actorPrefix property of the constructor prototype is used.
*/
DebuggerServer.addGlobalActor = function DS_addGlobalActor(aFunction, aName) {
let name = aName ? aName : aFunction.prototype.actorPrefix;
if (["from", "tabs", "selected"].indexOf(name) != -1) {
throw Error(name + " is not allowed");
}
if (DebuggerServer.globalActorFactories.hasOwnProperty(name)) {
throw Error(name + " already exists");
}
DebuggerServer.globalActorFactories[name] = aFunction;
};
/**
* Unregisters the handler for the specified browser-scoped request type.
* This may be used for example by add-ons when shutting down or upgrading.
*
* @param aFunction function
* The constructor function for this request type.
*/
DebuggerServer.removeGlobalActor = function DS_removeGlobalActor(aFunction) {
for (let name in DebuggerServer.globalActorFactories) {
let handler = DebuggerServer.globalActorFactories[name];
if (handler.name == aFunction.name) {
delete DebuggerServer.globalActorFactories[name];
}
}
};

View File

@ -17,16 +17,27 @@
*
* @param aHooks object
* An object with preNest and postNest methods for calling when entering
* and exiting a nested event loop, as well as addToBreakpointPool and
* removeFromBreakpointPool methods for handling breakpoint lifetime.
* and exiting a nested event loop, addToParentPool and
* removeFromParentPool methods for handling the lifetime of actors that
* will outlive the thread, like breakpoints, and also an optional (for
* content debugging) browser property for getting a reference to the
* content window.
*/
function ThreadActor(aHooks)
{
this._state = "detached";
this._frameActors = [];
this._environmentActors = [];
this._hooks = aHooks ? aHooks : {};
this._hooks = {};
if (aHooks) {
this._hooks = aHooks;
if (aHooks.browser) {
this.global = aHooks.browser.contentWindow.wrappedJSObject;
}
}
this._scripts = {};
this.findGlobals = this.globalManager.findGlobals.bind(this);
this.onNewGlobal = this.globalManager.onNewGlobal.bind(this);
}
/**
@ -39,6 +50,9 @@ ThreadActor.prototype = {
actorPrefix: "context",
get state() { return this._state; },
get attached() this.state == "attached" ||
this.state == "running" ||
this.state == "paused",
get _breakpointStore() { return ThreadActor._breakpointStore; },
@ -52,19 +66,10 @@ ThreadActor.prototype = {
clearDebuggees: function TA_clearDebuggees() {
if (this.dbg) {
let debuggees = this.dbg.getDebuggees();
for (let debuggee of debuggees) {
this.dbg.removeDebuggee(debuggee);
}
this.dbg.removeAllDebuggees();
}
this.conn.removeActorPool(this._threadLifetimePool || undefined);
this._threadLifetimePool = null;
// Unless we carefully take apart the scripts table this way, we end up
// leaking documents. It would be nice to track this down carefully, once
// we have the appropriate tools.
for (let url in this._scripts) {
delete this._scripts[url];
}
this._scripts = {};
},
@ -72,24 +77,25 @@ ThreadActor.prototype = {
* Add a debuggee global to the Debugger object.
*/
addDebuggee: function TA_addDebuggee(aGlobal) {
// Use the inspector xpcom component to turn on debugging
// for aGlobal's compartment. Ideally this won't be necessary
// medium- to long-term, and will be managed by the engine
// instead.
if (!this.dbg) {
this.dbg = new Debugger();
this.dbg.uncaughtExceptionHook = this.uncaughtExceptionHook.bind(this);
this.dbg.onDebuggerStatement = this.onDebuggerStatement.bind(this);
this.dbg.onNewScript = this.onNewScript.bind(this);
// Keep the debugger disabled until a client attaches.
this.dbg.enabled = this._state != "detached";
try {
this.dbg.addDebuggee(aGlobal);
} catch (e) {
// Ignore attempts to add the debugger's compartment as a debuggee.
dumpn("Ignoring request to add the debugger's compartment as a debuggee");
}
},
this.dbg.addDebuggee(aGlobal);
for (let s of this.dbg.findScripts()) {
this._addScript(s);
}
/**
* Initialize the Debugger.
*/
_initDebugger: function TA__initDebugger() {
this.dbg = new Debugger();
this.dbg.uncaughtExceptionHook = this.uncaughtExceptionHook.bind(this);
this.dbg.onDebuggerStatement = this.onDebuggerStatement.bind(this);
this.dbg.onNewScript = this.onNewScript.bind(this);
this.dbg.onNewGlobalObject = this.globalManager.onNewGlobal.bind(this);
// Keep the debugger disabled until a client attaches.
this.dbg.enabled = this._state != "detached";
},
/**
@ -104,6 +110,53 @@ ThreadActor.prototype = {
}
},
/**
* Add the provided window and all windows in its frame tree as debuggees.
*/
_addDebuggees: function TA__addDebuggees(aWindow) {
this.addDebuggee(aWindow);
let frames = aWindow.frames;
if (frames) {
for (let i = 0; i < frames.length; i++) {
this._addDebuggees(frames[i]);
}
}
},
/**
* An object that will be used by ThreadActors to tailor their behavior
* depending on the debugging context being required (chrome or content).
*/
globalManager: {
findGlobals: function TA_findGlobals() {
this._addDebuggees(this.global);
},
/**
* A function that the engine calls when a new global object has been
* created.
*
* @param aGlobal Debugger.Object
* The new global object that was created.
*/
onNewGlobal: function TA_onNewGlobal(aGlobal) {
// Content debugging only cares about new globals in the contant window,
// like iframe children.
if (aGlobal.hostAnnotations &&
aGlobal.hostAnnotations.type == "document" &&
aGlobal.hostAnnotations.element === this.global) {
this.addDebuggee(aGlobal);
}
// Notify the client.
this.conn.send({
from: this.actorID,
type: "newGlobal",
// TODO: after bug 801084 lands see if we need to JSONify this.
hostAnnotations: aGlobal.hostAnnotations
});
}
},
disconnect: function TA_disconnect() {
if (this._state == "paused") {
this.onResume();
@ -139,10 +192,13 @@ ThreadActor.prototype = {
this._state = "attached";
if (!this.dbg) {
this._initDebugger();
}
this.findGlobals();
this.dbg.enabled = true;
try {
// Put ourselves in the paused state.
// XXX: We need to put the debuggee in a paused state too.
let packet = this._paused();
if (!packet) {
return { error: "notAttached" };
@ -461,7 +517,7 @@ ThreadActor.prototype = {
}
if (!bpActor) {
bpActor = new BreakpointActor(this, location);
this._hooks.addToBreakpointPool(bpActor);
this._hooks.addToParentPool(bpActor);
if (scriptBreakpoints[location.line]) {
scriptBreakpoints[location.line].actor = bpActor;
}
@ -1045,13 +1101,13 @@ ThreadActor.prototype = {
},
/**
* Add the provided script to the server cache.
* Check if the provided script is allowed to be stored in the cache.
*
* @param aScript Debugger.Script
* The source script that will be stored.
* @returns true, if the script was added, false otherwise.
* @returns true, if the script can be added, false otherwise.
*/
_addScript: function TA__addScript(aScript) {
_allowScript: function TA__allowScript(aScript) {
// Ignore anything we don't have a URL for (eval scripts, for example).
if (!aScript.url)
return false;
@ -1063,6 +1119,20 @@ ThreadActor.prototype = {
if (aScript.url.indexOf("about:") == 0) {
return false;
}
return true;
},
/**
* Add the provided script to the server cache.
*
* @param aScript Debugger.Script
* The source script that will be stored.
* @returns true, if the script was added, false otherwise.
*/
_addScript: function TA__addScript(aScript) {
if (!this._allowScript(aScript)) {
return false;
}
// Use a sparse array for storing the scripts for each URL in order to
// optimize retrieval.
if (!this._scripts[aScript.url]) {
@ -1174,26 +1244,6 @@ PauseScopedActor.prototype = {
};
/**
* Utility function for updating an object with the properties of another
* object.
*
* @param aTarget Object
* The object being updated.
* @param aNewAttrs Object
* The new attributes being set on the target.
*/
function update(aTarget, aNewAttrs) {
for (let key in aNewAttrs) {
let desc = Object.getOwnPropertyDescriptor(aNewAttrs, key);
if (desc) {
Object.defineProperty(aTarget, key, desc);
}
}
}
/**
* A SourceActor provides information about the source of a script.
*
@ -1815,7 +1865,7 @@ BreakpointActor.prototype = {
let scriptBreakpoints = this.threadActor._breakpointStore[this.location.url];
delete scriptBreakpoints[this.location.line];
// Remove the actual breakpoint.
this.threadActor._hooks.removeFromBreakpointPool(this);
this.threadActor._hooks.removeFromParentPool(this);
for (let script of this.scripts) {
script.clearBreakpoint(this);
}
@ -2067,3 +2117,89 @@ function getFunctionName(aFunction) {
}
return name;
}
/**
* Creates an actor for handling chrome debugging. ChromeDebuggerActor is a
* thin wrapper over ThreadActor, slightly changing some of its behavior.
*
* @param aHooks object
* An object with preNest and postNest methods for calling when entering
* and exiting a nested event loop and also addToParentPool and
* removeFromParentPool methods for handling the lifetime of actors that
* will outlive the thread, like breakpoints.
*/
function ChromeDebuggerActor(aHooks)
{
ThreadActor.call(this, aHooks);
}
ChromeDebuggerActor.prototype = Object.create(ThreadActor.prototype);
update(ChromeDebuggerActor.prototype, {
constructor: ChromeDebuggerActor,
// A constant prefix that will be used to form the actor ID by the server.
actorPrefix: "chromeDebugger",
/**
* Override the eligibility check for scripts to make sure every script with a
* URL is stored when debugging chrome.
*/
_allowScript: function(aScript) !!aScript.url,
/**
* An object that will be used by ThreadActors to tailor their behavior
* depending on the debugging context being required (chrome or content).
* The methods that this object provides must be bound to the ThreadActor
* before use.
*/
globalManager: {
findGlobals: function CDA_findGlobals() {
// Fetch the list of globals from the debugger.
for (let g of this.dbg.findAllGlobals()) {
this.addDebuggee(g);
}
},
/**
* A function that the engine calls when a new global object has been
* created.
*
* @param aGlobal Debugger.Object
* The new global object that was created.
*/
onNewGlobal: function CDA_onNewGlobal(aGlobal) {
this.addDebuggee(aGlobal);
// Notify the client.
this.conn.send({
from: this.actorID,
type: "newGlobal",
// TODO: after bug 801084 lands see if we need to JSONify this.
hostAnnotations: aGlobal.hostAnnotations
});
}
}
});
// Utility functions.
/**
* Utility function for updating an object with the properties of another
* object.
*
* @param aTarget Object
* The object being updated.
* @param aNewAttrs Object
* The new attributes being set on the target.
*/
function update(aTarget, aNewAttrs) {
for (let key in aNewAttrs) {
let desc = Object.getOwnPropertyDescriptor(aNewAttrs, key);
if (desc) {
Object.defineProperty(aTarget, key, desc);
}
}
}

View File

@ -86,7 +86,7 @@ var DebuggerServer = {
*
* @return true if the connection should be permitted, false otherwise
*/
_defaultAllowConnection: function DH__defaultAllowConnection() {
_defaultAllowConnection: function DS__defaultAllowConnection() {
let title = L10N.getStr("remoteIncomingPromptTitle");
let msg = L10N.getStr("remoteIncomingPromptMessage");
let disableButton = L10N.getStr("remoteIncomingPromptDisable");
@ -114,7 +114,7 @@ var DebuggerServer = {
* The embedder-provider callback, that decides whether an incoming
* remote protocol conection should be allowed or refused.
*/
init: function DH_init(aAllowConnectionCallback) {
init: function DS_init(aAllowConnectionCallback) {
if (this.initialized) {
return;
}
@ -135,7 +135,7 @@ var DebuggerServer = {
* The embedder-provider callback, that decides whether an incoming
* remote protocol conection should be allowed or refused.
*/
initTransport: function DH_initTransport(aAllowConnectionCallback) {
initTransport: function DS_initTransport(aAllowConnectionCallback) {
if (this._transportInitialized) {
return;
}
@ -157,7 +157,7 @@ var DebuggerServer = {
* debugger server is no longer useful, to avoid memory leaks. After this
* method returns, the debugger server must be initialized again before use.
*/
destroy: function DH_destroy() {
destroy: function DS_destroy() {
if (Object.keys(this._connections).length == 0) {
this.closeListener();
delete this.globalActorFactories;
@ -176,14 +176,14 @@ var DebuggerServer = {
* that implements a createRootActor() function to create the
* server's root actor.
*/
addActors: function DH_addActors(aURL) {
addActors: function DS_addActors(aURL) {
loadSubScript.call(this, aURL);
},
/**
* Install Firefox-specific actors.
*/
addBrowserActors: function DH_addBrowserActors() {
addBrowserActors: function DS_addBrowserActors() {
this.addActors("chrome://global/content/devtools/dbg-browser-actors.js");
this.addActors("chrome://global/content/devtools/dbg-webconsole-actors.js");
this.addTabActor(this.WebConsoleActor, "consoleActor");
@ -198,7 +198,7 @@ var DebuggerServer = {
* @param aPort int
* The port to listen on.
*/
openListener: function DH_openListener(aPort) {
openListener: function DS_openListener(aPort) {
if (!Services.prefs.getBoolPref("devtools.debugger.remote-enabled")) {
return false;
}
@ -235,7 +235,7 @@ var DebuggerServer = {
* If set to true, then the socket will be closed, regardless of the
* number of open connections.
*/
closeListener: function DH_closeListener(aForce) {
closeListener: function DS_closeListener(aForce) {
if (!this._listener || this._socketConnections == 0) {
return false;
}
@ -259,7 +259,7 @@ var DebuggerServer = {
* @returns a client-side DebuggerTransport for communicating with
* the newly-created connection.
*/
connectPipe: function DH_connectPipe() {
connectPipe: function DS_connectPipe() {
this._checkInit();
let serverTransport = new LocalDebuggerTransport;
@ -273,7 +273,7 @@ var DebuggerServer = {
// nsIServerSocketListener implementation
onSocketAccepted: function DH_onSocketAccepted(aSocket, aTransport) {
onSocketAccepted: function DS_onSocketAccepted(aSocket, aTransport) {
if (!this._allowConnection()) {
return;
}
@ -289,12 +289,12 @@ var DebuggerServer = {
}
},
onStopListening: function DH_onStopListening() { },
onStopListening: function DS_onStopListening() { },
/**
* Raises an exception if the server has not been properly initialized.
*/
_checkInit: function DH_checkInit() {
_checkInit: function DS_checkInit() {
if (!this._transportInitialized) {
throw "DebuggerServer has not been initialized.";
}
@ -308,7 +308,7 @@ var DebuggerServer = {
* Create a new debugger connection for the given transport. Called
* after connectPipe() or after an incoming socket connection.
*/
_onConnection: function DH_onConnection(aTransport) {
_onConnection: function DS_onConnection(aTransport) {
let connID = "conn" + this._nextConnID++ + '.';
let conn = new DebuggerServerConnection(connID, aTransport);
this._connections[connID] = conn;
@ -323,11 +323,95 @@ var DebuggerServer = {
/**
* Remove the connection from the debugging server.
*/
_connectionClosed: function DH_connectionClosed(aConnection) {
_connectionClosed: function DS_connectionClosed(aConnection) {
delete this._connections[aConnection.prefix];
},
// DebuggerServer extension API.
/**
* Registers handlers for new tab-scoped request types defined dynamically.
* This is used for example by add-ons to augment the functionality of the tab
* actor. Note that the name or actorPrefix of the request type is not allowed
* to clash with existing protocol packet properties, like 'title', 'url' or
* 'actor', since that would break the protocol.
*
* @param aFunction function
* The constructor function for this request type.
* @param aName string [optional]
* The name of the new request type. If this is not present, the
* actorPrefix property of the constructor prototype is used.
*/
addTabActor: function DS_addTabActor(aFunction, aName) {
let name = aName ? aName : aFunction.prototype.actorPrefix;
if (["title", "url", "actor"].indexOf(name) != -1) {
throw Error(name + " is not allowed");
}
if (DebuggerServer.tabActorFactories.hasOwnProperty(name)) {
throw Error(name + " already exists");
}
DebuggerServer.tabActorFactories[name] = aFunction;
},
/**
* Unregisters the handler for the specified tab-scoped request type.
* This may be used for example by add-ons when shutting down or upgrading.
*
* @param aFunction function
* The constructor function for this request type.
*/
removeTabActor: function DS_removeTabActor(aFunction) {
for (let name in DebuggerServer.tabActorFactories) {
let handler = DebuggerServer.tabActorFactories[name];
if (handler.name == aFunction.name) {
delete DebuggerServer.tabActorFactories[name];
}
}
},
/**
* Registers handlers for new browser-scoped request types defined
* dynamically. This is used for example by add-ons to augment the
* functionality of the root actor. Note that the name or actorPrefix of the
* request type is not allowed to clash with existing protocol packet
* properties, like 'from', 'tabs' or 'selected', since that would break the
* protocol.
*
* @param aFunction function
* The constructor function for this request type.
* @param aName string [optional]
* The name of the new request type. If this is not present, the
* actorPrefix property of the constructor prototype is used.
*/
addGlobalActor: function DS_addGlobalActor(aFunction, aName) {
let name = aName ? aName : aFunction.prototype.actorPrefix;
if (["from", "tabs", "selected"].indexOf(name) != -1) {
throw Error(name + " is not allowed");
}
if (DebuggerServer.globalActorFactories.hasOwnProperty(name)) {
throw Error(name + " already exists");
}
DebuggerServer.globalActorFactories[name] = aFunction;
},
/**
* Unregisters the handler for the specified browser-scoped request type.
* This may be used for example by add-ons when shutting down or upgrading.
*
* @param aFunction function
* The constructor function for this request type.
*/
removeGlobalActor: function DS_removeGlobalActor(aFunction) {
for (let name in DebuggerServer.globalActorFactories) {
let handler = DebuggerServer.globalActorFactories[name];
if (handler.name == aFunction.name) {
delete DebuggerServer.globalActorFactories[name];
}
}
}
};
/**
* Construct an ActorPool.
*

View File

@ -16,16 +16,16 @@ function createRootActor()
this.conn.removeActor(aActor);
}.bind(this);
let hooks = {
addToBreakpointPool: addBreakpoint,
removeFromBreakpointPool: removeBreakpoint
addToParentPool: addBreakpoint,
removeFromParentPool: removeBreakpoint
};
let actor = new ThreadActor(hooks);
actor.addDebuggee(g);
actor._global = g;
actor.global = g;
actor.json = function() {
return { actor: actor.actorID,
threadActor: actor.actorID,
global: actor._global.__name };
global: actor.global.__name };
};
this.conn.addActor(actor);
this._globalActors.push(actor);