Index: third_party/WebKit/Source/devtools/front_end/elements/ElementsTreeElement.js |
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/ElementsTreeElement.js b/third_party/WebKit/Source/devtools/front_end/elements/ElementsTreeElement.js |
index c9b8db58f336663eac6a27ead6351cd660066c8a..57ec8c27bb9ff8095c8df75ada2db1a1891868a9 100644 |
--- a/third_party/WebKit/Source/devtools/front_end/elements/ElementsTreeElement.js |
+++ b/third_party/WebKit/Source/devtools/front_end/elements/ElementsTreeElement.js |
@@ -27,1618 +27,1554 @@ |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
*/ |
- |
/** |
- * @constructor |
- * @extends {TreeElement} |
- * @param {!WebInspector.DOMNode} node |
- * @param {boolean=} elementCloseTag |
+ * @unrestricted |
*/ |
-WebInspector.ElementsTreeElement = function(node, elementCloseTag) |
-{ |
+WebInspector.ElementsTreeElement = class extends TreeElement { |
+ /** |
+ * @param {!WebInspector.DOMNode} node |
+ * @param {boolean=} elementCloseTag |
+ */ |
+ constructor(node, elementCloseTag) { |
// The title will be updated in onattach. |
- TreeElement.call(this); |
+ super(); |
this._node = node; |
- this._gutterContainer = this.listItemElement.createChild("div", "gutter-container"); |
- this._gutterContainer.addEventListener("click", this._showContextMenu.bind(this)); |
- this._decorationsElement = this._gutterContainer.createChild("div", "hidden"); |
+ this._gutterContainer = this.listItemElement.createChild('div', 'gutter-container'); |
+ this._gutterContainer.addEventListener('click', this._showContextMenu.bind(this)); |
+ this._decorationsElement = this._gutterContainer.createChild('div', 'hidden'); |
this._elementCloseTag = elementCloseTag; |
if (this._node.nodeType() === Node.ELEMENT_NODE && !elementCloseTag) |
- this._canAddAttributes = true; |
+ this._canAddAttributes = true; |
this._searchQuery = null; |
this._expandedChildrenLimit = WebInspector.ElementsTreeElement.InitialChildrenLimit; |
-}; |
- |
-WebInspector.ElementsTreeElement.InitialChildrenLimit = 500; |
- |
-// A union of HTML4 and HTML5-Draft elements that explicitly |
-// or implicitly (for HTML5) forbid the closing tag. |
-WebInspector.ElementsTreeElement.ForbiddenClosingTagElements = new Set([ |
- "area", "base", "basefont", "br", "canvas", "col", "command", "embed", "frame", |
- "hr", "img", "input", "keygen", "link", "menuitem", "meta", "param", "source", "track", "wbr" |
-]); |
- |
-// These tags we do not allow editing their tag name. |
-WebInspector.ElementsTreeElement.EditTagBlacklist = new Set([ |
- "html", "head", "body" |
-]); |
- |
-/** |
- * @param {!WebInspector.ElementsTreeElement} treeElement |
- */ |
-WebInspector.ElementsTreeElement.animateOnDOMUpdate = function(treeElement) |
-{ |
- var tagName = treeElement.listItemElement.querySelector(".webkit-html-tag-name"); |
- WebInspector.runCSSAnimationOnce(tagName || treeElement.listItemElement, "dom-update-highlight"); |
-}; |
- |
-/** |
- * @param {!WebInspector.DOMNode} node |
- * @return {!Array<!WebInspector.DOMNode>} |
- */ |
-WebInspector.ElementsTreeElement.visibleShadowRoots = function(node) |
-{ |
+ } |
+ |
+ /** |
+ * @param {!WebInspector.ElementsTreeElement} treeElement |
+ */ |
+ static animateOnDOMUpdate(treeElement) { |
+ var tagName = treeElement.listItemElement.querySelector('.webkit-html-tag-name'); |
+ WebInspector.runCSSAnimationOnce(tagName || treeElement.listItemElement, 'dom-update-highlight'); |
+ } |
+ |
+ /** |
+ * @param {!WebInspector.DOMNode} node |
+ * @return {!Array<!WebInspector.DOMNode>} |
+ */ |
+ static visibleShadowRoots(node) { |
var roots = node.shadowRoots(); |
- if (roots.length && !WebInspector.moduleSetting("showUAShadowDOM").get()) |
- roots = roots.filter(filter); |
+ if (roots.length && !WebInspector.moduleSetting('showUAShadowDOM').get()) |
+ roots = roots.filter(filter); |
/** |
* @param {!WebInspector.DOMNode} root |
*/ |
- function filter(root) |
- { |
- return root.shadowRootType() !== WebInspector.DOMNode.ShadowRootTypes.UserAgent; |
+ function filter(root) { |
+ return root.shadowRootType() !== WebInspector.DOMNode.ShadowRootTypes.UserAgent; |
} |
return roots; |
-}; |
- |
-/** |
- * @param {!WebInspector.DOMNode} node |
- * @return {boolean} |
- */ |
-WebInspector.ElementsTreeElement.canShowInlineText = function(node) |
-{ |
- if (node.importedDocument() || node.templateContent() || WebInspector.ElementsTreeElement.visibleShadowRoots(node).length || node.hasPseudoElements()) |
- return false; |
+ } |
+ |
+ /** |
+ * @param {!WebInspector.DOMNode} node |
+ * @return {boolean} |
+ */ |
+ static canShowInlineText(node) { |
+ if (node.importedDocument() || node.templateContent() || |
+ WebInspector.ElementsTreeElement.visibleShadowRoots(node).length || node.hasPseudoElements()) |
+ return false; |
if (node.nodeType() !== Node.ELEMENT_NODE) |
- return false; |
+ return false; |
if (!node.firstChild || node.firstChild !== node.lastChild || node.firstChild.nodeType() !== Node.TEXT_NODE) |
- return false; |
+ return false; |
var textChild = node.firstChild; |
var maxInlineTextChildLength = 80; |
if (textChild.nodeValue().length < maxInlineTextChildLength) |
- return true; |
+ return true; |
return false; |
-}; |
- |
-/** |
- * @param {!WebInspector.ContextSubMenuItem} subMenu |
- * @param {!WebInspector.DOMNode} node |
- */ |
-WebInspector.ElementsTreeElement.populateForcedPseudoStateItems = function(subMenu, node) |
-{ |
- const pseudoClasses = ["active", "hover", "focus", "visited"]; |
+ } |
+ |
+ /** |
+ * @param {!WebInspector.ContextSubMenuItem} subMenu |
+ * @param {!WebInspector.DOMNode} node |
+ */ |
+ static populateForcedPseudoStateItems(subMenu, node) { |
+ const pseudoClasses = ['active', 'hover', 'focus', 'visited']; |
var forcedPseudoState = WebInspector.CSSModel.fromNode(node).pseudoState(node); |
for (var i = 0; i < pseudoClasses.length; ++i) { |
- var pseudoClassForced = forcedPseudoState.indexOf(pseudoClasses[i]) >= 0; |
- subMenu.appendCheckboxItem(":" + pseudoClasses[i], setPseudoStateCallback.bind(null, pseudoClasses[i], !pseudoClassForced), pseudoClassForced, false); |
+ var pseudoClassForced = forcedPseudoState.indexOf(pseudoClasses[i]) >= 0; |
+ subMenu.appendCheckboxItem( |
+ ':' + pseudoClasses[i], setPseudoStateCallback.bind(null, pseudoClasses[i], !pseudoClassForced), |
+ pseudoClassForced, false); |
} |
/** |
* @param {string} pseudoState |
* @param {boolean} enabled |
*/ |
- function setPseudoStateCallback(pseudoState, enabled) |
- { |
- WebInspector.CSSModel.fromNode(node).forcePseudoState(node, pseudoState, enabled); |
+ function setPseudoStateCallback(pseudoState, enabled) { |
+ WebInspector.CSSModel.fromNode(node).forcePseudoState(node, pseudoState, enabled); |
+ } |
+ } |
+ |
+ /** |
+ * @return {boolean} |
+ */ |
+ isClosingTag() { |
+ return !!this._elementCloseTag; |
+ } |
+ |
+ /** |
+ * @return {!WebInspector.DOMNode} |
+ */ |
+ node() { |
+ return this._node; |
+ } |
+ |
+ /** |
+ * @return {boolean} |
+ */ |
+ isEditing() { |
+ return !!this._editing; |
+ } |
+ |
+ /** |
+ * @param {string} searchQuery |
+ */ |
+ highlightSearchResults(searchQuery) { |
+ if (this._searchQuery !== searchQuery) |
+ this._hideSearchHighlight(); |
+ |
+ this._searchQuery = searchQuery; |
+ this._searchHighlightsVisible = true; |
+ this.updateTitle(null, true); |
+ } |
+ |
+ hideSearchHighlights() { |
+ delete this._searchHighlightsVisible; |
+ this._hideSearchHighlight(); |
+ } |
+ |
+ _hideSearchHighlight() { |
+ if (!this._highlightResult) |
+ return; |
+ |
+ function updateEntryHide(entry) { |
+ switch (entry.type) { |
+ case 'added': |
+ entry.node.remove(); |
+ break; |
+ case 'changed': |
+ entry.node.textContent = entry.oldText; |
+ break; |
+ } |
} |
-}; |
-WebInspector.ElementsTreeElement.prototype = { |
- /** |
- * @return {boolean} |
- */ |
- isClosingTag: function() |
- { |
- return !!this._elementCloseTag; |
- }, |
+ for (var i = (this._highlightResult.length - 1); i >= 0; --i) |
+ updateEntryHide(this._highlightResult[i]); |
- /** |
- * @return {!WebInspector.DOMNode} |
- */ |
- node: function() |
- { |
- return this._node; |
- }, |
+ delete this._highlightResult; |
+ } |
- /** |
- * @return {boolean} |
- */ |
- isEditing: function() |
- { |
- return !!this._editing; |
- }, |
+ /** |
+ * @param {boolean} inClipboard |
+ */ |
+ setInClipboard(inClipboard) { |
+ if (this._inClipboard === inClipboard) |
+ return; |
+ this._inClipboard = inClipboard; |
+ this.listItemElement.classList.toggle('in-clipboard', inClipboard); |
+ } |
- /** |
- * @param {string} searchQuery |
- */ |
- highlightSearchResults: function(searchQuery) |
- { |
- if (this._searchQuery !== searchQuery) |
- this._hideSearchHighlight(); |
- |
- this._searchQuery = searchQuery; |
- this._searchHighlightsVisible = true; |
- this.updateTitle(null, true); |
- }, |
- |
- hideSearchHighlights: function() |
- { |
- delete this._searchHighlightsVisible; |
- this._hideSearchHighlight(); |
- }, |
- |
- _hideSearchHighlight: function() |
- { |
- if (!this._highlightResult) |
- return; |
- |
- function updateEntryHide(entry) |
- { |
- switch (entry.type) { |
- case "added": |
- entry.node.remove(); |
- break; |
- case "changed": |
- entry.node.textContent = entry.oldText; |
- break; |
- } |
- } |
+ get hovered() { |
+ return this._hovered; |
+ } |
- for (var i = (this._highlightResult.length - 1); i >= 0; --i) |
- updateEntryHide(this._highlightResult[i]); |
+ set hovered(x) { |
+ if (this._hovered === x) |
+ return; |
- delete this._highlightResult; |
- }, |
+ this._hovered = x; |
- /** |
- * @param {boolean} inClipboard |
- */ |
- setInClipboard: function(inClipboard) |
- { |
- if (this._inClipboard === inClipboard) |
- return; |
- this._inClipboard = inClipboard; |
- this.listItemElement.classList.toggle("in-clipboard", inClipboard); |
- }, |
- |
- get hovered() |
- { |
- return this._hovered; |
- }, |
- |
- set hovered(x) |
- { |
- if (this._hovered === x) |
- return; |
- |
- this._hovered = x; |
- |
- if (this.listItemElement) { |
- if (x) { |
- this._createSelection(); |
- this.listItemElement.classList.add("hovered"); |
- } else { |
- this.listItemElement.classList.remove("hovered"); |
- } |
- } |
- }, |
+ if (this.listItemElement) { |
+ if (x) { |
+ this._createSelection(); |
+ this.listItemElement.classList.add('hovered'); |
+ } else { |
+ this.listItemElement.classList.remove('hovered'); |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * @return {number} |
+ */ |
+ expandedChildrenLimit() { |
+ return this._expandedChildrenLimit; |
+ } |
+ |
+ /** |
+ * @param {number} expandedChildrenLimit |
+ */ |
+ setExpandedChildrenLimit(expandedChildrenLimit) { |
+ this._expandedChildrenLimit = expandedChildrenLimit; |
+ } |
+ |
+ _createSelection() { |
+ var listItemElement = this.listItemElement; |
+ if (!listItemElement) |
+ return; |
+ |
+ if (!this.selectionElement) { |
+ this.selectionElement = createElement('div'); |
+ this.selectionElement.className = 'selection fill'; |
+ this.selectionElement.style.setProperty('margin-left', (-this._computeLeftIndent()) + 'px'); |
+ listItemElement.insertBefore(this.selectionElement, listItemElement.firstChild); |
+ } |
+ } |
+ |
+ /** |
+ * @override |
+ */ |
+ onbind() { |
+ if (!this._elementCloseTag) |
+ this._node[this.treeOutline.treeElementSymbol()] = this; |
+ } |
+ |
+ /** |
+ * @override |
+ */ |
+ onunbind() { |
+ if (this._node[this.treeOutline.treeElementSymbol()] === this) |
+ this._node[this.treeOutline.treeElementSymbol()] = null; |
+ } |
+ |
+ /** |
+ * @override |
+ */ |
+ onattach() { |
+ if (this._hovered) { |
+ this._createSelection(); |
+ this.listItemElement.classList.add('hovered'); |
+ } |
- /** |
- * @return {number} |
- */ |
- expandedChildrenLimit: function() |
- { |
- return this._expandedChildrenLimit; |
- }, |
+ this.updateTitle(); |
+ this._preventFollowingLinksOnDoubleClick(); |
+ this.listItemElement.draggable = true; |
+ } |
+ |
+ _preventFollowingLinksOnDoubleClick() { |
+ var links = this.listItemElement.querySelectorAll( |
+ 'li .webkit-html-tag > .webkit-html-attribute > .webkit-html-external-link, li .webkit-html-tag > .webkit-html-attribute > .webkit-html-resource-link'); |
+ if (!links) |
+ return; |
+ |
+ for (var i = 0; i < links.length; ++i) |
+ links[i].preventFollowOnDoubleClick = true; |
+ } |
+ |
+ /** |
+ * @override |
+ */ |
+ onpopulate() { |
+ this.populated = true; |
+ this.treeOutline.populateTreeElement(this); |
+ } |
+ |
+ /** |
+ * @override |
+ */ |
+ expandRecursively() { |
+ this._node.getSubtree(-1, TreeElement.prototype.expandRecursively.bind(this, Number.MAX_VALUE)); |
+ } |
+ |
+ /** |
+ * @override |
+ */ |
+ onexpand() { |
+ if (this._elementCloseTag) |
+ return; |
+ |
+ this.updateTitle(); |
+ } |
+ |
+ /** |
+ * @override |
+ */ |
+ oncollapse() { |
+ if (this._elementCloseTag) |
+ return; |
+ |
+ this.updateTitle(); |
+ } |
+ |
+ /** |
+ * @override |
+ * @param {boolean=} omitFocus |
+ * @param {boolean=} selectedByUser |
+ * @return {boolean} |
+ */ |
+ select(omitFocus, selectedByUser) { |
+ if (this._editing) |
+ return false; |
+ return super.select(omitFocus, selectedByUser); |
+ } |
+ |
+ /** |
+ * @override |
+ * @param {boolean=} selectedByUser |
+ * @return {boolean} |
+ */ |
+ onselect(selectedByUser) { |
+ this.treeOutline.suppressRevealAndSelect = true; |
+ this.treeOutline.selectDOMNode(this._node, selectedByUser); |
+ if (selectedByUser) |
+ this._node.highlight(); |
+ this._createSelection(); |
+ this.treeOutline.suppressRevealAndSelect = false; |
+ return true; |
+ } |
+ |
+ /** |
+ * @override |
+ * @return {boolean} |
+ */ |
+ ondelete() { |
+ var startTagTreeElement = this.treeOutline.findTreeElement(this._node); |
+ startTagTreeElement ? startTagTreeElement.remove() : this.remove(); |
+ return true; |
+ } |
+ |
+ /** |
+ * @override |
+ * @return {boolean} |
+ */ |
+ onenter() { |
+ // On Enter or Return start editing the first attribute |
+ // or create a new attribute on the selected element. |
+ if (this._editing) |
+ return false; |
+ |
+ this._startEditing(); |
+ |
+ // prevent a newline from being immediately inserted |
+ return true; |
+ } |
+ |
+ /** |
+ * @override |
+ */ |
+ selectOnMouseDown(event) { |
+ super.selectOnMouseDown(event); |
+ |
+ if (this._editing) |
+ return; |
+ |
+ // Prevent selecting the nearest word on double click. |
+ if (event.detail >= 2) |
+ event.preventDefault(); |
+ } |
+ |
+ /** |
+ * @override |
+ * @return {boolean} |
+ */ |
+ ondblclick(event) { |
+ if (this._editing || this._elementCloseTag) |
+ return false; |
+ |
+ if (this._startEditingTarget(/** @type {!Element} */ (event.target))) |
+ return false; |
+ |
+ if (this.isExpandable() && !this.expanded) |
+ this.expand(); |
+ return false; |
+ } |
+ |
+ /** |
+ * @return {boolean} |
+ */ |
+ hasEditableNode() { |
+ return !this._node.isShadowRoot() && !this._node.ancestorUserAgentShadowRoot(); |
+ } |
+ |
+ _insertInLastAttributePosition(tag, node) { |
+ if (tag.getElementsByClassName('webkit-html-attribute').length > 0) |
+ tag.insertBefore(node, tag.lastChild); |
+ else { |
+ var nodeName = tag.textContent.match(/^<(.*?)>$/)[1]; |
+ tag.textContent = ''; |
+ tag.createTextChild('<' + nodeName); |
+ tag.appendChild(node); |
+ tag.createTextChild('>'); |
+ } |
+ } |
- /** |
- * @param {number} expandedChildrenLimit |
- */ |
- setExpandedChildrenLimit: function(expandedChildrenLimit) |
- { |
- this._expandedChildrenLimit = expandedChildrenLimit; |
- }, |
- |
- _createSelection: function() |
- { |
- var listItemElement = this.listItemElement; |
- if (!listItemElement) |
- return; |
- |
- if (!this.selectionElement) { |
- this.selectionElement = createElement("div"); |
- this.selectionElement.className = "selection fill"; |
- this.selectionElement.style.setProperty("margin-left", (-this._computeLeftIndent()) + "px"); |
- listItemElement.insertBefore(this.selectionElement, listItemElement.firstChild); |
- } |
- }, |
+ /** |
+ * @param {!Element} eventTarget |
+ * @return {boolean} |
+ */ |
+ _startEditingTarget(eventTarget) { |
+ if (this.treeOutline.selectedDOMNode() !== this._node) |
+ return false; |
- /** |
- * @override |
- */ |
- onbind: function() |
- { |
- if (!this._elementCloseTag) |
- this._node[this.treeOutline.treeElementSymbol()] = this; |
- }, |
+ if (this._node.nodeType() !== Node.ELEMENT_NODE && this._node.nodeType() !== Node.TEXT_NODE) |
+ return false; |
- /** |
- * @override |
- */ |
- onunbind: function() |
- { |
- if (this._node[this.treeOutline.treeElementSymbol()] === this) |
- this._node[this.treeOutline.treeElementSymbol()] = null; |
- }, |
+ var textNode = eventTarget.enclosingNodeOrSelfWithClass('webkit-html-text-node'); |
+ if (textNode) |
+ return this._startEditingTextNode(textNode); |
- /** |
- * @override |
- */ |
- onattach: function() |
- { |
- if (this._hovered) { |
- this._createSelection(); |
- this.listItemElement.classList.add("hovered"); |
- } |
+ var attribute = eventTarget.enclosingNodeOrSelfWithClass('webkit-html-attribute'); |
+ if (attribute) |
+ return this._startEditingAttribute(attribute, eventTarget); |
- this.updateTitle(); |
- this._preventFollowingLinksOnDoubleClick(); |
- this.listItemElement.draggable = true; |
- }, |
- |
- _preventFollowingLinksOnDoubleClick: function() |
- { |
- var links = this.listItemElement.querySelectorAll("li .webkit-html-tag > .webkit-html-attribute > .webkit-html-external-link, li .webkit-html-tag > .webkit-html-attribute > .webkit-html-resource-link"); |
- if (!links) |
- return; |
- |
- for (var i = 0; i < links.length; ++i) |
- links[i].preventFollowOnDoubleClick = true; |
- }, |
- |
- onpopulate: function() |
- { |
- this.populated = true; |
- this.treeOutline.populateTreeElement(this); |
- }, |
- |
- expandRecursively: function() |
- { |
- /** |
- * @this {WebInspector.ElementsTreeElement} |
- */ |
- function callback() |
- { |
- TreeElement.prototype.expandRecursively.call(this, Number.MAX_VALUE); |
- } |
+ var tagName = eventTarget.enclosingNodeOrSelfWithClass('webkit-html-tag-name'); |
+ if (tagName) |
+ return this._startEditingTagName(tagName); |
- this._node.getSubtree(-1, callback.bind(this)); |
- }, |
+ var newAttribute = eventTarget.enclosingNodeOrSelfWithClass('add-attribute'); |
+ if (newAttribute) |
+ return this._addNewAttribute(); |
- /** |
- * @override |
- */ |
- onexpand: function() |
- { |
- if (this._elementCloseTag) |
- return; |
+ return false; |
+ } |
+ |
+ /** |
+ * @param {!Event} event |
+ */ |
+ _showContextMenu(event) { |
+ this.treeOutline.showContextMenu(this, event); |
+ } |
+ |
+ /** |
+ * @param {!WebInspector.ContextMenu} contextMenu |
+ * @param {!Event} event |
+ */ |
+ populateTagContextMenu(contextMenu, event) { |
+ // Add attribute-related actions. |
+ var treeElement = this._elementCloseTag ? this.treeOutline.findTreeElement(this._node) : this; |
+ contextMenu.appendItem( |
+ WebInspector.UIString.capitalize('Add ^attribute'), treeElement._addNewAttribute.bind(treeElement)); |
+ |
+ var attribute = event.target.enclosingNodeOrSelfWithClass('webkit-html-attribute'); |
+ var newAttribute = event.target.enclosingNodeOrSelfWithClass('add-attribute'); |
+ if (attribute && !newAttribute) |
+ contextMenu.appendItem( |
+ WebInspector.UIString.capitalize('Edit ^attribute'), |
+ this._startEditingAttribute.bind(this, attribute, event.target)); |
+ this.populateNodeContextMenu(contextMenu); |
+ WebInspector.ElementsTreeElement.populateForcedPseudoStateItems(contextMenu, treeElement.node()); |
+ contextMenu.appendSeparator(); |
+ this.populateScrollIntoView(contextMenu); |
+ } |
+ |
+ /** |
+ * @param {!WebInspector.ContextMenu} contextMenu |
+ */ |
+ populateScrollIntoView(contextMenu) { |
+ contextMenu.appendItem(WebInspector.UIString.capitalize('Scroll into ^view'), this._scrollIntoView.bind(this)); |
+ } |
+ |
+ populateTextContextMenu(contextMenu, textNode) { |
+ if (!this._editing) |
+ contextMenu.appendItem( |
+ WebInspector.UIString.capitalize('Edit ^text'), this._startEditingTextNode.bind(this, textNode)); |
+ this.populateNodeContextMenu(contextMenu); |
+ } |
+ |
+ populateNodeContextMenu(contextMenu) { |
+ // Add free-form node-related actions. |
+ var openTagElement = this._node[this.treeOutline.treeElementSymbol()] || this; |
+ var isEditable = this.hasEditableNode(); |
+ if (isEditable && !this._editing) |
+ contextMenu.appendItem(WebInspector.UIString('Edit as HTML'), this._editAsHTML.bind(this)); |
+ var isShadowRoot = this._node.isShadowRoot(); |
+ |
+ // Place it here so that all "Copy"-ing items stick together. |
+ var copyMenu = contextMenu.appendSubMenuItem(WebInspector.UIString('Copy')); |
+ var createShortcut = WebInspector.KeyboardShortcut.shortcutToString; |
+ var modifier = WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta; |
+ var treeOutline = this.treeOutline; |
+ var menuItem; |
+ if (!isShadowRoot) { |
+ menuItem = copyMenu.appendItem( |
+ WebInspector.UIString('Copy outerHTML'), treeOutline.performCopyOrCut.bind(treeOutline, false, this._node)); |
+ menuItem.setShortcut(createShortcut('V', modifier)); |
+ } |
+ if (this._node.nodeType() === Node.ELEMENT_NODE) |
+ copyMenu.appendItem(WebInspector.UIString.capitalize('Copy selector'), this._copyCSSPath.bind(this)); |
+ if (!isShadowRoot) |
+ copyMenu.appendItem(WebInspector.UIString('Copy XPath'), this._copyXPath.bind(this)); |
+ if (!isShadowRoot) { |
+ menuItem = copyMenu.appendItem( |
+ WebInspector.UIString('Cut element'), treeOutline.performCopyOrCut.bind(treeOutline, true, this._node), |
+ !this.hasEditableNode()); |
+ menuItem.setShortcut(createShortcut('X', modifier)); |
+ menuItem = copyMenu.appendItem( |
+ WebInspector.UIString('Copy element'), treeOutline.performCopyOrCut.bind(treeOutline, false, this._node)); |
+ menuItem.setShortcut(createShortcut('C', modifier)); |
+ menuItem = copyMenu.appendItem( |
+ WebInspector.UIString('Paste element'), treeOutline.pasteNode.bind(treeOutline, this._node), |
+ !treeOutline.canPaste(this._node)); |
+ menuItem.setShortcut(createShortcut('V', modifier)); |
+ } |
- this.updateTitle(); |
- }, |
+ contextMenu.appendSeparator(); |
+ menuItem = contextMenu.appendCheckboxItem( |
+ WebInspector.UIString('Hide element'), treeOutline.toggleHideElement.bind(treeOutline, this._node), |
+ treeOutline.isToggledToHidden(this._node)); |
+ menuItem.setShortcut(WebInspector.shortcutRegistry.shortcutTitleForAction('elements.hide-element')); |
- oncollapse: function() |
- { |
- if (this._elementCloseTag) |
- return; |
+ if (isEditable) |
+ contextMenu.appendItem(WebInspector.UIString('Delete element'), this.remove.bind(this)); |
+ contextMenu.appendSeparator(); |
- this.updateTitle(); |
- }, |
+ contextMenu.appendItem(WebInspector.UIString('Expand all'), this.expandRecursively.bind(this)); |
+ contextMenu.appendItem(WebInspector.UIString('Collapse all'), this.collapseRecursively.bind(this)); |
+ contextMenu.appendSeparator(); |
+ } |
- /** |
- * @override |
- * @param {boolean=} omitFocus |
- * @param {boolean=} selectedByUser |
- * @return {boolean} |
- */ |
- select: function(omitFocus, selectedByUser) |
- { |
- if (this._editing) |
- return false; |
- return TreeElement.prototype.select.call(this, omitFocus, selectedByUser); |
- }, |
+ _startEditing() { |
+ if (this.treeOutline.selectedDOMNode() !== this._node) |
+ return; |
- /** |
- * @override |
- * @param {boolean=} selectedByUser |
- * @return {boolean} |
- */ |
- onselect: function(selectedByUser) |
- { |
- this.treeOutline.suppressRevealAndSelect = true; |
- this.treeOutline.selectDOMNode(this._node, selectedByUser); |
- if (selectedByUser) |
- this._node.highlight(); |
- this._createSelection(); |
- this.treeOutline.suppressRevealAndSelect = false; |
- return true; |
- }, |
+ var listItem = this._listItemNode; |
- /** |
- * @override |
- * @return {boolean} |
- */ |
- ondelete: function() |
- { |
- var startTagTreeElement = this.treeOutline.findTreeElement(this._node); |
- startTagTreeElement ? startTagTreeElement.remove() : this.remove(); |
- return true; |
- }, |
+ if (this._canAddAttributes) { |
+ var attribute = listItem.getElementsByClassName('webkit-html-attribute')[0]; |
+ if (attribute) |
+ return this._startEditingAttribute( |
+ attribute, attribute.getElementsByClassName('webkit-html-attribute-value')[0]); |
- /** |
- * @override |
- * @return {boolean} |
- */ |
- onenter: function() |
- { |
- // On Enter or Return start editing the first attribute |
- // or create a new attribute on the selected element. |
- if (this._editing) |
- return false; |
+ return this._addNewAttribute(); |
+ } |
- this._startEditing(); |
+ if (this._node.nodeType() === Node.TEXT_NODE) { |
+ var textNode = listItem.getElementsByClassName('webkit-html-text-node')[0]; |
+ if (textNode) |
+ return this._startEditingTextNode(textNode); |
+ return; |
+ } |
+ } |
+ |
+ _addNewAttribute() { |
+ // Cannot just convert the textual html into an element without |
+ // a parent node. Use a temporary span container for the HTML. |
+ var container = createElement('span'); |
+ this._buildAttributeDOM(container, ' ', '', null); |
+ var attr = container.firstElementChild; |
+ attr.style.marginLeft = '2px'; // overrides the .editing margin rule |
+ attr.style.marginRight = '2px'; // overrides the .editing margin rule |
+ |
+ var tag = this.listItemElement.getElementsByClassName('webkit-html-tag')[0]; |
+ this._insertInLastAttributePosition(tag, attr); |
+ attr.scrollIntoViewIfNeeded(true); |
+ return this._startEditingAttribute(attr, attr); |
+ } |
+ |
+ _triggerEditAttribute(attributeName) { |
+ var attributeElements = this.listItemElement.getElementsByClassName('webkit-html-attribute-name'); |
+ for (var i = 0, len = attributeElements.length; i < len; ++i) { |
+ if (attributeElements[i].textContent === attributeName) { |
+ for (var elem = attributeElements[i].nextSibling; elem; elem = elem.nextSibling) { |
+ if (elem.nodeType !== Node.ELEMENT_NODE) |
+ continue; |
+ |
+ if (elem.classList.contains('webkit-html-attribute-value')) |
+ return this._startEditingAttribute(elem.parentNode, elem); |
+ } |
+ } |
+ } |
+ } |
- // prevent a newline from being immediately inserted |
- return true; |
- }, |
+ _startEditingAttribute(attribute, elementForSelection) { |
+ console.assert(this.listItemElement.isAncestor(attribute)); |
- selectOnMouseDown: function(event) |
- { |
- TreeElement.prototype.selectOnMouseDown.call(this, event); |
+ if (WebInspector.isBeingEdited(attribute)) |
+ return true; |
- if (this._editing) |
- return; |
+ var attributeNameElement = attribute.getElementsByClassName('webkit-html-attribute-name')[0]; |
+ if (!attributeNameElement) |
+ return false; |
- // Prevent selecting the nearest word on double click. |
- if (event.detail >= 2) |
- event.preventDefault(); |
- }, |
+ var attributeName = attributeNameElement.textContent; |
+ var attributeValueElement = attribute.getElementsByClassName('webkit-html-attribute-value')[0]; |
- /** |
- * @override |
- * @return {boolean} |
- */ |
- ondblclick: function(event) |
- { |
- if (this._editing || this._elementCloseTag) |
- return false; |
+ // Make sure elementForSelection is not a child of attributeValueElement. |
+ elementForSelection = |
+ attributeValueElement.isAncestor(elementForSelection) ? attributeValueElement : elementForSelection; |
- if (this._startEditingTarget(/** @type {!Element} */(event.target))) |
- return false; |
+ function removeZeroWidthSpaceRecursive(node) { |
+ if (node.nodeType === Node.TEXT_NODE) { |
+ node.nodeValue = node.nodeValue.replace(/\u200B/g, ''); |
+ return; |
+ } |
- if (this.isExpandable() && !this.expanded) |
- this.expand(); |
- return false; |
- }, |
+ if (node.nodeType !== Node.ELEMENT_NODE) |
+ return; |
- /** |
- * @return {boolean} |
- */ |
- hasEditableNode: function() |
- { |
- return !this._node.isShadowRoot() && !this._node.ancestorUserAgentShadowRoot(); |
- }, |
- |
- _insertInLastAttributePosition: function(tag, node) |
- { |
- if (tag.getElementsByClassName("webkit-html-attribute").length > 0) |
- tag.insertBefore(node, tag.lastChild); |
- else { |
- var nodeName = tag.textContent.match(/^<(.*?)>$/)[1]; |
- tag.textContent = ""; |
- tag.createTextChild("<" + nodeName); |
- tag.appendChild(node); |
- tag.createTextChild(">"); |
- } |
- }, |
+ for (var child = node.firstChild; child; child = child.nextSibling) |
+ removeZeroWidthSpaceRecursive(child); |
+ } |
- /** |
- * @param {!Element} eventTarget |
- * @return {boolean} |
- */ |
- _startEditingTarget: function(eventTarget) |
- { |
- if (this.treeOutline.selectedDOMNode() !== this._node) |
- return false; |
+ var attributeValue = attributeName && attributeValueElement ? this._node.getAttribute(attributeName) : undefined; |
+ if (attributeValue !== undefined) |
+ attributeValueElement.setTextContentTruncatedIfNeeded( |
+ attributeValue, WebInspector.UIString('<value is too large to edit>')); |
- if (this._node.nodeType() !== Node.ELEMENT_NODE && this._node.nodeType() !== Node.TEXT_NODE) |
- return false; |
+ // Remove zero-width spaces that were added by nodeTitleInfo. |
+ removeZeroWidthSpaceRecursive(attribute); |
- var textNode = eventTarget.enclosingNodeOrSelfWithClass("webkit-html-text-node"); |
- if (textNode) |
- return this._startEditingTextNode(textNode); |
+ var config = new WebInspector.InplaceEditor.Config( |
+ this._attributeEditingCommitted.bind(this), this._editingCancelled.bind(this), attributeName); |
- var attribute = eventTarget.enclosingNodeOrSelfWithClass("webkit-html-attribute"); |
- if (attribute) |
- return this._startEditingAttribute(attribute, eventTarget); |
+ /** |
+ * @param {!Event} event |
+ * @return {string} |
+ */ |
+ function postKeyDownFinishHandler(event) { |
+ WebInspector.handleElementValueModifications(event, attribute); |
+ return ''; |
+ } |
- var tagName = eventTarget.enclosingNodeOrSelfWithClass("webkit-html-tag-name"); |
- if (tagName) |
- return this._startEditingTagName(tagName); |
+ if (!attributeValueElement.textContent.asParsedURL()) |
+ config.setPostKeydownFinishHandler(postKeyDownFinishHandler); |
+ |
+ this._editing = WebInspector.InplaceEditor.startEditing(attribute, config); |
+ |
+ this.listItemElement.getComponentSelection().setBaseAndExtent(elementForSelection, 0, elementForSelection, 1); |
+ |
+ return true; |
+ } |
+ |
+ /** |
+ * @param {!Element} textNodeElement |
+ */ |
+ _startEditingTextNode(textNodeElement) { |
+ if (WebInspector.isBeingEdited(textNodeElement)) |
+ return true; |
+ |
+ var textNode = this._node; |
+ // We only show text nodes inline in elements if the element only |
+ // has a single child, and that child is a text node. |
+ if (textNode.nodeType() === Node.ELEMENT_NODE && textNode.firstChild) |
+ textNode = textNode.firstChild; |
+ |
+ var container = textNodeElement.enclosingNodeOrSelfWithClass('webkit-html-text-node'); |
+ if (container) |
+ container.textContent = textNode.nodeValue(); // Strip the CSS or JS highlighting if present. |
+ var config = new WebInspector.InplaceEditor.Config( |
+ this._textNodeEditingCommitted.bind(this, textNode), this._editingCancelled.bind(this)); |
+ this._editing = WebInspector.InplaceEditor.startEditing(textNodeElement, config); |
+ this.listItemElement.getComponentSelection().setBaseAndExtent(textNodeElement, 0, textNodeElement, 1); |
+ |
+ return true; |
+ } |
+ |
+ /** |
+ * @param {!Element=} tagNameElement |
+ */ |
+ _startEditingTagName(tagNameElement) { |
+ if (!tagNameElement) { |
+ tagNameElement = this.listItemElement.getElementsByClassName('webkit-html-tag-name')[0]; |
+ if (!tagNameElement) |
+ return false; |
+ } |
- var newAttribute = eventTarget.enclosingNodeOrSelfWithClass("add-attribute"); |
- if (newAttribute) |
- return this._addNewAttribute(); |
+ var tagName = tagNameElement.textContent; |
+ if (WebInspector.ElementsTreeElement.EditTagBlacklist.has(tagName.toLowerCase())) |
+ return false; |
- return false; |
- }, |
+ if (WebInspector.isBeingEdited(tagNameElement)) |
+ return true; |
+ |
+ var closingTagElement = this._distinctClosingTagElement(); |
/** |
* @param {!Event} event |
*/ |
- _showContextMenu: function(event) |
- { |
- this.treeOutline.showContextMenu(this, event); |
- }, |
+ function keyupListener(event) { |
+ if (closingTagElement) |
+ closingTagElement.textContent = '</' + tagNameElement.textContent + '>'; |
+ } |
/** |
- * @param {!WebInspector.ContextMenu} contextMenu |
- * @param {!Event} event |
+ * @param {!Element} element |
+ * @param {string} newTagName |
+ * @this {WebInspector.ElementsTreeElement} |
*/ |
- populateTagContextMenu: function(contextMenu, event) |
- { |
- // Add attribute-related actions. |
- var treeElement = this._elementCloseTag ? this.treeOutline.findTreeElement(this._node) : this; |
- contextMenu.appendItem(WebInspector.UIString.capitalize("Add ^attribute"), treeElement._addNewAttribute.bind(treeElement)); |
- |
- var attribute = event.target.enclosingNodeOrSelfWithClass("webkit-html-attribute"); |
- var newAttribute = event.target.enclosingNodeOrSelfWithClass("add-attribute"); |
- if (attribute && !newAttribute) |
- contextMenu.appendItem(WebInspector.UIString.capitalize("Edit ^attribute"), this._startEditingAttribute.bind(this, attribute, event.target)); |
- this.populateNodeContextMenu(contextMenu); |
- WebInspector.ElementsTreeElement.populateForcedPseudoStateItems(contextMenu, treeElement.node()); |
- contextMenu.appendSeparator(); |
- this.populateScrollIntoView(contextMenu); |
- }, |
+ function editingComitted(element, newTagName) { |
+ tagNameElement.removeEventListener('keyup', keyupListener, false); |
+ this._tagNameEditingCommitted.apply(this, arguments); |
+ } |
/** |
- * @param {!WebInspector.ContextMenu} contextMenu |
+ * @this {WebInspector.ElementsTreeElement} |
*/ |
- populateScrollIntoView: function(contextMenu) |
- { |
- contextMenu.appendItem(WebInspector.UIString.capitalize("Scroll into ^view"), this._scrollIntoView.bind(this)); |
- }, |
- |
- populateTextContextMenu: function(contextMenu, textNode) |
- { |
- if (!this._editing) |
- contextMenu.appendItem(WebInspector.UIString.capitalize("Edit ^text"), this._startEditingTextNode.bind(this, textNode)); |
- this.populateNodeContextMenu(contextMenu); |
- }, |
- |
- populateNodeContextMenu: function(contextMenu) |
- { |
- // Add free-form node-related actions. |
- var openTagElement = this._node[this.treeOutline.treeElementSymbol()] || this; |
- var isEditable = this.hasEditableNode(); |
- if (isEditable && !this._editing) |
- contextMenu.appendItem(WebInspector.UIString("Edit as HTML"), this._editAsHTML.bind(this)); |
- var isShadowRoot = this._node.isShadowRoot(); |
- |
- // Place it here so that all "Copy"-ing items stick together. |
- var copyMenu = contextMenu.appendSubMenuItem(WebInspector.UIString("Copy")); |
- var createShortcut = WebInspector.KeyboardShortcut.shortcutToString; |
- var modifier = WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta; |
- var treeOutline = this.treeOutline; |
- var menuItem; |
- if (!isShadowRoot) { |
- menuItem = copyMenu.appendItem(WebInspector.UIString("Copy outerHTML"), treeOutline.performCopyOrCut.bind(treeOutline, false, this._node)); |
- menuItem.setShortcut(createShortcut("V", modifier)); |
- } |
- if (this._node.nodeType() === Node.ELEMENT_NODE) |
- copyMenu.appendItem(WebInspector.UIString.capitalize("Copy selector"), this._copyCSSPath.bind(this)); |
- if (!isShadowRoot) |
- copyMenu.appendItem(WebInspector.UIString("Copy XPath"), this._copyXPath.bind(this)); |
- if (!isShadowRoot) { |
- menuItem = copyMenu.appendItem(WebInspector.UIString("Cut element"), treeOutline.performCopyOrCut.bind(treeOutline, true, this._node), !this.hasEditableNode()); |
- menuItem.setShortcut(createShortcut("X", modifier)); |
- menuItem = copyMenu.appendItem(WebInspector.UIString("Copy element"), treeOutline.performCopyOrCut.bind(treeOutline, false, this._node)); |
- menuItem.setShortcut(createShortcut("C", modifier)); |
- menuItem = copyMenu.appendItem(WebInspector.UIString("Paste element"), treeOutline.pasteNode.bind(treeOutline, this._node), !treeOutline.canPaste(this._node)); |
- menuItem.setShortcut(createShortcut("V", modifier)); |
- } |
- |
- contextMenu.appendSeparator(); |
- menuItem = contextMenu.appendCheckboxItem(WebInspector.UIString("Hide element"), treeOutline.toggleHideElement.bind(treeOutline, this._node), treeOutline.isToggledToHidden(this._node)); |
- menuItem.setShortcut(WebInspector.shortcutRegistry.shortcutTitleForAction("elements.hide-element")); |
- |
- |
- if (isEditable) |
- contextMenu.appendItem(WebInspector.UIString("Delete element"), this.remove.bind(this)); |
- contextMenu.appendSeparator(); |
- |
- contextMenu.appendItem(WebInspector.UIString("Expand all"), this.expandRecursively.bind(this)); |
- contextMenu.appendItem(WebInspector.UIString("Collapse all"), this.collapseRecursively.bind(this)); |
- contextMenu.appendSeparator(); |
- }, |
- |
- _startEditing: function() |
- { |
- if (this.treeOutline.selectedDOMNode() !== this._node) |
- return; |
- |
- var listItem = this._listItemNode; |
+ function editingCancelled() { |
+ tagNameElement.removeEventListener('keyup', keyupListener, false); |
+ this._editingCancelled.apply(this, arguments); |
+ } |
- if (this._canAddAttributes) { |
- var attribute = listItem.getElementsByClassName("webkit-html-attribute")[0]; |
- if (attribute) |
- return this._startEditingAttribute(attribute, attribute.getElementsByClassName("webkit-html-attribute-value")[0]); |
+ tagNameElement.addEventListener('keyup', keyupListener, false); |
+ |
+ var config = |
+ new WebInspector.InplaceEditor.Config(editingComitted.bind(this), editingCancelled.bind(this), tagName); |
+ this._editing = WebInspector.InplaceEditor.startEditing(tagNameElement, config); |
+ this.listItemElement.getComponentSelection().setBaseAndExtent(tagNameElement, 0, tagNameElement, 1); |
+ return true; |
+ } |
+ |
+ /** |
+ * @param {function(string, string)} commitCallback |
+ * @param {function()} disposeCallback |
+ * @param {?Protocol.Error} error |
+ * @param {string} initialValue |
+ */ |
+ _startEditingAsHTML(commitCallback, disposeCallback, error, initialValue) { |
+ if (error) |
+ return; |
+ if (this._editing) |
+ return; |
+ |
+ function consume(event) { |
+ if (event.eventPhase === Event.AT_TARGET) |
+ event.consume(true); |
+ } |
- return this._addNewAttribute(); |
- } |
+ initialValue = this._convertWhitespaceToEntities(initialValue).text; |
- if (this._node.nodeType() === Node.TEXT_NODE) { |
- var textNode = listItem.getElementsByClassName("webkit-html-text-node")[0]; |
- if (textNode) |
- return this._startEditingTextNode(textNode); |
- return; |
- } |
- }, |
- |
- _addNewAttribute: function() |
- { |
- // Cannot just convert the textual html into an element without |
- // a parent node. Use a temporary span container for the HTML. |
- var container = createElement("span"); |
- this._buildAttributeDOM(container, " ", "", null); |
- var attr = container.firstElementChild; |
- attr.style.marginLeft = "2px"; // overrides the .editing margin rule |
- attr.style.marginRight = "2px"; // overrides the .editing margin rule |
- |
- var tag = this.listItemElement.getElementsByClassName("webkit-html-tag")[0]; |
- this._insertInLastAttributePosition(tag, attr); |
- attr.scrollIntoViewIfNeeded(true); |
- return this._startEditingAttribute(attr, attr); |
- }, |
- |
- _triggerEditAttribute: function(attributeName) |
- { |
- var attributeElements = this.listItemElement.getElementsByClassName("webkit-html-attribute-name"); |
- for (var i = 0, len = attributeElements.length; i < len; ++i) { |
- if (attributeElements[i].textContent === attributeName) { |
- for (var elem = attributeElements[i].nextSibling; elem; elem = elem.nextSibling) { |
- if (elem.nodeType !== Node.ELEMENT_NODE) |
- continue; |
- |
- if (elem.classList.contains("webkit-html-attribute-value")) |
- return this._startEditingAttribute(elem.parentNode, elem); |
- } |
- } |
- } |
- }, |
+ this._htmlEditElement = createElement('div'); |
+ this._htmlEditElement.className = 'source-code elements-tree-editor'; |
- _startEditingAttribute: function(attribute, elementForSelection) |
- { |
- console.assert(this.listItemElement.isAncestor(attribute)); |
+ // Hide header items. |
+ var child = this.listItemElement.firstChild; |
+ while (child) { |
+ child.style.display = 'none'; |
+ child = child.nextSibling; |
+ } |
+ // Hide children item. |
+ if (this._childrenListNode) |
+ this._childrenListNode.style.display = 'none'; |
+ // Append editor. |
+ this.listItemElement.appendChild(this._htmlEditElement); |
+ this.listItemElement.classList.add('editing-as-html'); |
+ this.treeOutline.element.addEventListener('mousedown', consume, false); |
- if (WebInspector.isBeingEdited(attribute)) |
- return true; |
+ /** |
+ * @param {!Element} element |
+ * @param {string} newValue |
+ * @this {WebInspector.ElementsTreeElement} |
+ */ |
+ function commit(element, newValue) { |
+ commitCallback(initialValue, newValue); |
+ dispose.call(this); |
+ } |
- var attributeNameElement = attribute.getElementsByClassName("webkit-html-attribute-name")[0]; |
- if (!attributeNameElement) |
- return false; |
+ /** |
+ * @this {WebInspector.ElementsTreeElement} |
+ */ |
+ function dispose() { |
+ disposeCallback(); |
+ delete this._editing; |
+ this.treeOutline.setMultilineEditing(null); |
+ |
+ this.listItemElement.classList.remove('editing-as-html'); |
+ // Remove editor. |
+ this.listItemElement.removeChild(this._htmlEditElement); |
+ delete this._htmlEditElement; |
+ // Unhide children item. |
+ if (this._childrenListNode) |
+ this._childrenListNode.style.removeProperty('display'); |
+ // Unhide header items. |
+ var child = this.listItemElement.firstChild; |
+ while (child) { |
+ child.style.removeProperty('display'); |
+ child = child.nextSibling; |
+ } |
+ |
+ this.treeOutline.element.removeEventListener('mousedown', consume, false); |
+ this.treeOutline.focus(); |
+ } |
- var attributeName = attributeNameElement.textContent; |
- var attributeValueElement = attribute.getElementsByClassName("webkit-html-attribute-value")[0]; |
+ var config = new WebInspector.InplaceEditor.Config(commit.bind(this), dispose.bind(this)); |
+ config.setMultilineOptions( |
+ initialValue, {name: 'xml', htmlMode: true}, 'web-inspector-html', |
+ WebInspector.moduleSetting('domWordWrap').get(), true); |
+ WebInspector.InplaceEditor.startMultilineEditing(this._htmlEditElement, config).then(markAsBeingEdited.bind(this)); |
- // Make sure elementForSelection is not a child of attributeValueElement. |
- elementForSelection = attributeValueElement.isAncestor(elementForSelection) ? attributeValueElement : elementForSelection; |
+ /** |
+ * @param {!Object} controller |
+ * @this {WebInspector.ElementsTreeElement} |
+ */ |
+ function markAsBeingEdited(controller) { |
+ this._editing = /** @type {!WebInspector.InplaceEditor.Controller} */ (controller); |
+ this._editing.setWidth(this.treeOutline.visibleWidth() - this._computeLeftIndent()); |
+ this.treeOutline.setMultilineEditing(this._editing); |
+ } |
+ } |
- function removeZeroWidthSpaceRecursive(node) |
- { |
- if (node.nodeType === Node.TEXT_NODE) { |
- node.nodeValue = node.nodeValue.replace(/\u200B/g, ""); |
- return; |
- } |
+ _attributeEditingCommitted(element, newText, oldText, attributeName, moveDirection) { |
+ delete this._editing; |
- if (node.nodeType !== Node.ELEMENT_NODE) |
- return; |
+ var treeOutline = this.treeOutline; |
- for (var child = node.firstChild; child; child = child.nextSibling) |
- removeZeroWidthSpaceRecursive(child); |
+ /** |
+ * @param {?Protocol.Error=} error |
+ * @this {WebInspector.ElementsTreeElement} |
+ */ |
+ function moveToNextAttributeIfNeeded(error) { |
+ if (error) |
+ this._editingCancelled(element, attributeName); |
+ |
+ if (!moveDirection) |
+ return; |
+ |
+ treeOutline.runPendingUpdates(); |
+ |
+ // Search for the attribute's position, and then decide where to move to. |
+ var attributes = this._node.attributes(); |
+ for (var i = 0; i < attributes.length; ++i) { |
+ if (attributes[i].name !== attributeName) |
+ continue; |
+ |
+ if (moveDirection === 'backward') { |
+ if (i === 0) |
+ this._startEditingTagName(); |
+ else |
+ this._triggerEditAttribute(attributes[i - 1].name); |
+ } else { |
+ if (i === attributes.length - 1) |
+ this._addNewAttribute(); |
+ else |
+ this._triggerEditAttribute(attributes[i + 1].name); |
} |
- |
- var attributeValue = attributeName && attributeValueElement ? this._node.getAttribute(attributeName) : undefined; |
- if (attributeValue !== undefined) |
- attributeValueElement.setTextContentTruncatedIfNeeded(attributeValue, WebInspector.UIString("<value is too large to edit>")); |
- |
- // Remove zero-width spaces that were added by nodeTitleInfo. |
- removeZeroWidthSpaceRecursive(attribute); |
- |
- var config = new WebInspector.InplaceEditor.Config(this._attributeEditingCommitted.bind(this), this._editingCancelled.bind(this), attributeName); |
- |
- /** |
- * @param {!Event} event |
- * @return {string} |
- */ |
- function postKeyDownFinishHandler(event) |
- { |
- WebInspector.handleElementValueModifications(event, attribute); |
- return ""; |
+ return; |
+ } |
+ |
+ // Moving From the "New Attribute" position. |
+ if (moveDirection === 'backward') { |
+ if (newText === ' ') { |
+ // Moving from "New Attribute" that was not edited |
+ if (attributes.length > 0) |
+ this._triggerEditAttribute(attributes[attributes.length - 1].name); |
+ } else { |
+ // Moving from "New Attribute" that holds new value |
+ if (attributes.length > 1) |
+ this._triggerEditAttribute(attributes[attributes.length - 2].name); |
} |
+ } else if (moveDirection === 'forward') { |
+ if (!newText.isWhitespace()) |
+ this._addNewAttribute(); |
+ else |
+ this._startEditingTagName(); |
+ } |
+ } |
- if (!attributeValueElement.textContent.asParsedURL()) |
- config.setPostKeydownFinishHandler(postKeyDownFinishHandler); |
+ if ((attributeName.trim() || newText.trim()) && oldText !== newText) { |
+ this._node.setAttribute(attributeName, newText, moveToNextAttributeIfNeeded.bind(this)); |
+ return; |
+ } |
- this._editing = WebInspector.InplaceEditor.startEditing(attribute, config); |
+ this.updateTitle(); |
+ moveToNextAttributeIfNeeded.call(this); |
+ } |
- this.listItemElement.getComponentSelection().setBaseAndExtent(elementForSelection, 0, elementForSelection, 1); |
+ _tagNameEditingCommitted(element, newText, oldText, tagName, moveDirection) { |
+ delete this._editing; |
+ var self = this; |
- return true; |
- }, |
+ function cancel() { |
+ var closingTagElement = self._distinctClosingTagElement(); |
+ if (closingTagElement) |
+ closingTagElement.textContent = '</' + tagName + '>'; |
- /** |
- * @param {!Element} textNodeElement |
- */ |
- _startEditingTextNode: function(textNodeElement) |
- { |
- if (WebInspector.isBeingEdited(textNodeElement)) |
- return true; |
- |
- var textNode = this._node; |
- // We only show text nodes inline in elements if the element only |
- // has a single child, and that child is a text node. |
- if (textNode.nodeType() === Node.ELEMENT_NODE && textNode.firstChild) |
- textNode = textNode.firstChild; |
- |
- var container = textNodeElement.enclosingNodeOrSelfWithClass("webkit-html-text-node"); |
- if (container) |
- container.textContent = textNode.nodeValue(); // Strip the CSS or JS highlighting if present. |
- var config = new WebInspector.InplaceEditor.Config(this._textNodeEditingCommitted.bind(this, textNode), this._editingCancelled.bind(this)); |
- this._editing = WebInspector.InplaceEditor.startEditing(textNodeElement, config); |
- this.listItemElement.getComponentSelection().setBaseAndExtent(textNodeElement, 0, textNodeElement, 1); |
- |
- return true; |
- }, |
+ self._editingCancelled(element, tagName); |
+ moveToNextAttributeIfNeeded.call(self); |
+ } |
/** |
- * @param {!Element=} tagNameElement |
+ * @this {WebInspector.ElementsTreeElement} |
*/ |
- _startEditingTagName: function(tagNameElement) |
- { |
- if (!tagNameElement) { |
- tagNameElement = this.listItemElement.getElementsByClassName("webkit-html-tag-name")[0]; |
- if (!tagNameElement) |
- return false; |
- } |
- |
- var tagName = tagNameElement.textContent; |
- if (WebInspector.ElementsTreeElement.EditTagBlacklist.has(tagName.toLowerCase())) |
- return false; |
- |
- if (WebInspector.isBeingEdited(tagNameElement)) |
- return true; |
- |
- var closingTagElement = this._distinctClosingTagElement(); |
+ function moveToNextAttributeIfNeeded() { |
+ if (moveDirection !== 'forward') { |
+ this._addNewAttribute(); |
+ return; |
+ } |
+ |
+ var attributes = this._node.attributes(); |
+ if (attributes.length > 0) |
+ this._triggerEditAttribute(attributes[0].name); |
+ else |
+ this._addNewAttribute(); |
+ } |
- /** |
- * @param {!Event} event |
- */ |
- function keyupListener(event) |
- { |
- if (closingTagElement) |
- closingTagElement.textContent = "</" + tagNameElement.textContent + ">"; |
- } |
+ newText = newText.trim(); |
+ if (newText === oldText) { |
+ cancel(); |
+ return; |
+ } |
- /** |
- * @param {!Element} element |
- * @param {string} newTagName |
- * @this {WebInspector.ElementsTreeElement} |
- */ |
- function editingComitted(element, newTagName) |
- { |
- tagNameElement.removeEventListener("keyup", keyupListener, false); |
- this._tagNameEditingCommitted.apply(this, arguments); |
- } |
+ var treeOutline = this.treeOutline; |
+ var wasExpanded = this.expanded; |
- /** |
- * @this {WebInspector.ElementsTreeElement} |
- */ |
- function editingCancelled() |
- { |
- tagNameElement.removeEventListener("keyup", keyupListener, false); |
- this._editingCancelled.apply(this, arguments); |
- } |
- |
- tagNameElement.addEventListener("keyup", keyupListener, false); |
+ function changeTagNameCallback(error, nodeId) { |
+ if (error || !nodeId) { |
+ cancel(); |
+ return; |
+ } |
+ var newTreeItem = treeOutline.selectNodeAfterEdit(wasExpanded, error, nodeId); |
+ moveToNextAttributeIfNeeded.call(newTreeItem); |
+ } |
+ this._node.setNodeName(newText, changeTagNameCallback); |
+ } |
- var config = new WebInspector.InplaceEditor.Config(editingComitted.bind(this), editingCancelled.bind(this), tagName); |
- this._editing = WebInspector.InplaceEditor.startEditing(tagNameElement, config); |
- this.listItemElement.getComponentSelection().setBaseAndExtent(tagNameElement, 0, tagNameElement, 1); |
- return true; |
- }, |
+ /** |
+ * @param {!WebInspector.DOMNode} textNode |
+ * @param {!Element} element |
+ * @param {string} newText |
+ */ |
+ _textNodeEditingCommitted(textNode, element, newText) { |
+ delete this._editing; |
/** |
- * @param {function(string, string)} commitCallback |
- * @param {function()} disposeCallback |
- * @param {?Protocol.Error} error |
- * @param {string} initialValue |
+ * @this {WebInspector.ElementsTreeElement} |
*/ |
- _startEditingAsHTML: function(commitCallback, disposeCallback, error, initialValue) |
- { |
- if (error) |
- return; |
- if (this._editing) |
- return; |
- |
- function consume(event) |
- { |
- if (event.eventPhase === Event.AT_TARGET) |
- event.consume(true); |
- } |
+ function callback() { |
+ this.updateTitle(); |
+ } |
+ textNode.setNodeValue(newText, callback.bind(this)); |
+ } |
+ |
+ /** |
+ * @param {!Element} element |
+ * @param {*} context |
+ */ |
+ _editingCancelled(element, context) { |
+ delete this._editing; |
+ |
+ // Need to restore attributes structure. |
+ this.updateTitle(); |
+ } |
+ |
+ /** |
+ * @return {!Element} |
+ */ |
+ _distinctClosingTagElement() { |
+ // FIXME: Improve the Tree Element / Outline Abstraction to prevent crawling the DOM |
+ |
+ // For an expanded element, it will be the last element with class "close" |
+ // in the child element list. |
+ if (this.expanded) { |
+ var closers = this._childrenListNode.querySelectorAll('.close'); |
+ return closers[closers.length - 1]; |
+ } |
- initialValue = this._convertWhitespaceToEntities(initialValue).text; |
+ // Remaining cases are single line non-expanded elements with a closing |
+ // tag, or HTML elements without a closing tag (such as <br>). Return |
+ // null in the case where there isn't a closing tag. |
+ var tags = this.listItemElement.getElementsByClassName('webkit-html-tag'); |
+ return (tags.length === 1 ? null : tags[tags.length - 1]); |
+ } |
+ |
+ /** |
+ * @param {?WebInspector.ElementsTreeOutline.UpdateRecord=} updateRecord |
+ * @param {boolean=} onlySearchQueryChanged |
+ */ |
+ updateTitle(updateRecord, onlySearchQueryChanged) { |
+ // If we are editing, return early to prevent canceling the edit. |
+ // After editing is committed updateTitle will be called. |
+ if (this._editing) |
+ return; |
+ |
+ if (onlySearchQueryChanged) { |
+ this._hideSearchHighlight(); |
+ } else { |
+ var nodeInfo = this._nodeTitleInfo(updateRecord || null); |
+ if (this._node.nodeType() === Node.DOCUMENT_FRAGMENT_NODE && this._node.isInShadowTree() && |
+ this._node.shadowRootType()) { |
+ this.childrenListElement.classList.add('shadow-root'); |
+ var depth = 4; |
+ for (var node = this._node; depth && node; node = node.parentNode) { |
+ if (node.nodeType() === Node.DOCUMENT_FRAGMENT_NODE) |
+ depth--; |
+ } |
+ if (!depth) |
+ this.childrenListElement.classList.add('shadow-root-deep'); |
+ else |
+ this.childrenListElement.classList.add('shadow-root-depth-' + depth); |
+ } |
+ var highlightElement = createElement('span'); |
+ highlightElement.className = 'highlight'; |
+ highlightElement.appendChild(nodeInfo); |
+ this.title = highlightElement; |
+ this.updateDecorations(); |
+ this.listItemElement.insertBefore(this._gutterContainer, this.listItemElement.firstChild); |
+ delete this._highlightResult; |
+ } |
- this._htmlEditElement = createElement("div"); |
- this._htmlEditElement.className = "source-code elements-tree-editor"; |
+ delete this.selectionElement; |
+ if (this.selected) |
+ this._createSelection(); |
+ this._preventFollowingLinksOnDoubleClick(); |
+ this._highlightSearchResults(); |
+ } |
+ |
+ /** |
+ * @return {number} |
+ */ |
+ _computeLeftIndent() { |
+ var treeElement = this.parent; |
+ var depth = 0; |
+ while (treeElement !== null) { |
+ depth++; |
+ treeElement = treeElement.parent; |
+ } |
- // Hide header items. |
- var child = this.listItemElement.firstChild; |
- while (child) { |
- child.style.display = "none"; |
- child = child.nextSibling; |
- } |
- // Hide children item. |
- if (this._childrenListNode) |
- this._childrenListNode.style.display = "none"; |
- // Append editor. |
- this.listItemElement.appendChild(this._htmlEditElement); |
- this.listItemElement.classList.add("editing-as-html"); |
- this.treeOutline.element.addEventListener("mousedown", consume, false); |
- |
- /** |
- * @param {!Element} element |
- * @param {string} newValue |
- * @this {WebInspector.ElementsTreeElement} |
- */ |
- function commit(element, newValue) |
- { |
- commitCallback(initialValue, newValue); |
- dispose.call(this); |
- } |
+ /** Keep it in sync with elementsTreeOutline.css **/ |
+ return 12 * (depth - 2) + (this.isExpandable() ? 1 : 12); |
+ } |
- /** |
- * @this {WebInspector.ElementsTreeElement} |
- */ |
- function dispose() |
- { |
- disposeCallback(); |
- delete this._editing; |
- this.treeOutline.setMultilineEditing(null); |
- |
- this.listItemElement.classList.remove("editing-as-html"); |
- // Remove editor. |
- this.listItemElement.removeChild(this._htmlEditElement); |
- delete this._htmlEditElement; |
- // Unhide children item. |
- if (this._childrenListNode) |
- this._childrenListNode.style.removeProperty("display"); |
- // Unhide header items. |
- var child = this.listItemElement.firstChild; |
- while (child) { |
- child.style.removeProperty("display"); |
- child = child.nextSibling; |
- } |
- |
- this.treeOutline.element.removeEventListener("mousedown", consume, false); |
- this.treeOutline.focus(); |
- } |
+ updateDecorations() { |
+ this._gutterContainer.style.left = (-this._computeLeftIndent()) + 'px'; |
- var config = new WebInspector.InplaceEditor.Config(commit.bind(this), dispose.bind(this)); |
- config.setMultilineOptions(initialValue, { name: "xml", htmlMode: true }, "web-inspector-html", WebInspector.moduleSetting("domWordWrap").get(), true); |
- WebInspector.InplaceEditor.startMultilineEditing(this._htmlEditElement, config).then(markAsBeingEdited.bind(this)); |
- |
- /** |
- * @param {!Object} controller |
- * @this {WebInspector.ElementsTreeElement} |
- */ |
- function markAsBeingEdited(controller) |
- { |
- this._editing = /** @type {!WebInspector.InplaceEditor.Controller} */ (controller); |
- this._editing.setWidth(this.treeOutline.visibleWidth() - this._computeLeftIndent()); |
- this.treeOutline.setMultilineEditing(this._editing); |
- } |
- }, |
- |
- _attributeEditingCommitted: function(element, newText, oldText, attributeName, moveDirection) |
- { |
- delete this._editing; |
- |
- var treeOutline = this.treeOutline; |
- |
- /** |
- * @param {?Protocol.Error=} error |
- * @this {WebInspector.ElementsTreeElement} |
- */ |
- function moveToNextAttributeIfNeeded(error) |
- { |
- if (error) |
- this._editingCancelled(element, attributeName); |
- |
- if (!moveDirection) |
- return; |
- |
- treeOutline.runPendingUpdates(); |
- |
- // Search for the attribute's position, and then decide where to move to. |
- var attributes = this._node.attributes(); |
- for (var i = 0; i < attributes.length; ++i) { |
- if (attributes[i].name !== attributeName) |
- continue; |
- |
- if (moveDirection === "backward") { |
- if (i === 0) |
- this._startEditingTagName(); |
- else |
- this._triggerEditAttribute(attributes[i - 1].name); |
- } else { |
- if (i === attributes.length - 1) |
- this._addNewAttribute(); |
- else |
- this._triggerEditAttribute(attributes[i + 1].name); |
- } |
- return; |
- } |
- |
- // Moving From the "New Attribute" position. |
- if (moveDirection === "backward") { |
- if (newText === " ") { |
- // Moving from "New Attribute" that was not edited |
- if (attributes.length > 0) |
- this._triggerEditAttribute(attributes[attributes.length - 1].name); |
- } else { |
- // Moving from "New Attribute" that holds new value |
- if (attributes.length > 1) |
- this._triggerEditAttribute(attributes[attributes.length - 2].name); |
- } |
- } else if (moveDirection === "forward") { |
- if (!newText.isWhitespace()) |
- this._addNewAttribute(); |
- else |
- this._startEditingTagName(); |
- } |
- } |
+ if (this.isClosingTag()) |
+ return; |
+ var node = this._node; |
+ if (node.nodeType() !== Node.ELEMENT_NODE) |
+ return; |
- if ((attributeName.trim() || newText.trim()) && oldText !== newText) { |
- this._node.setAttribute(attributeName, newText, moveToNextAttributeIfNeeded.bind(this)); |
- return; |
- } |
+ if (!this.treeOutline._decoratorExtensions) |
+ /** @type {!Array.<!Runtime.Extension>} */ |
+ this.treeOutline._decoratorExtensions = runtime.extensions(WebInspector.DOMPresentationUtils.MarkerDecorator); |
- this.updateTitle(); |
- moveToNextAttributeIfNeeded.call(this); |
- }, |
+ var markerToExtension = new Map(); |
+ for (var i = 0; i < this.treeOutline._decoratorExtensions.length; ++i) |
+ markerToExtension.set( |
+ this.treeOutline._decoratorExtensions[i].descriptor()['marker'], this.treeOutline._decoratorExtensions[i]); |
- _tagNameEditingCommitted: function(element, newText, oldText, tagName, moveDirection) |
- { |
- delete this._editing; |
- var self = this; |
+ var promises = []; |
+ var decorations = []; |
+ var descendantDecorations = []; |
+ node.traverseMarkers(visitor); |
- function cancel() |
- { |
- var closingTagElement = self._distinctClosingTagElement(); |
- if (closingTagElement) |
- closingTagElement.textContent = "</" + tagName + ">"; |
+ /** |
+ * @param {!WebInspector.DOMNode} n |
+ * @param {string} marker |
+ */ |
+ function visitor(n, marker) { |
+ var extension = markerToExtension.get(marker); |
+ if (!extension) |
+ return; |
+ promises.push(extension.instance().then(collectDecoration.bind(null, n))); |
+ } |
- self._editingCancelled(element, tagName); |
- moveToNextAttributeIfNeeded.call(self); |
- } |
+ /** |
+ * @param {!WebInspector.DOMNode} n |
+ * @param {!WebInspector.DOMPresentationUtils.MarkerDecorator} decorator |
+ */ |
+ function collectDecoration(n, decorator) { |
+ var decoration = decorator.decorate(n); |
+ if (!decoration) |
+ return; |
+ (n === node ? decorations : descendantDecorations).push(decoration); |
+ } |
- /** |
- * @this {WebInspector.ElementsTreeElement} |
- */ |
- function moveToNextAttributeIfNeeded() |
- { |
- if (moveDirection !== "forward") { |
- this._addNewAttribute(); |
- return; |
- } |
- |
- var attributes = this._node.attributes(); |
- if (attributes.length > 0) |
- this._triggerEditAttribute(attributes[0].name); |
- else |
- this._addNewAttribute(); |
- } |
+ Promise.all(promises).then(updateDecorationsUI.bind(this)); |
- newText = newText.trim(); |
- if (newText === oldText) { |
- cancel(); |
- return; |
+ /** |
+ * @this {WebInspector.ElementsTreeElement} |
+ */ |
+ function updateDecorationsUI() { |
+ this._decorationsElement.removeChildren(); |
+ this._decorationsElement.classList.add('hidden'); |
+ this._gutterContainer.classList.toggle('has-decorations', decorations.length || descendantDecorations.length); |
+ |
+ if (!decorations.length && !descendantDecorations.length) |
+ return; |
+ |
+ var colors = new Set(); |
+ var titles = createElement('div'); |
+ |
+ for (var decoration of decorations) { |
+ var titleElement = titles.createChild('div'); |
+ titleElement.textContent = decoration.title; |
+ colors.add(decoration.color); |
+ } |
+ if (this.expanded && !decorations.length) |
+ return; |
+ |
+ var descendantColors = new Set(); |
+ if (descendantDecorations.length) { |
+ var element = titles.createChild('div'); |
+ element.textContent = WebInspector.UIString('Children:'); |
+ for (var decoration of descendantDecorations) { |
+ element = titles.createChild('div'); |
+ element.style.marginLeft = '15px'; |
+ element.textContent = decoration.title; |
+ descendantColors.add(decoration.color); |
} |
- |
- var treeOutline = this.treeOutline; |
- var wasExpanded = this.expanded; |
- |
- function changeTagNameCallback(error, nodeId) |
- { |
- if (error || !nodeId) { |
- cancel(); |
- return; |
- } |
- var newTreeItem = treeOutline.selectNodeAfterEdit(wasExpanded, error, nodeId); |
- moveToNextAttributeIfNeeded.call(newTreeItem); |
+ } |
+ |
+ var offset = 0; |
+ processColors.call(this, colors, 'elements-gutter-decoration'); |
+ if (!this.expanded) |
+ processColors.call(this, descendantColors, 'elements-gutter-decoration elements-has-decorated-children'); |
+ WebInspector.Tooltip.install(this._decorationsElement, titles); |
+ |
+ /** |
+ * @param {!Set<string>} colors |
+ * @param {string} className |
+ * @this {WebInspector.ElementsTreeElement} |
+ */ |
+ function processColors(colors, className) { |
+ for (var color of colors) { |
+ var child = this._decorationsElement.createChild('div', className); |
+ this._decorationsElement.classList.remove('hidden'); |
+ child.style.backgroundColor = color; |
+ child.style.borderColor = color; |
+ if (offset) |
+ child.style.marginLeft = offset + 'px'; |
+ offset += 3; |
} |
- this._node.setNodeName(newText, changeTagNameCallback); |
- }, |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * @param {!Node} parentElement |
+ * @param {string} name |
+ * @param {string} value |
+ * @param {?WebInspector.ElementsTreeOutline.UpdateRecord} updateRecord |
+ * @param {boolean=} forceValue |
+ * @param {!WebInspector.DOMNode=} node |
+ */ |
+ _buildAttributeDOM(parentElement, name, value, updateRecord, forceValue, node) { |
+ var closingPunctuationRegex = /[\/;:\)\]\}]/g; |
+ var highlightIndex = 0; |
+ var highlightCount; |
+ var additionalHighlightOffset = 0; |
+ var result; |
/** |
- * @param {!WebInspector.DOMNode} textNode |
- * @param {!Element} element |
- * @param {string} newText |
+ * @param {string} match |
+ * @param {number} replaceOffset |
+ * @return {string} |
*/ |
- _textNodeEditingCommitted: function(textNode, element, newText) |
- { |
- delete this._editing; |
- |
- /** |
- * @this {WebInspector.ElementsTreeElement} |
- */ |
- function callback() |
- { |
- this.updateTitle(); |
- } |
- textNode.setNodeValue(newText, callback.bind(this)); |
- }, |
+ function replacer(match, replaceOffset) { |
+ while (highlightIndex < highlightCount && result.entityRanges[highlightIndex].offset < replaceOffset) { |
+ result.entityRanges[highlightIndex].offset += additionalHighlightOffset; |
+ ++highlightIndex; |
+ } |
+ additionalHighlightOffset += 1; |
+ return match + '\u200B'; |
+ } |
/** |
* @param {!Element} element |
- * @param {*} context |
+ * @param {string} value |
+ * @this {WebInspector.ElementsTreeElement} |
*/ |
- _editingCancelled: function(element, context) |
- { |
- delete this._editing; |
+ function setValueWithEntities(element, value) { |
+ result = this._convertWhitespaceToEntities(value); |
+ highlightCount = result.entityRanges.length; |
+ value = result.text.replace(closingPunctuationRegex, replacer); |
+ while (highlightIndex < highlightCount) { |
+ result.entityRanges[highlightIndex].offset += additionalHighlightOffset; |
+ ++highlightIndex; |
+ } |
+ element.setTextContentTruncatedIfNeeded(value); |
+ WebInspector.highlightRangesWithStyleClass(element, result.entityRanges, 'webkit-html-entity-value'); |
+ } |
- // Need to restore attributes structure. |
- this.updateTitle(); |
- }, |
+ var hasText = (forceValue || value.length > 0); |
+ var attrSpanElement = parentElement.createChild('span', 'webkit-html-attribute'); |
+ var attrNameElement = attrSpanElement.createChild('span', 'webkit-html-attribute-name'); |
+ attrNameElement.textContent = name; |
- /** |
- * @return {!Element} |
- */ |
- _distinctClosingTagElement: function() |
- { |
- // FIXME: Improve the Tree Element / Outline Abstraction to prevent crawling the DOM |
- |
- // For an expanded element, it will be the last element with class "close" |
- // in the child element list. |
- if (this.expanded) { |
- var closers = this._childrenListNode.querySelectorAll(".close"); |
- return closers[closers.length - 1]; |
- } |
+ if (hasText) |
+ attrSpanElement.createTextChild('=\u200B"'); |
+ |
+ var attrValueElement = attrSpanElement.createChild('span', 'webkit-html-attribute-value'); |
- // Remaining cases are single line non-expanded elements with a closing |
- // tag, or HTML elements without a closing tag (such as <br>). Return |
- // null in the case where there isn't a closing tag. |
- var tags = this.listItemElement.getElementsByClassName("webkit-html-tag"); |
- return (tags.length === 1 ? null : tags[tags.length - 1]); |
- }, |
+ if (updateRecord && updateRecord.isAttributeModified(name)) |
+ WebInspector.runCSSAnimationOnce(hasText ? attrValueElement : attrNameElement, 'dom-update-highlight'); |
/** |
- * @param {?WebInspector.ElementsTreeOutline.UpdateRecord=} updateRecord |
- * @param {boolean=} onlySearchQueryChanged |
+ * @this {WebInspector.ElementsTreeElement} |
+ * @param {string} value |
+ * @return {!Element} |
*/ |
- updateTitle: function(updateRecord, onlySearchQueryChanged) |
- { |
- // If we are editing, return early to prevent canceling the edit. |
- // After editing is committed updateTitle will be called. |
- if (this._editing) |
- return; |
- |
- if (onlySearchQueryChanged) { |
- this._hideSearchHighlight(); |
+ function linkifyValue(value) { |
+ var rewrittenHref = node.resolveURL(value); |
+ if (rewrittenHref === null) { |
+ var span = createElement('span'); |
+ setValueWithEntities.call(this, span, value); |
+ return span; |
+ } |
+ value = value.replace(closingPunctuationRegex, '$&\u200B'); |
+ if (value.startsWith('data:')) |
+ value = value.trimMiddle(60); |
+ var anchor = WebInspector.linkifyURLAsNode(rewrittenHref, value, '', node.nodeName().toLowerCase() === 'a'); |
+ anchor.preventFollow = true; |
+ return anchor; |
+ } |
+ |
+ if (node && (name === 'src' || name === 'href')) { |
+ attrValueElement.appendChild(linkifyValue.call(this, value)); |
+ } else if ( |
+ node && (node.nodeName().toLowerCase() === 'img' || node.nodeName().toLowerCase() === 'source') && |
+ name === 'srcset') { |
+ var sources = value.split(','); |
+ for (var i = 0; i < sources.length; ++i) { |
+ if (i > 0) |
+ attrValueElement.createTextChild(', '); |
+ var source = sources[i].trim(); |
+ var indexOfSpace = source.indexOf(' '); |
+ var url, tail; |
+ |
+ if (indexOfSpace === -1) { |
+ url = source; |
} else { |
- var nodeInfo = this._nodeTitleInfo(updateRecord || null); |
- if (this._node.nodeType() === Node.DOCUMENT_FRAGMENT_NODE && this._node.isInShadowTree() && this._node.shadowRootType()) { |
- this.childrenListElement.classList.add("shadow-root"); |
- var depth = 4; |
- for (var node = this._node; depth && node; node = node.parentNode) { |
- if (node.nodeType() === Node.DOCUMENT_FRAGMENT_NODE) |
- depth--; |
- } |
- if (!depth) |
- this.childrenListElement.classList.add("shadow-root-deep"); |
- else |
- this.childrenListElement.classList.add("shadow-root-depth-" + depth); |
- } |
- var highlightElement = createElement("span"); |
- highlightElement.className = "highlight"; |
- highlightElement.appendChild(nodeInfo); |
- this.title = highlightElement; |
- this.updateDecorations(); |
- this.listItemElement.insertBefore(this._gutterContainer, this.listItemElement.firstChild); |
- delete this._highlightResult; |
+ url = source.substring(0, indexOfSpace); |
+ tail = source.substring(indexOfSpace); |
} |
- delete this.selectionElement; |
- if (this.selected) |
- this._createSelection(); |
- this._preventFollowingLinksOnDoubleClick(); |
- this._highlightSearchResults(); |
- }, |
+ attrValueElement.appendChild(linkifyValue.call(this, url)); |
- /** |
- * @return {number} |
- */ |
- _computeLeftIndent: function() |
- { |
- var treeElement = this.parent; |
- var depth = 0; |
- while (treeElement !== null) { |
- depth++; |
- treeElement = treeElement.parent; |
- } |
+ if (tail) |
+ attrValueElement.createTextChild(tail); |
+ } |
+ } else { |
+ setValueWithEntities.call(this, attrValueElement, value); |
+ } |
- /** Keep it in sync with elementsTreeOutline.css **/ |
- return 12 * (depth - 2) + (this.isExpandable() ? 1 : 12); |
- }, |
- |
- updateDecorations: function() |
- { |
- this._gutterContainer.style.left = (-this._computeLeftIndent()) + "px"; |
- |
- if (this.isClosingTag()) |
- return; |
- |
- var node = this._node; |
- if (node.nodeType() !== Node.ELEMENT_NODE) |
- return; |
- |
- if (!this.treeOutline._decoratorExtensions) |
- /** @type {!Array.<!Runtime.Extension>} */ |
- this.treeOutline._decoratorExtensions = runtime.extensions(WebInspector.DOMPresentationUtils.MarkerDecorator); |
- |
- var markerToExtension = new Map(); |
- for (var i = 0; i < this.treeOutline._decoratorExtensions.length; ++i) |
- markerToExtension.set(this.treeOutline._decoratorExtensions[i].descriptor()["marker"], this.treeOutline._decoratorExtensions[i]); |
- |
- var promises = []; |
- var decorations = []; |
- var descendantDecorations = []; |
- node.traverseMarkers(visitor); |
- |
- /** |
- * @param {!WebInspector.DOMNode} n |
- * @param {string} marker |
- */ |
- function visitor(n, marker) |
- { |
- var extension = markerToExtension.get(marker); |
- if (!extension) |
- return; |
- promises.push(extension.instance().then(collectDecoration.bind(null, n))); |
+ if (hasText) |
+ attrSpanElement.createTextChild('"'); |
+ } |
+ |
+ /** |
+ * @param {!Node} parentElement |
+ * @param {string} pseudoElementName |
+ */ |
+ _buildPseudoElementDOM(parentElement, pseudoElementName) { |
+ var pseudoElement = parentElement.createChild('span', 'webkit-html-pseudo-element'); |
+ pseudoElement.textContent = '::' + pseudoElementName; |
+ parentElement.createTextChild('\u200B'); |
+ } |
+ |
+ /** |
+ * @param {!Node} parentElement |
+ * @param {string} tagName |
+ * @param {boolean} isClosingTag |
+ * @param {boolean} isDistinctTreeElement |
+ * @param {?WebInspector.ElementsTreeOutline.UpdateRecord} updateRecord |
+ */ |
+ _buildTagDOM(parentElement, tagName, isClosingTag, isDistinctTreeElement, updateRecord) { |
+ var node = this._node; |
+ var classes = ['webkit-html-tag']; |
+ if (isClosingTag && isDistinctTreeElement) |
+ classes.push('close'); |
+ var tagElement = parentElement.createChild('span', classes.join(' ')); |
+ tagElement.createTextChild('<'); |
+ var tagNameElement = |
+ tagElement.createChild('span', isClosingTag ? 'webkit-html-close-tag-name' : 'webkit-html-tag-name'); |
+ tagNameElement.textContent = (isClosingTag ? '/' : '') + tagName; |
+ if (!isClosingTag) { |
+ if (node.hasAttributes()) { |
+ var attributes = node.attributes(); |
+ for (var i = 0; i < attributes.length; ++i) { |
+ var attr = attributes[i]; |
+ tagElement.createTextChild(' '); |
+ this._buildAttributeDOM(tagElement, attr.name, attr.value, updateRecord, false, node); |
} |
+ } |
+ if (updateRecord) { |
+ var hasUpdates = updateRecord.hasRemovedAttributes() || updateRecord.hasRemovedChildren(); |
+ hasUpdates |= !this.expanded && updateRecord.hasChangedChildren(); |
+ if (hasUpdates) |
+ WebInspector.runCSSAnimationOnce(tagNameElement, 'dom-update-highlight'); |
+ } |
+ } |
- /** |
- * @param {!WebInspector.DOMNode} n |
- * @param {!WebInspector.DOMPresentationUtils.MarkerDecorator} decorator |
- */ |
- function collectDecoration(n, decorator) |
- { |
- var decoration = decorator.decorate(n); |
- if (!decoration) |
- return; |
- (n === node ? decorations : descendantDecorations).push(decoration); |
+ tagElement.createTextChild('>'); |
+ parentElement.createTextChild('\u200B'); |
+ } |
+ |
+ /** |
+ * @param {string} text |
+ * @return {!{text: string, entityRanges: !Array.<!WebInspector.SourceRange>}} |
+ */ |
+ _convertWhitespaceToEntities(text) { |
+ var result = ''; |
+ var lastIndexAfterEntity = 0; |
+ var entityRanges = []; |
+ var charToEntity = WebInspector.ElementsTreeOutline.MappedCharToEntity; |
+ for (var i = 0, size = text.length; i < size; ++i) { |
+ var char = text.charAt(i); |
+ if (charToEntity[char]) { |
+ result += text.substring(lastIndexAfterEntity, i); |
+ var entityValue = '&' + charToEntity[char] + ';'; |
+ entityRanges.push({offset: result.length, length: entityValue.length}); |
+ result += entityValue; |
+ lastIndexAfterEntity = i + 1; |
+ } |
+ } |
+ if (result) |
+ result += text.substring(lastIndexAfterEntity); |
+ return {text: result || text, entityRanges: entityRanges}; |
+ } |
+ |
+ /** |
+ * @param {?WebInspector.ElementsTreeOutline.UpdateRecord} updateRecord |
+ * @return {!DocumentFragment} result |
+ */ |
+ _nodeTitleInfo(updateRecord) { |
+ var node = this._node; |
+ var titleDOM = createDocumentFragment(); |
+ |
+ switch (node.nodeType()) { |
+ case Node.ATTRIBUTE_NODE: |
+ this._buildAttributeDOM( |
+ titleDOM, /** @type {string} */ (node.name), /** @type {string} */ (node.value), updateRecord, true); |
+ break; |
+ |
+ case Node.ELEMENT_NODE: |
+ var pseudoType = node.pseudoType(); |
+ if (pseudoType) { |
+ this._buildPseudoElementDOM(titleDOM, pseudoType); |
+ break; |
} |
- Promise.all(promises).then(updateDecorationsUI.bind(this)); |
- |
- /** |
- * @this {WebInspector.ElementsTreeElement} |
- */ |
- function updateDecorationsUI() |
- { |
- this._decorationsElement.removeChildren(); |
- this._decorationsElement.classList.add("hidden"); |
- this._gutterContainer.classList.toggle("has-decorations", decorations.length || descendantDecorations.length); |
- |
- if (!decorations.length && !descendantDecorations.length) |
- return; |
- |
- var colors = new Set(); |
- var titles = createElement("div"); |
- |
- for (var decoration of decorations) { |
- var titleElement = titles.createChild("div"); |
- titleElement.textContent = decoration.title; |
- colors.add(decoration.color); |
- } |
- if (this.expanded && !decorations.length) |
- return; |
- |
- var descendantColors = new Set(); |
- if (descendantDecorations.length) { |
- var element = titles.createChild("div"); |
- element.textContent = WebInspector.UIString("Children:"); |
- for (var decoration of descendantDecorations) { |
- element = titles.createChild("div"); |
- element.style.marginLeft = "15px"; |
- element.textContent = decoration.title; |
- descendantColors.add(decoration.color); |
- } |
- } |
- |
- var offset = 0; |
- processColors.call(this, colors, "elements-gutter-decoration"); |
- if (!this.expanded) |
- processColors.call(this, descendantColors, "elements-gutter-decoration elements-has-decorated-children"); |
- WebInspector.Tooltip.install(this._decorationsElement, titles); |
- |
- /** |
- * @param {!Set<string>} colors |
- * @param {string} className |
- * @this {WebInspector.ElementsTreeElement} |
- */ |
- function processColors(colors, className) |
- { |
- for (var color of colors) { |
- var child = this._decorationsElement.createChild("div", className); |
- this._decorationsElement.classList.remove("hidden"); |
- child.style.backgroundColor = color; |
- child.style.borderColor = color; |
- if (offset) |
- child.style.marginLeft = offset + "px"; |
- offset += 3; |
- } |
- } |
+ var tagName = node.nodeNameInCorrectCase(); |
+ if (this._elementCloseTag) { |
+ this._buildTagDOM(titleDOM, tagName, true, true, updateRecord); |
+ break; |
} |
- }, |
- /** |
- * @param {!Node} parentElement |
- * @param {string} name |
- * @param {string} value |
- * @param {?WebInspector.ElementsTreeOutline.UpdateRecord} updateRecord |
- * @param {boolean=} forceValue |
- * @param {!WebInspector.DOMNode=} node |
- */ |
- _buildAttributeDOM: function(parentElement, name, value, updateRecord, forceValue, node) |
- { |
- var closingPunctuationRegex = /[\/;:\)\]\}]/g; |
- var highlightIndex = 0; |
- var highlightCount; |
- var additionalHighlightOffset = 0; |
- var result; |
- |
- /** |
- * @param {string} match |
- * @param {number} replaceOffset |
- * @return {string} |
- */ |
- function replacer(match, replaceOffset) { |
- while (highlightIndex < highlightCount && result.entityRanges[highlightIndex].offset < replaceOffset) { |
- result.entityRanges[highlightIndex].offset += additionalHighlightOffset; |
- ++highlightIndex; |
- } |
- additionalHighlightOffset += 1; |
- return match + "\u200B"; |
- } |
+ this._buildTagDOM(titleDOM, tagName, false, false, updateRecord); |
- /** |
- * @param {!Element} element |
- * @param {string} value |
- * @this {WebInspector.ElementsTreeElement} |
- */ |
- function setValueWithEntities(element, value) |
- { |
- result = this._convertWhitespaceToEntities(value); |
- highlightCount = result.entityRanges.length; |
- value = result.text.replace(closingPunctuationRegex, replacer); |
- while (highlightIndex < highlightCount) { |
- result.entityRanges[highlightIndex].offset += additionalHighlightOffset; |
- ++highlightIndex; |
- } |
- element.setTextContentTruncatedIfNeeded(value); |
- WebInspector.highlightRangesWithStyleClass(element, result.entityRanges, "webkit-html-entity-value"); |
+ if (this.isExpandable()) { |
+ if (!this.expanded) { |
+ var textNodeElement = titleDOM.createChild('span', 'webkit-html-text-node bogus'); |
+ textNodeElement.textContent = '\u2026'; |
+ titleDOM.createTextChild('\u200B'); |
+ this._buildTagDOM(titleDOM, tagName, true, false, updateRecord); |
+ } |
+ break; |
} |
- var hasText = (forceValue || value.length > 0); |
- var attrSpanElement = parentElement.createChild("span", "webkit-html-attribute"); |
- var attrNameElement = attrSpanElement.createChild("span", "webkit-html-attribute-name"); |
- attrNameElement.textContent = name; |
- |
- if (hasText) |
- attrSpanElement.createTextChild("=\u200B\""); |
- |
- var attrValueElement = attrSpanElement.createChild("span", "webkit-html-attribute-value"); |
- |
- if (updateRecord && updateRecord.isAttributeModified(name)) |
- WebInspector.runCSSAnimationOnce(hasText ? attrValueElement : attrNameElement, "dom-update-highlight"); |
- |
- /** |
- * @this {WebInspector.ElementsTreeElement} |
- * @param {string} value |
- * @return {!Element} |
- */ |
- function linkifyValue(value) |
- { |
- var rewrittenHref = node.resolveURL(value); |
- if (rewrittenHref === null) { |
- var span = createElement("span"); |
- setValueWithEntities.call(this, span, value); |
- return span; |
- } |
- value = value.replace(closingPunctuationRegex, "$&\u200B"); |
- if (value.startsWith("data:")) |
- value = value.trimMiddle(60); |
- var anchor = WebInspector.linkifyURLAsNode(rewrittenHref, value, "", node.nodeName().toLowerCase() === "a"); |
- anchor.preventFollow = true; |
- return anchor; |
+ if (WebInspector.ElementsTreeElement.canShowInlineText(node)) { |
+ var textNodeElement = titleDOM.createChild('span', 'webkit-html-text-node'); |
+ var result = this._convertWhitespaceToEntities(node.firstChild.nodeValue()); |
+ textNodeElement.textContent = result.text; |
+ WebInspector.highlightRangesWithStyleClass(textNodeElement, result.entityRanges, 'webkit-html-entity-value'); |
+ titleDOM.createTextChild('\u200B'); |
+ this._buildTagDOM(titleDOM, tagName, true, false, updateRecord); |
+ if (updateRecord && updateRecord.hasChangedChildren()) |
+ WebInspector.runCSSAnimationOnce(textNodeElement, 'dom-update-highlight'); |
+ if (updateRecord && updateRecord.isCharDataModified()) |
+ WebInspector.runCSSAnimationOnce(textNodeElement, 'dom-update-highlight'); |
+ break; |
} |
- if (node && (name === "src" || name === "href")) { |
- attrValueElement.appendChild(linkifyValue.call(this, value)); |
- } else if (node && (node.nodeName().toLowerCase() === "img" || node.nodeName().toLowerCase() === "source") && name === "srcset") { |
- var sources = value.split(","); |
- for (var i = 0; i < sources.length; ++i) { |
- if (i > 0) |
- attrValueElement.createTextChild(", "); |
- var source = sources[i].trim(); |
- var indexOfSpace = source.indexOf(" "); |
- var url, tail; |
- |
- if (indexOfSpace === -1) { |
- url = source; |
- } else { |
- url = source.substring(0, indexOfSpace); |
- tail = source.substring(indexOfSpace); |
- } |
- |
- attrValueElement.appendChild(linkifyValue.call(this, url)); |
- |
- if (tail) |
- attrValueElement.createTextChild(tail); |
- } |
+ if (this.treeOutline.isXMLMimeType || |
+ !WebInspector.ElementsTreeElement.ForbiddenClosingTagElements.has(tagName)) |
+ this._buildTagDOM(titleDOM, tagName, true, false, updateRecord); |
+ break; |
+ |
+ case Node.TEXT_NODE: |
+ if (node.parentNode && node.parentNode.nodeName().toLowerCase() === 'script') { |
+ var newNode = titleDOM.createChild('span', 'webkit-html-text-node webkit-html-js-node'); |
+ var text = node.nodeValue(); |
+ newNode.textContent = text.startsWith('\n') ? text.substring(1) : text; |
+ |
+ var javascriptSyntaxHighlighter = new WebInspector.DOMSyntaxHighlighter('text/javascript', true); |
+ javascriptSyntaxHighlighter.syntaxHighlightNode(newNode).then(updateSearchHighlight.bind(this)); |
+ } else if (node.parentNode && node.parentNode.nodeName().toLowerCase() === 'style') { |
+ var newNode = titleDOM.createChild('span', 'webkit-html-text-node webkit-html-css-node'); |
+ var text = node.nodeValue(); |
+ newNode.textContent = text.startsWith('\n') ? text.substring(1) : text; |
+ |
+ var cssSyntaxHighlighter = new WebInspector.DOMSyntaxHighlighter('text/css', true); |
+ cssSyntaxHighlighter.syntaxHighlightNode(newNode).then(updateSearchHighlight.bind(this)); |
} else { |
- setValueWithEntities.call(this, attrValueElement, value); |
+ titleDOM.createTextChild('"'); |
+ var textNodeElement = titleDOM.createChild('span', 'webkit-html-text-node'); |
+ var result = this._convertWhitespaceToEntities(node.nodeValue()); |
+ textNodeElement.textContent = result.text; |
+ WebInspector.highlightRangesWithStyleClass(textNodeElement, result.entityRanges, 'webkit-html-entity-value'); |
+ titleDOM.createTextChild('"'); |
+ if (updateRecord && updateRecord.isCharDataModified()) |
+ WebInspector.runCSSAnimationOnce(textNodeElement, 'dom-update-highlight'); |
} |
- |
- if (hasText) |
- attrSpanElement.createTextChild("\""); |
- }, |
+ break; |
+ |
+ case Node.COMMENT_NODE: |
+ var commentElement = titleDOM.createChild('span', 'webkit-html-comment'); |
+ commentElement.createTextChild('<!--' + node.nodeValue() + '-->'); |
+ break; |
+ |
+ case Node.DOCUMENT_TYPE_NODE: |
+ var docTypeElement = titleDOM.createChild('span', 'webkit-html-doctype'); |
+ docTypeElement.createTextChild('<!DOCTYPE ' + node.nodeName()); |
+ if (node.publicId) { |
+ docTypeElement.createTextChild(' PUBLIC "' + node.publicId + '"'); |
+ if (node.systemId) |
+ docTypeElement.createTextChild(' "' + node.systemId + '"'); |
+ } else if (node.systemId) |
+ docTypeElement.createTextChild(' SYSTEM "' + node.systemId + '"'); |
+ |
+ if (node.internalSubset) |
+ docTypeElement.createTextChild(' [' + node.internalSubset + ']'); |
+ |
+ docTypeElement.createTextChild('>'); |
+ break; |
+ |
+ case Node.CDATA_SECTION_NODE: |
+ var cdataElement = titleDOM.createChild('span', 'webkit-html-text-node'); |
+ cdataElement.createTextChild('<![CDATA[' + node.nodeValue() + ']]>'); |
+ break; |
+ |
+ case Node.DOCUMENT_FRAGMENT_NODE: |
+ var fragmentElement = titleDOM.createChild('span', 'webkit-html-fragment'); |
+ fragmentElement.textContent = node.nodeNameInCorrectCase().collapseWhitespace(); |
+ break; |
+ default: |
+ titleDOM.createTextChild(node.nodeNameInCorrectCase().collapseWhitespace()); |
+ } |
/** |
- * @param {!Node} parentElement |
- * @param {string} pseudoElementName |
+ * @this {WebInspector.ElementsTreeElement} |
*/ |
- _buildPseudoElementDOM: function(parentElement, pseudoElementName) |
- { |
- var pseudoElement = parentElement.createChild("span", "webkit-html-pseudo-element"); |
- pseudoElement.textContent = "::" + pseudoElementName; |
- parentElement.createTextChild("\u200B"); |
- }, |
+ function updateSearchHighlight() { |
+ delete this._highlightResult; |
+ this._highlightSearchResults(); |
+ } |
- /** |
- * @param {!Node} parentElement |
- * @param {string} tagName |
- * @param {boolean} isClosingTag |
- * @param {boolean} isDistinctTreeElement |
- * @param {?WebInspector.ElementsTreeOutline.UpdateRecord} updateRecord |
- */ |
- _buildTagDOM: function(parentElement, tagName, isClosingTag, isDistinctTreeElement, updateRecord) |
- { |
- var node = this._node; |
- var classes = [ "webkit-html-tag" ]; |
- if (isClosingTag && isDistinctTreeElement) |
- classes.push("close"); |
- var tagElement = parentElement.createChild("span", classes.join(" ")); |
- tagElement.createTextChild("<"); |
- var tagNameElement = tagElement.createChild("span", isClosingTag ? "webkit-html-close-tag-name" : "webkit-html-tag-name"); |
- tagNameElement.textContent = (isClosingTag ? "/" : "") + tagName; |
- if (!isClosingTag) { |
- if (node.hasAttributes()) { |
- var attributes = node.attributes(); |
- for (var i = 0; i < attributes.length; ++i) { |
- var attr = attributes[i]; |
- tagElement.createTextChild(" "); |
- this._buildAttributeDOM(tagElement, attr.name, attr.value, updateRecord, false, node); |
- } |
- } |
- if (updateRecord) { |
- var hasUpdates = updateRecord.hasRemovedAttributes() || updateRecord.hasRemovedChildren(); |
- hasUpdates |= !this.expanded && updateRecord.hasChangedChildren(); |
- if (hasUpdates) |
- WebInspector.runCSSAnimationOnce(tagNameElement, "dom-update-highlight"); |
- } |
- } |
+ return titleDOM; |
+ } |
+ |
+ remove() { |
+ if (this._node.pseudoType()) |
+ return; |
+ var parentElement = this.parent; |
+ if (!parentElement) |
+ return; |
+ |
+ if (!this._node.parentNode || this._node.parentNode.nodeType() === Node.DOCUMENT_NODE) |
+ return; |
+ this._node.removeNode(); |
+ } |
+ |
+ /** |
+ * @param {function(boolean)=} callback |
+ * @param {boolean=} startEditing |
+ */ |
+ toggleEditAsHTML(callback, startEditing) { |
+ if (this._editing && this._htmlEditElement && WebInspector.isBeingEdited(this._htmlEditElement)) { |
+ this._editing.commit(); |
+ return; |
+ } |
- tagElement.createTextChild(">"); |
- parentElement.createTextChild("\u200B"); |
- }, |
+ if (startEditing === false) |
+ return; |
/** |
- * @param {string} text |
- * @return {!{text: string, entityRanges: !Array.<!WebInspector.SourceRange>}} |
+ * @param {?Protocol.Error} error |
*/ |
- _convertWhitespaceToEntities: function(text) |
- { |
- var result = ""; |
- var lastIndexAfterEntity = 0; |
- var entityRanges = []; |
- var charToEntity = WebInspector.ElementsTreeOutline.MappedCharToEntity; |
- for (var i = 0, size = text.length; i < size; ++i) { |
- var char = text.charAt(i); |
- if (charToEntity[char]) { |
- result += text.substring(lastIndexAfterEntity, i); |
- var entityValue = "&" + charToEntity[char] + ";"; |
- entityRanges.push({offset: result.length, length: entityValue.length}); |
- result += entityValue; |
- lastIndexAfterEntity = i + 1; |
- } |
- } |
- if (result) |
- result += text.substring(lastIndexAfterEntity); |
- return {text: result || text, entityRanges: entityRanges}; |
- }, |
+ function selectNode(error) { |
+ if (callback) |
+ callback(!error); |
+ } |
/** |
- * @param {?WebInspector.ElementsTreeOutline.UpdateRecord} updateRecord |
- * @return {!DocumentFragment} result |
+ * @param {string} initialValue |
+ * @param {string} value |
*/ |
- _nodeTitleInfo: function(updateRecord) |
- { |
- var node = this._node; |
- var titleDOM = createDocumentFragment(); |
- |
- switch (node.nodeType()) { |
- case Node.ATTRIBUTE_NODE: |
- this._buildAttributeDOM(titleDOM, /** @type {string} */ (node.name), /** @type {string} */ (node.value), updateRecord, true); |
- break; |
- |
- case Node.ELEMENT_NODE: |
- var pseudoType = node.pseudoType(); |
- if (pseudoType) { |
- this._buildPseudoElementDOM(titleDOM, pseudoType); |
- break; |
- } |
- |
- var tagName = node.nodeNameInCorrectCase(); |
- if (this._elementCloseTag) { |
- this._buildTagDOM(titleDOM, tagName, true, true, updateRecord); |
- break; |
- } |
- |
- this._buildTagDOM(titleDOM, tagName, false, false, updateRecord); |
- |
- if (this.isExpandable()) { |
- if (!this.expanded) { |
- var textNodeElement = titleDOM.createChild("span", "webkit-html-text-node bogus"); |
- textNodeElement.textContent = "\u2026"; |
- titleDOM.createTextChild("\u200B"); |
- this._buildTagDOM(titleDOM, tagName, true, false, updateRecord); |
- } |
- break; |
- } |
- |
- if (WebInspector.ElementsTreeElement.canShowInlineText(node)) { |
- var textNodeElement = titleDOM.createChild("span", "webkit-html-text-node"); |
- var result = this._convertWhitespaceToEntities(node.firstChild.nodeValue()); |
- textNodeElement.textContent = result.text; |
- WebInspector.highlightRangesWithStyleClass(textNodeElement, result.entityRanges, "webkit-html-entity-value"); |
- titleDOM.createTextChild("\u200B"); |
- this._buildTagDOM(titleDOM, tagName, true, false, updateRecord); |
- if (updateRecord && updateRecord.hasChangedChildren()) |
- WebInspector.runCSSAnimationOnce(textNodeElement, "dom-update-highlight"); |
- if (updateRecord && updateRecord.isCharDataModified()) |
- WebInspector.runCSSAnimationOnce(textNodeElement, "dom-update-highlight"); |
- break; |
- } |
- |
- if (this.treeOutline.isXMLMimeType || !WebInspector.ElementsTreeElement.ForbiddenClosingTagElements.has(tagName)) |
- this._buildTagDOM(titleDOM, tagName, true, false, updateRecord); |
- break; |
- |
- case Node.TEXT_NODE: |
- if (node.parentNode && node.parentNode.nodeName().toLowerCase() === "script") { |
- var newNode = titleDOM.createChild("span", "webkit-html-text-node webkit-html-js-node"); |
- var text = node.nodeValue(); |
- newNode.textContent = text.startsWith("\n") ? text.substring(1) : text; |
- |
- var javascriptSyntaxHighlighter = new WebInspector.DOMSyntaxHighlighter("text/javascript", true); |
- javascriptSyntaxHighlighter.syntaxHighlightNode(newNode).then(updateSearchHighlight.bind(this)); |
- } else if (node.parentNode && node.parentNode.nodeName().toLowerCase() === "style") { |
- var newNode = titleDOM.createChild("span", "webkit-html-text-node webkit-html-css-node"); |
- var text = node.nodeValue(); |
- newNode.textContent = text.startsWith("\n") ? text.substring(1) : text; |
- |
- var cssSyntaxHighlighter = new WebInspector.DOMSyntaxHighlighter("text/css", true); |
- cssSyntaxHighlighter.syntaxHighlightNode(newNode).then(updateSearchHighlight.bind(this)); |
- } else { |
- titleDOM.createTextChild("\""); |
- var textNodeElement = titleDOM.createChild("span", "webkit-html-text-node"); |
- var result = this._convertWhitespaceToEntities(node.nodeValue()); |
- textNodeElement.textContent = result.text; |
- WebInspector.highlightRangesWithStyleClass(textNodeElement, result.entityRanges, "webkit-html-entity-value"); |
- titleDOM.createTextChild("\""); |
- if (updateRecord && updateRecord.isCharDataModified()) |
- WebInspector.runCSSAnimationOnce(textNodeElement, "dom-update-highlight"); |
- } |
- break; |
- |
- case Node.COMMENT_NODE: |
- var commentElement = titleDOM.createChild("span", "webkit-html-comment"); |
- commentElement.createTextChild("<!--" + node.nodeValue() + "-->"); |
- break; |
- |
- case Node.DOCUMENT_TYPE_NODE: |
- var docTypeElement = titleDOM.createChild("span", "webkit-html-doctype"); |
- docTypeElement.createTextChild("<!DOCTYPE " + node.nodeName()); |
- if (node.publicId) { |
- docTypeElement.createTextChild(" PUBLIC \"" + node.publicId + "\""); |
- if (node.systemId) |
- docTypeElement.createTextChild(" \"" + node.systemId + "\""); |
- } else if (node.systemId) |
- docTypeElement.createTextChild(" SYSTEM \"" + node.systemId + "\""); |
- |
- if (node.internalSubset) |
- docTypeElement.createTextChild(" [" + node.internalSubset + "]"); |
- |
- docTypeElement.createTextChild(">"); |
- break; |
- |
- case Node.CDATA_SECTION_NODE: |
- var cdataElement = titleDOM.createChild("span", "webkit-html-text-node"); |
- cdataElement.createTextChild("<![CDATA[" + node.nodeValue() + "]]>"); |
- break; |
- |
- case Node.DOCUMENT_FRAGMENT_NODE: |
- var fragmentElement = titleDOM.createChild("span", "webkit-html-fragment"); |
- fragmentElement.textContent = node.nodeNameInCorrectCase().collapseWhitespace(); |
- break; |
- default: |
- titleDOM.createTextChild(node.nodeNameInCorrectCase().collapseWhitespace()); |
- } |
+ function commitChange(initialValue, value) { |
+ if (initialValue !== value) |
+ node.setOuterHTML(value, selectNode); |
+ } |
- /** |
- * @this {WebInspector.ElementsTreeElement} |
- */ |
- function updateSearchHighlight() |
- { |
- delete this._highlightResult; |
- this._highlightSearchResults(); |
- } |
+ function disposeCallback() { |
+ if (callback) |
+ callback(false); |
+ } |
- return titleDOM; |
- }, |
+ var node = this._node; |
+ node.getOuterHTML(this._startEditingAsHTML.bind(this, commitChange, disposeCallback)); |
+ } |
- remove: function() |
- { |
- if (this._node.pseudoType()) |
- return; |
- var parentElement = this.parent; |
- if (!parentElement) |
- return; |
+ _copyCSSPath() { |
+ InspectorFrontendHost.copyText(WebInspector.DOMPresentationUtils.cssPath(this._node, true)); |
+ } |
- if (!this._node.parentNode || this._node.parentNode.nodeType() === Node.DOCUMENT_NODE) |
- return; |
- this._node.removeNode(); |
- }, |
+ _copyXPath() { |
+ InspectorFrontendHost.copyText(WebInspector.DOMPresentationUtils.xPath(this._node, true)); |
+ } |
- /** |
- * @param {function(boolean)=} callback |
- * @param {boolean=} startEditing |
- */ |
- toggleEditAsHTML: function(callback, startEditing) |
- { |
- if (this._editing && this._htmlEditElement && WebInspector.isBeingEdited(this._htmlEditElement)) { |
- this._editing.commit(); |
- return; |
- } |
+ _highlightSearchResults() { |
+ if (!this._searchQuery || !this._searchHighlightsVisible) |
+ return; |
+ this._hideSearchHighlight(); |
- if (startEditing === false) |
- return; |
+ var text = this.listItemElement.textContent; |
+ var regexObject = createPlainTextSearchRegex(this._searchQuery, 'gi'); |
- /** |
- * @param {?Protocol.Error} error |
- */ |
- function selectNode(error) |
- { |
- if (callback) |
- callback(!error); |
- } |
+ var match = regexObject.exec(text); |
+ var matchRanges = []; |
+ while (match) { |
+ matchRanges.push(new WebInspector.SourceRange(match.index, match[0].length)); |
+ match = regexObject.exec(text); |
+ } |
- /** |
- * @param {string} initialValue |
- * @param {string} value |
- */ |
- function commitChange(initialValue, value) |
- { |
- if (initialValue !== value) |
- node.setOuterHTML(value, selectNode); |
- } |
+ // Fall back for XPath, etc. matches. |
+ if (!matchRanges.length) |
+ matchRanges.push(new WebInspector.SourceRange(0, text.length)); |
+ |
+ this._highlightResult = []; |
+ WebInspector.highlightSearchResults(this.listItemElement, matchRanges, this._highlightResult); |
+ } |
+ |
+ _scrollIntoView() { |
+ function scrollIntoViewCallback(object) { |
+ /** |
+ * @suppressReceiverCheck |
+ * @this {!Element} |
+ */ |
+ function scrollIntoView() { |
+ this.scrollIntoViewIfNeeded(true); |
+ } |
+ |
+ if (object) |
+ object.callFunction(scrollIntoView); |
+ } |
- function disposeCallback() |
- { |
- if (callback) |
- callback(false); |
- } |
+ this._node.resolveToObject('', scrollIntoViewCallback); |
+ } |
- var node = this._node; |
- node.getOuterHTML(this._startEditingAsHTML.bind(this, commitChange, disposeCallback)); |
- }, |
- |
- _copyCSSPath: function() |
- { |
- InspectorFrontendHost.copyText(WebInspector.DOMPresentationUtils.cssPath(this._node, true)); |
- }, |
- |
- _copyXPath: function() |
- { |
- InspectorFrontendHost.copyText(WebInspector.DOMPresentationUtils.xPath(this._node, true)); |
- }, |
- |
- _highlightSearchResults: function() |
- { |
- if (!this._searchQuery || !this._searchHighlightsVisible) |
- return; |
- this._hideSearchHighlight(); |
- |
- var text = this.listItemElement.textContent; |
- var regexObject = createPlainTextSearchRegex(this._searchQuery, "gi"); |
- |
- var match = regexObject.exec(text); |
- var matchRanges = []; |
- while (match) { |
- matchRanges.push(new WebInspector.SourceRange(match.index, match[0].length)); |
- match = regexObject.exec(text); |
- } |
+ _editAsHTML() { |
+ var promise = WebInspector.Revealer.revealPromise(this.node()); |
+ promise.then(() => WebInspector.actionRegistry.action('elements.edit-as-html').execute()); |
+ } |
+}; |
- // Fall back for XPath, etc. matches. |
- if (!matchRanges.length) |
- matchRanges.push(new WebInspector.SourceRange(0, text.length)); |
- |
- this._highlightResult = []; |
- WebInspector.highlightSearchResults(this.listItemElement, matchRanges, this._highlightResult); |
- }, |
- |
- _scrollIntoView: function() |
- { |
- function scrollIntoViewCallback(object) |
- { |
- /** |
- * @suppressReceiverCheck |
- * @this {!Element} |
- */ |
- function scrollIntoView() |
- { |
- this.scrollIntoViewIfNeeded(true); |
- } |
- |
- if (object) |
- object.callFunction(scrollIntoView); |
- } |
+WebInspector.ElementsTreeElement.InitialChildrenLimit = 500; |
- this._node.resolveToObject("", scrollIntoViewCallback); |
- }, |
+// A union of HTML4 and HTML5-Draft elements that explicitly |
+// or implicitly (for HTML5) forbid the closing tag. |
+WebInspector.ElementsTreeElement.ForbiddenClosingTagElements = new Set([ |
+ 'area', 'base', 'basefont', 'br', 'canvas', 'col', 'command', 'embed', 'frame', 'hr', |
+ 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr' |
+]); |
+ |
+// These tags we do not allow editing their tag name. |
+WebInspector.ElementsTreeElement.EditTagBlacklist = new Set(['html', 'head', 'body']); |
- _editAsHTML: function() |
- { |
- var promise = WebInspector.Revealer.revealPromise(this.node()); |
- promise.then(() => WebInspector.actionRegistry.action("elements.edit-as-html").execute()); |
- }, |
- __proto__: TreeElement.prototype |
-}; |