mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-03 12:35:58 +00:00
Merge m-c to inbound.
This commit is contained in:
commit
f0eeb8a037
@ -10,6 +10,7 @@ const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
const DBG_STRINGS_URI = "chrome://browser/locale/devtools/debugger.properties";
|
||||
const NEW_SCRIPT_IGNORED_URLS = ["debugger eval code", "self-hosted"];
|
||||
const NEW_SCRIPT_DISPLAY_DELAY = 200; // ms
|
||||
const FETCH_SOURCE_RESPONSE_DELAY = 50; // ms
|
||||
const FRAME_STEP_CLEAR_DELAY = 100; // ms
|
||||
@ -1073,8 +1074,8 @@ SourceScripts.prototype = {
|
||||
* Handler for the debugger client's unsolicited newScript notification.
|
||||
*/
|
||||
_onNewScript: function SS__onNewScript(aNotification, aPacket) {
|
||||
// Ignore scripts generated from 'clientEvaluate' packets.
|
||||
if (aPacket.url == "debugger eval code") {
|
||||
// Ignore bogus scripts, e.g. generated from 'clientEvaluate' packets.
|
||||
if (NEW_SCRIPT_IGNORED_URLS.indexOf(aPacket.url) != -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1129,8 +1130,8 @@ SourceScripts.prototype = {
|
||||
_onScriptsAdded: function SS__onScriptsAdded(aResponse) {
|
||||
// Add all the sources in the debugger view sources container.
|
||||
for (let script of aResponse.scripts) {
|
||||
// Ignore scripts generated from 'clientEvaluate' packets.
|
||||
if (script.url == "debugger eval code") {
|
||||
// Ignore bogus scripts, e.g. generated from 'clientEvaluate' packets.
|
||||
if (NEW_SCRIPT_IGNORED_URLS.indexOf(script.url) != -1) {
|
||||
continue;
|
||||
}
|
||||
this._addSource(script);
|
||||
|
@ -56,6 +56,7 @@ ToolSidebar.prototype = {
|
||||
iframe.setAttribute("src", url);
|
||||
|
||||
let tab = this._tabbox.tabs.appendItem();
|
||||
tab.setAttribute("label", ""); // Avoid showing "undefined" while the tab is loading
|
||||
|
||||
let onIFrameLoaded = function() {
|
||||
tab.setAttribute("label", iframe.contentDocument.title);
|
||||
|
@ -130,7 +130,7 @@ this.Toolbox = function Toolbox(target, selectedTool, hostType) {
|
||||
if (!selectedTool) {
|
||||
selectedTool = Services.prefs.getCharPref(this._prefs.LAST_TOOL);
|
||||
}
|
||||
let definitions = gDevTools.getToolDefinitions();
|
||||
let definitions = gDevTools.getToolDefinitionMap();
|
||||
if (!definitions.get(selectedTool)) {
|
||||
selectedTool = "webconsole";
|
||||
}
|
||||
@ -248,11 +248,6 @@ Toolbox.prototype = {
|
||||
let domReady = function() {
|
||||
iframe.removeEventListener("DOMContentLoaded", domReady, true);
|
||||
|
||||
let vbox = this.doc.getElementById("toolbox-panel-" + this._currentToolId);
|
||||
if (vbox) {
|
||||
this.doc.commandDispatcher.advanceFocusIntoSubtree(vbox);
|
||||
}
|
||||
|
||||
this.isReady = true;
|
||||
|
||||
let closeButton = this.doc.getElementById("toolbox-close");
|
||||
@ -315,7 +310,7 @@ Toolbox.prototype = {
|
||||
* Add tabs to the toolbox UI for registered tools
|
||||
*/
|
||||
_buildTabs: function TBOX_buildTabs() {
|
||||
for (let [id, definition] of gDevTools.getToolDefinitions()) {
|
||||
for (let definition of gDevTools.getToolDefinitionArray()) {
|
||||
this._buildTabForTool(definition);
|
||||
}
|
||||
},
|
||||
@ -347,7 +342,6 @@ Toolbox.prototype = {
|
||||
* Tool definition of the tool to build a tab for.
|
||||
*/
|
||||
_buildTabForTool: function TBOX_buildTabForTool(toolDefinition) {
|
||||
const MAX_ORDINAL = 99;
|
||||
if (!toolDefinition.isTargetSupported(this._target)) {
|
||||
return;
|
||||
}
|
||||
@ -367,10 +361,6 @@ Toolbox.prototype = {
|
||||
radio.setAttribute("src", toolDefinition.icon);
|
||||
}
|
||||
|
||||
let ordinal = (typeof toolDefinition.ordinal == "number") ?
|
||||
toolDefinition.ordinal : MAX_ORDINAL;
|
||||
radio.setAttribute("ordinal", ordinal);
|
||||
|
||||
radio.addEventListener("command", function(id) {
|
||||
this.selectTool(id);
|
||||
}.bind(this, id));
|
||||
@ -422,7 +412,7 @@ Toolbox.prototype = {
|
||||
let deck = this.doc.getElementById("toolbox-deck");
|
||||
deck.selectedIndex = index;
|
||||
|
||||
let definition = gDevTools.getToolDefinitions().get(id);
|
||||
let definition = gDevTools.getToolDefinitionMap().get(id);
|
||||
|
||||
this._currentToolId = id;
|
||||
|
||||
@ -478,6 +468,10 @@ Toolbox.prototype = {
|
||||
/**
|
||||
* Create a host object based on the given host type.
|
||||
*
|
||||
* Warning: some hosts require that the toolbox target provides a reference to
|
||||
* the attached tab. Not all Targets have a tab property - make sure you correctly
|
||||
* mix and match hosts and targets.
|
||||
*
|
||||
* @param {string} hostType
|
||||
* The host type of the new host object
|
||||
*
|
||||
@ -539,7 +533,7 @@ Toolbox.prototype = {
|
||||
* Id of the tool that was registered
|
||||
*/
|
||||
_toolRegistered: function TBOX_toolRegistered(event, toolId) {
|
||||
let defs = gDevTools.getToolDefinitions();
|
||||
let defs = gDevTools.getToolDefinitionMap();
|
||||
let tool = defs.get(toolId);
|
||||
|
||||
this._buildTabForTool(tool);
|
||||
|
@ -25,8 +25,8 @@ const FORBIDDEN_IDS = new Set("toolbox", "");
|
||||
* set of tools and keeps track of open toolboxes in the browser.
|
||||
*/
|
||||
this.DevTools = function DevTools() {
|
||||
this._tools = new Map();
|
||||
this._toolboxes = new Map();
|
||||
this._tools = new Map(); // Map<toolId, tool>
|
||||
this._toolboxes = new Map(); // Map<target, toolbox>
|
||||
|
||||
// destroy() is an observer's handler so we need to preserve context.
|
||||
this.destroy = this.destroy.bind(this);
|
||||
@ -85,11 +85,16 @@ DevTools.prototype = {
|
||||
*
|
||||
* @param {string} toolId
|
||||
* id of the tool to unregister
|
||||
* @param {boolean} isQuitApplication
|
||||
* true to indicate that the call is due to app quit, so we should not
|
||||
* cause a cascade of costly events
|
||||
*/
|
||||
unregisterTool: function DT_unregisterTool(toolId) {
|
||||
unregisterTool: function DT_unregisterTool(toolId, isQuitApplication) {
|
||||
this._tools.delete(toolId);
|
||||
|
||||
this.emit("tool-unregistered", toolId);
|
||||
if (!isQuitApplication) {
|
||||
this.emit("tool-unregistered", toolId);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -99,7 +104,7 @@ DevTools.prototype = {
|
||||
* @return {Map} tools
|
||||
* A map of the the tool definitions registered in this instance
|
||||
*/
|
||||
getToolDefinitions: function DT_getToolDefinitions() {
|
||||
getToolDefinitionMap: function DT_getToolDefinitionMap() {
|
||||
let tools = new Map();
|
||||
|
||||
for (let [key, value] of this._tools) {
|
||||
@ -118,6 +123,31 @@ DevTools.prototype = {
|
||||
return tools;
|
||||
},
|
||||
|
||||
/**
|
||||
* Tools have an inherent ordering that can't be represented in a Map so
|
||||
* getToolDefinitionArray provides an alternative representation of the
|
||||
* definitions sorted by ordinal value.
|
||||
*
|
||||
* @return {Array} tools
|
||||
* A sorted array of the tool definitions registered in this instance
|
||||
*/
|
||||
getToolDefinitionArray: function DT_getToolDefinitionArray() {
|
||||
const MAX_ORDINAL = 99;
|
||||
|
||||
let definitions = [];
|
||||
for (let [id, definition] of this.getToolDefinitionMap()) {
|
||||
definitions.push(definition);
|
||||
}
|
||||
|
||||
definitions.sort(function(d1, d2) {
|
||||
let o1 = (typeof d1.ordinal == "number") ? d1.ordinal : MAX_ORDINAL;
|
||||
let o2 = (typeof d2.ordinal == "number") ? d2.ordinal : MAX_ORDINAL;
|
||||
return o1 - o2;
|
||||
});
|
||||
|
||||
return definitions;
|
||||
},
|
||||
|
||||
/**
|
||||
* Show a Toolbox for a target (either by creating a new one, or if a toolbox
|
||||
* already exists for the target, by bring to the front the existing one)
|
||||
@ -218,8 +248,13 @@ DevTools.prototype = {
|
||||
destroy: function() {
|
||||
Services.obs.removeObserver(this.destroy, "quit-application");
|
||||
|
||||
delete this._trackedBrowserWindows;
|
||||
delete this._toolboxes;
|
||||
for (let [key, tool] of this._tools) {
|
||||
this.unregisterTool(key, true);
|
||||
}
|
||||
|
||||
// Cleaning down the toolboxes: i.e.
|
||||
// for (let [target, toolbox] of this._toolboxes) toolbox.destroy();
|
||||
// Is taken care of by the gDevToolsBrowser.forgetBrowserWindow
|
||||
},
|
||||
};
|
||||
|
||||
@ -313,8 +348,43 @@ let gDevToolsBrowser = {
|
||||
* properties of the tool to add
|
||||
*/
|
||||
_addToolToWindows: function DT_addToolToWindows(toolDefinition) {
|
||||
// We need to insert the new tool in the right place, which means knowing
|
||||
// the tool that comes before the tool that we're trying to add
|
||||
let allDefs = gDevTools.getToolDefinitionArray();
|
||||
let prevDef;
|
||||
for (let def of allDefs) {
|
||||
if (def === toolDefinition) {
|
||||
break;
|
||||
}
|
||||
prevDef = def;
|
||||
}
|
||||
|
||||
for (let win of gDevToolsBrowser._trackedBrowserWindows) {
|
||||
gDevToolsBrowser._addToolToMenu(toolDefinition, win.document);
|
||||
let doc = win.document;
|
||||
let elements = gDevToolsBrowser._createToolMenuElements(toolDefinition, doc);
|
||||
|
||||
doc.getElementById("mainCommandSet").appendChild(elements.cmd);
|
||||
|
||||
if (elements.key) {
|
||||
doc.getElementById("mainKeyset").appendChild(elements.key);
|
||||
}
|
||||
|
||||
doc.getElementById("mainBroadcasterSet").appendChild(elements.bc);
|
||||
|
||||
let amp = doc.getElementById("appmenu_webDeveloper_popup");
|
||||
if (amp) {
|
||||
let ref = (prevDef != null) ?
|
||||
doc.getElementById("appmenuitem_" + prevDef.id).nextSibling :
|
||||
doc.getElementById("appmenu_devtools_separator");
|
||||
|
||||
amp.insertBefore(elements.appmenuitem, ref);
|
||||
}
|
||||
|
||||
let mp = doc.getElementById("menuWebDeveloperPopup");
|
||||
let ref = (prevDef != null) ?
|
||||
doc.getElementById("menuitem_" + prevDef.id).nextSibling :
|
||||
doc.getElementById("menu_devtools_separator");
|
||||
mp.insertBefore(elements.menuitem, ref);
|
||||
}
|
||||
},
|
||||
|
||||
@ -331,22 +401,20 @@ let gDevToolsBrowser = {
|
||||
let fragAppMenuItems = doc.createDocumentFragment();
|
||||
let fragMenuItems = doc.createDocumentFragment();
|
||||
|
||||
for (let [key, toolDefinition] of gDevTools._tools) {
|
||||
let frags = gDevToolsBrowser._addToolToMenu(toolDefinition, doc, true);
|
||||
for (let toolDefinition of gDevTools.getToolDefinitionArray()) {
|
||||
let elements = gDevToolsBrowser._createToolMenuElements(toolDefinition, doc);
|
||||
|
||||
if (!frags) {
|
||||
if (!elements) {
|
||||
return;
|
||||
}
|
||||
|
||||
let [cmd, key, bc, appmenuitem, menuitem] = frags;
|
||||
|
||||
fragCommands.appendChild(cmd);
|
||||
if (key) {
|
||||
fragKeys.appendChild(key);
|
||||
fragCommands.appendChild(elements.cmd);
|
||||
if (elements.key) {
|
||||
fragKeys.appendChild(elements.key);
|
||||
}
|
||||
fragBroadcasters.appendChild(bc);
|
||||
fragAppMenuItems.appendChild(appmenuitem);
|
||||
fragMenuItems.appendChild(menuitem);
|
||||
fragBroadcasters.appendChild(elements.bc);
|
||||
fragAppMenuItems.appendChild(elements.appmenuitem);
|
||||
fragMenuItems.appendChild(elements.menuitem);
|
||||
}
|
||||
|
||||
let mcs = doc.getElementById("mainCommandSet");
|
||||
@ -376,11 +444,8 @@ let gDevToolsBrowser = {
|
||||
* Tool definition of the tool to add a menu entry.
|
||||
* @param {XULDocument} doc
|
||||
* The document to which the tool menu item is to be added.
|
||||
* @param {Boolean} [noAppend]
|
||||
* Return an array of elements instead of appending them to the
|
||||
* document. Default is false.
|
||||
*/
|
||||
_addToolToMenu: function DT_addToolToMenu(toolDefinition, doc, noAppend) {
|
||||
_createToolMenuElements: function DT_createToolMenuElements(toolDefinition, doc) {
|
||||
let id = toolDefinition.id;
|
||||
|
||||
// Prevent multiple entries for the same tool.
|
||||
@ -429,30 +494,13 @@ let gDevToolsBrowser = {
|
||||
menuitem.setAttribute("accesskey", toolDefinition.accesskey);
|
||||
}
|
||||
|
||||
if (noAppend) {
|
||||
return [cmd, key, bc, appmenuitem, menuitem];
|
||||
} else {
|
||||
let mcs = doc.getElementById("mainCommandSet");
|
||||
mcs.appendChild(cmd);
|
||||
|
||||
if (key) {
|
||||
let mks = doc.getElementById("mainKeyset");
|
||||
mks.appendChild(key);
|
||||
}
|
||||
|
||||
let mbs = doc.getElementById("mainBroadcasterSet");
|
||||
mbs.appendChild(bc);
|
||||
|
||||
let amp = doc.getElementById("appmenu_webDeveloper_popup");
|
||||
if (amp) {
|
||||
let amps = doc.getElementById("appmenu_devtools_separator");
|
||||
amp.insertBefore(appmenuitem, amps);
|
||||
}
|
||||
|
||||
let mp = doc.getElementById("menuWebDeveloperPopup");
|
||||
let mps = doc.getElementById("menu_devtools_separator");
|
||||
mp.insertBefore(menuitem, mps);
|
||||
}
|
||||
return {
|
||||
cmd: cmd,
|
||||
key: key,
|
||||
bc: bc,
|
||||
appmenuitem: appmenuitem,
|
||||
menuitem: menuitem
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
@ -534,10 +582,6 @@ let gDevToolsBrowser = {
|
||||
* The window containing the menu entry
|
||||
*/
|
||||
forgetBrowserWindow: function DT_forgetBrowserWindow(win) {
|
||||
if (!gDevToolsBrowser._trackedBrowserWindows) {
|
||||
return;
|
||||
}
|
||||
|
||||
gDevToolsBrowser._trackedBrowserWindows.delete(win);
|
||||
|
||||
// Destroy toolboxes for closed window
|
||||
@ -557,7 +601,6 @@ let gDevToolsBrowser = {
|
||||
*/
|
||||
destroy: function() {
|
||||
Services.obs.removeObserver(gDevToolsBrowser.destroy, "quit-application");
|
||||
delete gDevToolsBrowser._trackedBrowserWindows;
|
||||
},
|
||||
}
|
||||
this.gDevToolsBrowser = gDevToolsBrowser;
|
||||
|
@ -32,11 +32,11 @@ function runTests(aTab) {
|
||||
};
|
||||
|
||||
ok(gDevTools, "gDevTools exists");
|
||||
is(gDevTools.getToolDefinitions().has(toolId), false,
|
||||
is(gDevTools.getToolDefinitionMap().has(toolId), false,
|
||||
"The tool is not registered");
|
||||
|
||||
gDevTools.registerTool(toolDefinition);
|
||||
is(gDevTools.getToolDefinitions().has(toolId), true,
|
||||
is(gDevTools.getToolDefinitionMap().has(toolId), true,
|
||||
"The tool is registered");
|
||||
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
@ -51,14 +51,14 @@ function continueTests(toolbox, panel) {
|
||||
ok(toolbox.getCurrentPanel(), "panel value is correct");
|
||||
is(toolbox.currentToolId, toolId, "toolbox _currentToolId is correct");
|
||||
|
||||
let toolDefinitions = gDevTools.getToolDefinitions();
|
||||
let toolDefinitions = gDevTools.getToolDefinitionMap();
|
||||
is(toolDefinitions.has(toolId), true, "The tool is in gDevTools");
|
||||
|
||||
let toolDefinition = toolDefinitions.get(toolId);
|
||||
is(toolDefinition.id, toolId, "toolDefinition id is correct");
|
||||
|
||||
gDevTools.unregisterTool(toolId);
|
||||
is(gDevTools.getToolDefinitions().has(toolId), false,
|
||||
is(gDevTools.getToolDefinitionMap().has(toolId), false,
|
||||
"The tool is no longer registered");
|
||||
|
||||
toolbox.destroy().then(function() {
|
||||
|
@ -40,7 +40,7 @@ function toolRegistered(event, toolId)
|
||||
{
|
||||
is(toolId, "test-tool", "tool-registered event handler sent tool id");
|
||||
|
||||
ok(gDevTools.getToolDefinitions().has(toolId), "tool added to map");
|
||||
ok(gDevTools.getToolDefinitionMap().has(toolId), "tool added to map");
|
||||
|
||||
// test that it appeared in the UI
|
||||
let doc = toolbox.frame.contentDocument;
|
||||
@ -81,7 +81,7 @@ function toolUnregistered(event, toolId)
|
||||
{
|
||||
is(toolId, "test-tool", "tool-unregistered event handler sent tool id");
|
||||
|
||||
ok(!gDevTools.getToolDefinitions().has(toolId), "tool removed from map");
|
||||
ok(!gDevTools.getToolDefinitionMap().has(toolId), "tool removed from map");
|
||||
|
||||
// test that it disappeared from the UI
|
||||
let doc = toolbox.frame.contentDocument;
|
||||
|
@ -276,7 +276,12 @@ function test() {
|
||||
is(inspector.highlighter.nodeInfo.classesBox.textContent, "",
|
||||
"No classes in the infobar before edit.");
|
||||
},
|
||||
execute: function() {
|
||||
execute: function(after) {
|
||||
inspector.once("markupmutation", function() {
|
||||
// needed because we need to make sure the infobar is updated
|
||||
// not just the markupview (which happens in this event loop)
|
||||
executeSoon(after);
|
||||
});
|
||||
let editor = markup.getContainer(doc.querySelector("#node18")).editor;
|
||||
let attr = editor.attrs["id"].querySelector(".editable");
|
||||
editField(attr, attr.textContent + ' class="newclass" style="color:green"');
|
||||
@ -310,7 +315,8 @@ function test() {
|
||||
is(doc.querySelector("#retag-me-2").parentNode, node,
|
||||
"retag-me-2 should be a child of the old element.");
|
||||
},
|
||||
execute: function() {
|
||||
execute: function(after) {
|
||||
inspector.once("markupmutation", after);
|
||||
let node = doc.querySelector("#retag-me");
|
||||
let editor = markup.getContainer(node).editor;
|
||||
let field = editor.tag;
|
||||
@ -367,9 +373,10 @@ function test() {
|
||||
|
||||
inspector.selection.once("new-node", function BIMET_testAsyncSetupNewNode() {
|
||||
test.before();
|
||||
test.execute();
|
||||
test.after();
|
||||
undoRedo(test, callback);
|
||||
test.execute(function() {
|
||||
test.after();
|
||||
undoRedo(test, callback);
|
||||
});
|
||||
});
|
||||
executeSoon(function BIMET_setNode2() {
|
||||
test.setup();
|
||||
|
@ -15,7 +15,6 @@ XPCSHELL_TESTS = unit
|
||||
|
||||
MOCHITEST_BROWSER_FILES = \
|
||||
browser_browser_basic.js \
|
||||
browser_promise_basic.js \
|
||||
browser_require_basic.js \
|
||||
browser_templater_basic.js \
|
||||
browser_toolbar_basic.js \
|
||||
|
@ -1,305 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that our Promise implementation works properly
|
||||
|
||||
let tempScope = {};
|
||||
Cu.import("resource://gre/modules/devtools/_Promise.jsm", tempScope);
|
||||
let Promise = tempScope.Promise;
|
||||
|
||||
function test() {
|
||||
addTab("about:blank", function() {
|
||||
info("Starting Promise Tests");
|
||||
testBasic();
|
||||
});
|
||||
}
|
||||
|
||||
var postResolution;
|
||||
|
||||
function testBasic() {
|
||||
postResolution = new Promise();
|
||||
ok(postResolution.isPromise, "We have a promise");
|
||||
ok(!postResolution.isComplete(), "Promise is initially incomplete");
|
||||
ok(!postResolution.isResolved(), "Promise is initially unresolved");
|
||||
ok(!postResolution.isRejected(), "Promise is initially unrejected");
|
||||
|
||||
// Test resolve() *after* then() in the same context
|
||||
var reply = postResolution.then(testPostResolution, fail)
|
||||
.resolve("postResolution");
|
||||
is(reply, postResolution, "return this; working ok");
|
||||
}
|
||||
|
||||
var preResolution;
|
||||
|
||||
function testPostResolution(data) {
|
||||
is(data, "postResolution", "data is postResolution");
|
||||
ok(postResolution.isComplete(), "postResolution Promise is complete");
|
||||
ok(postResolution.isResolved(), "postResolution Promise is resolved");
|
||||
ok(!postResolution.isRejected(), "postResolution Promise is unrejected");
|
||||
|
||||
try {
|
||||
info("Expected double resolve error");
|
||||
postResolution.resolve("double resolve");
|
||||
ok(false, "double resolve");
|
||||
}
|
||||
catch (ex) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
// Test resolve() *before* then() in the same context
|
||||
preResolution = new Promise();
|
||||
var reply = preResolution.resolve("preResolution")
|
||||
.then(testPreResolution, fail);
|
||||
is(reply, preResolution, "return this; working ok");
|
||||
}
|
||||
|
||||
var laterResolution;
|
||||
|
||||
function testPreResolution(data) {
|
||||
is(data, "preResolution", "data is preResolution");
|
||||
ok(preResolution.isComplete(), "preResolution Promise is complete");
|
||||
ok(preResolution.isResolved(), "preResolution Promise is resolved");
|
||||
ok(!preResolution.isRejected(), "preResolution Promise is unrejected");
|
||||
|
||||
// Test resolve() *after* then() in a later context
|
||||
laterResolution = new Promise();
|
||||
laterResolution.then(testLaterResolution, fail);
|
||||
executeSoon(function() {
|
||||
laterResolution.resolve("laterResolution");
|
||||
});
|
||||
}
|
||||
|
||||
var laterRejection;
|
||||
|
||||
function testLaterResolution(data) {
|
||||
is(data, "laterResolution", "data is laterResolution");
|
||||
ok(laterResolution.isComplete(), "laterResolution Promise is complete");
|
||||
ok(laterResolution.isResolved(), "laterResolution Promise is resolved");
|
||||
ok(!laterResolution.isRejected(), "laterResolution Promise is unrejected");
|
||||
|
||||
// Test reject() *after* then() in a later context
|
||||
laterRejection = new Promise().then(fail, testLaterRejection);
|
||||
executeSoon(function() {
|
||||
laterRejection.reject("laterRejection");
|
||||
});
|
||||
}
|
||||
|
||||
function testLaterRejection(data) {
|
||||
is(data, "laterRejection", "data is laterRejection");
|
||||
ok(laterRejection.isComplete(), "laterRejection Promise is complete");
|
||||
ok(!laterRejection.isResolved(), "laterRejection Promise is unresolved");
|
||||
ok(laterRejection.isRejected(), "laterRejection Promise is rejected");
|
||||
|
||||
// Test chaining
|
||||
var orig = new Promise();
|
||||
orig.chainPromise(function(data) {
|
||||
is(data, "origData", "data is origData");
|
||||
return data.replace(/orig/, "new");
|
||||
}).then(function(data) {
|
||||
is(data, "newData", "data is newData");
|
||||
testChain();
|
||||
});
|
||||
orig.resolve("origData");
|
||||
}
|
||||
|
||||
var member1;
|
||||
var member2;
|
||||
var member3;
|
||||
var laterGroup;
|
||||
|
||||
function testChain() {
|
||||
// Test an empty group
|
||||
var empty1 = Promise.group();
|
||||
ok(empty1.isComplete(), "empty1 Promise is complete");
|
||||
ok(empty1.isResolved(), "empty1 Promise is resolved");
|
||||
ok(!empty1.isRejected(), "empty1 Promise is unrejected");
|
||||
|
||||
// Test a group with no members
|
||||
var empty2 = Promise.group([]);
|
||||
ok(empty2.isComplete(), "empty2 Promise is complete");
|
||||
ok(empty2.isResolved(), "empty2 Promise is resolved");
|
||||
ok(!empty2.isRejected(), "empty2 Promise is unrejected");
|
||||
|
||||
// Test grouping using resolve() in a later context
|
||||
member1 = new Promise();
|
||||
member2 = new Promise();
|
||||
member3 = new Promise();
|
||||
laterGroup = Promise.group(member1, member2, member3);
|
||||
laterGroup.then(testLaterGroup, fail);
|
||||
|
||||
member1.then(function(data) {
|
||||
is(data, "member1", "member1 is member1");
|
||||
executeSoon(function() {
|
||||
member2.resolve("member2");
|
||||
});
|
||||
}, fail);
|
||||
member2.then(function(data) {
|
||||
is(data, "member2", "member2 is member2");
|
||||
executeSoon(function() {
|
||||
member3.resolve("member3");
|
||||
});
|
||||
}, fail);
|
||||
member3.then(function(data) {
|
||||
is(data, "member3", "member3 is member3");
|
||||
// The group should now fire
|
||||
}, fail);
|
||||
executeSoon(function() {
|
||||
member1.resolve("member1");
|
||||
});
|
||||
}
|
||||
|
||||
var tidyGroup;
|
||||
|
||||
function testLaterGroup(data) {
|
||||
is(data[0], "member1", "member1 is member1");
|
||||
is(data[1], "member2", "member2 is member2");
|
||||
is(data[2], "member3", "member3 is member3");
|
||||
is(data.length, 3, "data.length is right");
|
||||
ok(laterGroup.isComplete(), "laterGroup Promise is complete");
|
||||
ok(laterGroup.isResolved(), "laterGroup Promise is resolved");
|
||||
ok(!laterGroup.isRejected(), "laterGroup Promise is unrejected");
|
||||
|
||||
// Test grouping resolve() *before* then() in the same context
|
||||
tidyGroup = Promise.group([
|
||||
postResolution, preResolution, laterResolution,
|
||||
member1, member2, member3, laterGroup
|
||||
]);
|
||||
tidyGroup.then(testTidyGroup, fail);
|
||||
}
|
||||
|
||||
var failGroup;
|
||||
|
||||
function testTidyGroup(data) {
|
||||
is(data[0], "postResolution", "postResolution is postResolution");
|
||||
is(data[1], "preResolution", "preResolution is preResolution");
|
||||
is(data[2], "laterResolution", "laterResolution is laterResolution");
|
||||
is(data[3], "member1", "member1 is member1");
|
||||
is(data[6][1], "member2", "laterGroup is laterGroup");
|
||||
is(data.length, 7, "data.length is right");
|
||||
ok(tidyGroup.isComplete(), "tidyGroup Promise is complete");
|
||||
ok(tidyGroup.isResolved(), "tidyGroup Promise is resolved");
|
||||
ok(!tidyGroup.isRejected(), "tidyGroup Promise is unrejected");
|
||||
|
||||
// Test grouping resolve() *before* then() in the same context
|
||||
failGroup = Promise.group(postResolution, laterRejection);
|
||||
failGroup.then(fail, testFailGroup);
|
||||
}
|
||||
|
||||
function testFailGroup(data) {
|
||||
is(data, "laterRejection", "laterRejection is laterRejection");
|
||||
|
||||
postResolution = undefined;
|
||||
preResolution = undefined;
|
||||
laterResolution = undefined;
|
||||
member1 = undefined;
|
||||
member2 = undefined;
|
||||
member3 = undefined;
|
||||
laterGroup = undefined;
|
||||
laterRejection = undefined;
|
||||
|
||||
testTrap();
|
||||
}
|
||||
|
||||
function testTrap() {
|
||||
var p = new Promise();
|
||||
var message = "Expected exception";
|
||||
p.chainPromise(
|
||||
function() {
|
||||
throw new Error(message);
|
||||
}).trap(
|
||||
function(aError) {
|
||||
is(aError instanceof Error, true, "trap received exception");
|
||||
is(aError.message, message, "trap received correct exception");
|
||||
return 1;
|
||||
}).chainPromise(
|
||||
function(aResult) {
|
||||
is(aResult, 1, "trap restored correct result");
|
||||
testAlways();
|
||||
});
|
||||
p.resolve();
|
||||
}
|
||||
|
||||
function testAlways() {
|
||||
var shouldbeTrue1 = false;
|
||||
var shouldbeTrue2 = false;
|
||||
var p = new Promise();
|
||||
p.chainPromise(
|
||||
function() {
|
||||
throw new Error();
|
||||
}
|
||||
).chainPromise(// Promise rejected, should not be executed
|
||||
function() {
|
||||
ok(false, "This should not be executed");
|
||||
}
|
||||
).always(
|
||||
function(x) {
|
||||
shouldbeTrue1 = true;
|
||||
return "random value";
|
||||
}
|
||||
).trap(
|
||||
function(arg) {
|
||||
ok((arg instanceof Error), "The random value should be ignored");
|
||||
return 1;// We should still have this result later
|
||||
}
|
||||
).trap(
|
||||
function() {
|
||||
ok(false, "This should not be executed 2");
|
||||
}
|
||||
).always(
|
||||
function() {
|
||||
shouldbeTrue2 = true;
|
||||
}
|
||||
).then(
|
||||
function(aResult){
|
||||
ok(shouldbeTrue1, "First always must be executed");
|
||||
ok(shouldbeTrue2, "Second always must be executed");
|
||||
is(aResult, 1, "Result should be unaffected by always");
|
||||
|
||||
testComplete();
|
||||
}
|
||||
);
|
||||
p.resolve();
|
||||
}
|
||||
|
||||
function fail() {
|
||||
gBrowser.removeCurrentTab();
|
||||
info("Failed Promise Tests");
|
||||
ok(false, "fail called");
|
||||
finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* We wish to launch all tests with several configurations (at the moment,
|
||||
* non-debug and debug mode).
|
||||
*
|
||||
* If 0, we have not completed any test yet.
|
||||
* If 1, we have completed the tests in non-debug mode.
|
||||
* If 2, we have also completed the tests in debug mode.
|
||||
*/
|
||||
var configurationTestComplete = 0;
|
||||
function testComplete() {
|
||||
switch (configurationTestComplete) {
|
||||
case 0:
|
||||
info("Finished run in non-debug mode");
|
||||
configurationTestComplete = 1;
|
||||
Promise.Debug.setDebug(true);
|
||||
window.setTimeout(testBasic, 0);
|
||||
return;
|
||||
case 1:
|
||||
info("Finished run in debug mode");
|
||||
configurationTestComplete = 2;
|
||||
Promise.Debug.setDebug(false);
|
||||
window.setTimeout(finished, 0);
|
||||
return;
|
||||
default:
|
||||
ok(false, "Internal error in testComplete "+configurationTestComplete);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function finished() {
|
||||
gBrowser.removeCurrentTab();
|
||||
info("Finishing Promise Tests");
|
||||
finish();
|
||||
}
|
@ -11,7 +11,7 @@
|
||||
|
||||
var imports = {};
|
||||
Cu.import("resource:///modules/devtools/Templater.jsm", imports);
|
||||
Cu.import("resource://gre/modules/devtools/_Promise.jsm", imports);
|
||||
Cu.import("resource://gre/modules/commonjs/promise/core.js", imports);
|
||||
|
||||
function test() {
|
||||
addTab("http://example.com/browser/browser/devtools/shared/test/browser_templater_basic.html", function() {
|
||||
@ -278,9 +278,9 @@ var tests = [
|
||||
];
|
||||
|
||||
function delayReply(data) {
|
||||
var p = new imports.Promise();
|
||||
var d = imports.Promise.defer();
|
||||
executeSoon(function() {
|
||||
p.resolve(data);
|
||||
d.resolve(data);
|
||||
});
|
||||
return p;
|
||||
return d.promise;
|
||||
}
|
||||
|
@ -13,56 +13,43 @@ const Cu = Components.utils;
|
||||
const CONSOLEAPI_CLASS_ID = "{b49c18f8-3379-4fc0-8c90-d7772c1a9ff3}";
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource:///modules/devtools/gDevTools.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
|
||||
"resource:///modules/devtools/gDevTools.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
|
||||
"resource:///modules/devtools/Target.jsm");
|
||||
"resource:///modules/devtools/Target.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
"resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "WebConsoleUtils",
|
||||
"resource://gre/modules/devtools/WebConsoleUtils.jsm");
|
||||
"resource://gre/modules/devtools/WebConsoleUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/commonjs/promise/core.js");
|
||||
|
||||
const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
|
||||
let l10n = new WebConsoleUtils.l10n(STRINGS_URI);
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["HUDService"];
|
||||
|
||||
function LogFactory(aMessagePrefix)
|
||||
{
|
||||
function log(aMessage) {
|
||||
var _msg = aMessagePrefix + " " + aMessage + "\n";
|
||||
dump(_msg);
|
||||
}
|
||||
return log;
|
||||
}
|
||||
|
||||
let log = LogFactory("*** HUDService:");
|
||||
|
||||
// The HTML namespace.
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
// The XUL namespace.
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//// The HUD service
|
||||
|
||||
function HUD_SERVICE()
|
||||
{
|
||||
// These methods access the "this" object, but they're registered as
|
||||
// event listeners. So we hammer in the "this" binding.
|
||||
this.onWindowUnload = this.onWindowUnload.bind(this);
|
||||
|
||||
/**
|
||||
* Keeps a reference for each HeadsUpDisplay that is created
|
||||
*/
|
||||
this.hudReferences = {};
|
||||
};
|
||||
}
|
||||
|
||||
HUD_SERVICE.prototype =
|
||||
{
|
||||
/**
|
||||
* Keeps a reference for each HeadsUpDisplay that is created
|
||||
* @type object
|
||||
*/
|
||||
hudReferences: null,
|
||||
|
||||
/**
|
||||
* getter for UI commands to be used by the frontend
|
||||
*
|
||||
@ -72,12 +59,6 @@ HUD_SERVICE.prototype =
|
||||
return HeadsUpDisplayUICommands;
|
||||
},
|
||||
|
||||
/**
|
||||
* The sequencer is a generator (after initialization) that returns unique
|
||||
* integers
|
||||
*/
|
||||
sequencer: null,
|
||||
|
||||
/**
|
||||
* Firefox-specific current tab getter
|
||||
*
|
||||
@ -88,134 +69,22 @@ HUD_SERVICE.prototype =
|
||||
},
|
||||
|
||||
/**
|
||||
* Activate a HeadsUpDisplay for the given tab context.
|
||||
* Open a Web Console for the given target.
|
||||
*
|
||||
* @param nsIDOMElement aTab
|
||||
* The xul:tab element.
|
||||
* @see devtools/framework/Target.jsm for details about targets.
|
||||
*
|
||||
* @param object aTarget
|
||||
* The target that the web console will connect to.
|
||||
* @param nsIDOMElement aIframe
|
||||
* The iframe element into which to place the web console.
|
||||
* @param RemoteTarget aTarget
|
||||
* The target that the web console will connect to.
|
||||
* @return object
|
||||
* The new HeadsUpDisplay instance.
|
||||
* A Promise object for the opening of the new WebConsole instance.
|
||||
*/
|
||||
activateHUDForContext: function HS_activateHUDForContext(aTab, aIframe,
|
||||
aTarget)
|
||||
openWebConsole: function HS_openWebConsole(aTarget, aIframe)
|
||||
{
|
||||
let hudId = "hud_" + aTab.linkedPanel;
|
||||
if (hudId in this.hudReferences) {
|
||||
return this.hudReferences[hudId];
|
||||
}
|
||||
|
||||
this.wakeup();
|
||||
|
||||
let window = aTab.ownerDocument.defaultView;
|
||||
let gBrowser = window.gBrowser;
|
||||
|
||||
window.addEventListener("unload", this.onWindowUnload, false);
|
||||
|
||||
let hud = new WebConsole(aTab, aIframe, aTarget);
|
||||
this.hudReferences[hudId] = hud;
|
||||
|
||||
return hud;
|
||||
},
|
||||
|
||||
/**
|
||||
* Deactivate a HeadsUpDisplay for the given tab context.
|
||||
*
|
||||
* @param nsIDOMElement aTab
|
||||
* The xul:tab element you want to enable the Web Console for.
|
||||
* @return void
|
||||
*/
|
||||
deactivateHUDForContext: function HS_deactivateHUDForContext(aTab)
|
||||
{
|
||||
let hudId = "hud_" + aTab.linkedPanel;
|
||||
if (!(hudId in this.hudReferences)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let hud = this.getHudReferenceById(hudId);
|
||||
let document = hud.chromeDocument;
|
||||
|
||||
hud.destroy(function() {
|
||||
let id = WebConsoleUtils.supportsString(hudId);
|
||||
Services.obs.notifyObservers(id, "web-console-destroyed", null);
|
||||
});
|
||||
|
||||
delete this.hudReferences[hudId];
|
||||
|
||||
if (Object.keys(this.hudReferences).length == 0) {
|
||||
let autocompletePopup = document.
|
||||
getElementById("webConsole_autocompletePopup");
|
||||
if (autocompletePopup) {
|
||||
autocompletePopup.parentNode.removeChild(autocompletePopup);
|
||||
}
|
||||
|
||||
let window = document.defaultView;
|
||||
|
||||
window.removeEventListener("unload", this.onWindowUnload, false);
|
||||
|
||||
let gBrowser = window.gBrowser;
|
||||
let tabContainer = gBrowser.tabContainer;
|
||||
|
||||
this.suspend();
|
||||
}
|
||||
|
||||
let contentWindow = aTab.linkedBrowser.contentWindow;
|
||||
contentWindow.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* get a unique ID from the sequence generator
|
||||
*
|
||||
* @returns integer
|
||||
*/
|
||||
sequenceId: function HS_sequencerId()
|
||||
{
|
||||
if (!this.sequencer) {
|
||||
this.sequencer = this.createSequencer(-1);
|
||||
}
|
||||
return this.sequencer.next();
|
||||
},
|
||||
|
||||
/**
|
||||
* "Wake up" the Web Console activity. This is called when the first Web
|
||||
* Console is open. This method initializes the various observers we have.
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
wakeup: function HS_wakeup()
|
||||
{
|
||||
if (Object.keys(this.hudReferences).length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
WebConsoleObserver.init();
|
||||
},
|
||||
|
||||
/**
|
||||
* Suspend Web Console activity. This is called when all Web Consoles are
|
||||
* closed.
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
suspend: function HS_suspend()
|
||||
{
|
||||
delete this.lastFinishedRequestCallback;
|
||||
|
||||
WebConsoleObserver.uninit();
|
||||
},
|
||||
|
||||
/**
|
||||
* Shutdown all HeadsUpDisplays on quit-application-granted.
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
shutdown: function HS_shutdown()
|
||||
{
|
||||
for (let hud of this.hudReferences) {
|
||||
this.deactivateHUDForContext(hud.tab);
|
||||
}
|
||||
let hud = new WebConsole(aTarget, aIframe);
|
||||
this.hudReferences[hud.hudId] = hud;
|
||||
return hud.init();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -226,8 +95,13 @@ HUD_SERVICE.prototype =
|
||||
*/
|
||||
getHudByWindow: function HS_getHudByWindow(aContentWindow)
|
||||
{
|
||||
let hudId = this.getHudIdByWindow(aContentWindow);
|
||||
return hudId ? this.hudReferences[hudId] : null;
|
||||
for each (let hud in this.hudReferences) {
|
||||
let target = hud.target;
|
||||
if (target && target.tab && target.window === aContentWindow) {
|
||||
return hud;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -239,16 +113,8 @@ HUD_SERVICE.prototype =
|
||||
*/
|
||||
getHudIdByWindow: function HS_getHudIdByWindow(aContentWindow)
|
||||
{
|
||||
let window = this.currentContext();
|
||||
let index =
|
||||
window.gBrowser.getBrowserIndexForDocument(aContentWindow.document);
|
||||
if (index == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let tab = window.gBrowser.tabs[index];
|
||||
let hudId = "hud_" + tab.linkedPanel;
|
||||
return hudId in this.hudReferences ? hudId : null;
|
||||
let hud = this.getHudByWindow(aContentWindow);
|
||||
return hud ? hud.hudId : null;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -270,101 +136,54 @@ HUD_SERVICE.prototype =
|
||||
* @type function
|
||||
*/
|
||||
lastFinishedRequestCallback: null,
|
||||
|
||||
/**
|
||||
* Creates a generator that always returns a unique number for use in the
|
||||
* indexes
|
||||
*
|
||||
* @returns Generator
|
||||
*/
|
||||
createSequencer: function HS_createSequencer(aInt)
|
||||
{
|
||||
function sequencer(aInt)
|
||||
{
|
||||
while(1) {
|
||||
aInt++;
|
||||
yield aInt;
|
||||
}
|
||||
}
|
||||
return sequencer(aInt);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called whenever a browser window closes. Cleans up any consoles still
|
||||
* around.
|
||||
*
|
||||
* @param nsIDOMEvent aEvent
|
||||
* The dispatched event.
|
||||
* @returns void
|
||||
*/
|
||||
onWindowUnload: function HS_onWindowUnload(aEvent)
|
||||
{
|
||||
let window = aEvent.target.defaultView;
|
||||
|
||||
window.removeEventListener("unload", this.onWindowUnload, false);
|
||||
|
||||
let gBrowser = window.gBrowser;
|
||||
let tabContainer = gBrowser.tabContainer;
|
||||
|
||||
let tab = tabContainer.firstChild;
|
||||
while (tab != null) {
|
||||
this.deactivateHUDForContext(tab);
|
||||
tab = tab.nextSibling;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A WebConsole instance is an interactive console initialized *per tab*
|
||||
* A WebConsole instance is an interactive console initialized *per target*
|
||||
* that displays console log data as well as provides an interactive terminal to
|
||||
* manipulate the current tab's document content.
|
||||
* manipulate the target's document content.
|
||||
*
|
||||
* This object only wraps the iframe that holds the Web Console UI.
|
||||
*
|
||||
* @param nsIDOMElement aTab
|
||||
* The xul:tab for which you want the WebConsole object.
|
||||
* @constructor
|
||||
* @param object aTarget
|
||||
* The target that the web console will connect to.
|
||||
* @param nsIDOMElement aIframe
|
||||
* iframe into which we should create the WebConsole UI.
|
||||
* @param RemoteTarget aTarget
|
||||
* The target that the web console will connect to.
|
||||
*/
|
||||
function WebConsole(aTab, aIframe, aTarget)
|
||||
function WebConsole(aTarget, aIframe)
|
||||
{
|
||||
this.tab = aTab;
|
||||
if (this.tab == null) {
|
||||
throw new Error('Missing tab');
|
||||
}
|
||||
|
||||
this.iframe = aIframe;
|
||||
if (this.iframe == null) {
|
||||
console.trace();
|
||||
throw new Error('Missing iframe');
|
||||
}
|
||||
|
||||
this.chromeDocument = this.tab.ownerDocument;
|
||||
this.chromeWindow = this.chromeDocument.defaultView;
|
||||
this.hudId = "hud_" + this.tab.linkedPanel;
|
||||
|
||||
this.target = aTarget;
|
||||
|
||||
this._onIframeLoad = this._onIframeLoad.bind(this);
|
||||
|
||||
this.iframe.className = "web-console-frame";
|
||||
this.iframe.addEventListener("load", this._onIframeLoad, true);
|
||||
|
||||
this.positionConsole();
|
||||
this.chromeDocument = this.iframe.ownerDocument;
|
||||
this.chromeWindow = this.chromeDocument.defaultView;
|
||||
this.hudId = "hud_" + Date.now();
|
||||
this.target = aTarget;
|
||||
}
|
||||
|
||||
WebConsole.prototype = {
|
||||
/**
|
||||
* The xul:tab for which the current Web Console instance was created.
|
||||
* @type nsIDOMElement
|
||||
*/
|
||||
tab: null,
|
||||
|
||||
chromeWindow: null,
|
||||
chromeDocument: null,
|
||||
hudId: null,
|
||||
target: null,
|
||||
iframe: null,
|
||||
_destroyer: null,
|
||||
|
||||
_browserWindow: null,
|
||||
|
||||
get browserWindow()
|
||||
{
|
||||
if (!this._browserWindow) {
|
||||
let window = this.chromeWindow.top;
|
||||
let element = window.document.documentElement;
|
||||
if (element.getAttribute("windowtype") != "navigator:browser") {
|
||||
window = HUDService.currentContext();
|
||||
}
|
||||
this._browserWindow = window;
|
||||
}
|
||||
return this._browserWindow;
|
||||
},
|
||||
|
||||
/**
|
||||
* Getter for HUDService.lastFinishedRequestCallback.
|
||||
@ -380,7 +199,7 @@ WebConsole.prototype = {
|
||||
*/
|
||||
get mainPopupSet()
|
||||
{
|
||||
return this.chromeDocument.getElementById("mainPopupSet");
|
||||
return this.browserWindow.document.getElementById("mainPopupSet");
|
||||
},
|
||||
|
||||
/**
|
||||
@ -392,18 +211,48 @@ WebConsole.prototype = {
|
||||
return this.ui ? this.ui.outputNode : null;
|
||||
},
|
||||
|
||||
get gViewSourceUtils() this.chromeWindow.gViewSourceUtils,
|
||||
get gViewSourceUtils() this.browserWindow.gViewSourceUtils,
|
||||
|
||||
/**
|
||||
* The "load" event handler for the Web Console iframe.
|
||||
* @private
|
||||
* Initialize the Web Console instance.
|
||||
*
|
||||
* @return object
|
||||
* A Promise for the initialization.
|
||||
*/
|
||||
_onIframeLoad: function WC__onIframeLoad()
|
||||
init: function WC_init()
|
||||
{
|
||||
this.iframe.removeEventListener("load", this._onIframeLoad, true);
|
||||
let deferred = Promise.defer();
|
||||
|
||||
this.iframeWindow = this.iframe.contentWindow.wrappedJSObject;
|
||||
this.ui = new this.iframeWindow.WebConsoleFrame(this);
|
||||
let onIframeLoad = function() {
|
||||
this.iframe.removeEventListener("load", onIframeLoad, true);
|
||||
initUI();
|
||||
}.bind(this);
|
||||
|
||||
let initUI = function() {
|
||||
this.iframeWindow = this.iframe.contentWindow.wrappedJSObject;
|
||||
this.ui = new this.iframeWindow.WebConsoleFrame(this);
|
||||
this.ui.init().then(onSuccess, onFailure);
|
||||
}.bind(this);
|
||||
|
||||
let onSuccess = function() {
|
||||
deferred.resolve(this);
|
||||
}.bind(this);
|
||||
|
||||
let onFailure = function(aReason) {
|
||||
deferred.reject(aReason);
|
||||
};
|
||||
|
||||
let win, doc;
|
||||
if ((win = this.iframe.contentWindow) &&
|
||||
(doc = win.document) &&
|
||||
doc.readyState == "complete") {
|
||||
this.iframe.addEventListener("load", onIframeLoad, true);
|
||||
}
|
||||
else {
|
||||
initUI();
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -418,50 +267,6 @@ WebConsole.prototype = {
|
||||
return l10n.getFormatStr("webConsoleWindowTitleAndURL", [url]);
|
||||
},
|
||||
|
||||
consoleWindowUnregisterOnHide: true,
|
||||
|
||||
/**
|
||||
* Position the Web Console UI.
|
||||
*/
|
||||
positionConsole: function WC_positionConsole()
|
||||
{
|
||||
let lastIndex = -1;
|
||||
|
||||
if (this.outputNode && this.outputNode.getIndexOfFirstVisibleRow) {
|
||||
lastIndex = this.outputNode.getIndexOfFirstVisibleRow() +
|
||||
this.outputNode.getNumberOfVisibleRows() - 1;
|
||||
}
|
||||
|
||||
this._beforePositionConsole(lastIndex);
|
||||
},
|
||||
|
||||
/**
|
||||
* Common code that needs to execute before the Web Console is repositioned.
|
||||
* @private
|
||||
* @param number aLastIndex
|
||||
* The last visible message in the console output before repositioning
|
||||
* occurred.
|
||||
*/
|
||||
_beforePositionConsole:
|
||||
function WC__beforePositionConsole(aLastIndex)
|
||||
{
|
||||
if (!this.ui) {
|
||||
return;
|
||||
}
|
||||
|
||||
let onLoad = function() {
|
||||
this.iframe.removeEventListener("load", onLoad, true);
|
||||
this.iframeWindow = this.iframe.contentWindow.wrappedJSObject;
|
||||
this.ui.positionConsole(this.iframeWindow);
|
||||
|
||||
if (aLastIndex > -1 && aLastIndex < this.outputNode.getRowCount()) {
|
||||
this.outputNode.ensureIndexIsVisible(aLastIndex);
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
this.iframe.addEventListener("load", onLoad, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* The JSTerm object that manages the console's input.
|
||||
* @see webconsole.js::JSTerm
|
||||
@ -478,7 +283,9 @@ WebConsole.prototype = {
|
||||
*/
|
||||
_onClearButton: function WC__onClearButton()
|
||||
{
|
||||
this.chromeWindow.DeveloperToolbar.resetErrorsCount(this.tab);
|
||||
if (this.target.isLocalTab) {
|
||||
this.browserWindow.DeveloperToolbar.resetErrorsCount(this.target.tab);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -498,7 +305,7 @@ WebConsole.prototype = {
|
||||
*/
|
||||
openLink: function WC_openLink(aLink)
|
||||
{
|
||||
this.chromeWindow.openUILinkIn(aLink, "tab");
|
||||
this.browserWindow.openUILinkIn(aLink, "tab");
|
||||
},
|
||||
|
||||
/**
|
||||
@ -530,12 +337,13 @@ WebConsole.prototype = {
|
||||
viewSourceInStyleEditor:
|
||||
function WC_viewSourceInStyleEditor(aSourceURL, aSourceLine)
|
||||
{
|
||||
let styleSheets = this.tab.linkedBrowser.contentWindow.document.styleSheets;
|
||||
let styleSheets = {};
|
||||
if (this.target.isLocalTab) {
|
||||
styleSheets = this.target.window.document.styleSheets;
|
||||
}
|
||||
for each (let style in styleSheets) {
|
||||
if (style.href == aSourceURL) {
|
||||
let target = TargetFactory.forTab(this.tab);
|
||||
let gDevTools = this.chromeWindow.gDevTools;
|
||||
gDevTools.showToolbox(target, "styleeditor").then(function(toolbox) {
|
||||
gDevTools.showToolbox(this.target, "styleeditor").then(function(toolbox) {
|
||||
toolbox.getCurrentPanel().selectStyleSheet(style, aSourceLine);
|
||||
});
|
||||
return;
|
||||
@ -549,15 +357,20 @@ WebConsole.prototype = {
|
||||
* Destroy the object. Call this method to avoid memory leaks when the Web
|
||||
* Console is closed.
|
||||
*
|
||||
* @param function [aOnDestroy]
|
||||
* Optional function to invoke when the Web Console instance is
|
||||
* destroyed.
|
||||
* @return object
|
||||
* A Promise object that is resolved once the Web Console is closed.
|
||||
*/
|
||||
destroy: function WC_destroy(aOnDestroy)
|
||||
destroy: function WC_destroy()
|
||||
{
|
||||
// Make sure that the console panel does not try to call
|
||||
// deactivateHUDForContext() again.
|
||||
this.consoleWindowUnregisterOnHide = false;
|
||||
if (this._destroyer) {
|
||||
return this._destroyer.promise;
|
||||
}
|
||||
|
||||
delete HUDService.hudReferences[this.hudId];
|
||||
|
||||
let tabWindow = this.target.isLocalTab ? this.target.window : null;
|
||||
|
||||
this._destroyer = Promise.defer();
|
||||
|
||||
let popupset = this.mainPopupSet;
|
||||
let panels = popupset.querySelectorAll("panel[hudId=" + this.hudId + "]");
|
||||
@ -566,27 +379,27 @@ WebConsole.prototype = {
|
||||
}
|
||||
|
||||
let onDestroy = function WC_onDestroyUI() {
|
||||
// Remove the iframe and the consolePanel if the Web Console is inside a
|
||||
// floating panel.
|
||||
if (this.consolePanel && this.consolePanel.parentNode) {
|
||||
this.consolePanel.hidePopup();
|
||||
this.consolePanel.parentNode.removeChild(this.consolePanel);
|
||||
this.consolePanel = null;
|
||||
try {
|
||||
tabWindow && tabWindow.focus();
|
||||
}
|
||||
catch (ex) {
|
||||
// Tab focus can fail if the tab is closed.
|
||||
}
|
||||
|
||||
if (this.iframe.parentNode) {
|
||||
this.iframe.parentNode.removeChild(this.iframe);
|
||||
}
|
||||
let id = WebConsoleUtils.supportsString(this.hudId);
|
||||
Services.obs.notifyObservers(id, "web-console-destroyed", null);
|
||||
|
||||
aOnDestroy && aOnDestroy();
|
||||
this._destroyer.resolve(null);
|
||||
}.bind(this);
|
||||
|
||||
if (this.ui) {
|
||||
this.ui.destroy(onDestroy);
|
||||
this.ui.destroy().then(onDestroy);
|
||||
}
|
||||
else {
|
||||
onDestroy();
|
||||
}
|
||||
|
||||
return this._destroyer.promise;
|
||||
},
|
||||
};
|
||||
|
||||
@ -595,9 +408,16 @@ WebConsole.prototype = {
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var HeadsUpDisplayUICommands = {
|
||||
toggleHUD: function UIC_toggleHUD(aOptions)
|
||||
/**
|
||||
* Toggle the Web Console for the current tab.
|
||||
*
|
||||
* @return object
|
||||
* A Promise for either the opening of the toolbox that holds the Web
|
||||
* Console, or a Promise for the closing of the toolbox.
|
||||
*/
|
||||
toggleHUD: function UIC_toggleHUD()
|
||||
{
|
||||
var window = HUDService.currentContext();
|
||||
let window = HUDService.currentContext();
|
||||
let target = TargetFactory.forTab(window.gBrowser.selectedTab);
|
||||
let toolbox = gDevTools.getToolbox(target);
|
||||
|
||||
@ -606,84 +426,24 @@ var HeadsUpDisplayUICommands = {
|
||||
gDevTools.showToolbox(target, "webconsole");
|
||||
},
|
||||
|
||||
toggleRemoteHUD: function UIC_toggleRemoteHUD()
|
||||
{
|
||||
if (this.getOpenHUD()) {
|
||||
this.toggleHUD();
|
||||
return;
|
||||
}
|
||||
|
||||
let host = Services.prefs.getCharPref("devtools.debugger.remote-host");
|
||||
let port = Services.prefs.getIntPref("devtools.debugger.remote-port");
|
||||
|
||||
let check = { value: false };
|
||||
let input = { value: host + ":" + port };
|
||||
|
||||
let result = Services.prompt.prompt(null,
|
||||
l10n.getStr("remoteWebConsolePromptTitle"),
|
||||
l10n.getStr("remoteWebConsolePromptMessage"),
|
||||
input, null, check);
|
||||
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
let parts = input.value.split(":");
|
||||
if (parts.length != 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
[host, port] = parts;
|
||||
if (!host.length || !port.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
Services.prefs.setCharPref("devtools.debugger.remote-host", host);
|
||||
Services.prefs.setIntPref("devtools.debugger.remote-port", port);
|
||||
|
||||
this.toggleHUD({
|
||||
host: host,
|
||||
port: port,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Find the hudId for the active chrome window.
|
||||
* @return string|null
|
||||
* The hudId or null if the active chrome window has no open Web
|
||||
* Find if there is a Web Console open for the current tab and return the
|
||||
* instance.
|
||||
* @return object|null
|
||||
* The WebConsole object or null if the active tab has no open Web
|
||||
* Console.
|
||||
*/
|
||||
getOpenHUD: function UIC_getOpenHUD() {
|
||||
let chromeWindow = HUDService.currentContext();
|
||||
let hudId = "hud_" + chromeWindow.gBrowser.selectedTab.linkedPanel;
|
||||
return hudId in HUDService.hudReferences ? hudId : null;
|
||||
},
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// WebConsoleObserver
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var WebConsoleObserver = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
|
||||
|
||||
init: function WCO_init()
|
||||
getOpenHUD: function UIC_getOpenHUD()
|
||||
{
|
||||
Services.obs.addObserver(this, "quit-application-granted", false);
|
||||
},
|
||||
|
||||
observe: function WCO_observe(aSubject, aTopic)
|
||||
{
|
||||
if (aTopic == "quit-application-granted") {
|
||||
HUDService.shutdown();
|
||||
let tab = HUDService.currentContext().gBrowser.selectedTab;
|
||||
if (!tab || !TargetFactory.isKnownTab(tab)) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
uninit: function WCO_uninit()
|
||||
{
|
||||
Services.obs.removeObserver(this, "quit-application-granted");
|
||||
let target = TargetFactory.forTab(tab);
|
||||
let toolbox = gDevTools.getToolbox(target);
|
||||
let panel = toolbox ? toolbox.getPanel("webconsole") : null;
|
||||
return panel ? panel.hud : null;
|
||||
},
|
||||
};
|
||||
|
||||
const HUDService = new HUD_SERVICE();
|
||||
|
||||
|
@ -9,12 +9,12 @@ this.EXPORTED_SYMBOLS = [ "WebConsolePanel" ];
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/commonjs/promise/core.js");
|
||||
Cu.import("resource:///modules/devtools/EventEmitter.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "HUDService",
|
||||
"resource:///modules/HUDService.jsm");
|
||||
"resource:///modules/HUDService.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
|
||||
"resource:///modules/devtools/EventEmitter.jsm");
|
||||
|
||||
/**
|
||||
* A DevToolPanel that controls the Web Console.
|
||||
@ -26,32 +26,29 @@ function WebConsolePanel(iframeWindow, toolbox) {
|
||||
}
|
||||
|
||||
WebConsolePanel.prototype = {
|
||||
hud: null,
|
||||
|
||||
/**
|
||||
* open is effectively an asynchronous constructor
|
||||
* Open is effectively an asynchronous constructor.
|
||||
*
|
||||
* @return object
|
||||
* A Promise that is resolved when the Web Console completes opening.
|
||||
*/
|
||||
open: function StyleEditor_open() {
|
||||
let parentDoc = this._frameWindow.document.defaultView.parent.document;
|
||||
open: function WCP_open()
|
||||
{
|
||||
let parentDoc = this._toolbox.doc;
|
||||
let iframe = parentDoc.getElementById("toolbox-panel-iframe-webconsole");
|
||||
this.hud = HUDService.activateHUDForContext(this.target.tab, iframe,
|
||||
this._toolbox.target);
|
||||
let promise = HUDService.openWebConsole(this.target, iframe);
|
||||
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let hudId = this.hud.hudId;
|
||||
let onOpen = function _onWebConsoleOpen(aSubject) {
|
||||
aSubject.QueryInterface(Ci.nsISupportsString);
|
||||
if (hudId == aSubject.data) {
|
||||
Services.obs.removeObserver(onOpen, "web-console-created");
|
||||
|
||||
this._isReady = true;
|
||||
this.emit("ready");
|
||||
deferred.resolve(this);
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
Services.obs.addObserver(onOpen, "web-console-created", false);
|
||||
|
||||
return deferred.promise;
|
||||
return promise.then(function onSuccess(aWebConsole) {
|
||||
this.hud = aWebConsole;
|
||||
this._isReady = true;
|
||||
this.emit("ready");
|
||||
return this;
|
||||
}.bind(this), function onError(aReason) {
|
||||
Cu.reportError("WebConsolePanel open failed. " +
|
||||
aReason.error + ": " + aReason.message);
|
||||
});
|
||||
},
|
||||
|
||||
get target() this._toolbox.target,
|
||||
@ -61,28 +58,15 @@ WebConsolePanel.prototype = {
|
||||
|
||||
destroy: function WCP_destroy()
|
||||
{
|
||||
if (this.destroyer) {
|
||||
return this.destroyer.promise;
|
||||
if (this._destroyer) {
|
||||
return this._destroyer;
|
||||
}
|
||||
|
||||
this.destroyer = Promise.defer();
|
||||
this._destroyer = this.hud.destroy();
|
||||
this._destroyer.then(function() {
|
||||
this.emit("destroyed");
|
||||
}.bind(this));
|
||||
|
||||
let hudId = this.hud.hudId;
|
||||
|
||||
let onClose = function _onWebConsoleClose(aSubject)
|
||||
{
|
||||
aSubject.QueryInterface(Ci.nsISupportsString);
|
||||
if (hudId == aSubject.data) {
|
||||
Services.obs.removeObserver(onClose, "web-console-destroyed");
|
||||
|
||||
this.emit("destroyed");
|
||||
this.destroyer.resolve(null);
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
Services.obs.addObserver(onClose, "web-console-destroyed", false);
|
||||
HUDService.deactivateHUDForContext(this.hud.tab, false);
|
||||
|
||||
return this.destroyer.promise;
|
||||
return this._destroyer;
|
||||
},
|
||||
};
|
||||
|
@ -28,14 +28,17 @@ function testClosingAfterCompletion(hud) {
|
||||
|
||||
// Focus the inputNode and perform the keycombo to close the WebConsole.
|
||||
inputNode.focus();
|
||||
EventUtils.synthesizeKey("k", { accelKey: true, shiftKey: true });
|
||||
|
||||
// We can't test for errors right away, because the error occurs after a
|
||||
// setTimeout(..., 0) in the WebConsole code.
|
||||
executeSoon(function() {
|
||||
gDevTools.once("toolbox-destroyed", function() {
|
||||
browser.removeEventListener("error", errorListener, false);
|
||||
is(errorWhileClosing, false, "no error while closing the WebConsole");
|
||||
finishTest();
|
||||
});
|
||||
|
||||
if (Services.appinfo.OS == "Darwin") {
|
||||
EventUtils.synthesizeKey("k", { accelKey: true, altKey: true });
|
||||
} else {
|
||||
EventUtils.synthesizeKey("k", { accelKey: true, shiftKey: true });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,13 +57,14 @@ function openConsoles() {
|
||||
let tab = openTabs[i];
|
||||
openConsole(tab, function(index, hud) {
|
||||
ok(hud, "HUD is open for tab " + index);
|
||||
let window = hud.tab.linkedBrowser.contentWindow;
|
||||
let window = hud.target.tab.linkedBrowser.contentWindow;
|
||||
window.console.log("message for tab " + index);
|
||||
consolesOpen++;
|
||||
}.bind(null, i));
|
||||
}
|
||||
|
||||
waitForSuccess({
|
||||
timeout: 10000,
|
||||
name: "4 web consoles opened",
|
||||
validatorFn: function()
|
||||
{
|
||||
|
@ -67,6 +67,7 @@ function onStyleEditorReady(aEvent, aPanel)
|
||||
return sheet;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
waitForFocus(function() {
|
||||
|
@ -238,9 +238,11 @@ function finishTest()
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
hud.jsterm.clearOutput(true);
|
||||
if (hud.jsterm) {
|
||||
hud.jsterm.clearOutput(true);
|
||||
}
|
||||
|
||||
closeConsole(hud.tab, finish);
|
||||
closeConsole(hud.target.tab, finish);
|
||||
|
||||
hud = null;
|
||||
}
|
||||
|
@ -43,6 +43,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "AutocompletePopup",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "WebConsoleUtils",
|
||||
"resource://gre/modules/devtools/WebConsoleUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/commonjs/promise/core.js");
|
||||
|
||||
const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
|
||||
let l10n = new WebConsoleUtils.l10n(STRINGS_URI);
|
||||
|
||||
@ -168,9 +171,9 @@ const MIN_FONT_SIZE = 10;
|
||||
const PREF_CONNECTION_TIMEOUT = "devtools.debugger.remote-timeout";
|
||||
|
||||
/**
|
||||
* A WebConsoleFrame instance is an interactive console initialized *per tab*
|
||||
* A WebConsoleFrame instance is an interactive console initialized *per target*
|
||||
* that displays console log data as well as provides an interactive terminal to
|
||||
* manipulate the current tab's document content.
|
||||
* manipulate the target's document content.
|
||||
*
|
||||
* The WebConsoleFrame is responsible for the actual Web Console UI
|
||||
* implementation.
|
||||
@ -190,18 +193,9 @@ function WebConsoleFrame(aWebConsoleOwner)
|
||||
|
||||
this._toggleFilter = this._toggleFilter.bind(this);
|
||||
this._flushMessageQueue = this._flushMessageQueue.bind(this);
|
||||
this._connectionTimeout = this._connectionTimeout.bind(this);
|
||||
|
||||
this._outputTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
this._outputTimerInitialized = false;
|
||||
|
||||
this._initDefaultFilterPrefs();
|
||||
this._commandController = new CommandController(this);
|
||||
this.positionConsole(window);
|
||||
|
||||
this.jsterm = new JSTerm(this);
|
||||
this.jsterm.inputNode.focus();
|
||||
this._initConnection();
|
||||
}
|
||||
|
||||
WebConsoleFrame.prototype = {
|
||||
@ -222,13 +216,6 @@ WebConsoleFrame.prototype = {
|
||||
*/
|
||||
proxy: null,
|
||||
|
||||
/**
|
||||
* Timer used for the connection.
|
||||
* @private
|
||||
* @type object
|
||||
*/
|
||||
_connectTimer: null,
|
||||
|
||||
/**
|
||||
* Getter for the xul:popupset that holds any popups we open.
|
||||
* @type nsIDOMElement
|
||||
@ -306,7 +293,7 @@ WebConsoleFrame.prototype = {
|
||||
groupDepth: 0,
|
||||
|
||||
/**
|
||||
* The current tab location.
|
||||
* The current target location.
|
||||
* @type string
|
||||
*/
|
||||
contentLocation: "",
|
||||
@ -336,6 +323,8 @@ WebConsoleFrame.prototype = {
|
||||
*/
|
||||
get webConsoleClient() this.proxy ? this.proxy.webConsoleClient : null,
|
||||
|
||||
_destroyer: null,
|
||||
|
||||
_saveRequestAndResponseBodies: false,
|
||||
|
||||
/**
|
||||
@ -364,60 +353,51 @@ WebConsoleFrame.prototype = {
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize the WebConsoleFrame instance.
|
||||
* @return object
|
||||
* A Promise object for the initialization.
|
||||
*/
|
||||
init: function WCF_init()
|
||||
{
|
||||
this._initUI();
|
||||
return this._initConnection();
|
||||
},
|
||||
|
||||
/**
|
||||
* Connect to the server using the remote debugging protocol.
|
||||
*
|
||||
* @private
|
||||
* @return object
|
||||
* A Promise object that is resolved/reject based on the connection
|
||||
* result.
|
||||
*/
|
||||
_initConnection: function WCF__initConnection()
|
||||
{
|
||||
let deferred = Promise.defer();
|
||||
|
||||
this.proxy = new WebConsoleConnectionProxy(this, this.owner.target);
|
||||
|
||||
let timeout = Services.prefs.getIntPref(PREF_CONNECTION_TIMEOUT);
|
||||
this._connectTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
this._connectTimer.initWithCallback(this._connectionTimeout,
|
||||
timeout, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
let onSuccess = function() {
|
||||
this.saveRequestAndResponseBodies = this._saveRequestAndResponseBodies;
|
||||
deferred.resolve(this);
|
||||
}.bind(this);
|
||||
|
||||
this.proxy.connect(function() {
|
||||
// Don't complete connection if the connection timed-out.
|
||||
if (this._connectTimer) {
|
||||
this._connectTimer.cancel();
|
||||
this._connectTimer = null;
|
||||
this.saveRequestAndResponseBodies = this._saveRequestAndResponseBodies;
|
||||
this._onInitComplete();
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
let onFailure = function(aReason) {
|
||||
let node = this.createMessageNode(CATEGORY_JS, SEVERITY_ERROR,
|
||||
aReason.error + ": " + aReason.message);
|
||||
this.outputMessage(CATEGORY_JS, node);
|
||||
deferred.reject(aReason);
|
||||
}.bind(this);
|
||||
|
||||
/**
|
||||
* Connection timeout handler. This method simply prints a message informing
|
||||
* the user that the connection timed-out.
|
||||
* @private
|
||||
*/
|
||||
_connectionTimeout: function WCF__connectionTimeout()
|
||||
{
|
||||
this._connectTimer = null;
|
||||
let sendNotification = function() {
|
||||
let id = WebConsoleUtils.supportsString(this.hudId);
|
||||
Services.obs.notifyObservers(id, "web-console-created", null);
|
||||
}.bind(this);
|
||||
|
||||
let node = this.createMessageNode(CATEGORY_JS, SEVERITY_ERROR,
|
||||
l10n.getStr("connectionTimeout"));
|
||||
this.outputMessage(CATEGORY_JS, node);
|
||||
this.proxy.connect().then(onSuccess, onFailure).then(sendNotification);
|
||||
|
||||
// Allow initialization to complete.
|
||||
this._onInitComplete();
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset the connection timeout timer.
|
||||
* @private
|
||||
*/
|
||||
_resetConnectionTimeout: function WCF__resetConnectionTimeout()
|
||||
{
|
||||
let timer = this._connectTimer;
|
||||
if (timer) {
|
||||
let timeout = timer.delay;
|
||||
timer.cancel();
|
||||
timer.initWithCallback(this._connectionTimeout, timeout,
|
||||
Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -426,6 +406,18 @@ WebConsoleFrame.prototype = {
|
||||
*/
|
||||
_initUI: function WCF__initUI()
|
||||
{
|
||||
// Remember that this script is loaded in the webconsole.xul context:
|
||||
// |window| is the iframe global.
|
||||
this.window = window;
|
||||
this.document = this.window.document;
|
||||
this.rootElement = this.document.documentElement;
|
||||
|
||||
this._initDefaultFilterPrefs();
|
||||
|
||||
// Register the controller to handle "select all" properly.
|
||||
this._commandController = new CommandController(this);
|
||||
this.window.controllers.insertControllerAt(0, this._commandController);
|
||||
|
||||
let doc = this.document;
|
||||
|
||||
this.filterBox = doc.querySelector(".hud-filter-box");
|
||||
@ -482,6 +474,10 @@ WebConsoleFrame.prototype = {
|
||||
this.owner._onClearButton();
|
||||
this.jsterm.clearOutput(true);
|
||||
}.bind(this));
|
||||
|
||||
this.jsterm = new JSTerm(this);
|
||||
this.jsterm.init();
|
||||
this.jsterm.inputNode.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -567,54 +563,6 @@ WebConsoleFrame.prototype = {
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback method for when the Web Console initialization is complete. For
|
||||
* now this method sends the web-console-created notification using the
|
||||
* nsIObserverService.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onInitComplete: function WC__onInitComplete()
|
||||
{
|
||||
let id = WebConsoleUtils.supportsString(this.hudId);
|
||||
Services.obs.notifyObservers(id, "web-console-created", null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Position the console in a different location.
|
||||
*
|
||||
* Note: you do not usually call this method. This is called by the WebConsole
|
||||
* instance that owns this iframe. You need to call this if you write
|
||||
* a different owner or you manually reposition the iframe.
|
||||
*
|
||||
* @param object aNewWindow
|
||||
* Repositioning causes the iframe to reload - bug 254144. You need to
|
||||
* provide the new window object so we can reinitialize the UI as
|
||||
* needed.
|
||||
*/
|
||||
positionConsole: function WCF_positionConsole(aNewWindow)
|
||||
{
|
||||
this.window = aNewWindow;
|
||||
this.document = this.window.document;
|
||||
this.rootElement = this.document.documentElement;
|
||||
|
||||
// register the controller to handle "select all" properly
|
||||
this.window.controllers.insertControllerAt(0, this._commandController);
|
||||
|
||||
let oldOutputNode = this.outputNode;
|
||||
|
||||
this._initUI();
|
||||
this.jsterm && this.jsterm._initUI();
|
||||
|
||||
if (oldOutputNode && oldOutputNode.childNodes.length) {
|
||||
let parentNode = this.outputNode.parentNode;
|
||||
parentNode.replaceChild(oldOutputNode, this.outputNode);
|
||||
this.outputNode = oldOutputNode;
|
||||
}
|
||||
|
||||
this.jsterm && this.jsterm.inputNode.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Increase, decrease or reset the font size.
|
||||
*
|
||||
@ -2706,15 +2654,21 @@ WebConsoleFrame.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy the HUD object. Call this method to avoid memory leaks when the Web
|
||||
* Console is closed.
|
||||
* Destroy the WebConsoleFrame object. Call this method to avoid memory leaks
|
||||
* when the Web Console is closed.
|
||||
*
|
||||
* @param function [aOnDestroy]
|
||||
* Optional function to invoke when the Web Console instance is
|
||||
* destroyed.
|
||||
* @return object
|
||||
* A Promise that is resolved when the WebConsoleFrame instance is
|
||||
* destroyed.
|
||||
*/
|
||||
destroy: function WCF_destroy(aOnDestroy)
|
||||
destroy: function WCF_destroy()
|
||||
{
|
||||
if (this._destroyer) {
|
||||
return this._destroyer.promise;
|
||||
}
|
||||
|
||||
this._destroyer = Promise.defer();
|
||||
|
||||
this._cssNodes = {};
|
||||
this._outputQueue = [];
|
||||
this._pruneCategoriesQueue = {};
|
||||
@ -2726,22 +2680,26 @@ WebConsoleFrame.prototype = {
|
||||
}
|
||||
this._outputTimer = null;
|
||||
|
||||
if (this._connectTimer) {
|
||||
this._connectTimer.cancel();
|
||||
}
|
||||
this._connectTimer = null;
|
||||
|
||||
if (this.proxy) {
|
||||
this.proxy.disconnect(aOnDestroy);
|
||||
this.proxy = null;
|
||||
}
|
||||
|
||||
if (this.jsterm) {
|
||||
this.jsterm.destroy();
|
||||
this.jsterm = null;
|
||||
}
|
||||
|
||||
this._commandController = null;
|
||||
|
||||
let onDestroy = function() {
|
||||
this._destroyer.resolve(null);
|
||||
}.bind(this);
|
||||
|
||||
if (this.proxy) {
|
||||
this.proxy.disconnect().then(onDestroy);
|
||||
this.proxy = null;
|
||||
}
|
||||
else {
|
||||
onDestroy();
|
||||
}
|
||||
|
||||
return this._destroyer.promise;
|
||||
},
|
||||
};
|
||||
|
||||
@ -2763,12 +2721,8 @@ function JSTerm(aWebConsoleFrame)
|
||||
this.history = [];
|
||||
this.historyIndex = 0;
|
||||
this.historyPlaceHolder = 0; // this.history.length;
|
||||
this.autocompletePopup = new AutocompletePopup(this.hud.owner.chromeDocument);
|
||||
this.autocompletePopup.onSelect = this.onAutocompleteSelect.bind(this);
|
||||
this.autocompletePopup.onClick = this.acceptProposedCompletion.bind(this);
|
||||
this._keyPress = this.keyPress.bind(this);
|
||||
this._inputEventHandler = this.inputEventHandler.bind(this);
|
||||
this._initUI();
|
||||
}
|
||||
|
||||
JSTerm.prototype = {
|
||||
@ -2790,6 +2744,10 @@ JSTerm.prototype = {
|
||||
*/
|
||||
history: null,
|
||||
|
||||
autocompletePopup: null,
|
||||
inputNode: null,
|
||||
completeNode: null,
|
||||
|
||||
/**
|
||||
* Getter for the element that holds the messages we display.
|
||||
* @type nsIDOMElement
|
||||
@ -2808,10 +2766,14 @@ JSTerm.prototype = {
|
||||
|
||||
/**
|
||||
* Initialize the JSTerminal UI.
|
||||
* @private
|
||||
*/
|
||||
_initUI: function JST__initUI()
|
||||
init: function JST_init()
|
||||
{
|
||||
let chromeDocument = this.hud.owner.chromeDocument;
|
||||
this.autocompletePopup = new AutocompletePopup(chromeDocument);
|
||||
this.autocompletePopup.onSelect = this.onAutocompleteSelect.bind(this);
|
||||
this.autocompletePopup.onClick = this.acceptProposedCompletion.bind(this);
|
||||
|
||||
let doc = this.hud.document;
|
||||
this.completeNode = doc.querySelector(".jsterm-complete-node");
|
||||
this.inputNode = doc.querySelector(".jsterm-input-node");
|
||||
@ -3819,6 +3781,12 @@ JSTerm.prototype = {
|
||||
this.autocompletePopup.destroy();
|
||||
this.autocompletePopup = null;
|
||||
|
||||
let popup = this.hud.owner.chromeDocument
|
||||
.getElementById("webConsole_autocompletePopup");
|
||||
if (popup) {
|
||||
popup.parentNode.removeChild(popup);
|
||||
}
|
||||
|
||||
this.inputNode.removeEventListener("keypress", this._keyPress, false);
|
||||
this.inputNode.removeEventListener("input", this._inputEventHandler, false);
|
||||
this.inputNode.removeEventListener("keyup", this._inputEventHandler, false);
|
||||
@ -4047,6 +4015,11 @@ function WebConsoleConnectionProxy(aWebConsole, aTarget)
|
||||
this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
|
||||
this._onFileActivity = this._onFileActivity.bind(this);
|
||||
this._onTabNavigated = this._onTabNavigated.bind(this);
|
||||
this._onListTabs = this._onListTabs.bind(this);
|
||||
this._onAttachTab = this._onAttachTab.bind(this);
|
||||
this._onAttachConsole = this._onAttachConsole.bind(this);
|
||||
this._onCachedMessages = this._onCachedMessages.bind(this);
|
||||
this._connectionTimeout = this._connectionTimeout.bind(this);
|
||||
}
|
||||
|
||||
WebConsoleConnectionProxy.prototype = {
|
||||
@ -4092,6 +4065,16 @@ WebConsoleConnectionProxy.prototype = {
|
||||
*/
|
||||
connected: false,
|
||||
|
||||
/**
|
||||
* Timer used for the connection.
|
||||
* @private
|
||||
* @type object
|
||||
*/
|
||||
_connectTimer: null,
|
||||
|
||||
_connectDefer: null,
|
||||
_disconnecter: null,
|
||||
|
||||
/**
|
||||
* The WebConsoleActor ID.
|
||||
*
|
||||
@ -4130,11 +4113,31 @@ WebConsoleConnectionProxy.prototype = {
|
||||
/**
|
||||
* Initialize a debugger client and connect it to the debugger server.
|
||||
*
|
||||
* @param function [aCallback]
|
||||
* Optional function to invoke when connection is established.
|
||||
* @return object
|
||||
* A Promise object that is resolved/rejected based on the success of
|
||||
* the connection initialization.
|
||||
*/
|
||||
connect: function WCCP_connect(aCallback)
|
||||
connect: function WCCP_connect()
|
||||
{
|
||||
if (this._connectDefer) {
|
||||
return this._connectDefer.promise;
|
||||
}
|
||||
|
||||
this._connectDefer = Promise.defer();
|
||||
|
||||
let timeout = Services.prefs.getIntPref(PREF_CONNECTION_TIMEOUT);
|
||||
this._connectTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
this._connectTimer.initWithCallback(this._connectionTimeout,
|
||||
timeout, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
|
||||
let promise = this._connectDefer.promise;
|
||||
promise.then(function _onSucess() {
|
||||
this._connectTimer.cancel();
|
||||
this._connectTimer = null;
|
||||
}.bind(this), function _onFailure() {
|
||||
this._connectTimer = null;
|
||||
}.bind(this));
|
||||
|
||||
// TODO: convert the non-remote path to use the target API as well.
|
||||
let transport, client;
|
||||
if (this.target.isRemote) {
|
||||
@ -4143,7 +4146,6 @@ WebConsoleConnectionProxy.prototype = {
|
||||
else {
|
||||
this.initServer();
|
||||
transport = DebuggerServer.connectPipe();
|
||||
|
||||
client = this.client = new DebuggerClient(transport);
|
||||
}
|
||||
|
||||
@ -4157,39 +4159,54 @@ WebConsoleConnectionProxy.prototype = {
|
||||
if (this.target.isRemote) {
|
||||
if (!this.target.chrome) {
|
||||
// target.form is a TabActor grip
|
||||
this._attachTab(this.target.form, aCallback);
|
||||
this._attachTab(this.target.form);
|
||||
}
|
||||
else {
|
||||
// target.form is a RootActor grip
|
||||
this._consoleActor = this.target.form.consoleActor;
|
||||
this._attachConsole(aCallback);
|
||||
this._attachConsole();
|
||||
}
|
||||
}
|
||||
else {
|
||||
client.connect(function(aType, aTraits) {
|
||||
client.listTabs(this._onListTabs.bind(this, aCallback));
|
||||
client.listTabs(this._onListTabs);
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Connection timeout handler.
|
||||
* @private
|
||||
*/
|
||||
_connectionTimeout: function WCCP__connectionTimeout()
|
||||
{
|
||||
let error = {
|
||||
error: "timeout",
|
||||
message: l10n.getStr("connectionTimeout"),
|
||||
};
|
||||
|
||||
this._connectDefer.reject(error);
|
||||
},
|
||||
|
||||
/**
|
||||
* The "listTabs" response handler.
|
||||
*
|
||||
* @private
|
||||
* @param function [aCallback]
|
||||
* Optional function to invoke once the connection is established.
|
||||
* @param object aResponse
|
||||
* The JSON response object received from the server.
|
||||
*/
|
||||
_onListTabs: function WCCP__onListTabs(aCallback, aResponse)
|
||||
_onListTabs: function WCCP__onListTabs(aResponse)
|
||||
{
|
||||
if (aResponse.error) {
|
||||
Cu.reportError("listTabs failed: " + aResponse.error + " " +
|
||||
aResponse.message);
|
||||
this._connectDefer.reject(aResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
this._attachTab(aResponse.tabs[aResponse.selected], aCallback);
|
||||
this._attachTab(aResponse.tabs[aResponse.selected]);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -4198,74 +4215,65 @@ WebConsoleConnectionProxy.prototype = {
|
||||
* @private
|
||||
* @param object aTab
|
||||
* Grip for the tab to attach to.
|
||||
* @param function aCallback
|
||||
* Function to invoke when the connection is established.
|
||||
*/
|
||||
_attachTab: function WCCP__attachTab(aTab, aCallback)
|
||||
_attachTab: function WCCP__attachTab(aTab)
|
||||
{
|
||||
this._consoleActor = aTab.consoleActor;
|
||||
this._tabActor = aTab.actor;
|
||||
this.owner.onLocationChange(aTab.url, aTab.title);
|
||||
this.client.attachTab(this._tabActor,
|
||||
this._onAttachTab.bind(this, aCallback));
|
||||
this.client.attachTab(this._tabActor, this._onAttachTab);
|
||||
},
|
||||
|
||||
/**
|
||||
* The "attachTab" response handler.
|
||||
*
|
||||
* @private
|
||||
* @param function [aCallback]
|
||||
* Optional function to invoke once the connection is established.
|
||||
* @param object aResponse
|
||||
* The JSON response object received from the server.
|
||||
* @param object aTabClient
|
||||
* The TabClient instance for the attached tab.
|
||||
*/
|
||||
_onAttachTab: function WCCP__onAttachTab(aCallback, aResponse, aTabClient)
|
||||
_onAttachTab: function WCCP__onAttachTab(aResponse, aTabClient)
|
||||
{
|
||||
if (aResponse.error) {
|
||||
Cu.reportError("attachTab failed: " + aResponse.error + " " +
|
||||
aResponse.message);
|
||||
this._connectDefer.reject(aResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
this.tabClient = aTabClient;
|
||||
this._attachConsole(aCallback);
|
||||
this._attachConsole();
|
||||
},
|
||||
|
||||
/**
|
||||
* Attach to the Web Console actor.
|
||||
*
|
||||
* @private
|
||||
* @param function aCallback
|
||||
* Function to invoke when the connection is established.
|
||||
*/
|
||||
_attachConsole: function WCCP__attachConsole(aCallback)
|
||||
_attachConsole: function WCCP__attachConsole()
|
||||
{
|
||||
let listeners = ["PageError", "ConsoleAPI", "NetworkActivity",
|
||||
"FileActivity"];
|
||||
this.client.attachConsole(this._consoleActor, listeners,
|
||||
this._onAttachConsole.bind(this, aCallback));
|
||||
this._onAttachConsole);
|
||||
},
|
||||
|
||||
/**
|
||||
* The "attachConsole" response handler.
|
||||
*
|
||||
* @private
|
||||
* @param function [aCallback]
|
||||
* Optional function to invoke once the connection is established.
|
||||
* @param object aResponse
|
||||
* The JSON response object received from the server.
|
||||
* @param object aWebConsoleClient
|
||||
* The WebConsoleClient instance for the attached console, for the
|
||||
* specific tab we work with.
|
||||
*/
|
||||
_onAttachConsole:
|
||||
function WCCP__onAttachConsole(aCallback, aResponse, aWebConsoleClient)
|
||||
_onAttachConsole: function WCCP__onAttachConsole(aResponse, aWebConsoleClient)
|
||||
{
|
||||
if (aResponse.error) {
|
||||
Cu.reportError("attachConsole failed: " + aResponse.error + " " +
|
||||
aResponse.message);
|
||||
this._connectDefer.reject(aResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -4274,27 +4282,31 @@ WebConsoleConnectionProxy.prototype = {
|
||||
this._hasNativeConsoleAPI = aResponse.nativeConsoleAPI;
|
||||
|
||||
let msgs = ["PageError", "ConsoleAPI"];
|
||||
this.webConsoleClient.getCachedMessages(msgs,
|
||||
this._onCachedMessages.bind(this, aCallback));
|
||||
this.webConsoleClient.getCachedMessages(msgs, this._onCachedMessages);
|
||||
},
|
||||
|
||||
/**
|
||||
* The "cachedMessages" response handler.
|
||||
*
|
||||
* @private
|
||||
* @param function [aCallback]
|
||||
* Optional function to invoke once the connection is established.
|
||||
* @param object aResponse
|
||||
* The JSON response object received from the server.
|
||||
*/
|
||||
_onCachedMessages: function WCCP__onCachedMessages(aCallback, aResponse)
|
||||
_onCachedMessages: function WCCP__onCachedMessages(aResponse)
|
||||
{
|
||||
if (aResponse.error) {
|
||||
Cu.reportError("Web Console getCachedMessages error: " + aResponse.error +
|
||||
" " + aResponse.message);
|
||||
this._connectDefer.reject(aResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._connectTimer) {
|
||||
// This happens if the Promise is rejected (eg. a timeout), but the
|
||||
// connection attempt is successful, nonetheless.
|
||||
Cu.reportError("Web Console getCachedMessages error: invalid state.");
|
||||
}
|
||||
|
||||
this.owner.displayCachedMessages(aResponse.messages);
|
||||
|
||||
if (!this._hasNativeConsoleAPI) {
|
||||
@ -4302,7 +4314,7 @@ WebConsoleConnectionProxy.prototype = {
|
||||
}
|
||||
|
||||
this.connected = true;
|
||||
aCallback && aCallback();
|
||||
this._connectDefer.resolve(this);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -4432,30 +4444,33 @@ WebConsoleConnectionProxy.prototype = {
|
||||
/**
|
||||
* Disconnect the Web Console from the remote server.
|
||||
*
|
||||
* @param function [aOnDisconnect]
|
||||
* Optional function to invoke when the connection is dropped.
|
||||
* @return object
|
||||
* A Promise object that is resolved when disconnect completes.
|
||||
*/
|
||||
disconnect: function WCCP_disconnect(aOnDisconnect)
|
||||
disconnect: function WCCP_disconnect()
|
||||
{
|
||||
if (this._disconnecter) {
|
||||
return this._disconnecter.promise;
|
||||
}
|
||||
|
||||
this._disconnecter = Promise.defer();
|
||||
|
||||
if (!this.client) {
|
||||
aOnDisconnect && aOnDisconnect();
|
||||
return;
|
||||
this._disconnecter.resolve(null);
|
||||
return this._disconnecter.promise;
|
||||
}
|
||||
|
||||
let onDisconnect = function() {
|
||||
if (timer) {
|
||||
timer.cancel();
|
||||
timer = null;
|
||||
this._disconnecter.resolve(null);
|
||||
}
|
||||
if (aOnDisconnect) {
|
||||
aOnDisconnect();
|
||||
aOnDisconnect = null;
|
||||
}
|
||||
};
|
||||
}.bind(this);
|
||||
|
||||
let timer = null;
|
||||
let remoteTarget = this.target.isRemote;
|
||||
if (aOnDisconnect && !remoteTarget) {
|
||||
if (!remoteTarget) {
|
||||
timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
timer.initWithCallback(onDisconnect, 1500, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
@ -4476,20 +4491,21 @@ WebConsoleConnectionProxy.prototype = {
|
||||
this.connected = false;
|
||||
this.owner = null;
|
||||
|
||||
try {
|
||||
if (!remoteTarget) {
|
||||
if (!remoteTarget) {
|
||||
try {
|
||||
client.close(onDisconnect);
|
||||
}
|
||||
catch (ex) {
|
||||
Cu.reportError("Web Console disconnect exception: " + ex);
|
||||
Cu.reportError(ex.stack);
|
||||
onDisconnect();
|
||||
}
|
||||
}
|
||||
catch (ex) {
|
||||
Cu.reportError("Web Console disconnect exception: " + ex);
|
||||
Cu.reportError(ex.stack);
|
||||
else {
|
||||
onDisconnect();
|
||||
}
|
||||
|
||||
if (remoteTarget) {
|
||||
onDisconnect();
|
||||
}
|
||||
return this._disconnecter.promise;
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -2297,6 +2297,10 @@ html|*#gcli-output-frame {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#developer-toolbar-toolbox-button > .toolbarbutton-icon {
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
||||
.developer-toolbar-button {
|
||||
-moz-appearance: none;
|
||||
min-width: 78px;
|
||||
|
@ -2981,6 +2981,10 @@ html|*#gcli-output-frame {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#developer-toolbar-toolbox-button > .toolbarbutton-icon {
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
||||
.developer-toolbar-button {
|
||||
-moz-appearance: none;
|
||||
min-width: 78px;
|
||||
|
@ -1,409 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009-2011 Mozilla Foundation and contributors
|
||||
* Licensed under the New BSD license. See LICENSE.txt or:
|
||||
* http://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "Promise" ];
|
||||
|
||||
/**
|
||||
* Create an unfulfilled promise
|
||||
*
|
||||
* @param {*=} aTrace A debugging value
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
this.Promise = function Promise(aTrace) {
|
||||
this._status = Promise.PENDING;
|
||||
this._value = undefined;
|
||||
this._onSuccessHandlers = [];
|
||||
this._onErrorHandlers = [];
|
||||
this._trace = aTrace;
|
||||
|
||||
// Debugging help
|
||||
if (Promise.Debug._debug) {
|
||||
this._id = Promise.Debug._nextId++;
|
||||
Promise.Debug._outstanding[this._id] = this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Debugging options and tools.
|
||||
*/
|
||||
Promise.Debug = {
|
||||
/**
|
||||
* Set current debugging mode.
|
||||
*
|
||||
* @param {boolean} value If |true|, maintain _nextId, _outstanding, _recent.
|
||||
* Otherwise, cleanup debugging data.
|
||||
*/
|
||||
setDebug: function(value) {
|
||||
Promise.Debug._debug = value;
|
||||
if (!value) {
|
||||
Promise.Debug._outstanding = [];
|
||||
Promise.Debug._recent = [];
|
||||
}
|
||||
},
|
||||
|
||||
_debug: false,
|
||||
|
||||
/**
|
||||
* We give promises and ID so we can track which are outstanding.
|
||||
*/
|
||||
_nextId: 0,
|
||||
|
||||
/**
|
||||
* Outstanding promises. Handy for debugging (only).
|
||||
*/
|
||||
_outstanding: [],
|
||||
|
||||
/**
|
||||
* Recently resolved promises. Also for debugging only.
|
||||
*/
|
||||
_recent: []
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A promise can be in one of 2 states.
|
||||
* The ERROR and SUCCESS states are terminal, the PENDING state is the only
|
||||
* start state.
|
||||
*/
|
||||
Promise.ERROR = -1;
|
||||
Promise.PENDING = 0;
|
||||
Promise.SUCCESS = 1;
|
||||
|
||||
/**
|
||||
* Yeay for RTTI
|
||||
*/
|
||||
Promise.prototype.isPromise = true;
|
||||
|
||||
/**
|
||||
* Have we either been resolve()ed or reject()ed?
|
||||
*/
|
||||
Promise.prototype.isComplete = function() {
|
||||
return this._status != Promise.PENDING;
|
||||
};
|
||||
|
||||
/**
|
||||
* Have we resolve()ed?
|
||||
*/
|
||||
Promise.prototype.isResolved = function() {
|
||||
return this._status == Promise.SUCCESS;
|
||||
};
|
||||
|
||||
/**
|
||||
* Have we reject()ed?
|
||||
*/
|
||||
Promise.prototype.isRejected = function() {
|
||||
return this._status == Promise.ERROR;
|
||||
};
|
||||
|
||||
/**
|
||||
* Take the specified action of fulfillment of a promise, and (optionally)
|
||||
* a different action on promise rejection
|
||||
*/
|
||||
Promise.prototype.then = function(onSuccess, onError) {
|
||||
if (typeof onSuccess === 'function') {
|
||||
if (this._status === Promise.SUCCESS) {
|
||||
onSuccess.call(null, this._value);
|
||||
}
|
||||
else if (this._status === Promise.PENDING) {
|
||||
this._onSuccessHandlers.push(onSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof onError === 'function') {
|
||||
if (this._status === Promise.ERROR) {
|
||||
onError.call(null, this._value);
|
||||
}
|
||||
else if (this._status === Promise.PENDING) {
|
||||
this._onErrorHandlers.push(onError);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Like then() except that rather than returning <tt>this</tt> we return
|
||||
* a promise which resolves when the original promise resolves
|
||||
*/
|
||||
Promise.prototype.chainPromise = function(onSuccess) {
|
||||
var chain = new Promise();
|
||||
chain._chainedFrom = this;
|
||||
this.then(function(data) {
|
||||
try {
|
||||
chain.resolve(onSuccess(data));
|
||||
}
|
||||
catch (ex) {
|
||||
chain.reject(ex);
|
||||
}
|
||||
}, function(ex) {
|
||||
chain.reject(ex);
|
||||
});
|
||||
return chain;
|
||||
};
|
||||
|
||||
/**
|
||||
* Supply the fulfillment of a promise
|
||||
*/
|
||||
Promise.prototype.resolve = function(data) {
|
||||
return this._complete(this._onSuccessHandlers,
|
||||
Promise.SUCCESS, data, 'resolve');
|
||||
};
|
||||
|
||||
/**
|
||||
* Renege on a promise
|
||||
*/
|
||||
Promise.prototype.reject = function(data) {
|
||||
return this._complete(this._onErrorHandlers, Promise.ERROR, data, 'reject');
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal method to be called on resolve() or reject()
|
||||
* @private
|
||||
*/
|
||||
Promise.prototype._complete = function(list, status, data, name) {
|
||||
// Complain if we've already been completed
|
||||
if (this._status != Promise.PENDING) {
|
||||
Promise._error("Promise complete.", "Attempted ", name, "() with ", data);
|
||||
Promise._error("Previous status: ", this._status, ", value =", this._value);
|
||||
throw new Error('Promise already complete');
|
||||
}
|
||||
|
||||
if (list.length == 0 && status == Promise.ERROR) {
|
||||
var frame;
|
||||
var text;
|
||||
|
||||
//Complain if a rejection is ignored
|
||||
//(this is the equivalent of an empty catch-all clause)
|
||||
Promise._error("Promise rejection ignored and silently dropped", data);
|
||||
if (data.stack) {// This looks like an exception. Try harder to display it
|
||||
if (data.fileName && data.lineNumber) {
|
||||
Promise._error("Error originating at", data.fileName,
|
||||
", line", data.lineNumber );
|
||||
}
|
||||
try {
|
||||
for (frame = data.stack; frame; frame = frame.caller) {
|
||||
text += frame + "\n";
|
||||
}
|
||||
Promise._error("Attempting to extract exception stack", text);
|
||||
} catch (x) {
|
||||
Promise._error("Could not extract exception stack.");
|
||||
}
|
||||
} else {
|
||||
Promise._error("Exception stack not available.");
|
||||
}
|
||||
if (Components && Components.stack) {
|
||||
try {
|
||||
text = "";
|
||||
for (frame = Components.stack; frame; frame = frame.caller) {
|
||||
text += frame + "\n";
|
||||
}
|
||||
Promise._error("Attempting to extract current stack", text);
|
||||
} catch (x) {
|
||||
Promise._error("Could not extract current stack.");
|
||||
}
|
||||
} else {
|
||||
Promise._error("Current stack not available.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this._status = status;
|
||||
this._value = data;
|
||||
|
||||
// Call all the handlers, and then delete them
|
||||
list.forEach(function(handler) {
|
||||
handler.call(null, this._value);
|
||||
}, this);
|
||||
delete this._onSuccessHandlers;
|
||||
delete this._onErrorHandlers;
|
||||
|
||||
// Remove the given {promise} from the _outstanding list, and add it to the
|
||||
// _recent list, pruning more than 20 recent promises from that list
|
||||
delete Promise.Debug._outstanding[this._id];
|
||||
// The original code includes this very useful debugging aid, however there
|
||||
// is concern that it will create a memory leak, so we leave it out here.
|
||||
/*
|
||||
Promise._recent.push(this);
|
||||
while (Promise._recent.length > 20) {
|
||||
Promise._recent.shift();
|
||||
}
|
||||
*/
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Log an error on the most appropriate channel.
|
||||
*
|
||||
* If the console is available, this method uses |console.warn|. Otherwise,
|
||||
* this method falls back to |dump|.
|
||||
*
|
||||
* @param {...*} items Items to log.
|
||||
*/
|
||||
Promise._error = null;
|
||||
if (typeof console != "undefined" && console.warn) {
|
||||
Promise._error = function() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
args.unshift("Promise");
|
||||
console.warn.call(console, args);
|
||||
};
|
||||
} else {
|
||||
Promise._error = function() {
|
||||
var i;
|
||||
var len = arguments.length;
|
||||
dump("Promise: ");
|
||||
for (i = 0; i < len; ++i) {
|
||||
dump(arguments[i]+" ");
|
||||
}
|
||||
dump("\n");
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an array of promises and returns a promise that that is fulfilled once
|
||||
* all the promises in the array are fulfilled
|
||||
* @param promiseList The array of promises
|
||||
* @return the promise that is fulfilled when all the array is fulfilled
|
||||
*/
|
||||
Promise.group = function(promiseList) {
|
||||
if (!Array.isArray(promiseList)) {
|
||||
promiseList = Array.prototype.slice.call(arguments);
|
||||
}
|
||||
|
||||
// If the original array has nothing in it, return now to avoid waiting
|
||||
if (promiseList.length === 0) {
|
||||
return new Promise().resolve([]);
|
||||
}
|
||||
|
||||
var groupPromise = new Promise();
|
||||
var results = [];
|
||||
var fulfilled = 0;
|
||||
|
||||
var onSuccessFactory = function(index) {
|
||||
return function(data) {
|
||||
results[index] = data;
|
||||
fulfilled++;
|
||||
// If the group has already failed, silently drop extra results
|
||||
if (groupPromise._status !== Promise.ERROR) {
|
||||
if (fulfilled === promiseList.length) {
|
||||
groupPromise.resolve(results);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
promiseList.forEach(function(promise, index) {
|
||||
var onSuccess = onSuccessFactory(index);
|
||||
var onError = groupPromise.reject.bind(groupPromise);
|
||||
promise.then(onSuccess, onError);
|
||||
});
|
||||
|
||||
return groupPromise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Trap errors.
|
||||
*
|
||||
* This function serves as an asynchronous counterpart to |catch|.
|
||||
*
|
||||
* Example:
|
||||
* myPromise.chainPromise(a) //May reject
|
||||
* .chainPromise(b) //May reject
|
||||
* .chainPromise(c) //May reject
|
||||
* .trap(d) //Catch any rejection from a, b or c
|
||||
* .chainPromise(e) //If either a, b and c or
|
||||
* //d has resolved, execute
|
||||
*
|
||||
* Scenario 1:
|
||||
* If a, b, c resolve, e is executed as if d had not been added.
|
||||
*
|
||||
* Scenario 2:
|
||||
* If a, b or c rejects, d is executed. If d resolves, we proceed
|
||||
* with e as if nothing had happened. Otherwise, we proceed with
|
||||
* the rejection of d.
|
||||
*
|
||||
* @param {Function} aTrap Called if |this| promise is rejected,
|
||||
* with one argument: the rejection.
|
||||
* @return {Promise} A new promise. This promise resolves if all
|
||||
* previous promises have resolved or if |aTrap| succeeds.
|
||||
*/
|
||||
Promise.prototype.trap = function(aTrap) {
|
||||
var promise = new Promise();
|
||||
var resolve = Promise.prototype.resolve.bind(promise);
|
||||
var reject = function(aRejection) {
|
||||
try {
|
||||
//Attempt to handle issue
|
||||
var result = aTrap.call(aTrap, aRejection);
|
||||
promise.resolve(result);
|
||||
} catch (x) {
|
||||
promise.reject(x);
|
||||
}
|
||||
};
|
||||
this.then(resolve, reject);
|
||||
return promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute regardless of errors.
|
||||
*
|
||||
* This function serves as an asynchronous counterpart to |finally|.
|
||||
*
|
||||
* Example:
|
||||
* myPromise.chainPromise(a) //May reject
|
||||
* .chainPromise(b) //May reject
|
||||
* .chainPromise(c) //May reject
|
||||
* .always(d) //Executed regardless
|
||||
* .chainPromise(e)
|
||||
*
|
||||
* Whether |a|, |b| or |c| resolve or reject, |d| is executed.
|
||||
*
|
||||
* @param {Function} aTrap Called regardless of whether |this|
|
||||
* succeeds or fails.
|
||||
* @return {Promise} A new promise. This promise holds the same
|
||||
* resolution/rejection as |this|.
|
||||
*/
|
||||
Promise.prototype.always = function(aTrap) {
|
||||
var promise = new Promise();
|
||||
var resolve = function(result) {
|
||||
try {
|
||||
aTrap.call(aTrap);
|
||||
promise.resolve(result);
|
||||
} catch (x) {
|
||||
promise.reject(x);
|
||||
}
|
||||
};
|
||||
var reject = function(result) {
|
||||
try {
|
||||
aTrap.call(aTrap);
|
||||
promise.reject(result);
|
||||
} catch (x) {
|
||||
promise.reject(result);
|
||||
}
|
||||
};
|
||||
this.then(resolve, reject);
|
||||
return promise;
|
||||
};
|
||||
|
||||
|
||||
Promise.prototype.toString = function() {
|
||||
var status;
|
||||
switch (this._status) {
|
||||
case Promise.PENDING:
|
||||
status = "pending";
|
||||
break;
|
||||
case Promise.SUCCESS:
|
||||
status = "resolved";
|
||||
break;
|
||||
case Promise.ERROR:
|
||||
status = "rejected";
|
||||
break;
|
||||
default:
|
||||
status = "invalid status: "+this._status;
|
||||
}
|
||||
return "[Promise " + this._id + " (" + status + ")]";
|
||||
};
|
@ -1346,27 +1346,23 @@ SourceActor.prototype = {
|
||||
* Handler for the "source" packet.
|
||||
*/
|
||||
onSource: function SA_onSource(aRequest) {
|
||||
this
|
||||
return this
|
||||
._loadSource()
|
||||
.chainPromise(function(aSource) {
|
||||
.then(function(aSource) {
|
||||
return this._threadActor.createValueGrip(
|
||||
aSource, this.threadActor.threadLifetimePool);
|
||||
}.bind(this))
|
||||
.chainPromise(function (aSourceGrip) {
|
||||
.then(function (aSourceGrip) {
|
||||
return {
|
||||
from: this.actorID,
|
||||
source: aSourceGrip
|
||||
};
|
||||
}.bind(this))
|
||||
.trap(function (aError) {
|
||||
}.bind(this), function (aError) {
|
||||
return {
|
||||
"from": this.actorID,
|
||||
"error": "loadSourceError",
|
||||
"message": "Could not load the source for " + this._script.url + "."
|
||||
};
|
||||
}.bind(this))
|
||||
.chainPromise(function (aPacket) {
|
||||
this.conn.send(aPacket);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
@ -1404,7 +1400,7 @@ SourceActor.prototype = {
|
||||
* http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/
|
||||
*/
|
||||
_loadSource: function SA__loadSource() {
|
||||
let promise = new Promise();
|
||||
let deferred = defer();
|
||||
let url = this._script.url;
|
||||
let scheme;
|
||||
try {
|
||||
@ -1424,16 +1420,16 @@ SourceActor.prototype = {
|
||||
try {
|
||||
NetUtil.asyncFetch(url, function onFetch(aStream, aStatus) {
|
||||
if (!Components.isSuccessCode(aStatus)) {
|
||||
promise.reject(new Error("Request failed"));
|
||||
deferred.reject(new Error("Request failed"));
|
||||
return;
|
||||
}
|
||||
|
||||
let source = NetUtil.readInputStreamToString(aStream, aStream.available());
|
||||
promise.resolve(this._convertToUnicode(source));
|
||||
deferred.resolve(this._convertToUnicode(source));
|
||||
aStream.close();
|
||||
}.bind(this));
|
||||
} catch (ex) {
|
||||
promise.reject(new Error("Request failed"));
|
||||
deferred.reject(new Error("Request failed"));
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1451,7 +1447,7 @@ SourceActor.prototype = {
|
||||
let streamListener = {
|
||||
onStartRequest: function(aRequest, aContext, aStatusCode) {
|
||||
if (!Components.isSuccessCode(aStatusCode)) {
|
||||
promise.reject("Request failed");
|
||||
deferred.reject("Request failed");
|
||||
}
|
||||
},
|
||||
onDataAvailable: function(aRequest, aContext, aStream, aOffset, aCount) {
|
||||
@ -1459,12 +1455,12 @@ SourceActor.prototype = {
|
||||
},
|
||||
onStopRequest: function(aRequest, aContext, aStatusCode) {
|
||||
if (!Components.isSuccessCode(aStatusCode)) {
|
||||
promise.reject("Request failed");
|
||||
deferred.reject("Request failed");
|
||||
return;
|
||||
}
|
||||
|
||||
promise.resolve(this._convertToUnicode(chunks.join(""),
|
||||
channel.contentCharset));
|
||||
deferred.resolve(this._convertToUnicode(chunks.join(""),
|
||||
channel.contentCharset));
|
||||
}.bind(this)
|
||||
};
|
||||
|
||||
@ -1473,7 +1469,7 @@ SourceActor.prototype = {
|
||||
break;
|
||||
}
|
||||
|
||||
return promise;
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -24,7 +24,8 @@ let wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
|
||||
Cu.import("resource://gre/modules/jsdebugger.jsm");
|
||||
addDebuggerToGlobal(this);
|
||||
|
||||
Cu.import("resource://gre/modules/devtools/_Promise.jsm");
|
||||
Cu.import("resource://gre/modules/commonjs/promise/core.js");
|
||||
const { defer, resolve, reject } = Promise;
|
||||
|
||||
function dumpn(str) {
|
||||
if (wantLogging) {
|
||||
@ -664,16 +665,17 @@ DebuggerServerConnection.prototype = {
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
// XXX: The actor wasn't ready to reply yet, don't process new
|
||||
// requests until it does.
|
||||
// This should become an error once we've converted every user
|
||||
// of this to promises in bug 794078.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ret.from) {
|
||||
ret.from = aPacket.to;
|
||||
}
|
||||
|
||||
this.transport.send(ret);
|
||||
resolve(ret).then(function(returnPacket) {
|
||||
if (!returnPacket.from) {
|
||||
returnPacket.from = aPacket.to;
|
||||
}
|
||||
this.transport.send(returnPacket);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -183,7 +183,7 @@ WebConsoleActor.prototype =
|
||||
this.consoleProgressListener.destroy();
|
||||
this.consoleProgressListener = null;
|
||||
}
|
||||
this.conn.removeActorPool(this.actorPool);
|
||||
this.conn.removeActorPool(this._actorPool);
|
||||
this._actorPool = null;
|
||||
this.sandbox = null;
|
||||
this._sandboxWindowId = 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user