| 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
|
| -};
|
|
|