diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebInspectorUI/UserInterface/Base/DOMUtilities.js | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebInspectorUI/UserInterface/Base/DOMUtilities.js')
-rw-r--r-- | Source/WebInspectorUI/UserInterface/Base/DOMUtilities.js | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/Source/WebInspectorUI/UserInterface/Base/DOMUtilities.js b/Source/WebInspectorUI/UserInterface/Base/DOMUtilities.js new file mode 100644 index 000000000..ba423aa31 --- /dev/null +++ b/Source/WebInspectorUI/UserInterface/Base/DOMUtilities.js @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com> + * Copyright (C) 2009 Joseph Pecoraro + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +WebInspector.roleSelectorForNode = function(node) +{ + // This is proposed syntax for CSS 4 computed role selector :role(foo) and subject to change. + // See http://lists.w3.org/Archives/Public/www-style/2013Jul/0104.html + var title = ""; + var role = node.computedRole(); + if (role) + title = ":role(" + role + ")"; + return title; +}; + +WebInspector.linkifyAccessibilityNodeReference = function(node) +{ + if (!node) + return null; + // Same as linkifyNodeReference except the link text has the classnames removed... + // ...for list brevity, and both text and title have roleSelectorForNode appended. + var link = WebInspector.linkifyNodeReference(node); + var tagIdSelector = link.title; + var classSelectorIndex = tagIdSelector.indexOf("."); + if (classSelectorIndex > -1) + tagIdSelector = tagIdSelector.substring(0, classSelectorIndex); + var roleSelector = WebInspector.roleSelectorForNode(node); + link.textContent = tagIdSelector + roleSelector; + link.title += roleSelector; + return link; +}; + +WebInspector.linkifyNodeReference = function(node, maxLength) +{ + let displayName = node.displayName; + if (!isNaN(maxLength)) + displayName = displayName.truncate(maxLength); + + let link = document.createElement("span"); + link.append(displayName); + link.setAttribute("role", "link"); + + link.title = displayName; + + let nodeType = node.nodeType(); + if ((nodeType !== Node.DOCUMENT_NODE || node.parentNode) && nodeType !== Node.TEXT_NODE) + link.className = "node-link"; + + link.addEventListener("click", WebInspector.domTreeManager.inspectElement.bind(WebInspector.domTreeManager, node.id)); + link.addEventListener("mouseover", WebInspector.domTreeManager.highlightDOMNode.bind(WebInspector.domTreeManager, node.id, "all")); + link.addEventListener("mouseout", WebInspector.domTreeManager.hideDOMNodeHighlight.bind(WebInspector.domTreeManager)); + + return link; +}; + +function createSVGElement(tagName) +{ + return document.createElementNS("http://www.w3.org/2000/svg", tagName); +} + +WebInspector.cssPath = function(node) +{ + console.assert(node instanceof WebInspector.DOMNode, "Expected a DOMNode."); + if (node.nodeType() !== Node.ELEMENT_NODE) + return ""; + + let suffix = ""; + if (node.isPseudoElement()) { + suffix = "::" + node.pseudoType(); + node = node.parentNode; + } + + let components = []; + while (node) { + let component = WebInspector.cssPathComponent(node); + if (!component) + break; + components.push(component); + if (component.done) + break; + node = node.parentNode; + } + + components.reverse(); + return components.map((x) => x.value).join(" > ") + suffix; +}; + +WebInspector.cssPathComponent = function(node) +{ + console.assert(node instanceof WebInspector.DOMNode, "Expected a DOMNode."); + console.assert(!node.isPseudoElement()); + if (node.nodeType() !== Node.ELEMENT_NODE) + return null; + + let nodeName = node.nodeNameInCorrectCase(); + let lowerNodeName = node.nodeName().toLowerCase(); + + // html, head, and body are unique nodes. + if (lowerNodeName === "body" || lowerNodeName === "head" || lowerNodeName === "html") + return {value: nodeName, done: true}; + + // #id is unique. + let id = node.getAttribute("id"); + if (id) + return {value: node.escapedIdSelector, done: true}; + + // Root node does not have siblings. + if (!node.parentNode || node.parentNode.nodeType() === Node.DOCUMENT_NODE) + return {value: nodeName, done: true}; + + // Find uniqueness among siblings. + // - look for a unique className + // - look for a unique tagName + // - fallback to nth-child() + + function classNames(node) { + let classAttribute = node.getAttribute("class"); + return classAttribute ? classAttribute.trim().split(/\s+/) : []; + } + + let nthChildIndex = -1; + let hasUniqueTagName = true; + let uniqueClasses = new Set(classNames(node)); + + let siblings = node.parentNode.children; + let elementIndex = 0; + for (let sibling of siblings) { + if (sibling.nodeType() !== Node.ELEMENT_NODE) + continue; + + elementIndex++; + if (sibling === node) { + nthChildIndex = elementIndex; + continue; + } + + if (sibling.nodeNameInCorrectCase() === nodeName) + hasUniqueTagName = false; + + if (uniqueClasses.size) { + let siblingClassNames = classNames(sibling); + for (let className of siblingClassNames) + uniqueClasses.delete(className); + } + } + + let selector = nodeName; + if (lowerNodeName === "input" && node.getAttribute("type") && !uniqueClasses.size) + selector += `[type="${node.getAttribute("type")}"]`; + if (!hasUniqueTagName) { + if (uniqueClasses.size) + selector += node.escapedClassSelector; + else + selector += `:nth-child(${nthChildIndex})`; + } + + return {value: selector, done: false}; +}; + +WebInspector.xpath = function(node) +{ + console.assert(node instanceof WebInspector.DOMNode, "Expected a DOMNode."); + + if (node.nodeType() === Node.DOCUMENT_NODE) + return "/"; + + let components = []; + while (node) { + let component = WebInspector.xpathComponent(node); + if (!component) + break; + components.push(component); + if (component.done) + break; + node = node.parentNode; + } + + components.reverse(); + + let prefix = components.length && components[0].done ? "" : "/"; + return prefix + components.map((x) => x.value).join("/"); +}; + +WebInspector.xpathComponent = function(node) +{ + console.assert(node instanceof WebInspector.DOMNode, "Expected a DOMNode."); + + let index = WebInspector.xpathIndex(node); + if (index === -1) + return null; + + let value; + + switch (node.nodeType()) { + case Node.DOCUMENT_NODE: + return {value: "", done: true}; + case Node.ELEMENT_NODE: + var id = node.getAttribute("id"); + if (id) + return {value: `//*[@id="${id}"]`, done: true}; + value = node.localName(); + break; + case Node.ATTRIBUTE_NODE: + value = `@${node.nodeName()}`; + break; + case Node.TEXT_NODE: + case Node.CDATA_SECTION_NODE: + value = "text()"; + break; + case Node.COMMENT_NODE: + value = "comment()"; + break; + case Node.PROCESSING_INSTRUCTION_NODE: + value = "processing-instruction()"; + break; + default: + value = ""; + break; + } + + if (index > 0) + value += `[${index}]`; + + return {value, done: false}; +}; + +WebInspector.xpathIndex = function(node) +{ + // Root node. + if (!node.parentNode) + return 0; + + // No siblings. + let siblings = node.parentNode.children; + if (siblings.length <= 1) + return 0; + + // Find uniqueness among siblings. + // - look for a unique localName + // - fallback to index + + function isSimiliarNode(a, b) { + if (a === b) + return true; + + let aType = a.nodeType(); + let bType = b.nodeType(); + + if (aType === Node.ELEMENT_NODE && bType === Node.ELEMENT_NODE) + return a.localName() === b.localName(); + + // XPath CDATA and text() are the same. + if (aType === Node.CDATA_SECTION_NODE) + return aType === Node.TEXT_NODE; + if (bType === Node.CDATA_SECTION_NODE) + return bType === Node.TEXT_NODE; + + return aType === bType; + } + + let unique = true; + let xPathIndex = -1; + + let xPathIndexCounter = 1; // XPath indices start at 1. + for (let sibling of siblings) { + if (!isSimiliarNode(node, sibling)) + continue; + + if (node === sibling) { + xPathIndex = xPathIndexCounter; + if (!unique) + return xPathIndex; + } else { + unique = false; + if (xPathIndex !== -1) + return xPathIndex; + } + + xPathIndexCounter++; + } + + if (unique) + return 0; + + console.assert(xPathIndex > 0, "Should have found the node."); + return xPathIndex; +}; |