Changeset 289467 in webkit
- Timestamp:
- Feb 9, 2022, 5:49:12 AM (5 years ago)
- Location:
- trunk
- Files:
-
- 3 added
- 8 edited
-
Source/WebDriver/CMakeLists.txt (modified) (1 diff)
-
Source/WebDriver/ChangeLog (modified) (1 diff)
-
Source/WebDriver/Session.cpp (modified) (1 diff)
-
Source/WebKit/ChangeLog (modified) (1 diff)
-
Source/WebKit/Scripts/generate-automation-atom.py (added)
-
Source/WebKit/UIProcess/Automation/atoms/ElementDisplayed.js (modified) (1 diff)
-
Source/WebKit/UIProcess/Automation/atoms/ElementText.js (added)
-
Source/WebKit/UIProcess/Automation/atoms/FindNodes.js (modified) (3 diffs)
-
Source/WebKit/UIProcess/Automation/atoms/utils.js (added)
-
WebDriverTests/ChangeLog (modified) (1 diff)
-
WebDriverTests/TestExpectations.json (modified) (9 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebDriver/CMakeLists.txt
r275065 r289467 27 27 28 28 set(WebDriver_SCRIPTS 29 ${WEBKIT_DIR}/UIProcess/Automation/atoms/ElementAttribute.js 30 ${WEBKIT_DIR}/UIProcess/Automation/atoms/ElementDisplayed.js 31 ${WEBKIT_DIR}/UIProcess/Automation/atoms/ElementEnabled.js 32 ${WEBKIT_DIR}/UIProcess/Automation/atoms/EnterFullscreen.js 33 ${WEBKIT_DIR}/UIProcess/Automation/atoms/FindNodes.js 34 ${WEBKIT_DIR}/UIProcess/Automation/atoms/FormElementClear.js 35 ${WEBKIT_DIR}/UIProcess/Automation/atoms/FormSubmit.js 29 ${WebDriver_DERIVED_SOURCES_DIR}/ElementAttribute.js 30 ${WebDriver_DERIVED_SOURCES_DIR}/ElementDisplayed.js 31 ${WebDriver_DERIVED_SOURCES_DIR}/ElementEnabled.js 32 ${WebDriver_DERIVED_SOURCES_DIR}/ElementText.js 33 ${WebDriver_DERIVED_SOURCES_DIR}/EnterFullscreen.js 34 ${WebDriver_DERIVED_SOURCES_DIR}/FindNodes.js 35 ${WebDriver_DERIVED_SOURCES_DIR}/FormElementClear.js 36 ${WebDriver_DERIVED_SOURCES_DIR}/FormSubmit.js 36 37 ) 38 39 macro(GENERATE_ATOMS _inputs) 40 foreach (_file IN ITEMS ${_inputs}) 41 get_filename_component(_name ${_file} NAME) 42 add_custom_command( 43 OUTPUT ${_file} 44 MAIN_DEPENDENCY ${WEBKIT_DIR}/Scripts/generate-automation-atom.py 45 DEPENDS 46 ${WEBKIT_DIR}/UIProcess/Automation/atoms/${_name} 47 ${WEBKIT_DIR}/UIProcess/Automation/atoms/utils.js 48 COMMAND ${PYTHON_EXECUTABLE} ${WEBKIT_DIR}/Scripts/generate-automation-atom.py ${WEBKIT_DIR}/UIProcess/Automation/atoms/${_name} ${_file} 49 VERBATIM 50 ) 51 endforeach () 52 endmacro() 53 54 GENERATE_ATOMS("${WebDriver_SCRIPTS}") 37 55 38 56 MAKE_JS_FILE_ARRAYS( -
trunk/Source/WebDriver/ChangeLog
r289202 r289467 1 2022-02-09 Carlos Garcia Campos <cgarcia@igalia.com> 2 3 WebDriver: add a javascript atom to get the visible text 4 https://bugs.webkit.org/show_bug.cgi?id=174617 5 <rdar://problem/32307461> 6 7 Reviewed by Adrian Perez de Castro. 8 9 Generate the JavaScript atoms using the new command and use the new atom to get element text. 10 11 * CMakeLists.txt: 12 * Session.cpp: 13 (WebDriver::Session::getElementText): 14 1 15 2022-02-07 Carlos Garcia Campos <cgarcia@igalia.com> 2 16 -
trunk/Source/WebDriver/Session.cpp
r289202 r289467 1359 1359 parameters->setString("frameHandle"_s, m_currentBrowsingContext.value()); 1360 1360 // FIXME: Add an atom to properly implement this instead of just using innerText. 1361 parameters->setString("function"_s, "function(element) { return element.innerText.replace(/^[^\\S\\xa0]+|[^\\S\\xa0]+$/g, '') }"_s);1361 parameters->setString("function"_s, StringImpl::createWithoutCopying(ElementTextJavaScript, sizeof(ElementTextJavaScript))); 1362 1362 parameters->setArray("arguments"_s, WTFMove(arguments)); 1363 1363 m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [protectedThis, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { -
trunk/Source/WebKit/ChangeLog
r289463 r289467 1 2022-02-09 Carlos Garcia Campos <cgarcia@igalia.com> 2 3 WebDriver: add a javascript atom to get the visible text 4 https://bugs.webkit.org/show_bug.cgi?id=174617 5 <rdar://problem/32307461> 6 7 Reviewed by Adrian Perez de Castro. 8 9 Add a new JavaScript atom to get the visible text according to the spec. The new atom uses code from other atoms 10 like ElementDisplayed and it's also used now by FindNodes one. The atoms are now autogenerated using a script to 11 include the duplicated code from a common source utils.js. 12 13 * Scripts/generate-automation-atom.py: Added. 14 (collect_utils): 15 (parse_utils): 16 (append_functions): 17 (main): 18 * UIProcess/Automation/atoms/ElementDisplayed.js: 19 (isShown.nodeIsElement): Deleted. 20 (isShown.parentElementForElement): Deleted. 21 (isShown.enclosingNodeOrSelfMatchingPredicate): Deleted. 22 (isShown.enclosingElementOrSelfMatchingPredicate): Deleted. 23 (isShown.cascadedStylePropertyForElement): Deleted. 24 (isShown.elementSubtreeHasNonZeroDimensions): Deleted. 25 (isShown): Deleted. 26 (isShown.isElementSubtreeHiddenByOverflow): Deleted. 27 * UIProcess/Automation/atoms/ElementText.js: Added. 28 * UIProcess/Automation/atoms/FindNodes.js: 29 (tryToFindNode): 30 * UIProcess/Automation/atoms/utils.js: Added. 31 (utils.nodeIsElement): 32 (utils.enclosingNodeOrSelfMatchingPredicate): 33 (utils.parentElementForElement): 34 (utils.cascadedStylePropertyForElement): 35 (elementSubtreeHasNonZeroDimensions): 36 (isElementSubtreeHiddenByOverflow): 37 (utils.isShown): 38 (appendLines.currentLine): 39 (appendLines): 40 (utils.getText): 41 1 42 2022-02-09 Tim Horton <timothy_horton@apple.com> 2 43 -
trunk/Source/WebKit/UIProcess/Automation/atoms/ElementDisplayed.js
r267978 r289467 24 24 */ 25 25 26 function isShown(element) {26 function(element) { 27 27 "use strict"; 28 28 29 function nodeIsElement(node) { 30 return !!node && node.nodeType === Node.ELEMENT_NODE; 31 32 } 33 34 function parentElementForElement(element) { 35 if (!element) 36 return null; 37 38 return enclosingNodeOrSelfMatchingPredicate(element.parentNode, nodeIsElement); 39 } 40 41 function enclosingNodeOrSelfMatchingPredicate(targetNode, predicate) { 42 for (let node = targetNode; node && node !== targetNode.getRootNode(); node = node.parentNode) 43 if (predicate(node)) 44 return node; 45 46 return null; 47 } 48 49 function enclosingElementOrSelfMatchingPredicate(targetElement, predicate) { 50 for (let element = targetElement; element && element !== targetElement.getRootNode(); element = parentElementForElement(element)) 51 if (predicate(element)) 52 return element; 53 54 return null; 55 } 56 57 function cascadedStylePropertyForElement(element, property) { 58 if (!element || !property) 59 return null; 60 61 let computedStyle = window.getComputedStyle(element); 62 let computedStyleProperty = computedStyle.getPropertyValue(property); 63 if (computedStyleProperty && computedStyleProperty !== "inherit") 64 return computedStyleProperty; 65 66 // Ideally getPropertyValue would return the 'used' or 'actual' value, but 67 // it doesn't for legacy reasons. So we need to do our own poor man's cascade. 68 // Fall back to the first non-'inherit' value found in an ancestor. 69 // In any case, getPropertyValue will not return 'initial'. 70 71 // FIXME: will this incorrectly inherit non-inheritable CSS properties? 72 // I think all important non-inheritable properties (width, height, etc.) 73 // for our purposes here are specially resolved, so this may not be an issue. 74 // Specification is here: https://drafts.csswg.org/cssom/#resolved-values 75 let parentElement = parentElementForElement(element); 76 return cascadedStylePropertyForElement(parentElement, property); 77 } 78 79 function elementSubtreeHasNonZeroDimensions(element) { 80 let boundingBox = element.getBoundingClientRect(); 81 if (boundingBox.width > 0 && boundingBox.height > 0) 82 return true; 83 84 // Paths can have a zero width or height. Treat them as shown if the stroke width is positive. 85 if (element.tagName.toUpperCase() === "PATH" && boundingBox.width + boundingBox.height > 0) { 86 let strokeWidth = cascadedStylePropertyForElement(element, "stroke-width"); 87 return !!strokeWidth && (parseInt(strokeWidth, 10) > 0); 88 } 89 90 let cascadedOverflow = cascadedStylePropertyForElement(element, "overflow"); 91 if (cascadedOverflow === "hidden") 92 return false; 93 94 // If the container's overflow is not hidden and it has zero size, consider the 95 // container to have non-zero dimensions if a child node has non-zero dimensions. 96 return Array.from(element.childNodes).some((childNode) => { 97 if (childNode.nodeType === Node.TEXT_NODE) 98 return true; 99 100 if (nodeIsElement(childNode)) 101 return elementSubtreeHasNonZeroDimensions(childNode); 102 103 return false; 104 }); 105 } 106 107 function elementOverflowsContainer(element) { 108 let cascadedOverflow = cascadedStylePropertyForElement(element, "overflow"); 109 if (cascadedOverflow !== "hidden") 110 return false; 111 112 // FIXME: this needs to take into account the scroll position of the element, 113 // the display modes of it and its ancestors, and the container it overflows. 114 // See Selenium's bot.dom.getOverflowState atom for an exhaustive list of edge cases. 115 return true; 116 } 117 118 function isElementSubtreeHiddenByOverflow(element) { 119 if (!element) 120 return false; 121 122 if (!elementOverflowsContainer(element)) 123 return false; 124 125 if (!element.childNodes.length) 126 return false; 127 128 // This element's subtree is hidden by overflow if all child subtrees are as well. 129 return Array.from(element.childNodes).every((childNode) => { 130 // Returns true if the child node is overflowed or otherwise hidden. 131 // Base case: not an element, has zero size, scrolled out, or doesn't overflow container. 132 if (!nodeIsElement(childNode)) 133 return true; 134 135 if (!elementSubtreeHasNonZeroDimensions(childNode)) 136 return true; 137 138 // Recurse. 139 return isElementSubtreeHiddenByOverflow(childNode); 140 }); 141 } 142 143 // This is a partial reimplementation of Selenium's "element is displayed" algorithm. 144 // When the W3C specification's algorithm stabilizes, we should implement that. 145 146 if (!(element instanceof Element)) 147 throw new Error("Cannot check the displayedness of a non-Element argument."); 148 149 // If this command is misdirected to the wrong document, treat it as not shown. 150 if (!document.contains(element)) 151 return false; 152 153 // Special cases for specific tag names. 154 switch (element.tagName.toUpperCase()) { 155 case "BODY": 156 return true; 157 158 case "SCRIPT": 159 case "NOSCRIPT": 160 return false; 161 162 case "OPTGROUP": 163 case "OPTION": 164 // Option/optgroup are considered shown if the containing <select> is shown. 165 let enclosingSelectElement = enclosingNodeOrSelfMatchingPredicate(element, (e) => e.tagName.toUpperCase() === "SELECT"); 166 return isShown(enclosingSelectElement); 167 168 case "INPUT": 169 // <input type="hidden"> is considered not shown. 170 if (element.type === "hidden") 171 return false; 172 break; 173 174 case "MAP": 175 // FIXME: Selenium has special handling for <map> elements. We don't do anything now. 176 177 default: 178 break; 179 } 180 181 if (cascadedStylePropertyForElement(element, "visibility") !== "visible") 182 return false; 183 184 let hasAncestorWithZeroOpacity = !!enclosingElementOrSelfMatchingPredicate(element, (e) => { 185 return Number(cascadedStylePropertyForElement(e, "opacity")) === 0; 186 }); 187 let hasAncestorWithDisplayNone = !!enclosingElementOrSelfMatchingPredicate(element, (e) => { 188 return cascadedStylePropertyForElement(e, "display") === "none"; 189 }); 190 if (hasAncestorWithZeroOpacity || hasAncestorWithDisplayNone) 191 return false; 192 193 if (!elementSubtreeHasNonZeroDimensions(element)) 194 return false; 195 196 if (isElementSubtreeHiddenByOverflow(element)) 197 return false; 198 199 return true; 29 return utils.isShown(element); 200 30 } -
trunk/Source/WebKit/UIProcess/Automation/atoms/FindNodes.js
r289202 r289467 25 25 26 26 function(strategy, ancestorElement, query, firstResultOnly, timeoutDuration, callback) { 27 "use strict"; 28 27 29 ancestorElement = ancestorElement || document; 28 30 … … 68 70 let linkTextResult = []; 69 71 for (let link of ancestorElement.querySelectorAll("a")) { 70 if ( link.text.trim() == query) {72 if (utils.getText(link).trim() == query) { 71 73 linkTextResult.push(link); 72 74 if (firstResultOnly) … … 81 83 let partialLinkResult = []; 82 84 for (let link of ancestorElement.querySelectorAll("a")) { 83 if ( link.text.includes(query)) {85 if (utils.getText(link).includes(query)) { 84 86 partialLinkResult.push(link); 85 87 if (firstResultOnly) -
trunk/WebDriverTests/ChangeLog
r289202 r289467 1 2022-02-09 Carlos Garcia Campos <cgarcia@igalia.com> 2 3 WebDriver: add a javascript atom to get the visible text 4 https://bugs.webkit.org/show_bug.cgi?id=174617 5 <rdar://problem/32307461> 6 7 Reviewed by Adrian Perez de Castro. 8 9 Remove expectations of tests that are now passing. 10 11 * TestExpectations.json: 12 1 13 2022-02-07 Carlos Garcia Campos <cgarcia@igalia.com> 2 14 -
trunk/WebDriverTests/TestExpectations.json
r289202 r289467 134 134 "imported/selenium/py/test/selenium/webdriver/common/select_class_tests.py": { 135 135 "subtests": { 136 "testSelectByVisibleTextShouldNormalizeSpaces": {137 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/174617"}}138 },139 136 "testSelectByIndexMultiple": { 140 137 "expected": {"all": {"slow": true}} … … 150 147 "imported/selenium/py/test/selenium/webdriver/common/text_handling_tests.py": { 151 148 "subtests": { 152 "testShouldConvertANonBreakingSpaceIntoANormalSpaceCharacter": {153 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/174617"}}154 },155 "testShouldTreatANonBreakingSpaceAsAnyOtherWhitespaceCharacterWhenCollapsingWhitespace": {156 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/174617"}}157 },158 149 "testShouldBeAbleToSetMoreThanOneLineOfTextInATextArea": { 159 150 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/174617"}} … … 161 152 "testShouldHandleSiblingBlockLevelElements": { 162 153 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/174617"}} 163 },164 "testShouldOnlyIncludeVisibleText": {165 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/174617"}}166 154 } 167 155 } … … 840 828 "imported/w3c/webdriver/tests/find_element/find.py": { 841 829 "subtests": { 842 "test_find_element_link_text[<a href=#>link<br>text</a>-link\\ntext]": {843 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}844 },845 "test_find_element_link_text[<a href=# style='text-transform: uppercase'>link text</a>-LINK TEXT]": {846 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}847 },848 "test_find_element_partial_link_text[<a href=#>partial link<br>text</a>-k\\nt]": {849 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}850 },851 "test_find_element_partial_link_text[<a href=# style='text-transform: uppercase'>partial link text</a>-LINK]": {852 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}853 },854 830 "test_no_top_browsing_context": { 855 831 "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} … … 862 838 "imported/w3c/webdriver/tests/find_elements/find.py": { 863 839 "subtests": { 864 "test_find_elements_link_text[<a href=#>link<br>text</a>-link\\ntext]": {865 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}866 },867 "test_find_elements_link_text[<a href=# style='text-transform: uppercase'>link text</a>-LINK TEXT]": {868 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}869 },870 "test_find_elements_partial_link_text[<a href=#>partial link<br>text</a>-k\\nt]": {871 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}872 },873 "test_find_elements_partial_link_text[<a href=# style='text-transform: uppercase'>partial link text</a>-LINK]": {874 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}875 },876 840 "test_no_top_browsing_context": { 877 841 "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} … … 884 848 "imported/w3c/webdriver/tests/find_elements_from_element/find.py": { 885 849 "subtests": { 886 "test_find_elements_link_text[<a href=#>link<br>text</a>-link\\ntext]": {887 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}888 },889 "test_find_elements_link_text[<a href=# style='text-transform: uppercase'>link text</a>-LINK TEXT]": {890 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}891 },892 "test_find_elements_partial_link_text[<a href=#>partial link<br>text</a>-k\\nt]": {893 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}894 },895 "test_find_elements_partial_link_text[<a href=# style='text-transform: uppercase'>partial link text</a>-LINK]": {896 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}897 },898 850 "test_parent_of_document_node_errors": { 899 851 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}} … … 909 861 "imported/w3c/webdriver/tests/find_element_from_element/find.py": { 910 862 "subtests": { 911 "test_find_element_link_text[<a href=#>link<br>text</a>-link\\ntext]": {912 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}913 },914 "test_find_element_link_text[<a href=# style='text-transform: uppercase'>link text</a>-LINK TEXT]": {915 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}916 },917 "test_find_element_partial_link_text[<a href=#>partial link<br>text</a>-k\\nt]": {918 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}919 },920 "test_find_element_partial_link_text[<a href=# style='text-transform: uppercase'>partial link text</a>-LINK]": {921 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}922 },923 863 "test_parent_of_document_node_errors": { 924 864 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}} … … 1576 1516 "test_find_element[xpath-//a]": { 1577 1517 "expected": { "all": { "status": ["SKIP"]}} 1578 },1579 "test_find_element_link_text[<a href=#>link<br>text</a>-link\\ntext]": {1580 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}1581 },1582 "test_find_element_link_text[<a href=# style='text-transform: uppercase'>link text</a>-LINK TEXT]": {1583 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}1584 },1585 "test_find_element_partial_link_text[<a href=#>partial link<br>text</a>-k\\nt]": {1586 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}1587 },1588 "test_find_element_partial_link_text[<a href=# style='text-transform: uppercase'>partial link text</a>-LINK]": {1589 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}1590 1518 } 1591 1519 } … … 1598 1526 "test_find_elements[xpath-//a]": { 1599 1527 "expected": { "all": { "status": ["SKIP"]}} 1600 },1601 "test_find_elements_link_text[<a href=#>link<br>text</a>-link\\ntext]": {1602 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}1603 },1604 "test_find_elements_link_text[<a href=# style='text-transform: uppercase'>link text</a>-LINK TEXT]": {1605 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}1606 },1607 "test_find_elements_partial_link_text[<a href=#>partial link<br>text</a>-k\\nt]": {1608 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}1609 },1610 "test_find_elements_partial_link_text[<a href=# style='text-transform: uppercase'>partial link text</a>-LINK]": {1611 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/184964"}}1612 1528 } 1613 1529 }
Note:
See TracChangeset
for help on using the changeset viewer.