Bug 1053898 - display shadow nodes as links to real nodes
MozReview-Commit-ID: I8jnZ5xzbCU
--- a/devtools/client/inspector/markup/markup.js
+++ b/devtools/client/inspector/markup/markup.js
@@ -13,16 +13,17 @@ const EventEmitter = require("devtools/s
const {LocalizationHelper} = require("devtools/shared/l10n");
const {PluralForm} = require("devtools/shared/plural-form");
const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
const {scrollIntoViewIfNeeded} = require("devtools/client/shared/scroll");
const {UndoStack} = require("devtools/client/shared/undo");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
const {PrefObserver} = require("devtools/client/shared/prefs");
+const ShadowNodeContainer = require("devtools/client/inspector/markup/views/shadow-node-container");
const MarkupElementContainer = require("devtools/client/inspector/markup/views/element-container");
const MarkupReadOnlyContainer = require("devtools/client/inspector/markup/views/read-only-container");
const MarkupTextContainer = require("devtools/client/inspector/markup/views/text-container");
const RootContainer = require("devtools/client/inspector/markup/views/root-container");
const INSPECTOR_L10N =
new LocalizationHelper("devtools/client/locales/inspector.properties");
@@ -320,21 +321,20 @@ MarkupView.prototype = {
while (parentNode !== this.doc.body) {
if (parentNode.container) {
container = parentNode.container;
break;
}
parentNode = parentNode.parentNode;
}
- if (container instanceof MarkupElementContainer) {
+ if (typeof container._onContainerClick === "function") {
// With the newly found container, delegate the tooltip content creation
// and decision to show or not the tooltip
- container._buildEventTooltipContent(event.target,
- this.eventDetailsTooltip);
+ container._onContainerClick(event, this.eventDetailsTooltip);
}
},
_onMouseUp: function (event) {
if (this._draggedContainer) {
this._draggedContainer.onMouseUp(event);
}
@@ -957,21 +957,23 @@ MarkupView.prototype = {
return null;
}
if (this._containers.has(node)) {
return this.getContainer(node);
}
let container;
- let {nodeType, isPseudoElement} = node;
+ let {nodeType, isPseudoElement, isShadowNode} = node;
if (node === this.walker.rootNode) {
container = new RootContainer(this, node);
this._elt.appendChild(container.elt);
this._rootNode = node;
+ } else if (isShadowNode) {
+ container = new ShadowNodeContainer(this, node, this.inspector);
} else if (nodeType == nodeConstants.ELEMENT_NODE && !isPseudoElement) {
container = new MarkupElementContainer(this, node, this.inspector);
} else if (nodeType == nodeConstants.COMMENT_NODE ||
nodeType == nodeConstants.TEXT_NODE) {
container = new MarkupTextContainer(this, node, this.inspector);
} else {
container = new MarkupReadOnlyContainer(this, node, this.inspector);
}
--- a/devtools/client/inspector/markup/views/element-container.js
+++ b/devtools/client/inspector/markup/views/element-container.js
@@ -12,17 +12,17 @@ const {Task} = require("devtools/shared/
const nodeConstants = require("devtools/shared/dom-node-constants");
const clipboardHelper = require("devtools/shared/platform/clipboard");
const {setImageTooltip, setBrokenImageTooltip} =
require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
const MarkupContainer = require("devtools/client/inspector/markup/views/markup-container");
const ElementEditor = require("devtools/client/inspector/markup/views/element-editor");
const {extend} = require("devtools/shared/extend");
-// Lazy load this module as _buildEventTooltipContent is only called on click
+// Lazy load this module as _onContainerClick is only called on click
loader.lazyRequireGetter(this, "setEventTooltip",
"devtools/client/shared/widgets/tooltip/EventTooltipHelper", true);
/**
* An implementation of MarkupContainer for Elements that can contain
* child nodes.
* Allows editing of tag name, attributes, expanding / collapsing.
*
@@ -40,17 +40,18 @@ function MarkupElementContainer(markupVi
} else {
throw new Error("Invalid node for MarkupElementContainer");
}
this.tagLine.appendChild(this.editor.elt);
}
MarkupElementContainer.prototype = extend(MarkupContainer.prototype, {
- _buildEventTooltipContent: Task.async(function* (target, tooltip) {
+ _onContainerClick: Task.async(function* (event, tooltip) {
+ let target = event.target;
if (target.dataset.event) {
yield tooltip.hide();
let listenerInfo = yield this.node.getEventListenerInfo();
let toolbox = this.markup.toolbox;
setEventTooltip(tooltip, listenerInfo, toolbox);
--- a/devtools/client/inspector/markup/views/markup-container.js
+++ b/devtools/client/inspector/markup/views/markup-container.js
@@ -739,16 +739,21 @@ MarkupContainer.prototype = {
_onToggle: function (event) {
// Prevent the html tree from expanding when an event bubble or display node is
// clicked.
if (event.target.dataset.event || event.target.dataset.display) {
event.stopPropagation();
return;
}
+ if (event.target.classList.contains("reveal-link")) {
+ event.stopPropagation();
+ return;
+ }
+
this.markup.navigate(this);
if (this.hasChildren) {
this.markup.setNodeExpanded(this.node, !this.expanded, event.altKey);
}
event.stopPropagation();
},
/**
--- a/devtools/client/inspector/markup/views/moz.build
+++ b/devtools/client/inspector/markup/views/moz.build
@@ -7,11 +7,13 @@
DevToolsModules(
'element-container.js',
'element-editor.js',
'html-editor.js',
'markup-container.js',
'read-only-container.js',
'read-only-editor.js',
'root-container.js',
+ 'shadow-node-container.js',
+ 'shadow-node-editor.js',
'text-container.js',
'text-editor.js',
)
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/markup/views/shadow-node-container.js
@@ -0,0 +1,50 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const ShadowNodeEditor = require("devtools/client/inspector/markup/views/shadow-node-editor");
+const MarkupContainer = require("devtools/client/inspector/markup/views/markup-container");
+const {extend} = require("devtools/shared/extend");
+
+/**
+ * An implementation of MarkupContainer for Pseudo Elements,
+ * Doctype nodes, or any other type generic node that doesn't
+ * fit for other editors.
+ * Does not allow any editing, just viewing / selecting.
+ *
+ * @param {MarkupView} markupView
+ * The markup view that owns this container.
+ * @param {NodeFront} node
+ * The node to display.
+ */
+function ShadowNodeContainer(markupView, node) {
+ MarkupContainer.prototype.initialize.call(this, markupView, node,
+ "readonlycontainer");
+
+ this.editor = new ShadowNodeEditor(this, node);
+ this.tagLine.appendChild(this.editor.elt);
+}
+
+ShadowNodeContainer.prototype = extend(MarkupContainer.prototype, {
+ _onMouseDown: function (event) {
+ if (event.target.classList.contains("reveal-link")) {
+ event.stopPropagation();
+ event.preventDefault();
+ return;
+ }
+ MarkupContainer.prototype._onMouseDown.call(this, event);
+ },
+
+ _onContainerClick: async function (event) {
+ if (event.target.classList.contains("reveal-link")) {
+ let actorID = this.node._form.nodeActor;
+ let walkerFront = this.markup.inspector.walker;
+ let nodeFront = await walkerFront.getNodeFromActor(actorID, []);
+ this.markup.inspector.selection.setNodeFront(nodeFront);
+ }
+ }
+});
+
+module.exports = ShadowNodeContainer;
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/markup/views/shadow-node-editor.js
@@ -0,0 +1,56 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/**
+ * Creates an editor for non-editable nodes.
+ */
+function ReadOnlyEditor(container, node) {
+ this.container = container;
+ this.markup = this.container.markup;
+ this.buildMarkup();
+ this.tag.textContent = "→ <" + node.nodeName.toLowerCase() + ">";
+
+ // Make the "tag" part of this editor focusable.
+ this.tag.setAttribute("tabindex", "-1");
+}
+
+ReadOnlyEditor.prototype = {
+ buildMarkup: function () {
+ let doc = this.markup.doc;
+
+ this.elt = doc.createElement("span");
+ this.elt.classList.add("editor");
+
+ this.tag = doc.createElement("span");
+ this.tag.classList.add("tag");
+ this.elt.appendChild(this.tag);
+
+ this.revealLink = doc.createElement("span");
+ this.revealLink.classList.add("reveal-link");
+ this.revealLink.textContent = "reveal";
+ this.elt.appendChild(this.revealLink);
+ },
+
+ destroy: function () {
+ // We might be already destroyed.
+ if (!this.elt) {
+ return;
+ }
+
+ this.elt.remove();
+ this.elt = null;
+ this.tag = null;
+ },
+
+ /**
+ * Stub method for consistency with ElementEditor.
+ */
+ getInfoAtNode: function () {
+ return null;
+ }
+};
+
+module.exports = ReadOnlyEditor;
--- a/devtools/client/themes/markup.css
+++ b/devtools/client/themes/markup.css
@@ -316,16 +316,30 @@ ul.children + .tag-line::before {
.more-nodes {
padding-left: 16px;
}
.styleinspector-propertyeditor {
border: 1px solid #CCC;
}
+.reveal-link {
+ margin-inline-start: 10px;
+ cursor: pointer;
+ display: none;
+}
+
+.reveal-link:hover {
+ text-decoration: underline
+}
+
+.tag-line:hover .reveal-link {
+ display: inline;
+}
+
/* Draw a circle next to nodes that have a pseudo class lock.
Center vertically with the 1.4em line height on .tag-line */
.child.pseudoclass-locked::before {
content: "";
background: var(--theme-highlight-lightorange);
border-radius: 50%;
width: .8em;
height: .8em;