mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 707740 - Ability to lock in a pseudo class in the page inspector. r=dcamp
This commit is contained in:
parent
e6c503006c
commit
edb51b91d5
@ -43,7 +43,11 @@
|
|||||||
* ***** END LICENSE BLOCK ***** */
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
const Cu = Components.utils;
|
const Cu = Components.utils;
|
||||||
|
const Cc = Components.classes;
|
||||||
|
const Ci = Components.interfaces;
|
||||||
|
|
||||||
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
|
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
|
||||||
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["Highlighter"];
|
var EXPORTED_SYMBOLS = ["Highlighter"];
|
||||||
|
|
||||||
@ -59,6 +63,9 @@ const INSPECTOR_INVISIBLE_ELEMENTS = {
|
|||||||
"title": true,
|
"title": true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
|
||||||
|
// add ":visited" and ":link" after bug 713106 is fixed
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A highlighter mechanism.
|
* A highlighter mechanism.
|
||||||
*
|
*
|
||||||
@ -109,6 +116,8 @@ const INSPECTOR_INVISIBLE_ELEMENTS = {
|
|||||||
* "highlighting" - Highlighter is highlighting
|
* "highlighting" - Highlighter is highlighting
|
||||||
* "locked" - The selected node has been locked
|
* "locked" - The selected node has been locked
|
||||||
* "unlocked" - The selected ndoe has been unlocked
|
* "unlocked" - The selected ndoe has been unlocked
|
||||||
|
* "pseudoclasstoggled" - A pseudo-class lock has changed on the selected node
|
||||||
|
|
||||||
*
|
*
|
||||||
* Structure:
|
* Structure:
|
||||||
*
|
*
|
||||||
@ -238,6 +247,17 @@ Highlighter.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify that a pseudo-class lock was toggled on the highlighted element
|
||||||
|
*
|
||||||
|
* @param aPseudo - The pseudo-class to toggle, e.g. ":hover".
|
||||||
|
*/
|
||||||
|
pseudoClassLockToggled: function Highlighter_pseudoClassLockToggled(aPseudo)
|
||||||
|
{
|
||||||
|
this.emitEvent("pseudoclasstoggled", [aPseudo]);
|
||||||
|
this.updateInfobar();
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the highlighter size and position.
|
* Update the highlighter size and position.
|
||||||
*/
|
*/
|
||||||
@ -446,29 +466,80 @@ Highlighter.prototype = {
|
|||||||
|
|
||||||
let classesBox = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
|
let classesBox = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
|
||||||
classesBox.id = "highlighter-nodeinfobar-classes";
|
classesBox.id = "highlighter-nodeinfobar-classes";
|
||||||
|
|
||||||
|
let pseudoClassesBox = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
|
||||||
|
pseudoClassesBox.id = "highlighter-nodeinfobar-pseudo-classes";
|
||||||
|
|
||||||
// Add some content to force a better boundingClientRect down below.
|
// Add some content to force a better boundingClientRect down below.
|
||||||
classesBox.textContent = " ";
|
pseudoClassesBox.textContent = " ";
|
||||||
|
|
||||||
nodeInfobar.appendChild(tagNameLabel);
|
nodeInfobar.appendChild(tagNameLabel);
|
||||||
nodeInfobar.appendChild(idLabel);
|
nodeInfobar.appendChild(idLabel);
|
||||||
nodeInfobar.appendChild(classesBox);
|
nodeInfobar.appendChild(classesBox);
|
||||||
|
nodeInfobar.appendChild(pseudoClassesBox);
|
||||||
container.appendChild(arrowBoxTop);
|
container.appendChild(arrowBoxTop);
|
||||||
container.appendChild(nodeInfobar);
|
container.appendChild(nodeInfobar);
|
||||||
container.appendChild(arrowBoxBottom);
|
container.appendChild(arrowBoxBottom);
|
||||||
|
|
||||||
aParent.appendChild(container);
|
aParent.appendChild(container);
|
||||||
|
|
||||||
|
nodeInfobar.onclick = (function _onInfobarRightClick(aEvent) {
|
||||||
|
if (aEvent.button == 2) {
|
||||||
|
this.openPseudoClassMenu();
|
||||||
|
}
|
||||||
|
}).bind(this);
|
||||||
|
|
||||||
let barHeight = container.getBoundingClientRect().height;
|
let barHeight = container.getBoundingClientRect().height;
|
||||||
|
|
||||||
this.nodeInfo = {
|
this.nodeInfo = {
|
||||||
tagNameLabel: tagNameLabel,
|
tagNameLabel: tagNameLabel,
|
||||||
idLabel: idLabel,
|
idLabel: idLabel,
|
||||||
classesBox: classesBox,
|
classesBox: classesBox,
|
||||||
|
pseudoClassesBox: pseudoClassesBox,
|
||||||
container: container,
|
container: container,
|
||||||
barHeight: barHeight,
|
barHeight: barHeight,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the infobar's pseudo-class context menu.
|
||||||
|
*/
|
||||||
|
openPseudoClassMenu: function Highlighter_openPseudoClassMenu()
|
||||||
|
{
|
||||||
|
let menu = this.chromeDoc.createElement("menupopup");
|
||||||
|
menu.id = "infobar-context-menu";
|
||||||
|
|
||||||
|
let popupSet = this.chromeDoc.getElementById("mainPopupSet");
|
||||||
|
popupSet.appendChild(menu);
|
||||||
|
|
||||||
|
let fragment = this.buildPseudoClassMenu();
|
||||||
|
menu.appendChild(fragment);
|
||||||
|
|
||||||
|
menu.openPopup(this.nodeInfo.pseudoClassesBox, "end_before", 0, 0, true, false);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the menuitems for toggling the selection's pseudo-class state
|
||||||
|
*
|
||||||
|
* @returns DocumentFragment. The menuitems for toggling pseudo-classes.
|
||||||
|
*/
|
||||||
|
buildPseudoClassMenu: function IUI_buildPseudoClassesMenu()
|
||||||
|
{
|
||||||
|
let fragment = this.chromeDoc.createDocumentFragment();
|
||||||
|
for (let i = 0; i < PSEUDO_CLASSES.length; i++) {
|
||||||
|
let pseudo = PSEUDO_CLASSES[i];
|
||||||
|
let item = this.chromeDoc.createElement("menuitem");
|
||||||
|
item.setAttribute("type", "checkbox");
|
||||||
|
item.setAttribute("label", pseudo);
|
||||||
|
item.addEventListener("command",
|
||||||
|
this.pseudoClassLockToggled.bind(this, pseudo), false);
|
||||||
|
item.setAttribute("checked", DOMUtils.hasPseudoClassLock(this.node,
|
||||||
|
pseudo));
|
||||||
|
fragment.appendChild(item);
|
||||||
|
}
|
||||||
|
return fragment;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Highlight a rectangular region.
|
* Highlight a rectangular region.
|
||||||
*
|
*
|
||||||
@ -543,6 +614,14 @@ Highlighter.prototype = {
|
|||||||
|
|
||||||
classes.textContent = this.node.classList.length ?
|
classes.textContent = this.node.classList.length ?
|
||||||
"." + Array.join(this.node.classList, ".") : "";
|
"." + Array.join(this.node.classList, ".") : "";
|
||||||
|
|
||||||
|
// Pseudo-classes
|
||||||
|
let pseudos = PSEUDO_CLASSES.filter(function(pseudo) {
|
||||||
|
return DOMUtils.hasPseudoClassLock(this.node, pseudo);
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
let pseudoBox = this.nodeInfo.pseudoClassesBox;
|
||||||
|
pseudoBox.textContent = pseudos.join("");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -617,8 +696,8 @@ Highlighter.prototype = {
|
|||||||
*/
|
*/
|
||||||
computeZoomFactor: function Highlighter_computeZoomFactor() {
|
computeZoomFactor: function Highlighter_computeZoomFactor() {
|
||||||
this.zoom =
|
this.zoom =
|
||||||
this.win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
this.win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
.getInterface(Components.interfaces.nsIDOMWindowUtils)
|
.getInterface(Ci.nsIDOMWindowUtils)
|
||||||
.screenPixelsPerCSSPixel;
|
.screenPixelsPerCSSPixel;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -805,3 +884,6 @@ Highlighter.prototype = {
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
|
||||||
|
return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils)
|
||||||
|
});
|
||||||
|
@ -83,6 +83,8 @@ const INSPECTOR_NOTIFICATIONS = {
|
|||||||
EDITOR_SAVED: "inspector-editor-saved",
|
EDITOR_SAVED: "inspector-editor-saved",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
//// InspectorUI
|
//// InspectorUI
|
||||||
|
|
||||||
@ -362,6 +364,7 @@ InspectorUI.prototype = {
|
|||||||
show: this.openRuleView,
|
show: this.openRuleView,
|
||||||
hide: this.closeRuleView,
|
hide: this.closeRuleView,
|
||||||
onSelect: this.selectInRuleView,
|
onSelect: this.selectInRuleView,
|
||||||
|
onChanged: this.changeInRuleView,
|
||||||
panel: null,
|
panel: null,
|
||||||
unregister: this.destroyRuleView,
|
unregister: this.destroyRuleView,
|
||||||
sidebar: true,
|
sidebar: true,
|
||||||
@ -481,6 +484,7 @@ InspectorUI.prototype = {
|
|||||||
if (!aKeepStore) {
|
if (!aKeepStore) {
|
||||||
this.store.deleteStore(this.winID);
|
this.store.deleteStore(this.winID);
|
||||||
this.win.removeEventListener("pagehide", this, true);
|
this.win.removeEventListener("pagehide", this, true);
|
||||||
|
this.clearPseudoClassLocks();
|
||||||
} else {
|
} else {
|
||||||
// Update the store before closing.
|
// Update the store before closing.
|
||||||
if (this.selection) {
|
if (this.selection) {
|
||||||
@ -566,7 +570,7 @@ InspectorUI.prototype = {
|
|||||||
this.inspecting = false;
|
this.inspecting = false;
|
||||||
this.toolsDim(false);
|
this.toolsDim(false);
|
||||||
if (this.highlighter.getNode()) {
|
if (this.highlighter.getNode()) {
|
||||||
this.select(this.highlighter.getNode(), true, true, !aPreventScroll);
|
this.select(this.highlighter.getNode(), true, !aPreventScroll);
|
||||||
} else {
|
} else {
|
||||||
this.select(null, true, true);
|
this.select(null, true, true);
|
||||||
}
|
}
|
||||||
@ -574,15 +578,17 @@ InspectorUI.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select an object in the tree view.
|
* Select an object in the inspector.
|
||||||
* @param aNode
|
* @param aNode
|
||||||
* node to inspect
|
* node to inspect
|
||||||
* @param forceUpdate
|
* @param forceUpdate
|
||||||
* force an update?
|
* force an update?
|
||||||
* @param aScroll boolean
|
* @param aScroll boolean
|
||||||
* scroll the tree panel?
|
* scroll the tree panel?
|
||||||
|
* @param aFrom [optional] string
|
||||||
|
* which part of the UI the selection occured from
|
||||||
*/
|
*/
|
||||||
select: function IUI_select(aNode, forceUpdate, aScroll)
|
select: function IUI_select(aNode, forceUpdate, aScroll, aFrom)
|
||||||
{
|
{
|
||||||
// if currently editing an attribute value, using the
|
// if currently editing an attribute value, using the
|
||||||
// highlighter dismisses the editor
|
// highlighter dismisses the editor
|
||||||
@ -593,6 +599,10 @@ InspectorUI.prototype = {
|
|||||||
aNode = this.defaultSelection;
|
aNode = this.defaultSelection;
|
||||||
|
|
||||||
if (forceUpdate || aNode != this.selection) {
|
if (forceUpdate || aNode != this.selection) {
|
||||||
|
if (aFrom != "breadcrumbs") {
|
||||||
|
this.clearPseudoClassLocks();
|
||||||
|
}
|
||||||
|
|
||||||
this.selection = aNode;
|
this.selection = aNode;
|
||||||
if (!this.inspecting) {
|
if (!this.inspecting) {
|
||||||
this.highlighter.highlight(this.selection);
|
this.highlighter.highlight(this.selection);
|
||||||
@ -605,6 +615,41 @@ InspectorUI.prototype = {
|
|||||||
|
|
||||||
this.toolsSelect(aScroll);
|
this.toolsSelect(aScroll);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the pseudo-class lock on the currently inspected element. If the
|
||||||
|
* pseudo-class is :hover or :active, that pseudo-class will also be toggled
|
||||||
|
* on every ancestor of the element, mirroring real :hover and :active
|
||||||
|
* behavior.
|
||||||
|
*
|
||||||
|
* @param aPseudo the pseudo-class lock to toggle, e.g. ":hover"
|
||||||
|
*/
|
||||||
|
togglePseudoClassLock: function IUI_togglePseudoClassLock(aPseudo)
|
||||||
|
{
|
||||||
|
if (DOMUtils.hasPseudoClassLock(this.selection, aPseudo)) {
|
||||||
|
this.breadcrumbs.nodeHierarchy.forEach(function(crumb) {
|
||||||
|
DOMUtils.removePseudoClassLock(crumb.node, aPseudo);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let hierarchical = aPseudo == ":hover" || aPseudo == ":active";
|
||||||
|
let node = this.selection;
|
||||||
|
do {
|
||||||
|
DOMUtils.addPseudoClassLock(node, aPseudo);
|
||||||
|
node = node.parentNode;
|
||||||
|
} while (hierarchical && node.parentNode)
|
||||||
|
}
|
||||||
|
this.nodeChanged();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all pseudo-class locks applied to elements in the node hierarchy
|
||||||
|
*/
|
||||||
|
clearPseudoClassLocks: function IUI_clearPseudoClassLocks()
|
||||||
|
{
|
||||||
|
this.breadcrumbs.nodeHierarchy.forEach(function(crumb) {
|
||||||
|
DOMUtils.clearPseudoClassLocks(crumb.node);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the highlighted node is changed by a tool.
|
* Called when the highlighted node is changed by a tool.
|
||||||
@ -616,6 +661,7 @@ InspectorUI.prototype = {
|
|||||||
nodeChanged: function IUI_nodeChanged(aUpdater)
|
nodeChanged: function IUI_nodeChanged(aUpdater)
|
||||||
{
|
{
|
||||||
this.highlighter.invalidateSize();
|
this.highlighter.invalidateSize();
|
||||||
|
this.breadcrumbs.updateSelectors();
|
||||||
this.toolsOnChanged(aUpdater);
|
this.toolsOnChanged(aUpdater);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -641,6 +687,10 @@ InspectorUI.prototype = {
|
|||||||
self.select(self.highlighter.getNode(), false, false);
|
self.select(self.highlighter.getNode(), false, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.highlighter.addListener("pseudoclasstoggled", function(aPseudo) {
|
||||||
|
self.togglePseudoClassLock(aPseudo);
|
||||||
|
});
|
||||||
|
|
||||||
if (this.store.getValue(this.winID, "inspecting")) {
|
if (this.store.getValue(this.winID, "inspecting")) {
|
||||||
this.startInspecting();
|
this.startInspecting();
|
||||||
this.highlighter.unlock();
|
this.highlighter.unlock();
|
||||||
@ -911,6 +961,15 @@ InspectorUI.prototype = {
|
|||||||
if (this.ruleView)
|
if (this.ruleView)
|
||||||
this.ruleView.highlight(aNode);
|
this.ruleView.highlight(aNode);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the rules for the current node in the Css Rule View.
|
||||||
|
*/
|
||||||
|
changeInRuleView: function IUI_selectInRuleView()
|
||||||
|
{
|
||||||
|
if (this.ruleView)
|
||||||
|
this.ruleView.nodeChanged();
|
||||||
|
},
|
||||||
|
|
||||||
ruleViewChanged: function IUI_ruleViewChanged()
|
ruleViewChanged: function IUI_ruleViewChanged()
|
||||||
{
|
{
|
||||||
@ -1336,7 +1395,7 @@ InspectorUI.prototype = {
|
|||||||
toolsOnChanged: function IUI_toolsChanged(aUpdater)
|
toolsOnChanged: function IUI_toolsChanged(aUpdater)
|
||||||
{
|
{
|
||||||
this.toolsDo(function IUI_toolsOnChanged(aTool) {
|
this.toolsDo(function IUI_toolsOnChanged(aTool) {
|
||||||
if (aTool.isOpen && ("onChanged" in aTool) && aTool != aUpdater) {
|
if (("onChanged" in aTool) && aTool != aUpdater) {
|
||||||
aTool.onChanged.call(aTool.context);
|
aTool.onChanged.call(aTool.context);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -1730,6 +1789,13 @@ HTMLBreadcrumbs.prototype = {
|
|||||||
for (let i = 0; i < aNode.classList.length; i++) {
|
for (let i = 0; i < aNode.classList.length; i++) {
|
||||||
text += "." + aNode.classList[i];
|
text += "." + aNode.classList[i];
|
||||||
}
|
}
|
||||||
|
for (let i = 0; i < PSEUDO_CLASSES.length; i++) {
|
||||||
|
let pseudo = PSEUDO_CLASSES[i];
|
||||||
|
if (DOMUtils.hasPseudoClassLock(aNode, pseudo)) {
|
||||||
|
text += pseudo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1755,6 +1821,9 @@ HTMLBreadcrumbs.prototype = {
|
|||||||
|
|
||||||
let classesLabel = this.IUI.chromeDoc.createElement("label");
|
let classesLabel = this.IUI.chromeDoc.createElement("label");
|
||||||
classesLabel.className = "inspector-breadcrumbs-classes plain";
|
classesLabel.className = "inspector-breadcrumbs-classes plain";
|
||||||
|
|
||||||
|
let pseudosLabel = this.IUI.chromeDoc.createElement("label");
|
||||||
|
pseudosLabel.className = "inspector-breadcrumbs-pseudo-classes plain";
|
||||||
|
|
||||||
tagLabel.textContent = aNode.tagName.toLowerCase();
|
tagLabel.textContent = aNode.tagName.toLowerCase();
|
||||||
idLabel.textContent = aNode.id ? ("#" + aNode.id) : "";
|
idLabel.textContent = aNode.id ? ("#" + aNode.id) : "";
|
||||||
@ -1765,9 +1834,15 @@ HTMLBreadcrumbs.prototype = {
|
|||||||
}
|
}
|
||||||
classesLabel.textContent = classesText;
|
classesLabel.textContent = classesText;
|
||||||
|
|
||||||
|
let pseudos = PSEUDO_CLASSES.filter(function(pseudo) {
|
||||||
|
return DOMUtils.hasPseudoClassLock(aNode, pseudo);
|
||||||
|
}, this);
|
||||||
|
pseudosLabel.textContent = pseudos.join("");
|
||||||
|
|
||||||
fragment.appendChild(tagLabel);
|
fragment.appendChild(tagLabel);
|
||||||
fragment.appendChild(idLabel);
|
fragment.appendChild(idLabel);
|
||||||
fragment.appendChild(classesLabel);
|
fragment.appendChild(classesLabel);
|
||||||
|
fragment.appendChild(pseudosLabel);
|
||||||
|
|
||||||
return fragment;
|
return fragment;
|
||||||
},
|
},
|
||||||
@ -1807,7 +1882,7 @@ HTMLBreadcrumbs.prototype = {
|
|||||||
|
|
||||||
item.onmouseup = (function(aNode) {
|
item.onmouseup = (function(aNode) {
|
||||||
return function() {
|
return function() {
|
||||||
inspector.select(aNode, true, true);
|
inspector.select(aNode, true, true, "breadcrumbs");
|
||||||
}
|
}
|
||||||
})(nodes[i]);
|
})(nodes[i]);
|
||||||
|
|
||||||
@ -1961,7 +2036,7 @@ HTMLBreadcrumbs.prototype = {
|
|||||||
|
|
||||||
button.onBreadcrumbsClick = function onBreadcrumbsClick() {
|
button.onBreadcrumbsClick = function onBreadcrumbsClick() {
|
||||||
inspector.stopInspecting();
|
inspector.stopInspecting();
|
||||||
inspector.select(aNode, true, true);
|
inspector.select(aNode, true, true, "breadcrumbs");
|
||||||
};
|
};
|
||||||
|
|
||||||
button.onclick = (function _onBreadcrumbsRightClick(aEvent) {
|
button.onclick = (function _onBreadcrumbsRightClick(aEvent) {
|
||||||
@ -2076,6 +2151,20 @@ HTMLBreadcrumbs.prototype = {
|
|||||||
let element = this.nodeHierarchy[this.currentIndex].button;
|
let element = this.nodeHierarchy[this.currentIndex].button;
|
||||||
scrollbox.ensureElementIsVisible(element);
|
scrollbox.ensureElementIsVisible(element);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateSelectors: function BC_updateSelectors()
|
||||||
|
{
|
||||||
|
for (let i = this.nodeHierarchy.length - 1; i >= 0; i--) {
|
||||||
|
let crumb = this.nodeHierarchy[i];
|
||||||
|
let button = crumb.button;
|
||||||
|
|
||||||
|
while(button.hasChildNodes()) {
|
||||||
|
button.removeChild(button.firstChild);
|
||||||
|
}
|
||||||
|
button.appendChild(this.prettyPrintNodeAsXUL(crumb.node));
|
||||||
|
button.setAttribute("tooltiptext", this.prettyPrintNodeAsText(crumb.node));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the breadcrumbs display when a new node is selected.
|
* Update the breadcrumbs display when a new node is selected.
|
||||||
@ -2117,6 +2206,8 @@ HTMLBreadcrumbs.prototype = {
|
|||||||
|
|
||||||
// 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.updateSelectors();
|
||||||
},
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -2136,3 +2227,6 @@ XPCOMUtils.defineLazyGetter(this, "StyleInspector", function () {
|
|||||||
return obj.StyleInspector;
|
return obj.StyleInspector;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
|
||||||
|
return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
|
||||||
|
});
|
||||||
|
@ -72,6 +72,7 @@ _BROWSER_FILES = \
|
|||||||
browser_inspector_invalidate.js \
|
browser_inspector_invalidate.js \
|
||||||
browser_inspector_sidebarstate.js \
|
browser_inspector_sidebarstate.js \
|
||||||
browser_inspector_treePanel_menu.js \
|
browser_inspector_treePanel_menu.js \
|
||||||
|
browser_inspector_pseudoclass_lock.js \
|
||||||
head.js \
|
head.js \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
@ -0,0 +1,154 @@
|
|||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
let DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
|
||||||
|
|
||||||
|
let doc;
|
||||||
|
let div;
|
||||||
|
|
||||||
|
let pseudo = ":hover";
|
||||||
|
|
||||||
|
function test()
|
||||||
|
{
|
||||||
|
waitForExplicitFinish();
|
||||||
|
ignoreAllUncaughtExceptions();
|
||||||
|
gBrowser.selectedTab = gBrowser.addTab();
|
||||||
|
gBrowser.selectedBrowser.addEventListener("load", function() {
|
||||||
|
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
|
||||||
|
doc = content.document;
|
||||||
|
waitForFocus(createDocument, content);
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
content.location = "data:text/html,pseudo-class lock tests";
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDocument()
|
||||||
|
{
|
||||||
|
div = doc.createElement("div");
|
||||||
|
div.textContent = "test div";
|
||||||
|
|
||||||
|
let head = doc.getElementsByTagName('head')[0];
|
||||||
|
let style = doc.createElement('style');
|
||||||
|
let rules = doc.createTextNode('div { color: red; } div:hover { color: blue; }');
|
||||||
|
|
||||||
|
style.appendChild(rules);
|
||||||
|
head.appendChild(style);
|
||||||
|
doc.body.appendChild(div);
|
||||||
|
|
||||||
|
setupTests();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupTests()
|
||||||
|
{
|
||||||
|
Services.obs.addObserver(selectNode,
|
||||||
|
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
|
||||||
|
InspectorUI.openInspectorUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectNode()
|
||||||
|
{
|
||||||
|
Services.obs.removeObserver(selectNode,
|
||||||
|
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
|
||||||
|
|
||||||
|
executeSoon(function() {
|
||||||
|
InspectorUI.highlighter.addListener("nodeselected", openRuleView);
|
||||||
|
InspectorUI.inspectNode(div);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function openRuleView()
|
||||||
|
{
|
||||||
|
Services.obs.addObserver(performTests,
|
||||||
|
InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false);
|
||||||
|
|
||||||
|
InspectorUI.showSidebar();
|
||||||
|
InspectorUI.openRuleView();
|
||||||
|
}
|
||||||
|
|
||||||
|
function performTests()
|
||||||
|
{
|
||||||
|
Services.obs.removeObserver(performTests,
|
||||||
|
InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY);
|
||||||
|
|
||||||
|
InspectorUI.highlighter.removeListener("nodeselected", performTests);
|
||||||
|
|
||||||
|
// toggle the class
|
||||||
|
InspectorUI.highlighter.pseudoClassLockToggled(pseudo);
|
||||||
|
|
||||||
|
testAdded();
|
||||||
|
|
||||||
|
// toggle the lock off
|
||||||
|
InspectorUI.highlighter.pseudoClassLockToggled(pseudo);
|
||||||
|
|
||||||
|
testRemoved();
|
||||||
|
testRemovedFromUI();
|
||||||
|
|
||||||
|
// toggle it back on
|
||||||
|
InspectorUI.highlighter.pseudoClassLockToggled(pseudo);
|
||||||
|
|
||||||
|
// close the inspector
|
||||||
|
Services.obs.addObserver(testInspectorClosed,
|
||||||
|
InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
|
||||||
|
InspectorUI.closeInspectorUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testAdded()
|
||||||
|
{
|
||||||
|
// lock is applied to it and ancestors
|
||||||
|
let node = div;
|
||||||
|
do {
|
||||||
|
is(DOMUtils.hasPseudoClassLock(node, pseudo), true,
|
||||||
|
"pseudo-class lock has been applied");
|
||||||
|
node = node.parentNode;
|
||||||
|
} while (node.parentNode)
|
||||||
|
|
||||||
|
// infobar selector contains pseudo-class
|
||||||
|
let pseudoClassesBox = document.getElementById("highlighter-nodeinfobar-pseudo-classes");
|
||||||
|
is(pseudoClassesBox.textContent, pseudo, "pseudo-class in infobar selector");
|
||||||
|
|
||||||
|
// ruleview contains pseudo-class rule
|
||||||
|
is(InspectorUI.ruleView.element.children.length, 3,
|
||||||
|
"rule view is showing 3 rules for pseudo-class locked div");
|
||||||
|
|
||||||
|
is(InspectorUI.ruleView.element.children[1]._ruleEditor.rule.selectorText,
|
||||||
|
"div:hover", "rule view is showing " + pseudo + " rule");
|
||||||
|
}
|
||||||
|
|
||||||
|
function testRemoved()
|
||||||
|
{
|
||||||
|
// lock removed from node and ancestors
|
||||||
|
let node = div;
|
||||||
|
do {
|
||||||
|
is(DOMUtils.hasPseudoClassLock(node, pseudo), false,
|
||||||
|
"pseudo-class lock has been removed");
|
||||||
|
node = node.parentNode;
|
||||||
|
} while (node.parentNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
function testRemovedFromUI()
|
||||||
|
{
|
||||||
|
// infobar selector doesn't contain pseudo-class
|
||||||
|
let pseudoClassesBox = document.getElementById("highlighter-nodeinfobar-pseudo-classes");
|
||||||
|
is(pseudoClassesBox.textContent, "", "pseudo-class removed from infobar selector");
|
||||||
|
|
||||||
|
// ruleview no longer contains pseudo-class rule
|
||||||
|
is(InspectorUI.ruleView.element.children.length, 2,
|
||||||
|
"rule view is showing 2 rules after removing lock");
|
||||||
|
}
|
||||||
|
|
||||||
|
function testInspectorClosed()
|
||||||
|
{
|
||||||
|
Services.obs.removeObserver(testInspectorClosed,
|
||||||
|
InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED);
|
||||||
|
|
||||||
|
testRemoved();
|
||||||
|
|
||||||
|
finishUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishUp()
|
||||||
|
{
|
||||||
|
doc = div = null;
|
||||||
|
gBrowser.removeCurrentTab();
|
||||||
|
finish();
|
||||||
|
}
|
@ -116,7 +116,7 @@ function ElementStyle(aElement, aStore)
|
|||||||
// how their .style attribute reflects them as computed values.
|
// how their .style attribute reflects them as computed values.
|
||||||
this.dummyElement = doc.createElementNS(this.element.namespaceURI,
|
this.dummyElement = doc.createElementNS(this.element.namespaceURI,
|
||||||
this.element.tagName);
|
this.element.tagName);
|
||||||
this._populate();
|
this.populate();
|
||||||
}
|
}
|
||||||
// We're exporting _ElementStyle for unit tests.
|
// We're exporting _ElementStyle for unit tests.
|
||||||
var _ElementStyle = ElementStyle;
|
var _ElementStyle = ElementStyle;
|
||||||
@ -147,7 +147,7 @@ ElementStyle.prototype = {
|
|||||||
* Refresh the list of rules to be displayed for the active element.
|
* Refresh the list of rules to be displayed for the active element.
|
||||||
* Upon completion, this.rules[] will hold a list of Rule objects.
|
* Upon completion, this.rules[] will hold a list of Rule objects.
|
||||||
*/
|
*/
|
||||||
_populate: function ElementStyle_populate()
|
populate: function ElementStyle_populate()
|
||||||
{
|
{
|
||||||
this.rules = [];
|
this.rules = [];
|
||||||
|
|
||||||
@ -713,15 +713,33 @@ CssRuleView.prototype = {
|
|||||||
|
|
||||||
this._createEditors();
|
this._createEditors();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the rules for the currently highlighted element.
|
||||||
|
*/
|
||||||
|
nodeChanged: function CssRuleView_nodeChanged()
|
||||||
|
{
|
||||||
|
this._clearRules();
|
||||||
|
this._elementStyle.populate();
|
||||||
|
this._createEditors();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the rules.
|
||||||
|
*/
|
||||||
|
_clearRules: function CssRuleView_clearRules()
|
||||||
|
{
|
||||||
|
while (this.element.hasChildNodes()) {
|
||||||
|
this.element.removeChild(this.element.lastChild);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the rule view.
|
* Clear the rule view.
|
||||||
*/
|
*/
|
||||||
clear: function CssRuleView_clear()
|
clear: function CssRuleView_clear()
|
||||||
{
|
{
|
||||||
while (this.element.hasChildNodes()) {
|
this._clearRules();
|
||||||
this.element.removeChild(this.element.lastChild);
|
|
||||||
}
|
|
||||||
this._viewedElement = null;
|
this._viewedElement = null;
|
||||||
this._elementStyle = null;
|
this._elementStyle = null;
|
||||||
},
|
},
|
||||||
|
@ -2014,6 +2014,10 @@ html|*#highlighter-nodeinfobar-id {
|
|||||||
color: hsl(90, 79%, 52%);
|
color: hsl(90, 79%, 52%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html|*#highlighter-nodeinfobar-pseudo-classes {
|
||||||
|
color: hsl(20, 100%, 70%);
|
||||||
|
}
|
||||||
|
|
||||||
/* Highlighter - Node Infobar - box & arrow */
|
/* Highlighter - Node Infobar - box & arrow */
|
||||||
|
|
||||||
#highlighter-nodeinfobar {
|
#highlighter-nodeinfobar {
|
||||||
@ -2118,11 +2122,19 @@ html|*#highlighter-nodeinfobar-id {
|
|||||||
color: hsl(205,100%,70%);
|
color: hsl(205,100%,70%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inspector-breadcrumbs-button[checked] > .inspector-breadcrumbs-pseudo-classes {
|
||||||
|
color: hsl(20, 100%, 70%);
|
||||||
|
}
|
||||||
|
|
||||||
.inspector-breadcrumbs-id,
|
.inspector-breadcrumbs-id,
|
||||||
.inspector-breadcrumbs-classes {
|
.inspector-breadcrumbs-classes {
|
||||||
color: #8d99a6;
|
color: #8d99a6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inspector-breadcrumbs-pseudo-classes {
|
||||||
|
color: hsl(20, 100%, 85%);
|
||||||
|
}
|
||||||
|
|
||||||
/* Highlighter toolbar - breadcrumbs - LTR */
|
/* Highlighter toolbar - breadcrumbs - LTR */
|
||||||
|
|
||||||
.inspector-breadcrumbs-button:-moz-locale-dir(ltr):first-of-type {
|
.inspector-breadcrumbs-button:-moz-locale-dir(ltr):first-of-type {
|
||||||
|
@ -2761,6 +2761,10 @@ html|*#highlighter-nodeinfobar-id {
|
|||||||
color: hsl(90, 79%, 52%);
|
color: hsl(90, 79%, 52%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html|*#highlighter-nodeinfobar-pseudo-classes {
|
||||||
|
color: hsl(20, 100%, 70%);
|
||||||
|
}
|
||||||
|
|
||||||
/* Highlighter - Node Infobar - box & arrow */
|
/* Highlighter - Node Infobar - box & arrow */
|
||||||
|
|
||||||
#highlighter-nodeinfobar {
|
#highlighter-nodeinfobar {
|
||||||
@ -2859,11 +2863,19 @@ html|*#highlighter-nodeinfobar-id {
|
|||||||
color: hsl(205,100%,70%);
|
color: hsl(205,100%,70%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inspector-breadcrumbs-button[checked] > .inspector-breadcrumbs-pseudo-classes {
|
||||||
|
color: hsl(20, 100%, 70%);
|
||||||
|
}
|
||||||
|
|
||||||
.inspector-breadcrumbs-id,
|
.inspector-breadcrumbs-id,
|
||||||
.inspector-breadcrumbs-classes {
|
.inspector-breadcrumbs-classes {
|
||||||
color: #8d99a6;
|
color: #8d99a6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inspector-breadcrumbs-pseudo-classes {
|
||||||
|
color: hsl(20, 100%, 85%);
|
||||||
|
}
|
||||||
|
|
||||||
/* Highlighter toolbar - breadcrumbs - LTR */
|
/* Highlighter toolbar - breadcrumbs - LTR */
|
||||||
|
|
||||||
.inspector-breadcrumbs-button:-moz-locale-dir(ltr):first-of-type {
|
.inspector-breadcrumbs-button:-moz-locale-dir(ltr):first-of-type {
|
||||||
|
@ -2709,6 +2709,10 @@ html|*#highlighter-nodeinfobar-id {
|
|||||||
color: hsl(90, 79%, 52%);
|
color: hsl(90, 79%, 52%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html|*#highlighter-nodeinfobar-pseudo-classes {
|
||||||
|
color: hsl(20, 100%, 70%);
|
||||||
|
}
|
||||||
|
|
||||||
/* Highlighter - Node Infobar - box & arrow */
|
/* Highlighter - Node Infobar - box & arrow */
|
||||||
|
|
||||||
#highlighter-nodeinfobar {
|
#highlighter-nodeinfobar {
|
||||||
@ -2813,11 +2817,19 @@ html|*#highlighter-nodeinfobar-id {
|
|||||||
color: hsl(200,100%,70%);
|
color: hsl(200,100%,70%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inspector-breadcrumbs-button[checked] > .inspector-breadcrumbs-pseudo-classes {
|
||||||
|
color: hsl(20, 100%, 70%);
|
||||||
|
}
|
||||||
|
|
||||||
.inspector-breadcrumbs-id,
|
.inspector-breadcrumbs-id,
|
||||||
.inspector-breadcrumbs-classes {
|
.inspector-breadcrumbs-classes {
|
||||||
color: #8d99a6;
|
color: #8d99a6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inspector-breadcrumbs-pseudo-classes {
|
||||||
|
color: hsl(20, 100%, 85%);
|
||||||
|
}
|
||||||
|
|
||||||
/* Highlighter toolbar - breadcrumbs - LTR */
|
/* Highlighter toolbar - breadcrumbs - LTR */
|
||||||
|
|
||||||
.inspector-breadcrumbs-button:-moz-locale-dir(ltr):first-of-type {
|
.inspector-breadcrumbs-button:-moz-locale-dir(ltr):first-of-type {
|
||||||
|
Loading…
Reference in New Issue
Block a user