mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to inbound.
This commit is contained in:
commit
2d3527097e
@ -1058,6 +1058,7 @@ pref("devtools.toolbox.sideEnabled", true);
|
|||||||
pref("devtools.inspector.enabled", true);
|
pref("devtools.inspector.enabled", true);
|
||||||
pref("devtools.inspector.activeSidebar", "ruleview");
|
pref("devtools.inspector.activeSidebar", "ruleview");
|
||||||
pref("devtools.inspector.markupPreview", false);
|
pref("devtools.inspector.markupPreview", false);
|
||||||
|
pref("devtools.inspector.remote", false);
|
||||||
|
|
||||||
// Enable the Layout View
|
// Enable the Layout View
|
||||||
pref("devtools.layoutview.enabled", true);
|
pref("devtools.layoutview.enabled", true);
|
||||||
|
@ -54,6 +54,7 @@ FontInspector.prototype = {
|
|||||||
*/
|
*/
|
||||||
onNewNode: function FI_onNewNode() {
|
onNewNode: function FI_onNewNode() {
|
||||||
if (this.isActive() &&
|
if (this.isActive() &&
|
||||||
|
this.inspector.selection.isLocal() &&
|
||||||
this.inspector.selection.isConnected() &&
|
this.inspector.selection.isConnected() &&
|
||||||
this.inspector.selection.isElementNode() &&
|
this.inspector.selection.isElementNode() &&
|
||||||
this.inspector.selection.reason != "highlighter") {
|
this.inspector.selection.reason != "highlighter") {
|
||||||
|
@ -148,6 +148,11 @@ ToolSidebar.prototype = {
|
|||||||
*/
|
*/
|
||||||
handleEvent: function ToolSidebar_eventHandler(event) {
|
handleEvent: function ToolSidebar_eventHandler(event) {
|
||||||
if (event.type == "select") {
|
if (event.type == "select") {
|
||||||
|
if (this._currentTool == this.getCurrentTabID()) {
|
||||||
|
// Tool hasn't changed.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let previousTool = this._currentTool;
|
let previousTool = this._currentTool;
|
||||||
this._currentTool = this.getCurrentTabID();
|
this._currentTool = this.getCurrentTabID();
|
||||||
if (previousTool) {
|
if (previousTool) {
|
||||||
|
@ -15,6 +15,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "DebuggerServer",
|
|||||||
XPCOMUtils.defineLazyModuleGetter(this, "DebuggerClient",
|
XPCOMUtils.defineLazyModuleGetter(this, "DebuggerClient",
|
||||||
"resource://gre/modules/devtools/dbg-client.jsm");
|
"resource://gre/modules/devtools/dbg-client.jsm");
|
||||||
|
|
||||||
|
loader.lazyGetter(this, "InspectorFront", () => require("devtools/server/actors/inspector").InspectorFront);
|
||||||
|
|
||||||
const targets = new WeakMap();
|
const targets = new WeakMap();
|
||||||
const promiseTargets = new WeakMap();
|
const promiseTargets = new WeakMap();
|
||||||
|
|
||||||
@ -250,6 +252,17 @@ TabTarget.prototype = {
|
|||||||
return !!this._isThreadPaused;
|
return !!this._isThreadPaused;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get inspector() {
|
||||||
|
if (!this.form) {
|
||||||
|
throw new Error("Target.inspector requires an initialized remote actor.");
|
||||||
|
}
|
||||||
|
if (this._inspector) {
|
||||||
|
return this._inspector;
|
||||||
|
}
|
||||||
|
this._inspector = InspectorFront(this.client, this.form);
|
||||||
|
return this._inspector;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds remote protocol capabilities to the target, so that it can be used
|
* Adds remote protocol capabilities to the target, so that it can be used
|
||||||
* for tools that support the Remote Debugging Protocol even for local
|
* for tools that support the Remote Debugging Protocol even for local
|
||||||
|
@ -12,6 +12,9 @@ const ENSURE_SELECTION_VISIBLE_DELAY = 50; // ms
|
|||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
Cu.import("resource:///modules/devtools/DOMHelpers.jsm");
|
Cu.import("resource:///modules/devtools/DOMHelpers.jsm");
|
||||||
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
|
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
|
||||||
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
|
let promise = require("sdk/core/promise");
|
||||||
|
|
||||||
const LOW_PRIORITY_ELEMENTS = {
|
const LOW_PRIORITY_ELEMENTS = {
|
||||||
"HEAD": true,
|
"HEAD": true,
|
||||||
@ -25,6 +28,18 @@ const LOW_PRIORITY_ELEMENTS = {
|
|||||||
"TITLE": true,
|
"TITLE": true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function resolveNextTick(value) {
|
||||||
|
let deferred = promise.defer();
|
||||||
|
Services.tm.mainThread.dispatch(() => {
|
||||||
|
try {
|
||||||
|
deferred.resolve(value);
|
||||||
|
} catch(ex) {
|
||||||
|
console.error(ex);
|
||||||
|
}
|
||||||
|
}, Ci.nsIThread.DISPATCH_NORMAL);
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
//// HTML Breadcrumbs
|
//// HTML Breadcrumbs
|
||||||
|
|
||||||
@ -53,6 +68,8 @@ function HTMLBreadcrumbs(aInspector)
|
|||||||
exports.HTMLBreadcrumbs = HTMLBreadcrumbs;
|
exports.HTMLBreadcrumbs = HTMLBreadcrumbs;
|
||||||
|
|
||||||
HTMLBreadcrumbs.prototype = {
|
HTMLBreadcrumbs.prototype = {
|
||||||
|
get walker() this.inspector.walker,
|
||||||
|
|
||||||
_init: function BC__init()
|
_init: function BC__init()
|
||||||
{
|
{
|
||||||
this.container = this.chromeDoc.getElementById("inspector-breadcrumbs");
|
this.container = this.chromeDoc.getElementById("inspector-breadcrumbs");
|
||||||
@ -83,12 +100,37 @@ HTMLBreadcrumbs.prototype = {
|
|||||||
|
|
||||||
this.update = this.update.bind(this);
|
this.update = this.update.bind(this);
|
||||||
this.updateSelectors = this.updateSelectors.bind(this);
|
this.updateSelectors = this.updateSelectors.bind(this);
|
||||||
this.selection.on("new-node", this.update);
|
this.selection.on("new-node-front", this.update);
|
||||||
this.selection.on("pseudoclass", this.updateSelectors);
|
this.selection.on("pseudoclass", this.updateSelectors);
|
||||||
this.selection.on("attribute-changed", this.updateSelectors);
|
this.selection.on("attribute-changed", this.updateSelectors);
|
||||||
this.update();
|
this.update();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Include in a promise's then() chain to reject the chain
|
||||||
|
* when the breadcrumbs' selection has changed while the promise
|
||||||
|
* was outstanding.
|
||||||
|
*/
|
||||||
|
selectionGuard: function() {
|
||||||
|
let selection = this.selection.nodeFront;
|
||||||
|
return (result) => {
|
||||||
|
if (selection != this.selection.nodeFront) {
|
||||||
|
return promise.reject("selection-changed");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print any errors (except selection guard errors).
|
||||||
|
*/
|
||||||
|
selectionGuardEnd: function(err) {
|
||||||
|
if (err != "selection-changed") {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
promise.reject(err);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a string that represents the node: tagName#id.class1.class2.
|
* Build a string that represents the node: tagName#id.class1.class2.
|
||||||
*
|
*
|
||||||
@ -101,12 +143,19 @@ HTMLBreadcrumbs.prototype = {
|
|||||||
if (aNode.id) {
|
if (aNode.id) {
|
||||||
text += "#" + aNode.id;
|
text += "#" + aNode.id;
|
||||||
}
|
}
|
||||||
for (let i = 0; i < aNode.classList.length; i++) {
|
|
||||||
text += "." + aNode.classList[i];
|
if (aNode.className) {
|
||||||
|
let classList = aNode.className.split(/\s+/);
|
||||||
|
for (let i = 0; i < classList.length; i++) {
|
||||||
|
text += "." + classList[i];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX: needs updating when pseudoclass-lock is remotable
|
||||||
|
let rawNode = aNode.rawNode();
|
||||||
for (let i = 0; i < PSEUDO_CLASSES.length; i++) {
|
for (let i = 0; i < PSEUDO_CLASSES.length; i++) {
|
||||||
let pseudo = PSEUDO_CLASSES[i];
|
let pseudo = PSEUDO_CLASSES[i];
|
||||||
if (DOMUtils.hasPseudoClassLock(aNode, pseudo)) {
|
if (DOMUtils.hasPseudoClassLock(rawNode, pseudo)) {
|
||||||
text += pseudo;
|
text += pseudo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,14 +192,20 @@ HTMLBreadcrumbs.prototype = {
|
|||||||
tagLabel.textContent = aNode.tagName.toLowerCase();
|
tagLabel.textContent = aNode.tagName.toLowerCase();
|
||||||
idLabel.textContent = aNode.id ? ("#" + aNode.id) : "";
|
idLabel.textContent = aNode.id ? ("#" + aNode.id) : "";
|
||||||
|
|
||||||
|
if (aNode.className) {
|
||||||
let classesText = "";
|
let classesText = "";
|
||||||
for (let i = 0; i < aNode.classList.length; i++) {
|
let classList = aNode.className.split(/\s+/);
|
||||||
classesText += "." + aNode.classList[i];
|
for (let i = 0; i < classList.length; i++) {
|
||||||
|
classesText += "." + classList[i];
|
||||||
}
|
}
|
||||||
classesLabel.textContent = classesText;
|
classesLabel.textContent = classesText;
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX: Until we have pseudoclass lock in the node.
|
||||||
|
let rawNode = aNode.rawNode();
|
||||||
|
|
||||||
let pseudos = PSEUDO_CLASSES.filter(function(pseudo) {
|
let pseudos = PSEUDO_CLASSES.filter(function(pseudo) {
|
||||||
return DOMUtils.hasPseudoClassLock(aNode, pseudo);
|
return DOMUtils.hasPseudoClassLock(rawNode, pseudo);
|
||||||
}, this);
|
}, this);
|
||||||
pseudosLabel.textContent = pseudos.join("");
|
pseudosLabel.textContent = pseudos.join("");
|
||||||
|
|
||||||
@ -173,7 +228,7 @@ HTMLBreadcrumbs.prototype = {
|
|||||||
// We make sure that the targeted node is selected
|
// We make sure that the targeted node is selected
|
||||||
// because we want to use the nodemenu that only works
|
// because we want to use the nodemenu that only works
|
||||||
// for inspector.selection
|
// for inspector.selection
|
||||||
this.selection.setNode(aNode, "breadcrumbs");
|
this.selection.setNodeFront(aNode, "breadcrumbs");
|
||||||
|
|
||||||
let title = this.chromeDoc.createElement("menuitem");
|
let title = this.chromeDoc.createElement("menuitem");
|
||||||
title.setAttribute("label", this.inspector.strings.GetStringFromName("breadcrumbs.siblings"));
|
title.setAttribute("label", this.inspector.strings.GetStringFromName("breadcrumbs.siblings"));
|
||||||
@ -183,9 +238,11 @@ HTMLBreadcrumbs.prototype = {
|
|||||||
|
|
||||||
let items = [title, separator];
|
let items = [title, separator];
|
||||||
|
|
||||||
let nodes = aNode.parentNode.childNodes;
|
this.walker.siblings(aNode, {
|
||||||
|
whatToShow: Ci.nsIDOMNodeFilter.SHOW_ELEMENT
|
||||||
|
}).then(siblings => {
|
||||||
|
let nodes = siblings.nodes;
|
||||||
for (let i = 0; i < nodes.length; i++) {
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
if (nodes[i].nodeType == aNode.ELEMENT_NODE) {
|
|
||||||
let item = this.chromeDoc.createElement("menuitem");
|
let item = this.chromeDoc.createElement("menuitem");
|
||||||
if (nodes[i] === aNode) {
|
if (nodes[i] === aNode) {
|
||||||
item.setAttribute("disabled", "true");
|
item.setAttribute("disabled", "true");
|
||||||
@ -198,14 +255,14 @@ HTMLBreadcrumbs.prototype = {
|
|||||||
let selection = this.selection;
|
let selection = this.selection;
|
||||||
item.onmouseup = (function(aNode) {
|
item.onmouseup = (function(aNode) {
|
||||||
return function() {
|
return function() {
|
||||||
selection.setNode(aNode, "breadcrumbs");
|
selection.setNodeFront(aNode, "breadcrumbs");
|
||||||
}
|
}
|
||||||
})(nodes[i]);
|
})(nodes[i]);
|
||||||
|
|
||||||
items.push(item);
|
items.push(item);
|
||||||
}
|
|
||||||
}
|
|
||||||
this.inspector.showNodeMenu(aButton, "before_start", items);
|
this.inspector.showNodeMenu(aButton, "before_start", items);
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -252,33 +309,40 @@ HTMLBreadcrumbs.prototype = {
|
|||||||
|
|
||||||
if (event.type == "keypress" && this.selection.isElementNode()) {
|
if (event.type == "keypress" && this.selection.isElementNode()) {
|
||||||
let node = null;
|
let node = null;
|
||||||
|
|
||||||
|
|
||||||
|
this._keyPromise = this._keyPromise || promise.resolve(null);
|
||||||
|
|
||||||
|
this._keyPromise = (this._keyPromise || promise.resolve(null)).then(() => {
|
||||||
switch (event.keyCode) {
|
switch (event.keyCode) {
|
||||||
case this.chromeWin.KeyEvent.DOM_VK_LEFT:
|
case this.chromeWin.KeyEvent.DOM_VK_LEFT:
|
||||||
if (this.currentIndex != 0) {
|
if (this.currentIndex != 0) {
|
||||||
node = this.nodeHierarchy[this.currentIndex - 1].node;
|
node = promise.resolve(this.nodeHierarchy[this.currentIndex - 1].node);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case this.chromeWin.KeyEvent.DOM_VK_RIGHT:
|
case this.chromeWin.KeyEvent.DOM_VK_RIGHT:
|
||||||
if (this.currentIndex < this.nodeHierarchy.length - 1) {
|
if (this.currentIndex < this.nodeHierarchy.length - 1) {
|
||||||
node = this.nodeHierarchy[this.currentIndex + 1].node;
|
node = promise.resolve(this.nodeHierarchy[this.currentIndex + 1].node);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case this.chromeWin.KeyEvent.DOM_VK_UP:
|
case this.chromeWin.KeyEvent.DOM_VK_UP:
|
||||||
node = this.selection.node.previousSibling;
|
node = this.walker.previousSibling(this.selection.nodeFront, {
|
||||||
while (node && (node.nodeType != node.ELEMENT_NODE)) {
|
whatToShow: Ci.nsIDOMNodeFilter.SHOW_ELEMENT
|
||||||
node = node.previousSibling;
|
});
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case this.chromeWin.KeyEvent.DOM_VK_DOWN:
|
case this.chromeWin.KeyEvent.DOM_VK_DOWN:
|
||||||
node = this.selection.node.nextSibling;
|
node = this.walker.nextSibling(this.selection.nodeFront, {
|
||||||
while (node && (node.nodeType != node.ELEMENT_NODE)) {
|
whatToShow: Ci.nsIDOMNodeFilter.SHOW_ELEMENT
|
||||||
node = node.nextSibling;
|
});
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return node.then((node) => {
|
||||||
if (node) {
|
if (node) {
|
||||||
this.selection.setNode(node, "breadcrumbs");
|
this.selection.setNodeFront(node, "breadcrumbs");
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
@ -290,12 +354,18 @@ HTMLBreadcrumbs.prototype = {
|
|||||||
destroy: function BC_destroy()
|
destroy: function BC_destroy()
|
||||||
{
|
{
|
||||||
this.nodeHierarchy.forEach(function(crumb) {
|
this.nodeHierarchy.forEach(function(crumb) {
|
||||||
if (LayoutHelpers.isNodeConnected(crumb.node)) {
|
// This node might have already been destroyed during
|
||||||
DOMUtils.clearPseudoClassLocks(crumb.node);
|
// shutdown. Will clean this up when pseudo-class lock
|
||||||
|
// is ported to the walker.
|
||||||
|
if (crumb.node.actorID) {
|
||||||
|
let rawNode = crumb.node.rawNode();
|
||||||
|
if (LayoutHelpers.isNodeConnected(rawNode)) {
|
||||||
|
DOMUtils.clearPseudoClassLocks(rawNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.selection.off("new-node", this.update);
|
this.selection.off("new-node-front", this.update);
|
||||||
this.selection.off("pseudoclass", this.updateSelectors);
|
this.selection.off("pseudoclass", this.updateSelectors);
|
||||||
this.selection.off("attribute-changed", this.updateSelectors);
|
this.selection.off("attribute-changed", this.updateSelectors);
|
||||||
|
|
||||||
@ -401,7 +471,7 @@ HTMLBreadcrumbs.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
button.onBreadcrumbsClick = function onBreadcrumbsClick() {
|
button.onBreadcrumbsClick = function onBreadcrumbsClick() {
|
||||||
this.selection.setNode(aNode, "breadcrumbs");
|
this.selection.setNodeFront(aNode, "breadcrumbs");
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
|
|
||||||
button.onclick = (function _onBreadcrumbsRightClick(event) {
|
button.onclick = (function _onBreadcrumbsRightClick(event) {
|
||||||
@ -437,7 +507,7 @@ HTMLBreadcrumbs.prototype = {
|
|||||||
fragment.insertBefore(button, lastButtonInserted);
|
fragment.insertBefore(button, lastButtonInserted);
|
||||||
lastButtonInserted = button;
|
lastButtonInserted = button;
|
||||||
this.nodeHierarchy.splice(originalLength, 0, {node: toAppend, button: button});
|
this.nodeHierarchy.splice(originalLength, 0, {node: toAppend, button: button});
|
||||||
toAppend = this.DOMHelpers.getParentObject(toAppend);
|
toAppend = toAppend.parentNode();
|
||||||
}
|
}
|
||||||
this.container.appendChild(fragment, this.container.firstChild);
|
this.container.appendChild(fragment, this.container.firstChild);
|
||||||
},
|
},
|
||||||
@ -451,24 +521,37 @@ HTMLBreadcrumbs.prototype = {
|
|||||||
*/
|
*/
|
||||||
getInterestingFirstNode: function BC_getInterestingFirstNode(aNode)
|
getInterestingFirstNode: function BC_getInterestingFirstNode(aNode)
|
||||||
{
|
{
|
||||||
let nextChild = this.DOMHelpers.getChildObject(aNode, 0);
|
let deferred = promise.defer();
|
||||||
let fallback = null;
|
|
||||||
|
|
||||||
while (nextChild) {
|
var fallback = null;
|
||||||
if (nextChild.nodeType == aNode.ELEMENT_NODE) {
|
|
||||||
if (!(nextChild.tagName in LOW_PRIORITY_ELEMENTS)) {
|
var moreChildren = () => {
|
||||||
return nextChild;
|
this.walker.children(aNode, {
|
||||||
|
start: fallback,
|
||||||
|
maxNodes: 10,
|
||||||
|
whatToShow: Ci.nsIDOMNodeFilter.SHOW_ELEMENT
|
||||||
|
}).then(this.selectionGuard()).then(response => {
|
||||||
|
for (let node of response.nodes) {
|
||||||
|
if (!(node.tagName in LOW_PRIORITY_ELEMENTS)) {
|
||||||
|
deferred.resolve(node);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (!fallback) {
|
if (!fallback) {
|
||||||
fallback = nextChild;
|
fallback = node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nextChild = this.DOMHelpers.getNextSibling(nextChild);
|
if (response.hasLast) {
|
||||||
|
deferred.resolve(fallback);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
moreChildren();
|
||||||
}
|
}
|
||||||
return fallback;
|
}).then(null, this.selectionGuardEnd);
|
||||||
|
}
|
||||||
|
moreChildren();
|
||||||
|
return deferred.promise;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the "youngest" ancestor of a node which is already in the breadcrumbs.
|
* Find the "youngest" ancestor of a node which is already in the breadcrumbs.
|
||||||
*
|
*
|
||||||
@ -483,7 +566,7 @@ HTMLBreadcrumbs.prototype = {
|
|||||||
if (idx > -1) {
|
if (idx > -1) {
|
||||||
return idx;
|
return idx;
|
||||||
} else {
|
} else {
|
||||||
node = this.DOMHelpers.getParentObject(node);
|
node = node.parentNode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
@ -498,13 +581,16 @@ HTMLBreadcrumbs.prototype = {
|
|||||||
// If the last displayed node is the selected node
|
// If the last displayed node is the selected node
|
||||||
if (this.currentIndex == this.nodeHierarchy.length - 1) {
|
if (this.currentIndex == this.nodeHierarchy.length - 1) {
|
||||||
let node = this.nodeHierarchy[this.currentIndex].node;
|
let node = this.nodeHierarchy[this.currentIndex].node;
|
||||||
let child = this.getInterestingFirstNode(node);
|
return this.getInterestingFirstNode(node).then(child => {
|
||||||
// If the node has a child
|
// If the node has a child
|
||||||
if (child) {
|
if (child) {
|
||||||
// Show this child
|
// Show this child
|
||||||
this.expand(child);
|
this.expand(child);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return resolveNextTick(true);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -560,7 +646,7 @@ HTMLBreadcrumbs.prototype = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let idx = this.indexOf(this.selection.node);
|
let idx = this.indexOf(this.selection.nodeFront);
|
||||||
|
|
||||||
// Is the node already displayed in the breadcrumbs?
|
// Is the node already displayed in the breadcrumbs?
|
||||||
if (idx > -1) {
|
if (idx > -1) {
|
||||||
@ -571,24 +657,32 @@ HTMLBreadcrumbs.prototype = {
|
|||||||
if (this.nodeHierarchy.length > 0) {
|
if (this.nodeHierarchy.length > 0) {
|
||||||
// No. We drop all the element that are not direct ancestors
|
// No. We drop all the element that are not direct ancestors
|
||||||
// of the selection
|
// of the selection
|
||||||
let parent = this.DOMHelpers.getParentObject(this.selection.node);
|
let parent = this.selection.nodeFront.parentNode();
|
||||||
let idx = this.getCommonAncestor(parent);
|
let idx = this.getCommonAncestor(parent);
|
||||||
this.cutAfter(idx);
|
this.cutAfter(idx);
|
||||||
}
|
}
|
||||||
// we append the missing button between the end of the breadcrumbs display
|
// we append the missing button between the end of the breadcrumbs display
|
||||||
// and the current node.
|
// and the current node.
|
||||||
this.expand(this.selection.node);
|
this.expand(this.selection.nodeFront);
|
||||||
|
|
||||||
// we select the current node button
|
// we select the current node button
|
||||||
idx = this.indexOf(this.selection.node);
|
idx = this.indexOf(this.selection.nodeFront);
|
||||||
this.setCursor(idx);
|
this.setCursor(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let doneUpdating = this.inspector.updating("breadcrumbs");
|
||||||
// Add the first child of the very last node of the breadcrumbs if possible.
|
// Add the first child of the very last node of the breadcrumbs if possible.
|
||||||
this.ensureFirstChild();
|
this.ensureFirstChild().then(this.selectionGuard()).then(() => {
|
||||||
this.updateSelectors();
|
this.updateSelectors();
|
||||||
|
|
||||||
// Make sure the selected node and its neighbours are visible.
|
// Make sure the selected node and its neighbours are visible.
|
||||||
this.scroll();
|
this.scroll();
|
||||||
|
this.inspector.emit("breadcrumbs-updated", this.selection.nodeFront);
|
||||||
|
doneUpdating();
|
||||||
|
}).then(null, err => {
|
||||||
|
doneUpdating(this.selection.nodeFront);
|
||||||
|
this.selectionGuardEnd(err);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,6 +228,15 @@ Highlighter.prototype = {
|
|||||||
*/
|
*/
|
||||||
invalidateSize: function Highlighter_invalidateSize()
|
invalidateSize: function Highlighter_invalidateSize()
|
||||||
{
|
{
|
||||||
|
// The highlighter runs locally while the selection runs remotely,
|
||||||
|
// so we can't quite trust the selection's isConnected to protect us
|
||||||
|
// here, do the check manually.
|
||||||
|
if (!this.selection.node ||
|
||||||
|
!this.selection.node.ownerDocument ||
|
||||||
|
!this.selection.node.ownerDocument.defaultView) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let canHiglightNode = this.selection.isNode() &&
|
let canHiglightNode = this.selection.isNode() &&
|
||||||
this.selection.isConnected() &&
|
this.selection.isConnected() &&
|
||||||
this.selection.isElementNode();
|
this.selection.isElementNode();
|
||||||
|
@ -44,6 +44,20 @@ InspectorPanel.prototype = {
|
|||||||
* open is effectively an asynchronous constructor
|
* open is effectively an asynchronous constructor
|
||||||
*/
|
*/
|
||||||
open: function InspectorPanel_open() {
|
open: function InspectorPanel_open() {
|
||||||
|
return this.target.makeRemote().then(() => {
|
||||||
|
return this.target.inspector.getWalker();
|
||||||
|
}).then(walker => {
|
||||||
|
if (this._destroyPromise) {
|
||||||
|
walker.release().then(null, console.error);
|
||||||
|
}
|
||||||
|
this.walker = walker;
|
||||||
|
return this._getDefaultNodeForSelection();
|
||||||
|
}).then(defaultSelection => {
|
||||||
|
return this._deferredOpen(defaultSelection);
|
||||||
|
}).then(null, console.error);
|
||||||
|
},
|
||||||
|
|
||||||
|
_deferredOpen: function(defaultSelection) {
|
||||||
let deferred = Promise.defer();
|
let deferred = Promise.defer();
|
||||||
|
|
||||||
this.onNavigatedAway = this.onNavigatedAway.bind(this);
|
this.onNavigatedAway = this.onNavigatedAway.bind(this);
|
||||||
@ -57,13 +71,13 @@ InspectorPanel.prototype = {
|
|||||||
this.nodemenu.addEventListener("popuphiding", this._resetNodeMenu, true);
|
this.nodemenu.addEventListener("popuphiding", this._resetNodeMenu, true);
|
||||||
|
|
||||||
// Create an empty selection
|
// Create an empty selection
|
||||||
this._selection = new Selection();
|
this._selection = new Selection(this.walker);
|
||||||
this.onNewSelection = this.onNewSelection.bind(this);
|
this.onNewSelection = this.onNewSelection.bind(this);
|
||||||
this.selection.on("new-node", this.onNewSelection);
|
this.selection.on("new-node-front", this.onNewSelection);
|
||||||
this.onBeforeNewSelection = this.onBeforeNewSelection.bind(this);
|
this.onBeforeNewSelection = this.onBeforeNewSelection.bind(this);
|
||||||
this.selection.on("before-new-node", this.onBeforeNewSelection);
|
this.selection.on("before-new-node-front", this.onBeforeNewSelection);
|
||||||
this.onDetached = this.onDetached.bind(this);
|
this.onDetached = this.onDetached.bind(this);
|
||||||
this.selection.on("detached", this.onDetached);
|
this.selection.on("detached-front", this.onDetached);
|
||||||
|
|
||||||
this.breadcrumbs = new HTMLBreadcrumbs(this);
|
this.breadcrumbs = new HTMLBreadcrumbs(this);
|
||||||
|
|
||||||
@ -121,13 +135,7 @@ InspectorPanel.prototype = {
|
|||||||
this.isReady = true;
|
this.isReady = true;
|
||||||
|
|
||||||
// All the components are initialized. Let's select a node.
|
// All the components are initialized. Let's select a node.
|
||||||
if (this.target.isLocalTab) {
|
this._selection.setNodeFront(defaultSelection);
|
||||||
this._selection.setNode(
|
|
||||||
this._getDefaultNodeForSelection(this.browser.contentDocument));
|
|
||||||
} else if (this.target.window) {
|
|
||||||
this._selection.setNode(
|
|
||||||
this._getDefaultNodeForSelection(this.target.window.document));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.highlighter) {
|
if (this.highlighter) {
|
||||||
this.highlighter.unlock();
|
this.highlighter.unlock();
|
||||||
@ -146,13 +154,17 @@ InspectorPanel.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select node for default selection
|
* Return a promise that will resolve to the default node for selection.
|
||||||
*/
|
*/
|
||||||
_getDefaultNodeForSelection : function(document) {
|
_getDefaultNodeForSelection : function() {
|
||||||
// if available set body node as default selected node
|
// if available set body node as default selected node
|
||||||
// else set documentElement
|
// else set documentElement
|
||||||
var defaultNode = document.body || document.documentElement;
|
return this.walker.querySelector(this.walker.rootNode, "body").then(front => {
|
||||||
return defaultNode;
|
if (front) {
|
||||||
|
return front;
|
||||||
|
}
|
||||||
|
return this.walker.documentElement(this.walker.rootNode);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -262,35 +274,34 @@ InspectorPanel.prototype = {
|
|||||||
*/
|
*/
|
||||||
onNavigatedAway: function InspectorPanel_onNavigatedAway(event, payload) {
|
onNavigatedAway: function InspectorPanel_onNavigatedAway(event, payload) {
|
||||||
let newWindow = payload._navPayload || payload;
|
let newWindow = payload._navPayload || payload;
|
||||||
this.selection.setNode(null);
|
this.walker.release().then(null, console.error);
|
||||||
|
this.walker = null;
|
||||||
|
this.selection.setNodeFront(null);
|
||||||
|
this.selection.setWalker(null);
|
||||||
this._destroyMarkup();
|
this._destroyMarkup();
|
||||||
this.isDirty = false;
|
this.isDirty = false;
|
||||||
|
|
||||||
let onDOMReady = function() {
|
this.target.inspector.getWalker().then(walker => {
|
||||||
newWindow.removeEventListener("DOMContentLoaded", onDOMReady, true);
|
if (this._destroyPromise) {
|
||||||
|
walker.release().then(null, console.error);
|
||||||
if (this._destroyed) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.selection.node) {
|
this.walker = walker;
|
||||||
let defaultNode = this._getDefaultNodeForSelection(newWindow.document);
|
this.selection.setWalker(walker);
|
||||||
this.selection.setNode(defaultNode, "navigateaway");
|
this._getDefaultNodeForSelection().then(defaultNode => {
|
||||||
|
if (this._destroyPromise) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
this._initMarkup();
|
this.selection.setNodeFront(defaultNode, "navigateaway");
|
||||||
|
|
||||||
|
this._initMarkup();
|
||||||
this.once("markuploaded", () => {
|
this.once("markuploaded", () => {
|
||||||
this.markup.expandNode(this.selection.node);
|
this.markup.expandNode(this.selection.node);
|
||||||
});
|
|
||||||
|
|
||||||
this.setupSearchBox();
|
this.setupSearchBox();
|
||||||
}.bind(this);
|
});
|
||||||
|
});
|
||||||
if (newWindow.document.readyState == "loading") {
|
});
|
||||||
newWindow.addEventListener("DOMContentLoaded", onDOMReady, true);
|
|
||||||
} else {
|
|
||||||
onDOMReady();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -298,6 +309,69 @@ InspectorPanel.prototype = {
|
|||||||
*/
|
*/
|
||||||
onNewSelection: function InspectorPanel_onNewSelection() {
|
onNewSelection: function InspectorPanel_onNewSelection() {
|
||||||
this.cancelLayoutChange();
|
this.cancelLayoutChange();
|
||||||
|
|
||||||
|
// Wait for all the known tools to finish updating and then let the
|
||||||
|
// client know.
|
||||||
|
let selection = this.selection.nodeFront;
|
||||||
|
let selfUpdate = this.updating("inspector-panel");
|
||||||
|
Services.tm.mainThread.dispatch(() => {
|
||||||
|
try {
|
||||||
|
selfUpdate(selection);
|
||||||
|
} catch(ex) {
|
||||||
|
console.error(ex);
|
||||||
|
}
|
||||||
|
}, Ci.nsIThread.DISPATCH_NORMAL);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delay the "inspector-updated" notification while a tool
|
||||||
|
* is updating itself. Returns a function that must be
|
||||||
|
* invoked when the tool is done updating with the node
|
||||||
|
* that the tool is viewing.
|
||||||
|
*/
|
||||||
|
updating: function(name) {
|
||||||
|
if (this._updateProgress && this._updateProgress.node != this.selection.nodeFront) {
|
||||||
|
this.cancelUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._updateProgress) {
|
||||||
|
// Start an update in progress.
|
||||||
|
var self = this;
|
||||||
|
this._updateProgress = {
|
||||||
|
node: this.selection.nodeFront,
|
||||||
|
outstanding: new Set(),
|
||||||
|
checkDone: function() {
|
||||||
|
if (this !== self._updateProgress) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.node !== self.selection.nodeFront) {
|
||||||
|
self.cancelUpdate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.outstanding.size !== 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self._updateProgress = null;
|
||||||
|
self.emit("inspector-updated");
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let progress = this._updateProgress;
|
||||||
|
let done = function() {
|
||||||
|
progress.outstanding.delete(done);
|
||||||
|
progress.checkDone();
|
||||||
|
};
|
||||||
|
progress.outstanding.add(done);
|
||||||
|
return done;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel notification of inspector updates.
|
||||||
|
*/
|
||||||
|
cancelUpdate: function() {
|
||||||
|
this._updateProgress = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -317,18 +391,24 @@ InspectorPanel.prototype = {
|
|||||||
onDetached: function InspectorPanel_onDetached(event, parentNode) {
|
onDetached: function InspectorPanel_onDetached(event, parentNode) {
|
||||||
this.cancelLayoutChange();
|
this.cancelLayoutChange();
|
||||||
this.breadcrumbs.cutAfter(this.breadcrumbs.indexOf(parentNode));
|
this.breadcrumbs.cutAfter(this.breadcrumbs.indexOf(parentNode));
|
||||||
this.selection.setNode(parentNode, "detached");
|
this.selection.setNodeFront(parentNode, "detached");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy the inspector.
|
* Destroy the inspector.
|
||||||
*/
|
*/
|
||||||
destroy: function InspectorPanel__destroy() {
|
destroy: function InspectorPanel__destroy() {
|
||||||
if (this._destroyed) {
|
if (this._destroyPromise) {
|
||||||
return Promise.resolve(null);
|
return this._destroyPromise;
|
||||||
|
}
|
||||||
|
if (this.walker) {
|
||||||
|
this._destroyPromise = this.walker.release().then(null, console.error);
|
||||||
|
delete this.walker;
|
||||||
|
} else {
|
||||||
|
this._destroyPromise = Promise.resolve(null);
|
||||||
}
|
}
|
||||||
this._destroyed = true;
|
|
||||||
|
|
||||||
|
this.cancelUpdate();
|
||||||
this.cancelLayoutChange();
|
this.cancelLayoutChange();
|
||||||
|
|
||||||
if (this.browser) {
|
if (this.browser) {
|
||||||
@ -358,9 +438,10 @@ InspectorPanel.prototype = {
|
|||||||
this.nodemenu.removeEventListener("popuphiding", this._resetNodeMenu, true);
|
this.nodemenu.removeEventListener("popuphiding", this._resetNodeMenu, true);
|
||||||
this.breadcrumbs.destroy();
|
this.breadcrumbs.destroy();
|
||||||
this.searchSuggestions.destroy();
|
this.searchSuggestions.destroy();
|
||||||
this.selection.off("new-node", this.onNewSelection);
|
this.selection.off("new-node-front", this.onNewSelection);
|
||||||
this.selection.off("before-new-node", this.onBeforeNewSelection);
|
this.selection.off("before-new-node", this.onBeforeNewSelection);
|
||||||
this.selection.off("detached", this.onDetached);
|
this.selection.off("before-new-node-front", this.onBeforeNewSelection);
|
||||||
|
this.selection.off("detached-front", this.onDetached);
|
||||||
this._destroyMarkup();
|
this._destroyMarkup();
|
||||||
this._selection.destroy();
|
this._selection.destroy();
|
||||||
this._selection = null;
|
this._selection = null;
|
||||||
@ -374,7 +455,7 @@ InspectorPanel.prototype = {
|
|||||||
this.nodemenu = null;
|
this.nodemenu = null;
|
||||||
this.highlighter = null;
|
this.highlighter = null;
|
||||||
|
|
||||||
return Promise.resolve(null);
|
return this._destroyPromise;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -501,7 +582,7 @@ InspectorPanel.prototype = {
|
|||||||
if (this.selection.isElementNode()) {
|
if (this.selection.isElementNode()) {
|
||||||
if (DOMUtils.hasPseudoClassLock(this.selection.node, aPseudo)) {
|
if (DOMUtils.hasPseudoClassLock(this.selection.node, aPseudo)) {
|
||||||
this.breadcrumbs.nodeHierarchy.forEach(function(crumb) {
|
this.breadcrumbs.nodeHierarchy.forEach(function(crumb) {
|
||||||
DOMUtils.removePseudoClassLock(crumb.node, aPseudo);
|
DOMUtils.removePseudoClassLock(crumb.node.rawNode(), aPseudo);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let hierarchical = aPseudo == ":hover" || aPseudo == ":active";
|
let hierarchical = aPseudo == ":hover" || aPseudo == ":active";
|
||||||
@ -522,7 +603,7 @@ InspectorPanel.prototype = {
|
|||||||
clearPseudoClasses: function InspectorPanel_clearPseudoClasses() {
|
clearPseudoClasses: function InspectorPanel_clearPseudoClasses() {
|
||||||
this.breadcrumbs.nodeHierarchy.forEach(function(crumb) {
|
this.breadcrumbs.nodeHierarchy.forEach(function(crumb) {
|
||||||
try {
|
try {
|
||||||
DOMUtils.clearPseudoClassLocks(crumb.node);
|
DOMUtils.clearPseudoClassLocks(crumb.node.rawNode());
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
// Ignore dead nodes after navigation.
|
// Ignore dead nodes after navigation.
|
||||||
}
|
}
|
||||||
@ -534,6 +615,9 @@ InspectorPanel.prototype = {
|
|||||||
*/
|
*/
|
||||||
toggleHighlighter: function InspectorPanel_toggleHighlighter(event)
|
toggleHighlighter: function InspectorPanel_toggleHighlighter(event)
|
||||||
{
|
{
|
||||||
|
if (!this.highlighter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (event.type == "mouseover") {
|
if (event.type == "mouseover") {
|
||||||
this.highlighter.hide();
|
this.highlighter.hide();
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,13 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
const {Cu} = require("chrome");
|
const {Cu, Ci} = require("chrome");
|
||||||
let EventEmitter = require("devtools/shared/event-emitter");
|
let EventEmitter = require("devtools/shared/event-emitter");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API
|
* API
|
||||||
*
|
*
|
||||||
* new Selection(node=null, track={attributes,detached});
|
* new Selection(walker=null, node=null, track={attributes,detached});
|
||||||
* destroy()
|
* destroy()
|
||||||
* node (readonly)
|
* node (readonly)
|
||||||
* setNode(node, origin="unknown")
|
* setNode(node, origin="unknown")
|
||||||
@ -53,19 +53,22 @@ let EventEmitter = require("devtools/shared/event-emitter");
|
|||||||
* @param node Inner node.
|
* @param node Inner node.
|
||||||
* Can be null. Can be (un)set in the future via the "node" property;
|
* Can be null. Can be (un)set in the future via the "node" property;
|
||||||
* @param trackAttribute Tell if events should be fired when the attributes of
|
* @param trackAttribute Tell if events should be fired when the attributes of
|
||||||
* the ndoe change.
|
* the node change.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function Selection(node=null, track={attributes:true,detached:true}) {
|
function Selection(walker, node=null, track={attributes:true,detached:true}) {
|
||||||
EventEmitter.decorate(this);
|
EventEmitter.decorate(this);
|
||||||
|
|
||||||
this._onMutations = this._onMutations.bind(this);
|
this._onMutations = this._onMutations.bind(this);
|
||||||
this.track = track;
|
this.track = track;
|
||||||
|
this.setWalker(walker);
|
||||||
this.setNode(node);
|
this.setNode(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.Selection = Selection;
|
exports.Selection = Selection;
|
||||||
|
|
||||||
Selection.prototype = {
|
Selection.prototype = {
|
||||||
|
_walker: null,
|
||||||
_node: null,
|
_node: null,
|
||||||
|
|
||||||
_onMutations: function(mutations) {
|
_onMutations: function(mutations) {
|
||||||
@ -86,60 +89,41 @@ Selection.prototype = {
|
|||||||
|
|
||||||
if (attributeChange)
|
if (attributeChange)
|
||||||
this.emit("attribute-changed");
|
this.emit("attribute-changed");
|
||||||
if (detached)
|
if (detached) {
|
||||||
this.emit("detached", parentNode);
|
this.emit("detached", parentNode ? parentNode.rawNode() : null);
|
||||||
},
|
this.emit("detached-front", parentNode);
|
||||||
|
|
||||||
_attachEvents: function SN__attachEvents() {
|
|
||||||
if (!this.window || !this.isNode() || !this.track) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.track.attributes) {
|
|
||||||
this._nodeObserver = new this.window.MutationObserver(this._onMutations);
|
|
||||||
this._nodeObserver.observe(this.node, {attributes: true});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.track.detached) {
|
|
||||||
this._docObserver = new this.window.MutationObserver(this._onMutations);
|
|
||||||
this._docObserver.observe(this.document.documentElement, {childList: true, subtree: true});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_detachEvents: function SN__detachEvents() {
|
|
||||||
// `disconnect` fail if node's document has
|
|
||||||
// been deleted.
|
|
||||||
try {
|
|
||||||
if (this._nodeObserver)
|
|
||||||
this._nodeObserver.disconnect();
|
|
||||||
} catch(e) {}
|
|
||||||
try {
|
|
||||||
if (this._docObserver)
|
|
||||||
this._docObserver.disconnect();
|
|
||||||
} catch(e) {}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
destroy: function SN_destroy() {
|
destroy: function SN_destroy() {
|
||||||
this._detachEvents();
|
|
||||||
this.setNode(null);
|
this.setNode(null);
|
||||||
|
this.setWalker(null);
|
||||||
},
|
},
|
||||||
|
|
||||||
setNode: function SN_setNode(value, reason="unknown") {
|
setWalker: function(walker) {
|
||||||
this.reason = reason;
|
if (this._walker) {
|
||||||
if (value !== this._node) {
|
this._walker.off("mutations", this._onMutations);
|
||||||
this.emit("before-new-node", value, reason);
|
}
|
||||||
let previousNode = this._node;
|
this._walker = walker;
|
||||||
this._detachEvents();
|
if (this._walker) {
|
||||||
this._node = value;
|
this._walker.on("mutations", this._onMutations);
|
||||||
this._attachEvents();
|
|
||||||
this.emit("new-node", previousNode, this.reason);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Not remote-safe
|
||||||
|
setNode: function SN_setNode(value, reason="unknown") {
|
||||||
|
if (value) {
|
||||||
|
value = this._walker.frontForRawNode(value);
|
||||||
|
}
|
||||||
|
this.setNodeFront(value, reason);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Not remote-safe
|
||||||
get node() {
|
get node() {
|
||||||
return this._node;
|
return this._node;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Not remote-safe
|
||||||
get window() {
|
get window() {
|
||||||
if (this.isNode()) {
|
if (this.isNode()) {
|
||||||
return this.node.ownerDocument.defaultView;
|
return this.node.ownerDocument.defaultView;
|
||||||
@ -147,6 +131,7 @@ Selection.prototype = {
|
|||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Not remote-safe
|
||||||
get document() {
|
get document() {
|
||||||
if (this.isNode()) {
|
if (this.isNode()) {
|
||||||
return this.node.ownerDocument;
|
return this.node.ownerDocument;
|
||||||
@ -154,28 +139,79 @@ Selection.prototype = {
|
|||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setNodeFront: function(value, reason="unknown") {
|
||||||
|
this.reason = reason;
|
||||||
|
if (value !== this._nodeFront) {
|
||||||
|
let rawValue = value ? value.rawNode() : value;
|
||||||
|
this.emit("before-new-node", rawValue, reason);
|
||||||
|
this.emit("before-new-node-front", value, reason);
|
||||||
|
let previousNode = this._node;
|
||||||
|
let previousFront = this._nodeFront;
|
||||||
|
this._node = rawValue;
|
||||||
|
this._nodeFront = value;
|
||||||
|
this.emit("new-node", previousNode, this.reason);
|
||||||
|
this.emit("new-node-front", value, this.reason);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get documentFront() {
|
||||||
|
return this._walker.document(this._nodeFront);
|
||||||
|
},
|
||||||
|
|
||||||
|
get nodeFront() {
|
||||||
|
return this._nodeFront;
|
||||||
|
},
|
||||||
|
|
||||||
isRoot: function SN_isRootNode() {
|
isRoot: function SN_isRootNode() {
|
||||||
return this.isNode() &&
|
return this.isNode() &&
|
||||||
this.isConnected() &&
|
this.isConnected() &&
|
||||||
this.node.ownerDocument.documentElement === this.node;
|
this._nodeFront.isDocumentElement;
|
||||||
},
|
},
|
||||||
|
|
||||||
isNode: function SN_isNode() {
|
isNode: function SN_isNode() {
|
||||||
return (this.node &&
|
if (!this._nodeFront) {
|
||||||
!Cu.isDeadWrapper(this.node) &&
|
return false;
|
||||||
this.node.ownerDocument &&
|
}
|
||||||
this.node.ownerDocument.defaultView &&
|
|
||||||
this.node instanceof this.node.ownerDocument.defaultView.Node);
|
// As long as tools are still accessing node.rawNode(),
|
||||||
|
// this needs to stay here.
|
||||||
|
if (this._node && Cu.isDeadWrapper(this._node)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
isLocal: function SN_nsLocal() {
|
||||||
|
return !!this._node;
|
||||||
},
|
},
|
||||||
|
|
||||||
isConnected: function SN_isConnected() {
|
isConnected: function SN_isConnected() {
|
||||||
|
let node = this._nodeFront;
|
||||||
|
if (!node || !node.actorID) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// As long as there are still tools going around
|
||||||
|
// accessing node.rawNode, this needs to stay.
|
||||||
|
let rawNode = node.rawNode();
|
||||||
|
if (rawNode) {
|
||||||
try {
|
try {
|
||||||
let doc = this.document;
|
let doc = this.document;
|
||||||
return doc && doc.defaultView && doc.documentElement.contains(this.node);
|
return (doc && doc.defaultView && doc.documentElement.contains(rawNode));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// "can't access dead object" error
|
// "can't access dead object" error
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while(node) {
|
||||||
|
if (node === this._walker.rootNode) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
node = node.parentNode();
|
||||||
|
};
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
isHTMLNode: function SN_isHTMLNode() {
|
isHTMLNode: function SN_isHTMLNode() {
|
||||||
@ -186,50 +222,50 @@ Selection.prototype = {
|
|||||||
// Node type
|
// Node type
|
||||||
|
|
||||||
isElementNode: function SN_isElementNode() {
|
isElementNode: function SN_isElementNode() {
|
||||||
return this.isNode() && this.node.nodeType == this.window.Node.ELEMENT_NODE;
|
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.ELEMENT_NODE;
|
||||||
},
|
},
|
||||||
|
|
||||||
isAttributeNode: function SN_isAttributeNode() {
|
isAttributeNode: function SN_isAttributeNode() {
|
||||||
return this.isNode() && this.node.nodeType == this.window.Node.ATTRIBUTE_NODE;
|
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.ATTRIBUTE_NODE;
|
||||||
},
|
},
|
||||||
|
|
||||||
isTextNode: function SN_isTextNode() {
|
isTextNode: function SN_isTextNode() {
|
||||||
return this.isNode() && this.node.nodeType == this.window.Node.TEXT_NODE;
|
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.TEXT_NODE;
|
||||||
},
|
},
|
||||||
|
|
||||||
isCDATANode: function SN_isCDATANode() {
|
isCDATANode: function SN_isCDATANode() {
|
||||||
return this.isNode() && this.node.nodeType == this.window.Node.CDATA_SECTION_NODE;
|
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.CDATA_SECTION_NODE;
|
||||||
},
|
},
|
||||||
|
|
||||||
isEntityRefNode: function SN_isEntityRefNode() {
|
isEntityRefNode: function SN_isEntityRefNode() {
|
||||||
return this.isNode() && this.node.nodeType == this.window.Node.ENTITY_REFERENCE_NODE;
|
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.ENTITY_REFERENCE_NODE;
|
||||||
},
|
},
|
||||||
|
|
||||||
isEntityNode: function SN_isEntityNode() {
|
isEntityNode: function SN_isEntityNode() {
|
||||||
return this.isNode() && this.node.nodeType == this.window.Node.ENTITY_NODE;
|
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.ENTITY_NODE;
|
||||||
},
|
},
|
||||||
|
|
||||||
isProcessingInstructionNode: function SN_isProcessingInstructionNode() {
|
isProcessingInstructionNode: function SN_isProcessingInstructionNode() {
|
||||||
return this.isNode() && this.node.nodeType == this.window.Node.PROCESSING_INSTRUCTION_NODE;
|
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.PROCESSING_INSTRUCTION_NODE;
|
||||||
},
|
},
|
||||||
|
|
||||||
isCommentNode: function SN_isCommentNode() {
|
isCommentNode: function SN_isCommentNode() {
|
||||||
return this.isNode() && this.node.nodeType == this.window.Node.PROCESSING_INSTRUCTION_NODE;
|
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.PROCESSING_INSTRUCTION_NODE;
|
||||||
},
|
},
|
||||||
|
|
||||||
isDocumentNode: function SN_isDocumentNode() {
|
isDocumentNode: function SN_isDocumentNode() {
|
||||||
return this.isNode() && this.node.nodeType == this.window.Node.DOCUMENT_NODE;
|
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE;
|
||||||
},
|
},
|
||||||
|
|
||||||
isDocumentTypeNode: function SN_isDocumentTypeNode() {
|
isDocumentTypeNode: function SN_isDocumentTypeNode() {
|
||||||
return this.isNode() && this.node.nodeType ==this.window. Node.DOCUMENT_TYPE_NODE;
|
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.DOCUMENT_TYPE_NODE;
|
||||||
},
|
},
|
||||||
|
|
||||||
isDocumentFragmentNode: function SN_isDocumentFragmentNode() {
|
isDocumentFragmentNode: function SN_isDocumentFragmentNode() {
|
||||||
return this.isNode() && this.node.nodeType == this.window.Node.DOCUMENT_FRAGMENT_NODE;
|
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.DOCUMENT_FRAGMENT_NODE;
|
||||||
},
|
},
|
||||||
|
|
||||||
isNotationNode: function SN_isNotationNode() {
|
isNotationNode: function SN_isNotationNode() {
|
||||||
return this.isNode() && this.node.nodeType == this.window.Node.NOTATION_NODE;
|
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.NOTATION_NODE;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ function test()
|
|||||||
{
|
{
|
||||||
inspector = aInspector;
|
inspector = aInspector;
|
||||||
cursor = 0;
|
cursor = 0;
|
||||||
inspector.selection.on("new-node", nodeSelected);
|
inspector.on("breadcrumbs-updated", nodeSelected);
|
||||||
executeSoon(function() {
|
executeSoon(function() {
|
||||||
inspector.selection.setNode(nodes[0].node);
|
inspector.selection.setNode(nodes[0].node);
|
||||||
});
|
});
|
||||||
@ -52,17 +52,16 @@ function test()
|
|||||||
|
|
||||||
function nodeSelected()
|
function nodeSelected()
|
||||||
{
|
{
|
||||||
executeSoon(function() {
|
|
||||||
performTest();
|
performTest();
|
||||||
cursor++;
|
cursor++;
|
||||||
|
|
||||||
if (cursor >= nodes.length) {
|
if (cursor >= nodes.length) {
|
||||||
inspector.selection.off("new-node", nodeSelected);
|
inspector.off("breadcrumbs-updated", nodeSelected);
|
||||||
finishUp();
|
finishUp();
|
||||||
} else {
|
} else {
|
||||||
let node = nodes[cursor].node;
|
let node = nodes[cursor].node;
|
||||||
inspector.selection.setNode(node);
|
inspector.selection.setNode(node);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function performTest()
|
function performTest()
|
||||||
|
@ -34,11 +34,12 @@ function test()
|
|||||||
{
|
{
|
||||||
inspector = aInspector;
|
inspector = aInspector;
|
||||||
|
|
||||||
executeSoon(function() {
|
// Make sure the body element is selected initially.
|
||||||
inspector.selection.once("new-node", highlightHeaderNode);
|
node = doc.querySelector("body");
|
||||||
// Test that navigating around without a selected node gets us to the
|
inspector.once("inspector-updated", () => {
|
||||||
// head element.
|
is(inspector.selection.node, node, "Body should be selected initially.");
|
||||||
node = doc.querySelector("h1");
|
node = doc.querySelector("h1")
|
||||||
|
inspector.once("inspector-updated", highlightHeaderNode);
|
||||||
let bc = inspector.breadcrumbs;
|
let bc = inspector.breadcrumbs;
|
||||||
bc.nodeHierarchy[bc.currentIndex].button.focus();
|
bc.nodeHierarchy[bc.currentIndex].button.focus();
|
||||||
EventUtils.synthesizeKey("VK_RIGHT", { });
|
EventUtils.synthesizeKey("VK_RIGHT", { });
|
||||||
@ -50,7 +51,7 @@ function test()
|
|||||||
is(inspector.selection.node, node, "selected h1 element");
|
is(inspector.selection.node, node, "selected h1 element");
|
||||||
|
|
||||||
executeSoon(function() {
|
executeSoon(function() {
|
||||||
inspector.selection.once("new-node", highlightParagraphNode);
|
inspector.once("inspector-updated", highlightParagraphNode);
|
||||||
// Test that moving to the next sibling works.
|
// Test that moving to the next sibling works.
|
||||||
node = doc.querySelector("p");
|
node = doc.querySelector("p");
|
||||||
EventUtils.synthesizeKey("VK_DOWN", { });
|
EventUtils.synthesizeKey("VK_DOWN", { });
|
||||||
@ -62,7 +63,7 @@ function test()
|
|||||||
is(inspector.selection.node, node, "selected p element");
|
is(inspector.selection.node, node, "selected p element");
|
||||||
|
|
||||||
executeSoon(function() {
|
executeSoon(function() {
|
||||||
inspector.selection.once("new-node", highlightHeaderNodeAgain);
|
inspector.once("inspector-updated", highlightHeaderNodeAgain);
|
||||||
// Test that moving to the previous sibling works.
|
// Test that moving to the previous sibling works.
|
||||||
node = doc.querySelector("h1");
|
node = doc.querySelector("h1");
|
||||||
EventUtils.synthesizeKey("VK_UP", { });
|
EventUtils.synthesizeKey("VK_UP", { });
|
||||||
@ -74,7 +75,7 @@ function test()
|
|||||||
is(inspector.selection.node, node, "selected h1 element");
|
is(inspector.selection.node, node, "selected h1 element");
|
||||||
|
|
||||||
executeSoon(function() {
|
executeSoon(function() {
|
||||||
inspector.selection.once("new-node", highlightParentNode);
|
inspector.once("inspector-updated", highlightParentNode);
|
||||||
// Test that moving to the parent works.
|
// Test that moving to the parent works.
|
||||||
node = doc.querySelector("body");
|
node = doc.querySelector("body");
|
||||||
EventUtils.synthesizeKey("VK_LEFT", { });
|
EventUtils.synthesizeKey("VK_LEFT", { });
|
||||||
|
@ -34,9 +34,10 @@ function test()
|
|||||||
let tmp = {};
|
let tmp = {};
|
||||||
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tmp);
|
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tmp);
|
||||||
ok(!tmp.LayoutHelpers.isNodeConnected(node), "Node considered as disconnected.");
|
ok(!tmp.LayoutHelpers.isNodeConnected(node), "Node considered as disconnected.");
|
||||||
executeSoon(function() {
|
|
||||||
is(inspector.selection.node, parentNode, "parent of selection got selected");
|
|
||||||
|
|
||||||
|
// Wait for the selection to process the mutation
|
||||||
|
inspector.walker.on("mutations", () => {
|
||||||
|
is(inspector.selection.node, parentNode, "parent of selection got selected");
|
||||||
finishUp();
|
finishUp();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ function startInspectorTests(toolbox)
|
|||||||
let p = doc.querySelector("p");
|
let p = doc.querySelector("p");
|
||||||
|
|
||||||
inspector.selection.setNode(p);
|
inspector.selection.setNode(p);
|
||||||
|
inspector.once("inspector-updated", () => {
|
||||||
testHighlighter(p);
|
testHighlighter(p);
|
||||||
testMarkupView(p);
|
testMarkupView(p);
|
||||||
testBreadcrumbs(p);
|
testBreadcrumbs(p);
|
||||||
@ -49,7 +49,7 @@ function startInspectorTests(toolbox)
|
|||||||
span.scrollIntoView();
|
span.scrollIntoView();
|
||||||
|
|
||||||
inspector.selection.setNode(span);
|
inspector.selection.setNode(span);
|
||||||
|
inspector.once("inspector-updated", () => {
|
||||||
testHighlighter(span);
|
testHighlighter(span);
|
||||||
testMarkupView(span);
|
testMarkupView(span);
|
||||||
testBreadcrumbs(span);
|
testBreadcrumbs(span);
|
||||||
@ -61,6 +61,8 @@ function startInspectorTests(toolbox)
|
|||||||
executeSoon(runContextMenuTest);
|
executeSoon(runContextMenuTest);
|
||||||
});
|
});
|
||||||
toolbox.destroy();
|
toolbox.destroy();
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -79,7 +81,7 @@ function testMarkupView(node)
|
|||||||
function testBreadcrumbs(node)
|
function testBreadcrumbs(node)
|
||||||
{
|
{
|
||||||
let b = getActiveInspector().breadcrumbs;
|
let b = getActiveInspector().breadcrumbs;
|
||||||
let expectedText = b.prettyPrintNodeAsText(node);
|
let expectedText = b.prettyPrintNodeAsText(getNodeFront(node));
|
||||||
let button = b.container.querySelector("button[checked=true]");
|
let button = b.container.querySelector("button[checked=true]");
|
||||||
ok(button, "A crumbs is checked=true");
|
ok(button, "A crumbs is checked=true");
|
||||||
is(button.getAttribute("tooltiptext"), expectedText, "Crumb refers to the right node");
|
is(button.getAttribute("tooltiptext"), expectedText, "Crumb refers to the right node");
|
||||||
|
@ -31,6 +31,7 @@ function test() {
|
|||||||
function checkDocTypeMenuItems() {
|
function checkDocTypeMenuItems() {
|
||||||
info("Checking context menu entries for doctype node");
|
info("Checking context menu entries for doctype node");
|
||||||
inspector.selection.setNode(doc.doctype);
|
inspector.selection.setNode(doc.doctype);
|
||||||
|
inspector.once("inspector-updated", () => {
|
||||||
let docTypeNode = getMarkupTagNodeContaining("<!DOCTYPE html>");
|
let docTypeNode = getMarkupTagNodeContaining("<!DOCTYPE html>");
|
||||||
|
|
||||||
// Right-click doctype tag
|
// Right-click doctype tag
|
||||||
@ -46,6 +47,7 @@ function test() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
checkElementMenuItems();
|
checkElementMenuItems();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkElementMenuItems() {
|
function checkElementMenuItems() {
|
||||||
|
@ -51,11 +51,11 @@ function createDocument()
|
|||||||
function selectNode(aInspector)
|
function selectNode(aInspector)
|
||||||
{
|
{
|
||||||
inspector = aInspector;
|
inspector = aInspector;
|
||||||
inspector.selection.setNode(div);
|
|
||||||
inspector.sidebar.once("ruleview-ready", function() {
|
inspector.sidebar.once("ruleview-ready", function() {
|
||||||
ruleview = inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
|
ruleview = inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
|
||||||
inspector.sidebar.select("ruleview");
|
inspector.sidebar.select("ruleview");
|
||||||
performTests();
|
inspector.selection.setNode(div);
|
||||||
|
inspector.once("inspector-updated", performTests);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,15 +75,16 @@ function performTests()
|
|||||||
// toggle it back on
|
// toggle it back on
|
||||||
inspector.togglePseudoClass(pseudo);
|
inspector.togglePseudoClass(pseudo);
|
||||||
|
|
||||||
testNavigate();
|
testNavigate(() => {
|
||||||
|
|
||||||
// close the inspector
|
// close the inspector
|
||||||
finishUp();
|
finishUp();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function testNavigate()
|
function testNavigate(callback)
|
||||||
{
|
{
|
||||||
inspector.selection.setNode(parentDiv);
|
inspector.selection.setNode(parentDiv);
|
||||||
|
inspector.once("inspector-updated", () => {
|
||||||
|
|
||||||
// make sure it's still on after naving to parent
|
// make sure it's still on after naving to parent
|
||||||
is(DOMUtils.hasPseudoClassLock(div, pseudo), true,
|
is(DOMUtils.hasPseudoClassLock(div, pseudo), true,
|
||||||
@ -91,13 +92,20 @@ function testNavigate()
|
|||||||
|
|
||||||
inspector.selection.setNode(div2);
|
inspector.selection.setNode(div2);
|
||||||
|
|
||||||
|
inspector.once("inspector-updated", () => {
|
||||||
|
|
||||||
// make sure it's removed after naving to a non-hierarchy node
|
// make sure it's removed after naving to a non-hierarchy node
|
||||||
is(DOMUtils.hasPseudoClassLock(div, pseudo), false,
|
is(DOMUtils.hasPseudoClassLock(div, pseudo), false,
|
||||||
"pseudo-class lock is removed after inspecting sibling node");
|
"pseudo-class lock is removed after inspecting sibling node");
|
||||||
|
|
||||||
// toggle it back on
|
// toggle it back on
|
||||||
inspector.selection.setNode(div);
|
inspector.selection.setNode(div);
|
||||||
|
inspector.once("inspector-updated", () => {
|
||||||
inspector.togglePseudoClass(pseudo);
|
inspector.togglePseudoClass(pseudo);
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function testAdded()
|
function testAdded()
|
||||||
|
@ -5,6 +5,13 @@
|
|||||||
const Cu = Components.utils;
|
const Cu = Components.utils;
|
||||||
const Ci = Components.interfaces;
|
const Ci = Components.interfaces;
|
||||||
const Cc = Components.classes;
|
const Cc = Components.classes;
|
||||||
|
|
||||||
|
Services.prefs.setBoolPref("devtools.debugger.log", true);
|
||||||
|
SimpleTest.registerCleanupFunction(() => {
|
||||||
|
Services.prefs.clearUserPref("devtools.debugger.log");
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
let tempScope = {};
|
let tempScope = {};
|
||||||
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tempScope);
|
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tempScope);
|
||||||
let LayoutHelpers = tempScope.LayoutHelpers;
|
let LayoutHelpers = tempScope.LayoutHelpers;
|
||||||
@ -19,6 +26,12 @@ let console = tempScope.console;
|
|||||||
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
|
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
|
||||||
Services.scriptloader.loadSubScript(testDir + "../../../commandline/test/helpers.js", this);
|
Services.scriptloader.loadSubScript(testDir + "../../../commandline/test/helpers.js", this);
|
||||||
|
|
||||||
|
SimpleTest.registerCleanupFunction(() => {
|
||||||
|
console.error("Here we are\n")
|
||||||
|
let {DebuggerServer} = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
|
||||||
|
console.error("DebuggerServer open connections: " + Object.getOwnPropertyNames(DebuggerServer._connections).length);
|
||||||
|
});
|
||||||
|
|
||||||
function openInspector(callback)
|
function openInspector(callback)
|
||||||
{
|
{
|
||||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||||
@ -33,6 +46,12 @@ function getActiveInspector()
|
|||||||
return gDevTools.getToolbox(target).getPanel("inspector");
|
return gDevTools.getToolbox(target).getPanel("inspector");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getNodeFront(node)
|
||||||
|
{
|
||||||
|
let inspector = getActiveInspector();
|
||||||
|
return inspector.walker.frontForRawNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
function isHighlighting()
|
function isHighlighting()
|
||||||
{
|
{
|
||||||
let outline = getActiveInspector().highlighter.outline;
|
let outline = getActiveInspector().highlighter.outline;
|
||||||
|
@ -307,8 +307,11 @@ function test() {
|
|||||||
id: "node18",
|
id: "node18",
|
||||||
});
|
});
|
||||||
|
|
||||||
is(inspector.highlighter.nodeInfo.classesBox.textContent, "",
|
/**
|
||||||
"No classes in the infobar before edit.");
|
* XXX: disabled until the remote markup view is enabled
|
||||||
|
* is(inspector.highlighter.nodeInfo.classesBox.textContent, "",
|
||||||
|
* "No classes in the infobar before edit.");
|
||||||
|
*/
|
||||||
},
|
},
|
||||||
execute: function(after) {
|
execute: function(after) {
|
||||||
inspector.once("markupmutation", function() {
|
inspector.once("markupmutation", function() {
|
||||||
@ -326,8 +329,12 @@ function test() {
|
|||||||
class: "newclass",
|
class: "newclass",
|
||||||
style: "color:green"
|
style: "color:green"
|
||||||
});
|
});
|
||||||
is(inspector.highlighter.nodeInfo.classesBox.textContent, ".newclass",
|
|
||||||
"Correct classes in the infobar after edit.");
|
/**
|
||||||
|
* XXX: disabled until the remote markup view is enabled
|
||||||
|
*is(inspector.highlighter.nodeInfo.classesBox.textContent, ".newclass",
|
||||||
|
* "Correct classes in the infobar after edit.");
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
testAsyncSetup(test, editTagName);
|
testAsyncSetup(test, editTagName);
|
||||||
|
@ -250,6 +250,7 @@ TargetEventsHandler.prototype = {
|
|||||||
case "will-navigate": {
|
case "will-navigate": {
|
||||||
// Reset UI.
|
// Reset UI.
|
||||||
NetMonitorView.RequestsMenu.reset();
|
NetMonitorView.RequestsMenu.reset();
|
||||||
|
NetMonitorView.Sidebar.reset();
|
||||||
NetMonitorView.NetworkDetails.reset();
|
NetMonitorView.NetworkDetails.reset();
|
||||||
|
|
||||||
// Reset global helpers cache.
|
// Reset global helpers cache.
|
||||||
|
@ -165,7 +165,7 @@ let NetMonitorView = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (aTabIndex !== undefined) {
|
if (aTabIndex !== undefined) {
|
||||||
$("#details-pane").selectedIndex = aTabIndex;
|
$("#event-details-pane").selectedIndex = aTabIndex;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -351,6 +351,61 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
|||||||
|
|
||||||
this.refreshSummary();
|
this.refreshSummary();
|
||||||
this.refreshZebra();
|
this.refreshZebra();
|
||||||
|
|
||||||
|
if (aId == this._preferredItemId) {
|
||||||
|
this.selectedItem = requestItem;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new custom request form populated with the data from
|
||||||
|
* the currently selected request.
|
||||||
|
*/
|
||||||
|
cloneSelectedRequest: function() {
|
||||||
|
let selected = this.selectedItem.attachment;
|
||||||
|
|
||||||
|
// Create the element node for the network request item.
|
||||||
|
let menuView = this._createMenuView(selected.method, selected.url);
|
||||||
|
|
||||||
|
let newItem = this.push([menuView], {
|
||||||
|
attachment: Object.create(selected, {
|
||||||
|
isCustom: { value: true }
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
// Immediately switch to new request pane.
|
||||||
|
this.selectedItem = newItem;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a new HTTP request using the data in the custom request form.
|
||||||
|
*/
|
||||||
|
sendCustomRequest: function() {
|
||||||
|
let selected = this.selectedItem.attachment;
|
||||||
|
|
||||||
|
let data = Object.create(selected, {
|
||||||
|
headers: { value: selected.requestHeaders.headers }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (selected.requestPostData) {
|
||||||
|
data.body = selected.requestPostData.postData.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetMonitorController.webConsoleClient.sendHTTPRequest(data, (response) => {
|
||||||
|
let id = response.eventActor.actor;
|
||||||
|
this._preferredItemId = id;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.closeCustomRequest();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the currently selected custom request.
|
||||||
|
*/
|
||||||
|
closeCustomRequest: function() {
|
||||||
|
this.remove(this.selectedItem);
|
||||||
|
|
||||||
|
NetMonitorView.Sidebar.toggle(false);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -690,11 +745,11 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
|||||||
break;
|
break;
|
||||||
case "status":
|
case "status":
|
||||||
requestItem.attachment.status = value;
|
requestItem.attachment.status = value;
|
||||||
this._updateMenuView(requestItem, key, value);
|
this.updateMenuView(requestItem, key, value);
|
||||||
break;
|
break;
|
||||||
case "statusText":
|
case "statusText":
|
||||||
requestItem.attachment.statusText = value;
|
requestItem.attachment.statusText = value;
|
||||||
this._updateMenuView(requestItem, key,
|
this.updateMenuView(requestItem, key,
|
||||||
requestItem.attachment.status + " " +
|
requestItem.attachment.status + " " +
|
||||||
requestItem.attachment.statusText);
|
requestItem.attachment.statusText);
|
||||||
break;
|
break;
|
||||||
@ -703,11 +758,11 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
|||||||
break;
|
break;
|
||||||
case "contentSize":
|
case "contentSize":
|
||||||
requestItem.attachment.contentSize = value;
|
requestItem.attachment.contentSize = value;
|
||||||
this._updateMenuView(requestItem, key, value);
|
this.updateMenuView(requestItem, key, value);
|
||||||
break;
|
break;
|
||||||
case "mimeType":
|
case "mimeType":
|
||||||
requestItem.attachment.mimeType = value;
|
requestItem.attachment.mimeType = value;
|
||||||
this._updateMenuView(requestItem, key, value);
|
this.updateMenuView(requestItem, key, value);
|
||||||
break;
|
break;
|
||||||
case "responseContent":
|
case "responseContent":
|
||||||
requestItem.attachment.responseContent = value;
|
requestItem.attachment.responseContent = value;
|
||||||
@ -715,7 +770,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
|||||||
case "totalTime":
|
case "totalTime":
|
||||||
requestItem.attachment.totalTime = value;
|
requestItem.attachment.totalTime = value;
|
||||||
requestItem.attachment.endedMillis = requestItem.attachment.startedMillis + value;
|
requestItem.attachment.endedMillis = requestItem.attachment.startedMillis + value;
|
||||||
this._updateMenuView(requestItem, key, value);
|
this.updateMenuView(requestItem, key, value);
|
||||||
this._registerLastRequestEnd(requestItem.attachment.endedMillis);
|
this._registerLastRequestEnd(requestItem.attachment.endedMillis);
|
||||||
break;
|
break;
|
||||||
case "eventTimings":
|
case "eventTimings":
|
||||||
@ -757,23 +812,11 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
|||||||
* The network request view.
|
* The network request view.
|
||||||
*/
|
*/
|
||||||
_createMenuView: function(aMethod, aUrl) {
|
_createMenuView: function(aMethod, aUrl) {
|
||||||
let uri = nsIURL(aUrl);
|
|
||||||
let nameWithQuery = this._getUriNameWithQuery(uri);
|
|
||||||
let hostPort = this._getUriHostPort(uri);
|
|
||||||
|
|
||||||
let template = $("#requests-menu-item-template");
|
let template = $("#requests-menu-item-template");
|
||||||
let fragment = document.createDocumentFragment();
|
let fragment = document.createDocumentFragment();
|
||||||
|
|
||||||
let method = $(".requests-menu-method", template);
|
this.updateMenuView(template, 'method', aMethod);
|
||||||
method.setAttribute("value", aMethod);
|
this.updateMenuView(template, 'url', aUrl);
|
||||||
|
|
||||||
let file = $(".requests-menu-file", template);
|
|
||||||
file.setAttribute("value", nameWithQuery);
|
|
||||||
file.setAttribute("tooltiptext", nameWithQuery);
|
|
||||||
|
|
||||||
let domain = $(".requests-menu-domain", template);
|
|
||||||
domain.setAttribute("value", hostPort);
|
|
||||||
domain.setAttribute("tooltiptext", hostPort);
|
|
||||||
|
|
||||||
let waterfall = $(".requests-menu-waterfall", template);
|
let waterfall = $(".requests-menu-waterfall", template);
|
||||||
waterfall.style.backgroundImage = this._cachedWaterfallBackground;
|
waterfall.style.backgroundImage = this._cachedWaterfallBackground;
|
||||||
@ -796,22 +839,48 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
|||||||
* @param any aValue
|
* @param any aValue
|
||||||
* The new value to be shown.
|
* The new value to be shown.
|
||||||
*/
|
*/
|
||||||
_updateMenuView: function(aItem, aKey, aValue) {
|
updateMenuView: function(aItem, aKey, aValue) {
|
||||||
|
let target = aItem.target || aItem;
|
||||||
|
|
||||||
switch (aKey) {
|
switch (aKey) {
|
||||||
|
case "method": {
|
||||||
|
let node = $(".requests-menu-method", target);
|
||||||
|
node.setAttribute("value", aValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "url": {
|
||||||
|
let uri;
|
||||||
|
try {
|
||||||
|
uri = nsIURL(aValue);
|
||||||
|
} catch(e) {
|
||||||
|
break; // User input may not make a well-formed url yet.
|
||||||
|
}
|
||||||
|
let nameWithQuery = this._getUriNameWithQuery(uri);
|
||||||
|
let hostPort = this._getUriHostPort(uri);
|
||||||
|
|
||||||
|
let node = $(".requests-menu-file", target);
|
||||||
|
node.setAttribute("value", nameWithQuery);
|
||||||
|
node.setAttribute("tooltiptext", nameWithQuery);
|
||||||
|
|
||||||
|
let domain = $(".requests-menu-domain", target);
|
||||||
|
domain.setAttribute("value", hostPort);
|
||||||
|
domain.setAttribute("tooltiptext", hostPort);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "status": {
|
case "status": {
|
||||||
let node = $(".requests-menu-status", aItem.target);
|
let node = $(".requests-menu-status", target);
|
||||||
node.setAttribute("code", aValue);
|
node.setAttribute("code", aValue);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "statusText": {
|
case "statusText": {
|
||||||
let node = $(".requests-menu-status-and-method", aItem.target);
|
let node = $(".requests-menu-status-and-method", target);
|
||||||
node.setAttribute("tooltiptext", aValue);
|
node.setAttribute("tooltiptext", aValue);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "contentSize": {
|
case "contentSize": {
|
||||||
let kb = aValue / 1024;
|
let kb = aValue / 1024;
|
||||||
let size = L10N.numberWithDecimals(kb, CONTENT_SIZE_DECIMALS);
|
let size = L10N.numberWithDecimals(kb, CONTENT_SIZE_DECIMALS);
|
||||||
let node = $(".requests-menu-size", aItem.target);
|
let node = $(".requests-menu-size", target);
|
||||||
let text = L10N.getFormatStr("networkMenu.sizeKB", size);
|
let text = L10N.getFormatStr("networkMenu.sizeKB", size);
|
||||||
node.setAttribute("value", text);
|
node.setAttribute("value", text);
|
||||||
node.setAttribute("tooltiptext", text);
|
node.setAttribute("tooltiptext", text);
|
||||||
@ -819,14 +888,14 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
|||||||
}
|
}
|
||||||
case "mimeType": {
|
case "mimeType": {
|
||||||
let type = this._getAbbreviatedMimeType(aValue);
|
let type = this._getAbbreviatedMimeType(aValue);
|
||||||
let node = $(".requests-menu-type", aItem.target);
|
let node = $(".requests-menu-type", target);
|
||||||
let text = CONTENT_MIME_TYPE_ABBREVIATIONS[type] || type;
|
let text = CONTENT_MIME_TYPE_ABBREVIATIONS[type] || type;
|
||||||
node.setAttribute("value", text);
|
node.setAttribute("value", text);
|
||||||
node.setAttribute("tooltiptext", aValue);
|
node.setAttribute("tooltiptext", aValue);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "totalTime": {
|
case "totalTime": {
|
||||||
let node = $(".requests-menu-timings-total", aItem.target);
|
let node = $(".requests-menu-timings-total", target);
|
||||||
let text = L10N.getFormatStr("networkMenu.totalMS", aValue); // integer
|
let text = L10N.getFormatStr("networkMenu.totalMS", aValue); // integer
|
||||||
node.setAttribute("value", text);
|
node.setAttribute("value", text);
|
||||||
node.setAttribute("tooltiptext", text);
|
node.setAttribute("tooltiptext", text);
|
||||||
@ -1089,10 +1158,10 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
|||||||
*/
|
*/
|
||||||
_onSelect: function({ detail: item }) {
|
_onSelect: function({ detail: item }) {
|
||||||
if (item) {
|
if (item) {
|
||||||
NetMonitorView.NetworkDetails.populate(item.attachment);
|
NetMonitorView.Sidebar.populate(item.attachment);
|
||||||
NetMonitorView.NetworkDetails.toggle(true);
|
NetMonitorView.Sidebar.toggle(true);
|
||||||
} else {
|
} else {
|
||||||
NetMonitorView.NetworkDetails.toggle(false);
|
NetMonitorView.Sidebar.toggle(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1104,6 +1173,14 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
|||||||
drain("resize-events", RESIZE_REFRESH_RATE, () => this._flushWaterfallViews(true));
|
drain("resize-events", RESIZE_REFRESH_RATE, () => this._flushWaterfallViews(true));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the context menu opening. Hide items if no request is selected.
|
||||||
|
*/
|
||||||
|
_onContextShowing: function() {
|
||||||
|
let element = $("#request-menu-context-resend");
|
||||||
|
element.hidden = !this.selectedItem || this.selectedItem.attachment.isCustom;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the specified unix time is the first one to be known of,
|
* Checks if the specified unix time is the first one to be known of,
|
||||||
* and saves it if so.
|
* and saves it if so.
|
||||||
@ -1239,6 +1316,162 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
|||||||
_resizeTimeout: null
|
_resizeTimeout: null
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functions handling the sidebar details view.
|
||||||
|
*/
|
||||||
|
function SidebarView() {
|
||||||
|
dumpn("SidebarView was instantiated");
|
||||||
|
}
|
||||||
|
|
||||||
|
SidebarView.prototype = {
|
||||||
|
/**
|
||||||
|
* Sets this view hidden or visible. It's visible by default.
|
||||||
|
*
|
||||||
|
* @param boolean aVisibleFlag
|
||||||
|
* Specifies the intended visibility.
|
||||||
|
*/
|
||||||
|
toggle: function(aVisibleFlag) {
|
||||||
|
NetMonitorView.toggleDetailsPane({ visible: aVisibleFlag });
|
||||||
|
NetMonitorView.RequestsMenu._flushWaterfallViews(true);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates this view with the specified data.
|
||||||
|
*
|
||||||
|
* @param object aData
|
||||||
|
* The data source (this should be the attachment of a request item).
|
||||||
|
*/
|
||||||
|
populate: function(aData) {
|
||||||
|
if (aData.isCustom) {
|
||||||
|
NetMonitorView.CustomRequest.populate(aData);
|
||||||
|
$("#details-pane").selectedIndex = 0;
|
||||||
|
} else {
|
||||||
|
NetMonitorView.NetworkDetails.populate(aData);
|
||||||
|
$("#details-pane").selectedIndex = 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides this container.
|
||||||
|
*/
|
||||||
|
reset: function() {
|
||||||
|
this.toggle(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functions handling the custom request view.
|
||||||
|
*/
|
||||||
|
function CustomRequestView() {
|
||||||
|
dumpn("CustomRequestView was instantiated");
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRequestView.prototype = {
|
||||||
|
/**
|
||||||
|
* Populates this view with the specified data.
|
||||||
|
*
|
||||||
|
* @param object aData
|
||||||
|
* The data source (this should be the attachment of a request item).
|
||||||
|
*/
|
||||||
|
populate: function(aData) {
|
||||||
|
$("#custom-url-value").value = aData.url;
|
||||||
|
$("#custom-method-value").value = aData.method;
|
||||||
|
$("#custom-headers-value").value =
|
||||||
|
writeHeaderText(aData.requestHeaders.headers);
|
||||||
|
|
||||||
|
if (aData.requestPostData) {
|
||||||
|
let body = aData.requestPostData.postData.text;
|
||||||
|
|
||||||
|
gNetwork.getString(body).then((aString) => {
|
||||||
|
$("#custom-postdata-value").value = aString;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateCustomQuery(aData.url);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle user input in the custom request form.
|
||||||
|
*
|
||||||
|
* @param object aField
|
||||||
|
* the field that the user updated.
|
||||||
|
*/
|
||||||
|
onUpdate: function(aField) {
|
||||||
|
let selectedItem = NetMonitorView.RequestsMenu.selectedItem;
|
||||||
|
let field = aField;
|
||||||
|
let value;
|
||||||
|
|
||||||
|
switch(aField) {
|
||||||
|
case 'method':
|
||||||
|
value = $("#custom-method-value").value.trim();
|
||||||
|
selectedItem.attachment.method = value;
|
||||||
|
break;
|
||||||
|
case 'url':
|
||||||
|
value = $("#custom-url-value").value;
|
||||||
|
this.updateCustomQuery(value);
|
||||||
|
selectedItem.attachment.url = value;
|
||||||
|
break;
|
||||||
|
case 'query':
|
||||||
|
let query = $("#custom-query-value").value;
|
||||||
|
this.updateCustomUrl(query);
|
||||||
|
field = 'url';
|
||||||
|
value = $("#custom-url-value").value
|
||||||
|
selectedItem.attachment.url = value;
|
||||||
|
break;
|
||||||
|
case 'body':
|
||||||
|
value = $("#custom-postdata-value").value;
|
||||||
|
selectedItem.attachment.requestPostData = {
|
||||||
|
postData: {
|
||||||
|
text: value
|
||||||
|
}
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'headers':
|
||||||
|
let headersText = $("#custom-headers-value").value;
|
||||||
|
value = parseHeaderText(headersText);
|
||||||
|
selectedItem.attachment.requestHeaders = {
|
||||||
|
headers: value
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetMonitorView.RequestsMenu.updateMenuView(selectedItem, field, value);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the query string field based on the url.
|
||||||
|
*
|
||||||
|
* @param object aUrl
|
||||||
|
* url to extract query string from.
|
||||||
|
*/
|
||||||
|
updateCustomQuery: function(aUrl) {
|
||||||
|
let paramsArray = parseQueryString(nsIURL(aUrl).query);
|
||||||
|
if (!paramsArray) {
|
||||||
|
$("#custom-query").hidden = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$("#custom-query").hidden = false;
|
||||||
|
$("#custom-query-value").value = writeQueryText(paramsArray);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the url based on the query string field.
|
||||||
|
*
|
||||||
|
* @param object aQueryText
|
||||||
|
* contents of the query string field.
|
||||||
|
*/
|
||||||
|
updateCustomUrl: function(aQueryText) {
|
||||||
|
let params = parseQueryText(aQueryText);
|
||||||
|
let queryString = writeQueryString(params);
|
||||||
|
|
||||||
|
let url = $("#custom-url-value").value;
|
||||||
|
let oldQuery = nsIURL(url).query;
|
||||||
|
let path = url.replace(oldQuery, queryString);
|
||||||
|
|
||||||
|
$("#custom-url-value").value = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Functions handling the requests details view.
|
* Functions handling the requests details view.
|
||||||
*/
|
*/
|
||||||
@ -1253,9 +1486,9 @@ NetworkDetailsView.prototype = {
|
|||||||
* Initialization function, called when the network monitor is started.
|
* Initialization function, called when the network monitor is started.
|
||||||
*/
|
*/
|
||||||
initialize: function() {
|
initialize: function() {
|
||||||
dumpn("Initializing the RequestsMenuView");
|
dumpn("Initializing the NetworkDetailsView");
|
||||||
|
|
||||||
this.widget = $("#details-pane");
|
this.widget = $("#event-details-pane");
|
||||||
|
|
||||||
this._headers = new VariablesView($("#all-headers"),
|
this._headers = new VariablesView($("#all-headers"),
|
||||||
Heritage.extend(GENERIC_VARIABLES_VIEW_SETTINGS, {
|
Heritage.extend(GENERIC_VARIABLES_VIEW_SETTINGS, {
|
||||||
@ -1292,25 +1525,13 @@ NetworkDetailsView.prototype = {
|
|||||||
* Destruction function, called when the network monitor is closed.
|
* Destruction function, called when the network monitor is closed.
|
||||||
*/
|
*/
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
dumpn("Destroying the SourcesView");
|
dumpn("Destroying the NetworkDetailsView");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets this view hidden or visible. It's visible by default.
|
* Resets this container (removes all the networking information).
|
||||||
*
|
|
||||||
* @param boolean aVisibleFlag
|
|
||||||
* Specifies the intended visibility.
|
|
||||||
*/
|
|
||||||
toggle: function(aVisibleFlag) {
|
|
||||||
NetMonitorView.toggleDetailsPane({ visible: aVisibleFlag });
|
|
||||||
NetMonitorView.RequestsMenu._flushWaterfallViews(true);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hides and resets this container (removes all the networking information).
|
|
||||||
*/
|
*/
|
||||||
reset: function() {
|
reset: function() {
|
||||||
this.toggle(false);
|
|
||||||
this._dataSrc = null;
|
this._dataSrc = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1581,21 +1802,14 @@ NetworkDetailsView.prototype = {
|
|||||||
*
|
*
|
||||||
* @param string aName
|
* @param string aName
|
||||||
* The type of params to populate (get or post).
|
* The type of params to populate (get or post).
|
||||||
* @param string aParams
|
* @param string aQueryString
|
||||||
* A query string of params (e.g. "?foo=bar&baz=42").
|
* A query string of params (e.g. "?foo=bar&baz=42").
|
||||||
*/
|
*/
|
||||||
_addParams: function(aName, aParams) {
|
_addParams: function(aName, aQueryString) {
|
||||||
// Make sure there's at least one param available.
|
let paramsArray = parseQueryString(aQueryString);
|
||||||
if (!aParams || !aParams.contains("=")) {
|
if (!paramsArray) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Turn the params string into an array containing { name: value } tuples.
|
|
||||||
let paramsArray = aParams.replace(/^[?&]/, "").split("&").map((e) =>
|
|
||||||
let (param = e.split("=")) {
|
|
||||||
name: NetworkHelper.convertToUnicode(unescape(param[0])),
|
|
||||||
value: NetworkHelper.convertToUnicode(unescape(param[1]))
|
|
||||||
});
|
|
||||||
|
|
||||||
let paramsScope = this._params.addScope(aName);
|
let paramsScope = this._params.addScope(aName);
|
||||||
paramsScope.expanded = true;
|
paramsScope.expanded = true;
|
||||||
|
|
||||||
@ -1807,6 +2021,110 @@ function nsIURL(aUrl, aStore = nsIURL.store) {
|
|||||||
}
|
}
|
||||||
nsIURL.store = new Map();
|
nsIURL.store = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a url's query string into its components
|
||||||
|
*
|
||||||
|
* @param string aQueryString
|
||||||
|
* The query part of a url
|
||||||
|
* @return array
|
||||||
|
* Array of query params {name, value}
|
||||||
|
*/
|
||||||
|
function parseQueryString(aQueryString) {
|
||||||
|
// Make sure there's at least one param available.
|
||||||
|
if (!aQueryString || !aQueryString.contains("=")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Turn the params string into an array containing { name: value } tuples.
|
||||||
|
let paramsArray = aQueryString.replace(/^[?&]/, "").split("&").map((e) =>
|
||||||
|
let (param = e.split("=")) {
|
||||||
|
name: NetworkHelper.convertToUnicode(unescape(param[0])),
|
||||||
|
value: NetworkHelper.convertToUnicode(unescape(param[1]))
|
||||||
|
});
|
||||||
|
return paramsArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse text representation of HTTP headers.
|
||||||
|
*
|
||||||
|
* @param string aText
|
||||||
|
* Text of headers
|
||||||
|
* @return array
|
||||||
|
* Array of headers info {name, value}
|
||||||
|
*/
|
||||||
|
function parseHeaderText(aText) {
|
||||||
|
return parseRequestText(aText, ":");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse readable text list of a query string.
|
||||||
|
*
|
||||||
|
* @param string aText
|
||||||
|
* Text of query string represetation
|
||||||
|
* @return array
|
||||||
|
* Array of query params {name, value}
|
||||||
|
*/
|
||||||
|
function parseQueryText(aText) {
|
||||||
|
return parseRequestText(aText, "=");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a text representation of a name:value list with
|
||||||
|
* the given name:value divider character.
|
||||||
|
*
|
||||||
|
* @param string aText
|
||||||
|
* Text of list
|
||||||
|
* @return array
|
||||||
|
* Array of headers info {name, value}
|
||||||
|
*/
|
||||||
|
function parseRequestText(aText, aDivider) {
|
||||||
|
let regex = new RegExp("(.+?)\\" + aDivider + "\\s*(.+)");
|
||||||
|
let pairs = [];
|
||||||
|
for (let line of aText.split("\n")) {
|
||||||
|
let matches;
|
||||||
|
if (matches = regex.exec(line)) {
|
||||||
|
let [, name, value] = matches;
|
||||||
|
pairs.push({name: name, value: value});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pairs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write out a list of headers into a chunk of text
|
||||||
|
*
|
||||||
|
* @param array aHeaders
|
||||||
|
* Array of headers info {name, value}
|
||||||
|
* @return string aText
|
||||||
|
* List of headers in text format
|
||||||
|
*/
|
||||||
|
function writeHeaderText(aHeaders) {
|
||||||
|
return [(name + ": " + value) for ({name, value} of aHeaders)].join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write out a list of query params into a chunk of text
|
||||||
|
*
|
||||||
|
* @param array aParams
|
||||||
|
* Array of query params {name, value}
|
||||||
|
* @return string
|
||||||
|
* List of query params in text format
|
||||||
|
*/
|
||||||
|
function writeQueryText(aParams) {
|
||||||
|
return [(name + "=" + value) for ({name, value} of aParams)].join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write out a list of query params into a query string
|
||||||
|
*
|
||||||
|
* @param array aParams
|
||||||
|
* Array of query params {name, value}
|
||||||
|
* @return string
|
||||||
|
* Query string that can be appended to a url.
|
||||||
|
*/
|
||||||
|
function writeQueryString(aParams) {
|
||||||
|
return [(name + "=" + value) for ({name, value} of aParams)].join("&");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper for draining a rapid succession of events and invoking a callback
|
* Helper for draining a rapid succession of events and invoking a callback
|
||||||
* once everything settles down.
|
* once everything settles down.
|
||||||
@ -1822,4 +2140,6 @@ drain.store = new Map();
|
|||||||
*/
|
*/
|
||||||
NetMonitorView.Toolbar = new ToolbarView();
|
NetMonitorView.Toolbar = new ToolbarView();
|
||||||
NetMonitorView.RequestsMenu = new RequestsMenuView();
|
NetMonitorView.RequestsMenu = new RequestsMenuView();
|
||||||
|
NetMonitorView.Sidebar = new SidebarView();
|
||||||
|
NetMonitorView.CustomRequest = new CustomRequestView();
|
||||||
NetMonitorView.NetworkDetails = new NetworkDetailsView();
|
NetMonitorView.NetworkDetails = new NetworkDetailsView();
|
||||||
|
@ -15,6 +15,10 @@
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#custom-pane {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
#timings-summary-blocked {
|
#timings-summary-blocked {
|
||||||
display: none; /* This doesn't work yet. */
|
display: none; /* This doesn't work yet. */
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,16 @@
|
|||||||
<script type="text/javascript" src="netmonitor-controller.js"/>
|
<script type="text/javascript" src="netmonitor-controller.js"/>
|
||||||
<script type="text/javascript" src="netmonitor-view.js"/>
|
<script type="text/javascript" src="netmonitor-view.js"/>
|
||||||
|
|
||||||
|
<popupset id="networkPopupSet">
|
||||||
|
<menupopup id="network-request-popup"
|
||||||
|
onpopupshowing="NetMonitorView.RequestsMenu._onContextShowing(event);">
|
||||||
|
<menuitem id="request-menu-context-resend"
|
||||||
|
label="&netmonitorUI.summary.resend;"
|
||||||
|
accesskey="&netmonitorUI.summary.resend.accesskey;"
|
||||||
|
oncommand="NetMonitorView.RequestsMenu.cloneSelectedRequest();"/>
|
||||||
|
</menupopup>
|
||||||
|
</popupset>
|
||||||
|
|
||||||
<box id="body"
|
<box id="body"
|
||||||
class="devtools-responsive-container"
|
class="devtools-responsive-container"
|
||||||
flex="1">
|
flex="1">
|
||||||
@ -103,7 +113,7 @@
|
|||||||
</toolbar>
|
</toolbar>
|
||||||
<label id="requests-menu-empty-notice"
|
<label id="requests-menu-empty-notice"
|
||||||
value="&netmonitorUI.emptyNotice2;"/>
|
value="&netmonitorUI.emptyNotice2;"/>
|
||||||
<vbox id="requests-menu-contents" flex="1">
|
<vbox id="requests-menu-contents" flex="1" context="network-request-popup">
|
||||||
<hbox id="requests-menu-item-template" hidden="true">
|
<hbox id="requests-menu-item-template" hidden="true">
|
||||||
<hbox class="requests-menu-subitem requests-menu-status-and-method"
|
<hbox class="requests-menu-subitem requests-menu-status-and-method"
|
||||||
align="center">
|
align="center">
|
||||||
@ -194,9 +204,71 @@
|
|||||||
|
|
||||||
<splitter class="devtools-side-splitter"/>
|
<splitter class="devtools-side-splitter"/>
|
||||||
|
|
||||||
<tabbox id="details-pane"
|
<deck id="details-pane"
|
||||||
class="devtools-sidebar-tabs"
|
|
||||||
hidden="true">
|
hidden="true">
|
||||||
|
<vbox id="custom-pane"
|
||||||
|
class="tabpanel-content">
|
||||||
|
<hbox align="baseline">
|
||||||
|
<label value="&netmonitorUI.custom.newRequest;"
|
||||||
|
class="plain tabpanel-summary-label
|
||||||
|
custom-header"/>
|
||||||
|
<hbox flex="1" pack="end">
|
||||||
|
<button class="devtools-toolbarbutton"
|
||||||
|
label="&netmonitorUI.custom.send;"
|
||||||
|
onclick="NetMonitorView.RequestsMenu.sendCustomRequest();"/>
|
||||||
|
<button class="devtools-toolbarbutton"
|
||||||
|
label="&netmonitorUI.custom.cancel;"
|
||||||
|
onclick="NetMonitorView.RequestsMenu.closeCustomRequest();"/>
|
||||||
|
</hbox>
|
||||||
|
</hbox>
|
||||||
|
<hbox id="custom-method-and-url"
|
||||||
|
class="tabpanel-summary-container"
|
||||||
|
align="center">
|
||||||
|
<textbox id="custom-method-value"
|
||||||
|
oninput="NetMonitorView.CustomRequest.onUpdate('method');"
|
||||||
|
multiline="true"
|
||||||
|
cols="6"
|
||||||
|
rows="1"/>
|
||||||
|
<textbox id="custom-url-value"
|
||||||
|
flex="1"
|
||||||
|
oninput="NetMonitorView.CustomRequest.onUpdate('url');"/>
|
||||||
|
</hbox>
|
||||||
|
<vbox id="custom-query"
|
||||||
|
class="tabpanel-summary-container custom-section">
|
||||||
|
<label class="plain tabpanel-summary-label"
|
||||||
|
value="&netmonitorUI.custom.query;"/>
|
||||||
|
<textbox id="custom-query-value"
|
||||||
|
class="tabpanel-summary-input"
|
||||||
|
multiline="true"
|
||||||
|
rows="4"
|
||||||
|
wrap="off"
|
||||||
|
oninput="NetMonitorView.CustomRequest.onUpdate('query');"/>
|
||||||
|
</vbox>
|
||||||
|
<vbox id="custom-headers"
|
||||||
|
class="tabpanel-summary-container custom-section">
|
||||||
|
<label class="plain tabpanel-summary-label"
|
||||||
|
value="&netmonitorUI.custom.headers;"/>
|
||||||
|
<textbox id="custom-headers-value"
|
||||||
|
class="tabpanel-summary-input"
|
||||||
|
multiline="true"
|
||||||
|
rows="6"
|
||||||
|
wrap="off"
|
||||||
|
oninput="NetMonitorView.CustomRequest.onUpdate('headers');"/>
|
||||||
|
</vbox>
|
||||||
|
<vbox id="custom-postdata"
|
||||||
|
class="tabpanel-summary-container custom-section">
|
||||||
|
<label class="plain tabpanel-summary-label"
|
||||||
|
value="&netmonitorUI.custom.postData;"/>
|
||||||
|
<textbox id="custom-postdata-value"
|
||||||
|
class="tabpanel-summary-input"
|
||||||
|
multiline="true"
|
||||||
|
rows="6"
|
||||||
|
wrap="off"
|
||||||
|
oninput="NetMonitorView.CustomRequest.onUpdate('body');"/>
|
||||||
|
</vbox>
|
||||||
|
</vbox>
|
||||||
|
<tabbox id="event-details-pane"
|
||||||
|
class="devtools-sidebar-tabs">
|
||||||
<tabs>
|
<tabs>
|
||||||
<tab label="&netmonitorUI.tab.headers;"/>
|
<tab label="&netmonitorUI.tab.headers;"/>
|
||||||
<tab label="&netmonitorUI.tab.cookies;"/>
|
<tab label="&netmonitorUI.tab.cookies;"/>
|
||||||
@ -239,6 +311,10 @@
|
|||||||
class="plain tabpanel-summary-value"
|
class="plain tabpanel-summary-value"
|
||||||
crop="end"
|
crop="end"
|
||||||
flex="1"/>
|
flex="1"/>
|
||||||
|
<button id="headers-summary-resend"
|
||||||
|
label="&netmonitorUI.summary.resend;"
|
||||||
|
class="devtools-toolbarbutton"
|
||||||
|
onclick="NetMonitorView.RequestsMenu.cloneSelectedRequest();"/>
|
||||||
</hbox>
|
</hbox>
|
||||||
<hbox id="headers-summary-version"
|
<hbox id="headers-summary-version"
|
||||||
class="tabpanel-summary-container"
|
class="tabpanel-summary-container"
|
||||||
@ -372,6 +448,7 @@
|
|||||||
</tabpanel>
|
</tabpanel>
|
||||||
</tabpanels>
|
</tabpanels>
|
||||||
</tabbox>
|
</tabbox>
|
||||||
|
</deck>
|
||||||
</box>
|
</box>
|
||||||
|
|
||||||
</window>
|
</window>
|
||||||
|
@ -42,6 +42,7 @@ MOCHITEST_BROWSER_TESTS = \
|
|||||||
browser_net_accessibility-01.js \
|
browser_net_accessibility-01.js \
|
||||||
browser_net_accessibility-02.js \
|
browser_net_accessibility-02.js \
|
||||||
browser_net_footer-summary.js \
|
browser_net_footer-summary.js \
|
||||||
|
browser_net_resend.js \
|
||||||
head.js \
|
head.js \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@ function test() {
|
|||||||
NetMonitorView.toggleDetailsPane({ visible: true }, 2)
|
NetMonitorView.toggleDetailsPane({ visible: true }, 2)
|
||||||
RequestsMenu.selectedIndex = 0;
|
RequestsMenu.selectedIndex = 0;
|
||||||
|
|
||||||
let tab = document.querySelectorAll("#details-pane tab")[2];
|
let tab = document.querySelectorAll("#event-details-pane tab")[2];
|
||||||
let tabpanel = document.querySelectorAll("#details-pane tabpanel")[2];
|
let tabpanel = document.querySelectorAll("#event-details-pane tabpanel")[2];
|
||||||
|
|
||||||
is(tab.getAttribute("selected"), "true",
|
is(tab.getAttribute("selected"), "true",
|
||||||
"The params tab in the network details pane should be selected.");
|
"The params tab in the network details pane should be selected.");
|
||||||
|
167
browser/devtools/netmonitor/test/browser_net_resend.js
Normal file
167
browser/devtools/netmonitor/test/browser_net_resend.js
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
let gPanelWin;
|
||||||
|
let gPanelDoc;
|
||||||
|
|
||||||
|
const ADD_QUERY = "t1=t2";
|
||||||
|
const ADD_HEADER = "Test-header: true";
|
||||||
|
const ADD_POSTDATA = "t3=t4";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if resending a request works.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
initNetMonitor(POST_DATA_URL).then(([aTab, aDebuggee, aMonitor]) => {
|
||||||
|
info("Starting test... ");
|
||||||
|
|
||||||
|
gPanelWin = aMonitor.panelWin;
|
||||||
|
gPanelDoc = gPanelWin.document;
|
||||||
|
|
||||||
|
let { NetMonitorView } = gPanelWin;
|
||||||
|
let { RequestsMenu } = NetMonitorView;
|
||||||
|
|
||||||
|
RequestsMenu.lazyUpdate = false;
|
||||||
|
|
||||||
|
waitForNetworkEvents(aMonitor, 0, 2).then(() => {
|
||||||
|
let origItem = RequestsMenu.getItemAtIndex(0);
|
||||||
|
RequestsMenu.selectedItem = origItem;
|
||||||
|
|
||||||
|
// add a new custom request cloned from selected request
|
||||||
|
RequestsMenu.cloneSelectedRequest();
|
||||||
|
testCustomForm(origItem.attachment);
|
||||||
|
|
||||||
|
let customItem = RequestsMenu.selectedItem;
|
||||||
|
testCustomItem(customItem, origItem);
|
||||||
|
|
||||||
|
// edit the custom request
|
||||||
|
editCustomForm(() => {
|
||||||
|
testCustomItemChanged(customItem, origItem);
|
||||||
|
|
||||||
|
waitForNetworkEvents(aMonitor, 0, 1).then(() => {
|
||||||
|
let sentItem = RequestsMenu.selectedItem;
|
||||||
|
testSentRequest(sentItem.attachment, origItem.attachment);
|
||||||
|
finishUp(aMonitor);
|
||||||
|
});
|
||||||
|
// send the new request
|
||||||
|
RequestsMenu.sendCustomRequest();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
aDebuggee.performRequests();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function testCustomItem(aItem, aOrigItem) {
|
||||||
|
let method = aItem.target.querySelector(".requests-menu-method").value;
|
||||||
|
let origMethod = aOrigItem.target.querySelector(".requests-menu-method").value;
|
||||||
|
is(method, origMethod, "menu item is showing the same method as original request");
|
||||||
|
|
||||||
|
let file = aItem.target.querySelector(".requests-menu-file").value;
|
||||||
|
let origFile = aOrigItem.target.querySelector(".requests-menu-file").value;
|
||||||
|
is(file, origFile, "menu item is showing the same file name as original request");
|
||||||
|
|
||||||
|
let domain = aItem.target.querySelector(".requests-menu-domain").value;
|
||||||
|
let origDomain = aOrigItem.target.querySelector(".requests-menu-domain").value;
|
||||||
|
is(domain, origDomain, "menu item is showing the same domain as original request");
|
||||||
|
}
|
||||||
|
|
||||||
|
function testCustomItemChanged(aItem, aOrigItem) {
|
||||||
|
let file = aItem.target.querySelector(".requests-menu-file").value;
|
||||||
|
let expectedFile = aOrigItem.target.querySelector(".requests-menu-file").value + "&" + ADD_QUERY;
|
||||||
|
|
||||||
|
is(file, expectedFile, "menu item is updated to reflect url entered in form");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test that the New Request form was populated correctly
|
||||||
|
*/
|
||||||
|
function testCustomForm(aData) {
|
||||||
|
is(gPanelDoc.getElementById("custom-method-value").value, aData.method,
|
||||||
|
"new request form showing correct method");
|
||||||
|
|
||||||
|
is(gPanelDoc.getElementById("custom-url-value").value, aData.url,
|
||||||
|
"new request form showing correct url");
|
||||||
|
|
||||||
|
let query = gPanelDoc.getElementById("custom-query-value");
|
||||||
|
is(query.value, "foo=bar\nbaz=42\ntype=urlencoded",
|
||||||
|
"new request form showing correct query string");
|
||||||
|
|
||||||
|
let headers = gPanelDoc.getElementById("custom-headers-value").value.split("\n");
|
||||||
|
for (let {name, value} of aData.requestHeaders.headers) {
|
||||||
|
ok(headers.indexOf(name + ": " + value) >= 0, "form contains header from request");
|
||||||
|
}
|
||||||
|
|
||||||
|
let postData = gPanelDoc.getElementById("custom-postdata-value");
|
||||||
|
is(postData.value, aData.requestPostData.postData.text,
|
||||||
|
"new request form showing correct post data");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add some params and headers to the request form
|
||||||
|
*/
|
||||||
|
function editCustomForm(callback) {
|
||||||
|
gPanelWin.focus();
|
||||||
|
|
||||||
|
let query = gPanelDoc.getElementById("custom-query-value");
|
||||||
|
query.addEventListener("focus", function onFocus() {
|
||||||
|
query.removeEventListener("focus", onFocus, false);
|
||||||
|
|
||||||
|
// add params to url query string field
|
||||||
|
type(["VK_RETURN"]);
|
||||||
|
type(ADD_QUERY);
|
||||||
|
|
||||||
|
let headers = gPanelDoc.getElementById("custom-headers-value");
|
||||||
|
headers.addEventListener("focus", function onFocus() {
|
||||||
|
headers.removeEventListener("focus", onFocus, false);
|
||||||
|
|
||||||
|
// add a header
|
||||||
|
type(["VK_RETURN"]);
|
||||||
|
type(ADD_HEADER);
|
||||||
|
|
||||||
|
let postData = gPanelDoc.getElementById("custom-postdata-value");
|
||||||
|
postData.addEventListener("focus", function onFocus() {
|
||||||
|
postData.removeEventListener("focus", onFocus, false);
|
||||||
|
|
||||||
|
// add to POST data
|
||||||
|
type(ADD_POSTDATA);
|
||||||
|
callback();
|
||||||
|
}, false);
|
||||||
|
postData.focus();
|
||||||
|
}, false);
|
||||||
|
headers.focus();
|
||||||
|
}, false);
|
||||||
|
query.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure newly created event matches expected request
|
||||||
|
*/
|
||||||
|
function testSentRequest(aData, aOrigData) {
|
||||||
|
is(aData.method, aOrigData.method, "correct method in sent request");
|
||||||
|
is(aData.url, aOrigData.url + "&" + ADD_QUERY, "correct url in sent request");
|
||||||
|
|
||||||
|
let hasHeader = aData.requestHeaders.headers.some((header) => {
|
||||||
|
return (header.name + ": " + header.value) == ADD_HEADER;
|
||||||
|
})
|
||||||
|
ok(hasHeader, "new header added to sent request");
|
||||||
|
|
||||||
|
is(aData.requestPostData.postData.text,
|
||||||
|
aOrigData.requestPostData.postData.text + ADD_POSTDATA,
|
||||||
|
"post data added to sent request");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function type(aString) {
|
||||||
|
for (let ch of aString) {
|
||||||
|
EventUtils.synthesizeKey(ch, {}, gPanelWin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishUp(aMonitor) {
|
||||||
|
gPanelWin = null;
|
||||||
|
gPanelDoc = null;
|
||||||
|
|
||||||
|
teardown(aMonitor).then(finish);
|
||||||
|
}
|
@ -140,6 +140,7 @@ MOCHITEST_BROWSER_FILES = \
|
|||||||
browser_console_keyboard_accessibility.js \
|
browser_console_keyboard_accessibility.js \
|
||||||
browser_console_filters.js \
|
browser_console_filters.js \
|
||||||
browser_console_dead_objects.js \
|
browser_console_dead_objects.js \
|
||||||
|
browser_console_iframe_messages.js \
|
||||||
head.js \
|
head.js \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
@ -239,6 +240,10 @@ MOCHITEST_BROWSER_FILES += \
|
|||||||
test-bug-837351-security-errors.html \
|
test-bug-837351-security-errors.html \
|
||||||
test-bug-869003-top-window.html \
|
test-bug-869003-top-window.html \
|
||||||
test-bug-869003-iframe.html \
|
test-bug-869003-iframe.html \
|
||||||
|
test-consoleiframes.html \
|
||||||
|
test-iframe1.html \
|
||||||
|
test-iframe2.html \
|
||||||
|
test-iframe3.html \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
include $(topsrcdir)/config/rules.mk
|
include $(topsrcdir)/config/rules.mk
|
||||||
|
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Check that cached messages from nested iframes are displayed in the
|
||||||
|
// Web/Browser Console.
|
||||||
|
|
||||||
|
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-consoleiframes.html";
|
||||||
|
|
||||||
|
function test()
|
||||||
|
{
|
||||||
|
expectUncaughtException();
|
||||||
|
addTab(TEST_URI);
|
||||||
|
browser.addEventListener("load", function onLoad() {
|
||||||
|
browser.removeEventListener("load", onLoad, true);
|
||||||
|
|
||||||
|
// Test for cached nsIConsoleMessages.
|
||||||
|
Services.console.logStringMessage("test1 for bug859756");
|
||||||
|
|
||||||
|
info("open web console");
|
||||||
|
openConsole(null, consoleOpened);
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function consoleOpened(hud)
|
||||||
|
{
|
||||||
|
ok(hud, "web console opened");
|
||||||
|
|
||||||
|
waitForMessages({
|
||||||
|
webconsole: hud,
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
text: "main file",
|
||||||
|
category: CATEGORY_WEBDEV,
|
||||||
|
severity: SEVERITY_LOG,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "blah",
|
||||||
|
category: CATEGORY_JS,
|
||||||
|
severity: SEVERITY_ERROR
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "iframe 1",
|
||||||
|
category: CATEGORY_WEBDEV,
|
||||||
|
severity: SEVERITY_LOG,
|
||||||
|
count: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "iframe 2",
|
||||||
|
category: CATEGORY_WEBDEV,
|
||||||
|
severity: SEVERITY_LOG
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}).then(() => {
|
||||||
|
closeConsole(null, onWebConsoleClose);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onWebConsoleClose()
|
||||||
|
{
|
||||||
|
info("web console closed");
|
||||||
|
HUDConsoleUI.toggleBrowserConsole().then(onBrowserConsoleOpen);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onBrowserConsoleOpen(hud)
|
||||||
|
{
|
||||||
|
ok(hud, "browser console opened");
|
||||||
|
Services.console.logStringMessage("test2 for bug859756");
|
||||||
|
|
||||||
|
waitForMessages({
|
||||||
|
webconsole: hud,
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
text: "main file",
|
||||||
|
category: CATEGORY_WEBDEV,
|
||||||
|
severity: SEVERITY_LOG,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "blah",
|
||||||
|
category: CATEGORY_JS,
|
||||||
|
severity: SEVERITY_ERROR
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "iframe 1",
|
||||||
|
category: CATEGORY_WEBDEV,
|
||||||
|
severity: SEVERITY_LOG,
|
||||||
|
count: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "iframe 2",
|
||||||
|
category: CATEGORY_WEBDEV,
|
||||||
|
severity: SEVERITY_LOG
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}).then(() => {
|
||||||
|
closeConsole(null, finishTest);
|
||||||
|
});
|
||||||
|
}
|
@ -15,6 +15,7 @@ let TargetFactory = tempScope.devtools.TargetFactory;
|
|||||||
Components.utils.import("resource://gre/modules/devtools/Console.jsm", tempScope);
|
Components.utils.import("resource://gre/modules/devtools/Console.jsm", tempScope);
|
||||||
let console = tempScope.console;
|
let console = tempScope.console;
|
||||||
let Promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}).Promise;
|
let Promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}).Promise;
|
||||||
|
// Promise._reportErrors = true; // please never leave me.
|
||||||
|
|
||||||
let gPendingOutputTest = 0;
|
let gPendingOutputTest = 0;
|
||||||
|
|
||||||
|
13
browser/devtools/webconsole/test/test-consoleiframes.html
Normal file
13
browser/devtools/webconsole/test/test-consoleiframes.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script>
|
||||||
|
console.log("main file");
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>iframe console test</h1>
|
||||||
|
<iframe src="test-iframe1.html"></iframe>
|
||||||
|
<iframe src="test-iframe2.html"></iframe>
|
||||||
|
<iframe src="test-iframe3.html"></iframe>
|
||||||
|
</body>
|
||||||
|
</html>
|
10
browser/devtools/webconsole/test/test-iframe1.html
Normal file
10
browser/devtools/webconsole/test/test-iframe1.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script>
|
||||||
|
console.log("iframe 1");
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>iframe 1</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
11
browser/devtools/webconsole/test/test-iframe2.html
Normal file
11
browser/devtools/webconsole/test/test-iframe2.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script>
|
||||||
|
console.log("iframe 2");
|
||||||
|
blah;
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>iframe 2</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
11
browser/devtools/webconsole/test/test-iframe3.html
Normal file
11
browser/devtools/webconsole/test/test-iframe3.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script>
|
||||||
|
console.log("iframe 3");
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>iframe 3</h1>
|
||||||
|
<iframe src="test-iframe1.html"></iframe>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -383,18 +383,12 @@ WebConsoleFrame.prototype = {
|
|||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
_persistLog: null,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter for the persistent logging preference. This value is cached per
|
* Getter for the persistent logging preference.
|
||||||
* instance to avoid reading the pref too often.
|
|
||||||
* @type boolean
|
* @type boolean
|
||||||
*/
|
*/
|
||||||
get persistLog() {
|
get persistLog() {
|
||||||
if (this._persistLog === null) {
|
return Services.prefs.getBoolPref(PREF_PERSISTLOG);
|
||||||
this._persistLog = Services.prefs.getBoolPref(PREF_PERSISTLOG);
|
|
||||||
}
|
|
||||||
return this._persistLog;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -168,3 +168,36 @@
|
|||||||
- in the network details timings tab identifying the amount of time spent
|
- in the network details timings tab identifying the amount of time spent
|
||||||
- in a "receive" state. -->
|
- in a "receive" state. -->
|
||||||
<!ENTITY netmonitorUI.timings.receive "Receiving:">
|
<!ENTITY netmonitorUI.timings.receive "Receiving:">
|
||||||
|
|
||||||
|
<!-- LOCALIZATION NOTE (debuggerUI.custom.headers): This is the label displayed
|
||||||
|
- on the button in the headers tab that opens a form to resend the currently
|
||||||
|
displayed request -->
|
||||||
|
<!ENTITY netmonitorUI.summary.resend "Resend">
|
||||||
|
|
||||||
|
<!-- LOCALIZATION NOTE (debuggerUI.custom.headers): This is the access key
|
||||||
|
- for the Resend menu item displayed in the context menu for a request -->
|
||||||
|
<!ENTITY netmonitorUI.summary.resend.accesskey "R">
|
||||||
|
|
||||||
|
<!-- LOCALIZATION NOTE (debuggerUI.custom.newRequest): This is the label displayed
|
||||||
|
- as the title of the new custom request form -->
|
||||||
|
<!ENTITY netmonitorUI.custom.newRequest "New Request">
|
||||||
|
|
||||||
|
<!-- LOCALIZATION NOTE (debuggerUI.custom.query): This is the label displayed
|
||||||
|
- above the query string entry in the custom request form -->
|
||||||
|
<!ENTITY netmonitorUI.custom.query "Query String:">
|
||||||
|
|
||||||
|
<!-- LOCALIZATION NOTE (debuggerUI.custom.headers): This is the label displayed
|
||||||
|
- above the request headers entry in the custom request form -->
|
||||||
|
<!ENTITY netmonitorUI.custom.headers "Request Headers:">
|
||||||
|
|
||||||
|
<!-- LOCALIZATION NOTE (debuggerUI.custom.headers): This is the label displayed
|
||||||
|
- above the request body entry in the custom request form -->
|
||||||
|
<!ENTITY netmonitorUI.custom.postData "Request Body:">
|
||||||
|
|
||||||
|
<!-- LOCALIZATION NOTE (debuggerUI.custom.headers): This is the label displayed
|
||||||
|
- on the button which sends the custom request -->
|
||||||
|
<!ENTITY netmonitorUI.custom.send "Send">
|
||||||
|
|
||||||
|
<!-- LOCALIZATION NOTE (debuggerUI.custom.headers): This is the label displayed
|
||||||
|
- on the button which cancels and closes the custom request form -->
|
||||||
|
<!ENTITY netmonitorUI.custom.cancel "Cancel">
|
||||||
|
@ -383,6 +383,11 @@ box.requests-menu-status[code^="5"] {
|
|||||||
padding-top: 2px;
|
padding-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#headers-summary-resend {
|
||||||
|
margin: 0 6px;
|
||||||
|
min-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Response tabpanel */
|
/* Response tabpanel */
|
||||||
|
|
||||||
#response-content-info-header {
|
#response-content-info-header {
|
||||||
@ -422,6 +427,20 @@ box.requests-menu-status[code^="5"] {
|
|||||||
transition: transform 0.2s ease-out;
|
transition: transform 0.2s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Custom request form */
|
||||||
|
|
||||||
|
#custom-pane {
|
||||||
|
padding: 0.6em 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-header {
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-section {
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
/* Footer */
|
/* Footer */
|
||||||
|
|
||||||
#requests-menu-footer {
|
#requests-menu-footer {
|
||||||
|
@ -383,6 +383,11 @@ box.requests-menu-status[code^="5"] {
|
|||||||
padding-top: 2px;
|
padding-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#headers-summary-resend {
|
||||||
|
margin: 0 6px;
|
||||||
|
min-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Response tabpanel */
|
/* Response tabpanel */
|
||||||
|
|
||||||
#response-content-info-header {
|
#response-content-info-header {
|
||||||
@ -422,6 +427,20 @@ box.requests-menu-status[code^="5"] {
|
|||||||
transition: transform 0.2s ease-out;
|
transition: transform 0.2s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Custom request form */
|
||||||
|
|
||||||
|
#custom-pane {
|
||||||
|
padding: 0.6em 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-header {
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-section {
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
/* Footer */
|
/* Footer */
|
||||||
|
|
||||||
#requests-menu-footer {
|
#requests-menu-footer {
|
||||||
|
@ -383,6 +383,11 @@ box.requests-menu-status[code^="5"] {
|
|||||||
padding-top: 2px;
|
padding-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#headers-summary-resend {
|
||||||
|
margin: 0 6px;
|
||||||
|
min-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Response tabpanel */
|
/* Response tabpanel */
|
||||||
|
|
||||||
#response-content-info-header {
|
#response-content-info-header {
|
||||||
@ -422,6 +427,20 @@ box.requests-menu-status[code^="5"] {
|
|||||||
transition: transform 0.2s ease-out;
|
transition: transform 0.2s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Custom request form */
|
||||||
|
|
||||||
|
#custom-pane {
|
||||||
|
padding: 0.6em 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-header {
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-section {
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
/* Footer */
|
/* Footer */
|
||||||
|
|
||||||
#requests-menu-footer {
|
#requests-menu-footer {
|
||||||
|
@ -89,6 +89,7 @@ function WebConsoleActor(aConnection, aParentActor)
|
|||||||
|
|
||||||
this._protoChains = new Map();
|
this._protoChains = new Map();
|
||||||
this._dbgGlobals = new Map();
|
this._dbgGlobals = new Map();
|
||||||
|
this._netEvents = new Map();
|
||||||
this._getDebuggerGlobal(this.window);
|
this._getDebuggerGlobal(this.window);
|
||||||
|
|
||||||
this._onObserverNotification = this._onObserverNotification.bind(this);
|
this._onObserverNotification = this._onObserverNotification.bind(this);
|
||||||
@ -147,6 +148,15 @@ WebConsoleActor.prototype =
|
|||||||
*/
|
*/
|
||||||
_dbgGlobals: null,
|
_dbgGlobals: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds a map between nsIChannel objects and NetworkEventActors for requests
|
||||||
|
* created with sendHTTPRequest.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @type Map
|
||||||
|
*/
|
||||||
|
_netEvents: null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object that holds the JSTerm API, the helper functions, for the default
|
* Object that holds the JSTerm API, the helper functions, for the default
|
||||||
* window object.
|
* window object.
|
||||||
@ -252,6 +262,8 @@ WebConsoleActor.prototype =
|
|||||||
"last-pb-context-exited");
|
"last-pb-context-exited");
|
||||||
}
|
}
|
||||||
this._actorPool = null;
|
this._actorPool = null;
|
||||||
|
|
||||||
|
this._netEvents.clear();
|
||||||
this._protoChains.clear();
|
this._protoChains.clear();
|
||||||
this._dbgGlobals.clear();
|
this._dbgGlobals.clear();
|
||||||
this._jstermHelpers = null;
|
this._jstermHelpers = null;
|
||||||
@ -551,7 +563,7 @@ WebConsoleActor.prototype =
|
|||||||
else {
|
else {
|
||||||
message = {
|
message = {
|
||||||
_type: "LogMessage",
|
_type: "LogMessage",
|
||||||
message: aMessage.message,
|
message: this._createStringGrip(aMessage.message),
|
||||||
timeStamp: aMessage.timeStamp,
|
timeStamp: aMessage.timeStamp,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -948,10 +960,15 @@ WebConsoleActor.prototype =
|
|||||||
*/
|
*/
|
||||||
preparePageErrorForRemote: function WCA_preparePageErrorForRemote(aPageError)
|
preparePageErrorForRemote: function WCA_preparePageErrorForRemote(aPageError)
|
||||||
{
|
{
|
||||||
|
let lineText = aPageError.sourceLine;
|
||||||
|
if (lineText && lineText.length > DebuggerServer.LONG_STRING_INITIAL_LENGTH) {
|
||||||
|
lineText = lineText.substr(0, DebuggerServer.LONG_STRING_INITIAL_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
errorMessage: this._createStringGrip(aPageError.errorMessage),
|
errorMessage: this._createStringGrip(aPageError.errorMessage),
|
||||||
sourceName: aPageError.sourceName,
|
sourceName: aPageError.sourceName,
|
||||||
lineText: this._createStringGrip(aPageError.sourceLine),
|
lineText: lineText,
|
||||||
lineNumber: aPageError.lineNumber,
|
lineNumber: aPageError.lineNumber,
|
||||||
columnNumber: aPageError.columnNumber,
|
columnNumber: aPageError.columnNumber,
|
||||||
category: aPageError.category,
|
category: aPageError.category,
|
||||||
@ -995,10 +1012,10 @@ WebConsoleActor.prototype =
|
|||||||
* A new NetworkEventActor is returned. This is used for tracking the
|
* A new NetworkEventActor is returned. This is used for tracking the
|
||||||
* network request and response.
|
* network request and response.
|
||||||
*/
|
*/
|
||||||
onNetworkEvent: function WCA_onNetworkEvent(aEvent)
|
onNetworkEvent: function WCA_onNetworkEvent(aEvent, aChannel)
|
||||||
{
|
{
|
||||||
let actor = new NetworkEventActor(aEvent, this);
|
let actor = this.getNetworkEventActor(aChannel);
|
||||||
this._actorPool.addActor(actor);
|
actor.init(aEvent);
|
||||||
|
|
||||||
let packet = {
|
let packet = {
|
||||||
from: this.actorID,
|
from: this.actorID,
|
||||||
@ -1011,6 +1028,57 @@ WebConsoleActor.prototype =
|
|||||||
return actor;
|
return actor;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the NetworkEventActor for a nsIChannel, if it exists,
|
||||||
|
* otherwise create a new one.
|
||||||
|
*
|
||||||
|
* @param object aChannel
|
||||||
|
* The channel for the network event.
|
||||||
|
*/
|
||||||
|
getNetworkEventActor: function WCA_getNetworkEventActor(aChannel) {
|
||||||
|
let actor = this._netEvents.get(aChannel);
|
||||||
|
if (actor) {
|
||||||
|
// delete from map as we should only need to do this check once
|
||||||
|
this._netEvents.delete(aChannel);
|
||||||
|
actor.channel = null;
|
||||||
|
return actor;
|
||||||
|
}
|
||||||
|
|
||||||
|
actor = new NetworkEventActor(aChannel, this);
|
||||||
|
this._actorPool.addActor(actor);
|
||||||
|
return actor;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a new HTTP request from the target's window.
|
||||||
|
*
|
||||||
|
* @param object aMessage
|
||||||
|
* Object with 'request' - the HTTP request details.
|
||||||
|
*/
|
||||||
|
onSendHTTPRequest: function WCA_onSendHTTPRequest(aMessage)
|
||||||
|
{
|
||||||
|
let details = aMessage.request;
|
||||||
|
|
||||||
|
// send request from target's window
|
||||||
|
let request = new this._window.XMLHttpRequest();
|
||||||
|
request.open(details.method, details.url, true);
|
||||||
|
|
||||||
|
for (let {name, value} of details.headers) {
|
||||||
|
request.setRequestHeader(name, value);
|
||||||
|
}
|
||||||
|
request.send(details.body);
|
||||||
|
|
||||||
|
let actor = this.getNetworkEventActor(request.channel);
|
||||||
|
|
||||||
|
// map channel to actor so we can associate future events with it
|
||||||
|
this._netEvents.set(request.channel, actor);
|
||||||
|
|
||||||
|
return {
|
||||||
|
from: this.actorID,
|
||||||
|
eventActor: actor.grip()
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for file activity. This method sends the file request information
|
* Handler for file activity. This method sends the file request information
|
||||||
* to the remote Web Console client.
|
* to the remote Web Console client.
|
||||||
@ -1108,7 +1176,7 @@ WebConsoleActor.prototype =
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
WebConsoleActor.prototype.requestTypes =
|
WebConsoleActor.prototype.requestTypes =
|
||||||
@ -1120,32 +1188,31 @@ WebConsoleActor.prototype.requestTypes =
|
|||||||
autocomplete: WebConsoleActor.prototype.onAutocomplete,
|
autocomplete: WebConsoleActor.prototype.onAutocomplete,
|
||||||
clearMessagesCache: WebConsoleActor.prototype.onClearMessagesCache,
|
clearMessagesCache: WebConsoleActor.prototype.onClearMessagesCache,
|
||||||
setPreferences: WebConsoleActor.prototype.onSetPreferences,
|
setPreferences: WebConsoleActor.prototype.onSetPreferences,
|
||||||
|
sendHTTPRequest: WebConsoleActor.prototype.onSendHTTPRequest
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an actor for a network event.
|
* Creates an actor for a network event.
|
||||||
*
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param object aNetworkEvent
|
* @param object aChannel
|
||||||
* The network event you want to use the actor for.
|
* The nsIChannel associated with this event.
|
||||||
* @param object aWebConsoleActor
|
* @param object aWebConsoleActor
|
||||||
* The parent WebConsoleActor instance for this object.
|
* The parent WebConsoleActor instance for this object.
|
||||||
*/
|
*/
|
||||||
function NetworkEventActor(aNetworkEvent, aWebConsoleActor)
|
function NetworkEventActor(aChannel, aWebConsoleActor)
|
||||||
{
|
{
|
||||||
this.parent = aWebConsoleActor;
|
this.parent = aWebConsoleActor;
|
||||||
this.conn = this.parent.conn;
|
this.conn = this.parent.conn;
|
||||||
|
this.channel = aChannel;
|
||||||
this._startedDateTime = aNetworkEvent.startedDateTime;
|
|
||||||
this._isXHR = aNetworkEvent.isXHR;
|
|
||||||
|
|
||||||
this._request = {
|
this._request = {
|
||||||
method: aNetworkEvent.method,
|
method: null,
|
||||||
url: aNetworkEvent.url,
|
url: null,
|
||||||
httpVersion: aNetworkEvent.httpVersion,
|
httpVersion: null,
|
||||||
headers: [],
|
headers: [],
|
||||||
cookies: [],
|
cookies: [],
|
||||||
headersSize: aNetworkEvent.headersSize,
|
headersSize: null,
|
||||||
postData: {},
|
postData: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1159,10 +1226,6 @@ function NetworkEventActor(aNetworkEvent, aWebConsoleActor)
|
|||||||
|
|
||||||
// Keep track of LongStringActors owned by this NetworkEventActor.
|
// Keep track of LongStringActors owned by this NetworkEventActor.
|
||||||
this._longStringActors = new Set();
|
this._longStringActors = new Set();
|
||||||
|
|
||||||
this._discardRequestBody = aNetworkEvent.discardRequestBody;
|
|
||||||
this._discardResponseBody = aNetworkEvent.discardResponseBody;
|
|
||||||
this._private = aNetworkEvent.private;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkEventActor.prototype =
|
NetworkEventActor.prototype =
|
||||||
@ -1201,6 +1264,10 @@ NetworkEventActor.prototype =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._longStringActors = new Set();
|
this._longStringActors = new Set();
|
||||||
|
|
||||||
|
if (this.channel) {
|
||||||
|
this.parent._netEvents.delete(this.channel);
|
||||||
|
}
|
||||||
this.parent.releaseActor(this);
|
this.parent.releaseActor(this);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1213,6 +1280,27 @@ NetworkEventActor.prototype =
|
|||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the properties of this actor based on it's corresponding
|
||||||
|
* network event.
|
||||||
|
*
|
||||||
|
* @param object aNetworkEvent
|
||||||
|
* The network event associated with this actor.
|
||||||
|
*/
|
||||||
|
init: function NEA_init(aNetworkEvent)
|
||||||
|
{
|
||||||
|
this._startedDateTime = aNetworkEvent.startedDateTime;
|
||||||
|
this._isXHR = aNetworkEvent.isXHR;
|
||||||
|
|
||||||
|
for (let prop of ['method', 'url', 'httpVersion', 'headersSize']) {
|
||||||
|
this._request[prop] = aNetworkEvent[prop];
|
||||||
|
}
|
||||||
|
|
||||||
|
this._discardRequestBody = aNetworkEvent.discardRequestBody;
|
||||||
|
this._discardResponseBody = aNetworkEvent.discardResponseBody;
|
||||||
|
this._private = aNetworkEvent.private;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The "getRequestHeaders" packet type handler.
|
* The "getRequestHeaders" packet type handler.
|
||||||
*
|
*
|
||||||
@ -1540,4 +1628,3 @@ NetworkEventActor.prototype.requestTypes =
|
|||||||
DebuggerServer.addTabActor(WebConsoleActor, "consoleActor");
|
DebuggerServer.addTabActor(WebConsoleActor, "consoleActor");
|
||||||
DebuggerServer.addGlobalActor(WebConsoleActor, "consoleActor");
|
DebuggerServer.addGlobalActor(WebConsoleActor, "consoleActor");
|
||||||
|
|
||||||
|
|
||||||
|
@ -284,6 +284,23 @@ WebConsoleClient.prototype = {
|
|||||||
this._client.request(packet, aOnResponse);
|
this._client.request(packet, aOnResponse);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a HTTP request with the given data.
|
||||||
|
*
|
||||||
|
* @param string aData
|
||||||
|
* The details of the HTTP request.
|
||||||
|
* @param function aOnResponse
|
||||||
|
* The function invoked when the response is received.
|
||||||
|
*/
|
||||||
|
sendHTTPRequest: function WCC_sendHTTPRequest(aData, aOnResponse) {
|
||||||
|
let packet = {
|
||||||
|
to: this._actor,
|
||||||
|
type: "sendHTTPRequest",
|
||||||
|
request: aData
|
||||||
|
};
|
||||||
|
this._client.request(packet, aOnResponse);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the given Web Console listeners.
|
* Start the given Web Console listeners.
|
||||||
*
|
*
|
||||||
|
@ -147,6 +147,30 @@ this.WebConsoleUtils = {
|
|||||||
getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
|
getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively gather a list of inner window ids given a
|
||||||
|
* top level window.
|
||||||
|
*
|
||||||
|
* @param nsIDOMWindow aWindow
|
||||||
|
* @return Array
|
||||||
|
* list of inner window ids.
|
||||||
|
*/
|
||||||
|
getInnerWindowIDsForFrames: function WCU_getInnerWindowIDsForFrames(aWindow)
|
||||||
|
{
|
||||||
|
let innerWindowID = this.getInnerWindowId(aWindow);
|
||||||
|
let ids = [innerWindowID];
|
||||||
|
|
||||||
|
if (aWindow.frames) {
|
||||||
|
for (let i = 0; i < aWindow.frames.length; i++) {
|
||||||
|
let frame = aWindow.frames[i];
|
||||||
|
ids = ids.concat(this.getInnerWindowIDsForFrames(frame));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ids;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the ID of the outer window of this DOM window.
|
* Gets the ID of the outer window of this DOM window.
|
||||||
*
|
*
|
||||||
@ -986,7 +1010,7 @@ ConsoleServiceListener.prototype =
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the cached page errors for the current inner window.
|
* Get the cached page errors for the current inner window and its (i)frames.
|
||||||
*
|
*
|
||||||
* @param boolean [aIncludePrivate=false]
|
* @param boolean [aIncludePrivate=false]
|
||||||
* Tells if you want to also retrieve messages coming from private
|
* Tells if you want to also retrieve messages coming from private
|
||||||
@ -997,22 +1021,36 @@ ConsoleServiceListener.prototype =
|
|||||||
*/
|
*/
|
||||||
getCachedMessages: function CSL_getCachedMessages(aIncludePrivate = false)
|
getCachedMessages: function CSL_getCachedMessages(aIncludePrivate = false)
|
||||||
{
|
{
|
||||||
let innerWindowID = this.window ?
|
|
||||||
WebConsoleUtils.getInnerWindowId(this.window) : null;
|
|
||||||
let errors = Services.console.getMessageArray() || [];
|
let errors = Services.console.getMessageArray() || [];
|
||||||
|
|
||||||
|
// if !this.window, we're in a browser console. Still need to filter
|
||||||
|
// private messages.
|
||||||
|
if (!this.window) {
|
||||||
|
return errors.filter((aError) => {
|
||||||
|
if (aError instanceof Ci.nsIScriptError) {
|
||||||
|
if (!aIncludePrivate && aError.isFromPrivateWindow) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let ids = WebConsoleUtils.getInnerWindowIDsForFrames(this.window);
|
||||||
|
|
||||||
return errors.filter((aError) => {
|
return errors.filter((aError) => {
|
||||||
if (aError instanceof Ci.nsIScriptError) {
|
if (aError instanceof Ci.nsIScriptError) {
|
||||||
if (!aIncludePrivate && aError.isFromPrivateWindow) {
|
if (!aIncludePrivate && aError.isFromPrivateWindow) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (innerWindowID &&
|
if (ids &&
|
||||||
(aError.innerWindowID != innerWindowID ||
|
(ids.indexOf(aError.innerWindowID) == -1 ||
|
||||||
!this.isCategoryAllowed(aError.category))) {
|
!this.isCategoryAllowed(aError.category))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (innerWindowID) {
|
else if (ids && ids[0]) {
|
||||||
// If this is not an nsIScriptError and we need to do window-based
|
// If this is not an nsIScriptError and we need to do window-based
|
||||||
// filtering we skip this message.
|
// filtering we skip this message.
|
||||||
return false;
|
return false;
|
||||||
@ -1115,7 +1153,7 @@ ConsoleAPIListener.prototype =
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the cached messages for the current inner window.
|
* Get the cached messages for the current inner window and its (i)frames.
|
||||||
*
|
*
|
||||||
* @param boolean [aIncludePrivate=false]
|
* @param boolean [aIncludePrivate=false]
|
||||||
* Tells if you want to also retrieve messages coming from private
|
* Tells if you want to also retrieve messages coming from private
|
||||||
@ -1125,14 +1163,24 @@ ConsoleAPIListener.prototype =
|
|||||||
*/
|
*/
|
||||||
getCachedMessages: function CAL_getCachedMessages(aIncludePrivate = false)
|
getCachedMessages: function CAL_getCachedMessages(aIncludePrivate = false)
|
||||||
{
|
{
|
||||||
let innerWindowId = this.window ?
|
let messages = [];
|
||||||
WebConsoleUtils.getInnerWindowId(this.window) : null;
|
|
||||||
let events = ConsoleAPIStorage.getEvents(innerWindowId);
|
// if !this.window, we're in a browser console. Retrieve all events
|
||||||
if (aIncludePrivate) {
|
// for filtering based on privacy.
|
||||||
return events;
|
if (!this.window) {
|
||||||
|
messages = ConsoleAPIStorage.getEvents();
|
||||||
|
} else {
|
||||||
|
let ids = WebConsoleUtils.getInnerWindowIDsForFrames(this.window);
|
||||||
|
ids.forEach((id) => {
|
||||||
|
messages = messages.concat(ConsoleAPIStorage.getEvents(id));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return events.filter((m) => !m.private);
|
if (aIncludePrivate) {
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages.filter((m) => !m.private);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1700,10 +1748,10 @@ NetworkResponseListener.prototype = {
|
|||||||
* window is given, all browser network requests are logged.
|
* window is given, all browser network requests are logged.
|
||||||
* @param object aOwner
|
* @param object aOwner
|
||||||
* The network monitor owner. This object needs to hold:
|
* The network monitor owner. This object needs to hold:
|
||||||
* - onNetworkEvent(aRequestInfo). This method is invoked once for every
|
* - onNetworkEvent(aRequestInfo, aChannel). This method is invoked once for
|
||||||
* new network request and it is given one arguments: the initial network
|
* every new network request and it is given two arguments: the initial network
|
||||||
* request information. onNetworkEvent() must return an object which
|
* request information, and the channel. onNetworkEvent() must return an object
|
||||||
* holds several add*() methods which are used to add further network
|
* which holds several add*() methods which are used to add further network
|
||||||
* request/response information.
|
* request/response information.
|
||||||
* - saveRequestAndResponseBodies property which tells if you want to log
|
* - saveRequestAndResponseBodies property which tells if you want to log
|
||||||
* request and response bodies.
|
* request and response bodies.
|
||||||
@ -2004,7 +2052,7 @@ NetworkMonitor.prototype = {
|
|||||||
cookies = NetworkHelper.parseCookieHeader(cookieHeader);
|
cookies = NetworkHelper.parseCookieHeader(cookieHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
httpActivity.owner = this.owner.onNetworkEvent(event);
|
httpActivity.owner = this.owner.onNetworkEvent(event, aChannel);
|
||||||
|
|
||||||
this._setupResponseListener(httpActivity);
|
this._setupResponseListener(httpActivity);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user