diff --git a/accessible/tests/mochitest/browser.js b/accessible/tests/mochitest/browser.js
index 09bef2b845b1..f84f545d8c23 100644
--- a/accessible/tests/mochitest/browser.js
+++ b/accessible/tests/mochitest/browser.js
@@ -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);
+}
diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
index 0ca16f3b7bdc..5f48b21cc04a 100644
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -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);
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index 5075e6719dfe..adbd51f2bf57 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -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);
diff --git a/browser/devtools/commandline/gcli.jsm b/browser/devtools/commandline/gcli.jsm
index b3bde36bdb6f..f23b374157c0 100644
--- a/browser/devtools/commandline/gcli.jsm
+++ b/browser/devtools/commandline/gcli.jsm
@@ -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.
*
* The returned object has the following members:
- *
char: The character to which this arg trace refers.
+ *
character: The character to which this arg trace refers.
*
arg: The Argument to which this character is assigned.
*
part: One of ['prefix'|'text'|suffix'] - how was this char understood
*
@@ -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);
//
\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" +
- "\n" +
+ "\n" +
" \n" +
- " \n" +
+ " \n" +
" \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
+ " \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
});
diff --git a/browser/devtools/commandline/test/browser_gcli_completion.js b/browser/devtools/commandline/test/browser_gcli_completion.js
index ba6389ea1ad1..65347e16acc4 100644
--- a/browser/devtools/commandline/test/browser_gcli_completion.js
+++ b/browser/devtools/commandline/test/browser_gcli_completion.js
@@ -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 [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: ' [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'
+ }
+ }
+ });
+};
+
// });
diff --git a/browser/devtools/commandline/test/mockCommands.js b/browser/devtools/commandline/test/mockCommands.js
index eb2c88b84d28..d22173d73479 100644
--- a/browser/devtools/commandline/test/mockCommands.js
+++ b/browser/devtools/commandline/test/mockCommands.js
@@ -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"
diff --git a/browser/devtools/debugger/DebuggerUI.jsm b/browser/devtools/debugger/DebuggerUI.jsm
index 22d4cb615958..c589392474ff 100644
--- a/browser/devtools/debugger/DebuggerUI.jsm
+++ b/browser/devtools/debugger/DebuggerUI.jsm
@@ -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();
}
},
diff --git a/browser/devtools/debugger/debugger-controller.js b/browser/devtools/debugger/debugger-controller.js
index 251a38203057..7080aff3dfd0 100644
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -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.
*/
diff --git a/browser/devtools/debugger/debugger-toolbar.js b/browser/devtools/debugger/debugger-toolbar.js
index 4ffd62db35f5..745d33802321 100644
--- a/browser/devtools/debugger/debugger-toolbar.js
+++ b/browser/devtools/debugger/debugger-toolbar.js
@@ -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;
+ // }
},
/**
diff --git a/browser/devtools/debugger/debugger-view.js b/browser/devtools/debugger/debugger-view.js
index b77ecf54e2ac..f0ec5b1ebdba 100644
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -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
+ // <, 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;
}
diff --git a/browser/devtools/debugger/test/Makefile.in b/browser/devtools/debugger/test/Makefile.in
index e166872588c2..ae887073e16c 100644
--- a/browser/devtools/debugger/test/Makefile.in
+++ b/browser/devtools/debugger/test/Makefile.in
@@ -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)
diff --git a/browser/devtools/debugger/test/browser_dbg_chrome-debugging.js b/browser/devtools/debugger/test/browser_dbg_chrome-debugging.js
new file mode 100644
index 000000000000..1342791aed43
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_chrome-debugging.js
@@ -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();
+ });
+ });
+}
diff --git a/browser/devtools/debugger/test/browser_dbg_createRemote.js b/browser/devtools/debugger/test/browser_dbg_createRemote.js
index ca5ed4fcd992..6f3de1ee8ee0 100644
--- a/browser/devtools/debugger/test/browser_dbg_createRemote.js
+++ b/browser/devtools/debugger/test/browser_dbg_createRemote.js
@@ -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) {
diff --git a/browser/devtools/debugger/test/browser_dbg_iframes.js b/browser/devtools/debugger/test/browser_dbg_iframes.js
index c08b1415bc13..3411df6adb0a 100644
--- a/browser/devtools/debugger/test/browser_dbg_iframes.js
+++ b/browser/devtools/debugger/test/browser_dbg_iframes.js
@@ -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();
});
}
diff --git a/browser/devtools/debugger/test/browser_dbg_location-changes-blank.js b/browser/devtools/debugger/test/browser_dbg_location-changes-blank.js
index 000e47c902ed..b631cac16398 100644
--- a/browser/devtools/debugger/test/browser_dbg_location-changes-blank.js
+++ b/browser/devtools/debugger/test/browser_dbg_location-changes-blank.js
@@ -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.");
-
- testLocationChange();
- }
- }, 0);
- });
+ 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...'");
- gDebuggee.simpleCall();
+ testLocationChange();
}
function testLocationChange()
diff --git a/browser/devtools/debugger/test/browser_dbg_location-changes-new.js b/browser/devtools/debugger/test/browser_dbg_location-changes-new.js
index 9609a90330a8..cfad4119c06b 100644
--- a/browser/devtools/debugger/test/browser_dbg_location-changes-new.js
+++ b/browser/devtools/debugger/test/browser_dbg_location-changes-new.js
@@ -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.");
-
- testLocationChange();
- }
- }, 0);
- });
+ 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...'");
- gDebuggee.simpleCall();
+ testLocationChange();
}
function testLocationChange()
diff --git a/browser/devtools/debugger/test/browser_dbg_progress-listener-bug.js b/browser/devtools/debugger/test/browser_dbg_progress-listener-bug.js
new file mode 100644
index 000000000000..73e1ed50fac6
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_progress-listener-bug.js
@@ -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;
+});
diff --git a/browser/devtools/debugger/test/browser_dbg_update-editor-mode.html b/browser/devtools/debugger/test/browser_dbg_update-editor-mode.html
index 9f0007a1e13a..2cb5d3f50f41 100644
--- a/browser/devtools/debugger/test/browser_dbg_update-editor-mode.html
+++ b/browser/devtools/debugger/test/browser_dbg_update-editor-mode.html
@@ -7,6 +7,9 @@
http://creativecommons.org/publicdomain/zero/1.0/ -->
+
diff --git a/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js b/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js
index 2b5cd2c74d70..acb4100b4038 100644
--- a/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js
+++ b/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js
@@ -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();
});
diff --git a/browser/devtools/shared/DeveloperToolbar.jsm b/browser/devtools/shared/DeveloperToolbar.jsm
index e98e158cf26a..8a921e65c85d 100644
--- a/browser/devtools/shared/DeveloperToolbar.jsm
+++ b/browser/devtools/shared/DeveloperToolbar.jsm
@@ -552,7 +552,7 @@ function OutputPanel(aChromeDoc, aInput, aLoadCallback)
+ sandbox="allow-same-origin"/>
*/
@@ -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)
+ flex="1"
+ sandbox="allow-same-origin"/>
*/
@@ -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);
diff --git a/browser/installer/windows/nsis/stub.nsi b/browser/installer/windows/nsis/stub.nsi
index ef083f29b15d..c13c559c7531 100644
--- a/browser/installer/windows/nsis/stub.nsi
+++ b/browser/installer/windows/nsis/stub.nsi
@@ -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}
diff --git a/browser/locales/en-US/chrome/browser/devtools/gcli.properties b/browser/locales/en-US/chrome/browser/devtools/gcli.properties
index ff21415b1b18..c6fd0da612fc 100644
--- a/browser/locales/en-US/chrome/browser/devtools/gcli.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/gcli.properties
@@ -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. ) can be used to emphasize the core
-# concept.
-helpSearchManual2=search string 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
diff --git a/extensions/gnomevfs/nsGnomeVFSProtocolHandler.cpp b/extensions/gnomevfs/nsGnomeVFSProtocolHandler.cpp
index 0f2fe9d75437..27fc3b86df22 100644
--- a/extensions/gnomevfs/nsGnomeVFSProtocolHandler.cpp
+++ b/extensions/gnomevfs/nsGnomeVFSProtocolHandler.cpp
@@ -10,6 +10,7 @@ extern "C" {
#include
}
+#include "NSPRFormatTime.h" // must be before anything that includes prtime.h
#include "nsServiceManagerUtils.h"
#include "nsComponentManagerUtils.h"
#include "mozilla/ModuleUtils.h"
diff --git a/js/src/jit-test/tests/debug/Debugger-debuggees-19.js b/js/src/jit-test/tests/debug/Debugger-debuggees-19.js
new file mode 100644
index 000000000000..b32920a7c18d
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-debuggees-19.js
@@ -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, '');
diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp
index 0314386dcef5..518391b04f91 100644
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -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),
diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h
index b592eee6429e..5f80cfe0bc59 100644
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -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);
diff --git a/mobile/android/base/background/announcements/AnnouncementsFetchResourceDelegate.java b/mobile/android/base/background/announcements/AnnouncementsFetchResourceDelegate.java
index 32607adca143..41c4967ea926 100644
--- a/mobile/android/base/background/announcements/AnnouncementsFetchResourceDelegate.java
+++ b/mobile/android/base/background/announcements/AnnouncementsFetchResourceDelegate.java
@@ -52,6 +52,9 @@ public void addHeaders(HttpRequestBase request, DefaultHttpClient client) {
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) {
diff --git a/python/mozboot/bin/bootstrap.py b/python/mozboot/bin/bootstrap.py
index 0e2c3e1c286e..32690be138eb 100755
--- a/python/mozboot/bin/bootstrap.py
+++ b/python/mozboot/bin/bootstrap.py
@@ -33,6 +33,7 @@
'mozboot/bootstrap.py',
'mozboot/centos.py',
'mozboot/fedora.py',
+ 'mozboot/gentoo.py',
'mozboot/mint.py',
'mozboot/openbsd.py',
'mozboot/osx.py',
diff --git a/python/mozboot/mozboot/bootstrap.py b/python/mozboot/mozboot/bootstrap.py
index 9b4b9567d068..5fc6f523bcb1 100644
--- a/python/mozboot/mozboot/bootstrap.py
+++ b/python/mozboot/mozboot/bootstrap.py
@@ -10,6 +10,7 @@
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 @@ def bootstrap(self):
cls = CentOSBootstrapper
elif distro == 'Fedora':
cls = FedoraBootstrapper
+ elif distro == 'Gentoo Base System':
+ cls = GentooBootstrapper
elif distro == 'Mint':
cls = MintBootstrapper
elif distro == 'Ubuntu':
diff --git a/python/mozboot/mozboot/gentoo.py b/python/mozboot/mozboot/gentoo.py
new file mode 100644
index 000000000000..f1d972f1b4b5
--- /dev/null
+++ b/python/mozboot/mozboot/gentoo.py
@@ -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'])
diff --git a/services/sync/tests/unit/test_addons_store.js b/services/sync/tests/unit/test_addons_store.js
index 5c77030d7291..86198c8d39b8 100644
--- a/services/sync/tests/unit/test_addons_store.js
+++ b/services/sync/tests/unit/test_addons_store.js
@@ -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.");
diff --git a/services/sync/tests/unit/test_errorhandler_filelog.js b/services/sync/tests/unit/test_errorhandler_filelog.js
index 4b5482e6153e..4fadd83f8ef7 100644
--- a/services/sync/tests/unit/test_errorhandler_filelog.js
+++ b/services/sync/tests/unit/test_errorhandler_filelog.js
@@ -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");
});
diff --git a/testing/mochitest/browser-test.js b/testing/mochitest/browser-test.js
index 69e4c55e4df7..2d8cf22580df 100644
--- a/testing/mochitest/browser-test.js
+++ b/testing/mochitest/browser-test.js
@@ -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)
diff --git a/toolkit/content/aboutSupport.js b/toolkit/content/aboutSupport.js
index 7d6022230f78..d61985a5aa95 100644
--- a/toolkit/content/aboutSupport.js
+++ b/toolkit/content/aboutSupport.js
@@ -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 = "";
},
};
diff --git a/toolkit/content/aboutSupport.xhtml b/toolkit/content/aboutSupport.xhtml
index 397aa06cc035..87bb8471aadc 100644
--- a/toolkit/content/aboutSupport.xhtml
+++ b/toolkit/content/aboutSupport.xhtml
@@ -209,7 +209,7 @@
-
+
&aboutSupport.userJSTitle;
&aboutSupport.userJSDescription;
diff --git a/toolkit/devtools/debugger/dbg-client.jsm b/toolkit/devtools/debugger/dbg-client.jsm
index e7ea804e2c9a..a5f6d3033fc0 100644
--- a/toolkit/devtools/debugger/dbg-client.jsm
+++ b/toolkit/devtools/debugger/dbg-client.jsm
@@ -173,6 +173,7 @@ const UnsolicitedNotifications = {
"locationChange": "locationChange",
"networkEvent": "networkEvent",
"networkEventUpdate": "networkEventUpdate",
+ "newGlobal": "newGlobal",
"newScript": "newScript",
"tabDetached": "tabDetached",
"tabNavigated": "tabNavigated",
diff --git a/toolkit/devtools/debugger/server/dbg-browser-actors.js b/toolkit/devtools/debugger/server/dbg-browser-actors.js
index b50cf045807e..f51371b48911 100644
--- a/toolkit/devtools/debugger/server/dbg-browser-actors.js
+++ b/toolkit/devtools/debugger/server/dbg-browser-actors.js
@@ -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];
- }
- }
-};
diff --git a/toolkit/devtools/debugger/server/dbg-script-actors.js b/toolkit/devtools/debugger/server/dbg-script-actors.js
index 072b45d4da31..76de83e386c1 100644
--- a/toolkit/devtools/debugger/server/dbg-script-actors.js
+++ b/toolkit/devtools/debugger/server/dbg-script-actors.js
@@ -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);
+ }
+ }
+}
diff --git a/toolkit/devtools/debugger/server/dbg-server.js b/toolkit/devtools/debugger/server/dbg-server.js
index c5a5d2e27087..ce6b13aab817 100644
--- a/toolkit/devtools/debugger/server/dbg-server.js
+++ b/toolkit/devtools/debugger/server/dbg-server.js
@@ -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.
*
diff --git a/toolkit/devtools/debugger/tests/unit/testactors.js b/toolkit/devtools/debugger/tests/unit/testactors.js
index 39dd443a79f3..64748cac7e7c 100644
--- a/toolkit/devtools/debugger/tests/unit/testactors.js
+++ b/toolkit/devtools/debugger/tests/unit/testactors.js
@@ -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);