Changeset 253030 in webkit
- Timestamp:
- Dec 3, 2019 2:03:32 AM (4 years ago)
- Location:
- trunk
- Files:
-
- 16 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r253028 r253030 1 2019-12-03 Carlos Garcia Campos <cgarcia@igalia.com> 2 3 WebDriver: handle elements of type file in send keys command 4 https://bugs.webkit.org/show_bug.cgi?id=188514 5 6 Reviewed by Brian Burg. 7 8 Export symbols now used by WebKit::WebAutomationSessionProxy. 9 10 * html/HTMLInputElement.h: 11 1 12 2019-12-03 Carlos Garcia Campos <cgarcia@igalia.com> 2 13 -
trunk/Source/WebCore/html/HTMLInputElement.h
r251110 r253030 119 119 120 120 WEBCORE_EXPORT bool isEmailField() const; 121 bool isFileUpload() const;121 WEBCORE_EXPORT bool isFileUpload() const; 122 122 bool isImageButton() const; 123 123 WEBCORE_EXPORT bool isNumberField() const; … … 236 236 unsigned effectiveMaxLength() const; 237 237 238 bool multiple() const;238 WEBCORE_EXPORT bool multiple() const; 239 239 240 240 bool isAutoFilled() const { return m_isAutoFilled; } -
trunk/Source/WebDriver/Capabilities.h
r252323 r253030 71 71 Optional<String> platformName; 72 72 Optional<bool> acceptInsecureCerts; 73 Optional<bool> strictFileInteractability; 73 74 Optional<bool> setWindowRect; 74 75 Optional<Timeouts> timeouts; -
trunk/Source/WebDriver/ChangeLog
r253029 r253030 1 2019-12-03 Carlos Garcia Campos <cgarcia@igalia.com> 2 3 WebDriver: handle elements of type file in send keys command 4 https://bugs.webkit.org/show_bug.cgi?id=188514 5 6 Reviewed by Brian Burg. 7 8 Handle the case of inpout element being a file upload in send keys command. 9 10 * Capabilities.h: Add strictFileInteractability. 11 * Session.cpp: 12 (WebDriver::Session::elementIsFileUpload): Helper to check if the given element is a file upload and whether 13 it's multiple or not. 14 (WebDriver::Session::parseElementIsFileUploadResult): Parse the result of elementIsFileUpload(). 15 (WebDriver::Session::elementClick): If the element is a file upload, fail with invalid argument error. 16 (WebDriver::Session::setInputFileUploadFiles): Send setFilesForInputFileUpload message to the browser with the 17 selected files. 18 (WebDriver::Session::elementSendKeys): If the element is a file upload, call setInputFileUploadFiles() 19 instead. Also handle the strictFileInteractability capability to decide whether to focus and check 20 interactability on the input element or not. 21 * Session.h: 22 * WebDriverService.cpp: 23 (WebDriver::WebDriverService::parseCapabilities const): Handle strictFileInteractability. 24 (WebDriver::WebDriverService::validatedCapabilities const): Ditto. 25 (WebDriver::WebDriverService::matchCapabilities const): Ditto. 26 (WebDriver::WebDriverService::createSession): Ditto. 27 1 28 2019-12-03 Carlos Garcia Campos <cgarcia@igalia.com> 2 29 -
trunk/Source/WebDriver/Session.cpp
r253029 r253030 31 31 #include "WebDriverAtoms.h" 32 32 #include <wtf/CryptographicallyRandomNumber.h> 33 #include <wtf/FileSystem.h> 33 34 #include <wtf/HashSet.h> 34 35 #include <wtf/HexNumber.h> … … 1529 1530 } 1530 1531 1532 void Session::elementIsFileUpload(const String& elementID, Function<void (CommandResult&&)>&& completionHandler) 1533 { 1534 RefPtr<JSON::Array> arguments = JSON::Array::create(); 1535 arguments->pushString(createElement(elementID)->toJSONString()); 1536 1537 static const char isFileUploadScript[] = 1538 "function(element) {" 1539 " if (element.tagName.toLowerCase() === 'input' && element.type === 'file')" 1540 " return { 'fileUpload': true, 'multiple': element.hasAttribute('multiple') };" 1541 " return { 'fileUpload': false };" 1542 "}"; 1543 1544 RefPtr<JSON::Object> parameters = JSON::Object::create(); 1545 parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value()); 1546 if (m_currentBrowsingContext) 1547 parameters->setString("frameHandle"_s, m_currentBrowsingContext.value()); 1548 parameters->setString("function"_s, isFileUploadScript); 1549 parameters->setArray("arguments"_s, WTFMove(arguments)); 1550 m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { 1551 if (response.isError || !response.responseObject) { 1552 completionHandler(CommandResult::fail(WTFMove(response.responseObject))); 1553 return; 1554 } 1555 String valueString; 1556 if (!response.responseObject->getString("result"_s, valueString)) { 1557 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); 1558 return; 1559 } 1560 RefPtr<JSON::Value> resultValue; 1561 if (!JSON::Value::parseJSON(valueString, resultValue)) { 1562 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); 1563 return; 1564 } 1565 completionHandler(CommandResult::success(WTFMove(resultValue))); 1566 }); 1567 } 1568 1569 Optional<Session::FileUploadType> Session::parseElementIsFileUploadResult(const RefPtr<JSON::Value>& resultValue) 1570 { 1571 RefPtr<JSON::Object> result; 1572 if (!resultValue->asObject(result)) 1573 return WTF::nullopt; 1574 1575 bool isFileUpload; 1576 if (!result->getBoolean("fileUpload"_s, isFileUpload) || !isFileUpload) 1577 return WTF::nullopt; 1578 1579 bool multiple; 1580 if (!result->getBoolean("multiple"_s, multiple) || !multiple) 1581 return FileUploadType::Single; 1582 1583 return FileUploadType::Multiple; 1584 } 1585 1531 1586 void Session::selectOptionElement(const String& elementID, Function<void (CommandResult&&)>&& completionHandler) 1532 1587 { … … 1556 1611 return; 1557 1612 } 1558 OptionSet<ElementLayoutOption> options = { ElementLayoutOption::ScrollIntoViewIfNeeded, ElementLayoutOption::UseViewportCoordinates }; 1559 computeElementLayout(elementID, options, [this, protectedThis = protectedThis.copyRef(), elementID, completionHandler = WTFMove(completionHandler)](Optional<Rect>&& rect, Optional<Point>&& inViewCenter, bool isObscured, RefPtr<JSON::Object>&& error) mutable { 1560 if (!rect || error) { 1561 completionHandler(CommandResult::fail(WTFMove(error))); 1562 return; 1563 } 1564 if (isObscured) { 1565 completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementClickIntercepted)); 1566 return; 1567 } 1568 if (!inViewCenter) { 1569 completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementNotInteractable)); 1570 return; 1571 } 1572 1573 getElementTagName(elementID, [this, elementID, inViewCenter = WTFMove(inViewCenter), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { 1574 bool isOptionElement = false; 1575 if (!result.isError()) { 1576 String tagName; 1577 if (result.result()->asString(tagName)) 1578 isOptionElement = tagName == "option"; 1613 elementIsFileUpload(elementID, [this, protectedThis = protectedThis.copyRef(), elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { 1614 if (result.isError()) { 1615 completionHandler(WTFMove(result)); 1616 return; 1617 } 1618 1619 if (parseElementIsFileUploadResult(result.result())) { 1620 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); 1621 return; 1622 } 1623 OptionSet<ElementLayoutOption> options = { ElementLayoutOption::ScrollIntoViewIfNeeded, ElementLayoutOption::UseViewportCoordinates }; 1624 computeElementLayout(elementID, options, [this, protectedThis = protectedThis.copyRef(), elementID, completionHandler = WTFMove(completionHandler)](Optional<Rect>&& rect, Optional<Point>&& inViewCenter, bool isObscured, RefPtr<JSON::Object>&& error) mutable { 1625 if (!rect || error) { 1626 completionHandler(CommandResult::fail(WTFMove(error))); 1627 return; 1579 1628 } 1580 1581 Function<void (CommandResult&&)> continueAfterClickFunction = [this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { 1582 if (result.isError()) { 1583 completionHandler(WTFMove(result)); 1584 return; 1629 if (isObscured) { 1630 completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementClickIntercepted)); 1631 return; 1632 } 1633 if (!inViewCenter) { 1634 completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementNotInteractable)); 1635 return; 1636 } 1637 1638 getElementTagName(elementID, [this, elementID, inViewCenter = WTFMove(inViewCenter), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { 1639 bool isOptionElement = false; 1640 if (!result.isError()) { 1641 String tagName; 1642 if (result.result()->asString(tagName)) 1643 isOptionElement = tagName == "option"; 1585 1644 } 1586 1645 1587 waitForNavigationToComplete(WTFMove(completionHandler)); 1588 }; 1589 if (isOptionElement) 1590 selectOptionElement(elementID, WTFMove(continueAfterClickFunction)); 1591 else 1592 performMouseInteraction(inViewCenter.value().x, inViewCenter.value().y, MouseButton::Left, MouseInteraction::SingleClick, WTFMove(continueAfterClickFunction)); 1646 Function<void (CommandResult&&)> continueAfterClickFunction = [this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { 1647 if (result.isError()) { 1648 completionHandler(WTFMove(result)); 1649 return; 1650 } 1651 1652 waitForNavigationToComplete(WTFMove(completionHandler)); 1653 }; 1654 if (isOptionElement) 1655 selectOptionElement(elementID, WTFMove(continueAfterClickFunction)); 1656 else 1657 performMouseInteraction(inViewCenter.value().x, inViewCenter.value().y, MouseButton::Left, MouseInteraction::SingleClick, WTFMove(continueAfterClickFunction)); 1658 }); 1593 1659 }); 1594 1660 }); … … 1696 1762 }); 1697 1763 }); 1764 }); 1765 } 1766 1767 void Session::setInputFileUploadFiles(const String& elementID, const String& text, bool multiple, Function<void (CommandResult&&)>&& completionHandler) 1768 { 1769 Vector<String> files = text.split('\n'); 1770 if (files.isEmpty()) { 1771 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); 1772 return; 1773 } 1774 1775 if (!multiple && files.size() != 1) { 1776 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); 1777 return; 1778 } 1779 1780 RefPtr<JSON::Array> filenames = JSON::Array::create(); 1781 for (const auto& file : files) { 1782 if (!FileSystem::fileExists(file)) { 1783 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); 1784 return; 1785 } 1786 filenames->pushString(file); 1787 } 1788 1789 RefPtr<JSON::Object> parameters = JSON::Object::create(); 1790 parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value()); 1791 parameters->setString("frameHandle"_s, m_currentBrowsingContext.valueOr(emptyString())); 1792 parameters->setString("nodeHandle"_s, elementID); 1793 parameters->setArray("filenames"_s, WTFMove(filenames)); 1794 m_host->sendCommandToBackend("setFilesForInputFileUpload"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), elementID, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable { 1795 if (response.isError) { 1796 completionHandler(CommandResult::fail(WTFMove(response.responseObject))); 1797 return; 1798 } 1799 1800 completionHandler(CommandResult::success()); 1698 1801 }); 1699 1802 } … … 1849 1952 return; 1850 1953 } 1851 // FIXME: move this to an atom. 1852 static const char focusScript[] = 1853 "function focus(element) {" 1854 " let doc = element.ownerDocument || element;" 1855 " let prevActiveElement = doc.activeElement;" 1856 " if (element != prevActiveElement && prevActiveElement)" 1857 " prevActiveElement.blur();" 1858 " element.focus();" 1859 " let tagName = element.tagName.toUpperCase();" 1860 " if (tagName === 'BODY' || element === document.documentElement)" 1861 " return;" 1862 " let isTextElement = tagName === 'TEXTAREA' || (tagName === 'INPUT' && element.type === 'text');" 1863 " if (isTextElement && element.selectionEnd == 0)" 1864 " element.setSelectionRange(element.value.length, element.value.length);" 1865 " if (element != doc.activeElement)" 1866 " throw {name: 'ElementNotInteractable', message: 'Element is not focusable.'};" 1867 "}"; 1868 1869 RefPtr<JSON::Array> arguments = JSON::Array::create(); 1870 arguments->pushString(createElement(elementID)->toJSONString()); 1871 RefPtr<JSON::Object> parameters = JSON::Object::create(); 1872 parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value()); 1873 if (m_currentBrowsingContext) 1874 parameters->setString("frameHandle"_s, m_currentBrowsingContext.value()); 1875 parameters->setString("function"_s, focusScript); 1876 parameters->setArray("arguments"_s, WTFMove(arguments)); 1877 m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), text, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable { 1878 if (response.isError || !response.responseObject) { 1879 completionHandler(CommandResult::fail(WTFMove(response.responseObject))); 1880 return; 1881 } 1882 1883 unsigned stickyModifiers = 0; 1884 auto textLength = text.length(); 1885 Vector<KeyboardInteraction> interactions; 1886 interactions.reserveInitialCapacity(textLength); 1887 for (unsigned i = 0; i < textLength; ++i) { 1888 auto key = text[i]; 1889 KeyboardInteraction interaction; 1890 KeyModifier modifier; 1891 auto virtualKey = virtualKeyForKey(key, modifier); 1892 if (!virtualKey.isNull()) { 1893 interaction.key = virtualKey; 1894 if (modifier != KeyModifier::None) { 1895 stickyModifiers ^= modifier; 1896 if (stickyModifiers & modifier) 1897 interaction.type = KeyboardInteractionType::KeyPress; 1898 else 1899 interaction.type = KeyboardInteractionType::KeyRelease; 1954 elementIsFileUpload(elementID, [this, protectedThis = protectedThis.copyRef(), elementID, text, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { 1955 if (result.isError()) { 1956 completionHandler(WTFMove(result)); 1957 return; 1958 } 1959 1960 auto fileUploadType = parseElementIsFileUploadResult(result.result()); 1961 if (!fileUploadType || capabilities().strictFileInteractability.valueOr(false)) { 1962 // FIXME: move this to an atom. 1963 static const char focusScript[] = 1964 "function focus(element) {" 1965 " let doc = element.ownerDocument || element;" 1966 " let prevActiveElement = doc.activeElement;" 1967 " if (element != prevActiveElement && prevActiveElement)" 1968 " prevActiveElement.blur();" 1969 " element.focus();" 1970 " let tagName = element.tagName.toUpperCase();" 1971 " if (tagName === 'BODY' || element === document.documentElement)" 1972 " return;" 1973 " let isTextElement = tagName === 'TEXTAREA' || (tagName === 'INPUT' && element.type === 'text');" 1974 " if (isTextElement && element.selectionEnd == 0)" 1975 " element.setSelectionRange(element.value.length, element.value.length);" 1976 " if (element != doc.activeElement)" 1977 " throw {name: 'ElementNotInteractable', message: 'Element is not focusable.'};" 1978 "}"; 1979 1980 RefPtr<JSON::Array> arguments = JSON::Array::create(); 1981 arguments->pushString(createElement(elementID)->toJSONString()); 1982 RefPtr<JSON::Object> parameters = JSON::Object::create(); 1983 parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value()); 1984 if (m_currentBrowsingContext) 1985 parameters->setString("frameHandle"_s, m_currentBrowsingContext.value()); 1986 parameters->setString("function"_s, focusScript); 1987 parameters->setArray("arguments"_s, WTFMove(arguments)); 1988 m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), fileUploadType, elementID, text, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable { 1989 if (response.isError || !response.responseObject) { 1990 completionHandler(CommandResult::fail(WTFMove(response.responseObject))); 1991 return; 1900 1992 } 1901 } else 1902 interaction.text = String(&key, 1); 1903 interactions.uncheckedAppend(WTFMove(interaction)); 1904 } 1905 1906 // Reset sticky modifiers if needed. 1907 if (stickyModifiers) { 1908 if (stickyModifiers & KeyModifier::Shift) 1909 interactions.append({ KeyboardInteractionType::KeyRelease, WTF::nullopt, Optional<String>("Shift"_s) }); 1910 if (stickyModifiers & KeyModifier::Control) 1911 interactions.append({ KeyboardInteractionType::KeyRelease, WTF::nullopt, Optional<String>("Control"_s) }); 1912 if (stickyModifiers & KeyModifier::Alternate) 1913 interactions.append({ KeyboardInteractionType::KeyRelease, WTF::nullopt, Optional<String>("Alternate"_s) }); 1914 if (stickyModifiers & KeyModifier::Meta) 1915 interactions.append({ KeyboardInteractionType::KeyRelease, WTF::nullopt, Optional<String>("Meta"_s) }); 1916 } 1917 1918 performKeyboardInteractions(WTFMove(interactions), WTFMove(completionHandler)); 1993 1994 if (fileUploadType) { 1995 setInputFileUploadFiles(elementID, text, fileUploadType.value() == FileUploadType::Multiple, WTFMove(completionHandler)); 1996 return; 1997 } 1998 1999 unsigned stickyModifiers = 0; 2000 auto textLength = text.length(); 2001 Vector<KeyboardInteraction> interactions; 2002 interactions.reserveInitialCapacity(textLength); 2003 for (unsigned i = 0; i < textLength; ++i) { 2004 auto key = text[i]; 2005 KeyboardInteraction interaction; 2006 KeyModifier modifier; 2007 auto virtualKey = virtualKeyForKey(key, modifier); 2008 if (!virtualKey.isNull()) { 2009 interaction.key = virtualKey; 2010 if (modifier != KeyModifier::None) { 2011 stickyModifiers ^= modifier; 2012 if (stickyModifiers & modifier) 2013 interaction.type = KeyboardInteractionType::KeyPress; 2014 else 2015 interaction.type = KeyboardInteractionType::KeyRelease; 2016 } 2017 } else 2018 interaction.text = String(&key, 1); 2019 interactions.uncheckedAppend(WTFMove(interaction)); 2020 } 2021 2022 // Reset sticky modifiers if needed. 2023 if (stickyModifiers) { 2024 if (stickyModifiers & KeyModifier::Shift) 2025 interactions.append({ KeyboardInteractionType::KeyRelease, WTF::nullopt, Optional<String>("Shift"_s) }); 2026 if (stickyModifiers & KeyModifier::Control) 2027 interactions.append({ KeyboardInteractionType::KeyRelease, WTF::nullopt, Optional<String>("Control"_s) }); 2028 if (stickyModifiers & KeyModifier::Alternate) 2029 interactions.append({ KeyboardInteractionType::KeyRelease, WTF::nullopt, Optional<String>("Alternate"_s) }); 2030 if (stickyModifiers & KeyModifier::Meta) 2031 interactions.append({ KeyboardInteractionType::KeyRelease, WTF::nullopt, Optional<String>("Meta"_s) }); 2032 } 2033 2034 performKeyboardInteractions(WTFMove(interactions), WTFMove(completionHandler)); 2035 }); 2036 } else { 2037 setInputFileUploadFiles(elementID, text, fileUploadType.value() == FileUploadType::Multiple, WTFMove(completionHandler)); 2038 return; 2039 } 1919 2040 }); 1920 2041 }); -
trunk/Source/WebDriver/Session.h
r253029 r253030 170 170 void computeElementLayout(const String& elementID, OptionSet<ElementLayoutOption>, Function<void (Optional<Rect>&&, Optional<Point>&&, bool, RefPtr<JSON::Object>&&)>&&); 171 171 172 void elementIsFileUpload(const String& elementID, Function<void (CommandResult&&)>&&); 173 enum class FileUploadType { Single, Multiple }; 174 Optional<FileUploadType> parseElementIsFileUploadResult(const RefPtr<JSON::Value>&); 172 175 void selectOptionElement(const String& elementID, Function<void (CommandResult&&)>&&); 176 void setInputFileUploadFiles(const String& elementID, const String& text, bool multiple, Function<void (CommandResult&&)>&&); 177 void didSetInputFileUploadFiles(bool wasCancelled); 173 178 174 179 enum class MouseInteraction { Move, Down, Up, SingleClick, DoubleClick }; -
trunk/Source/WebDriver/WebDriverService.cpp
r252408 r253030 483 483 if (matchedCapabilities.getObject("proxy"_s, proxy)) 484 484 capabilities.proxy = deserializeProxy(*proxy); 485 bool strictFileInteractability; 486 if (matchedCapabilities.getBoolean("strictFileInteractability"_s, strictFileInteractability)) 487 capabilities.strictFileInteractability = strictFileInteractability; 485 488 RefPtr<JSON::Object> timeouts; 486 489 if (matchedCapabilities.getObject("timeouts"_s, timeouts)) … … 540 543 return nullptr; 541 544 result->setValue(it->key, RefPtr<JSON::Value>(it->value)); 545 } else if (it->key == "strictFileInteractability") { 546 bool strictFileInteractability; 547 if (!it->value->asBoolean(strictFileInteractability)) 548 return nullptr; 549 result->setBoolean(it->key, strictFileInteractability); 542 550 } else if (it->key == "timeouts") { 543 551 RefPtr<JSON::Object> timeouts; … … 593 601 if (platformCapabilities.acceptInsecureCerts) 594 602 matchedCapabilities->setBoolean("acceptInsecureCerts"_s, platformCapabilities.acceptInsecureCerts.value()); 603 if (platformCapabilities.strictFileInteractability) 604 matchedCapabilities->setBoolean("strictFileInteractability"_s, platformCapabilities.strictFileInteractability.value()); 595 605 if (platformCapabilities.setWindowRect) 596 606 matchedCapabilities->setBoolean("setWindowRect"_s, platformCapabilities.setWindowRect.value()); … … 801 811 capabilitiesObject->setString("platformName"_s, capabilities.platformName.valueOr(emptyString())); 802 812 capabilitiesObject->setBoolean("acceptInsecureCerts"_s, capabilities.acceptInsecureCerts.valueOr(false)); 813 capabilitiesObject->setBoolean("strictFileInteractability"_s, capabilities.strictFileInteractability.valueOr(false)); 803 814 capabilitiesObject->setBoolean("setWindowRect"_s, capabilities.setWindowRect.valueOr(true)); 804 815 switch (capabilities.unhandledPromptBehavior.valueOr(UnhandledPromptBehavior::DismissAndNotify)) { -
trunk/Source/WebKit/ChangeLog
r253029 r253030 1 2019-12-03 Carlos Garcia Campos <cgarcia@igalia.com> 2 3 WebDriver: handle elements of type file in send keys command 4 https://bugs.webkit.org/show_bug.cgi?id=188514 5 6 Reviewed by Brian Burg. 7 8 Add setFilesForInputFileUpload method to Automation. It's like setFilesToSelectForFileUpload, but it works 9 differently, so I'm keeping both to not break safaridriver. The new one simply sends the file list to the web 10 process to be set on the input element, instead of saving the file list, wait for the driver to trigger the open 11 panel, intercept and complete the open panel request and send a dismiss open panel event to the driver. 12 13 * UIProcess/Automation/Automation.json: Add setFilesForInputFileUpload. 14 * UIProcess/Automation/WebAutomationSession.cpp: 15 (WebKit::WebAutomationSession::setFilesForInputFileUpload): Send SetFilesForInputFileUpload message to the web process. 16 * UIProcess/Automation/WebAutomationSession.h: 17 * WebProcess/Automation/WebAutomationSessionProxy.cpp: 18 (WebKit::WebAutomationSessionProxy::setFilesForInputFileUpload): Create a FileList with the received paths and 19 call HTMLInputElement::setFiles() on the given element. 20 * WebProcess/Automation/WebAutomationSessionProxy.h: 21 * WebProcess/Automation/WebAutomationSessionProxy.messages.in: Add SetFilesForInputFileUpload message. 22 1 23 2019-12-03 Carlos Garcia Campos <cgarcia@igalia.com> 2 24 -
trunk/Source/WebKit/UIProcess/Automation/Automation.json
r245320 r253030 625 625 }, 626 626 { 627 "name": "setFilesForInputFileUpload", 628 "description": "Sets the choosen files for the given input file upload element.", 629 "parameters": [ 630 { "name": "browsingContextHandle", "$ref": "BrowsingContextHandle", "description": "The handle for the browsing context." }, 631 { "name": "frameHandle", "$ref": "FrameHandle", "description": "The handle for the frame that contains the input element." }, 632 { "name": "nodeHandle", "$ref": "NodeHandle", "description": "The handle of the input element." }, 633 { "name": "filenames", "type": "array", "items": { "type": "string" }, "description": "Absolute paths to the choosen files." } 634 ], 635 "async": true 636 }, 637 { 627 638 "name": "getAllCookies", 628 639 "description": "Returns all cookies visible to the specified browsing context.", -
trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.cpp
r251011 r253030 1240 1240 } 1241 1241 1242 void WebAutomationSession::setFilesForInputFileUpload(const String& browsingContextHandle, const String& frameHandle, const String& nodeHandle, const JSON::Array& filenames, Ref<SetFilesForInputFileUploadCallback>&& callback) 1243 { 1244 WebPageProxy* page = webPageProxyForHandle(browsingContextHandle); 1245 if (!page) 1246 ASYNC_FAIL_WITH_PREDEFINED_ERROR(WindowNotFound); 1247 1248 bool frameNotFound = false; 1249 auto frameID = webFrameIDForHandle(frameHandle, frameNotFound); 1250 if (frameNotFound) 1251 ASYNC_FAIL_WITH_PREDEFINED_ERROR(FrameNotFound); 1252 1253 Vector<String> newFileList; 1254 newFileList.reserveInitialCapacity(filenames.length()); 1255 for (size_t i = 0; i < filenames.length(); ++i) { 1256 String filename; 1257 if (!filenames.get(i)->asString(filename)) 1258 ASYNC_FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InternalError, "The parameter 'filenames' contains a non-string value."); 1259 1260 newFileList.append(filename); 1261 } 1262 1263 CompletionHandler<void(Optional<String>)> completionHandler = [callback = callback.copyRef()](Optional<String> errorType) mutable { 1264 if (errorType) { 1265 callback->sendFailure(STRING_FOR_PREDEFINED_ERROR_MESSAGE(*errorType)); 1266 return; 1267 } 1268 1269 callback->sendSuccess(); 1270 }; 1271 1272 page->process().sendWithAsyncReply(Messages::WebAutomationSessionProxy::SetFilesForInputFileUpload(page->webPageID(), frameID, nodeHandle, WTFMove(newFileList)), WTFMove(completionHandler)); 1273 } 1274 1242 1275 static Ref<Inspector::Protocol::Automation::Cookie> buildObjectForCookie(const WebCore::Cookie& cookie) 1243 1276 { -
trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.h
r249275 r253030 192 192 void setUserInputForCurrentJavaScriptPrompt(Inspector::ErrorString&, const String& browsingContextHandle, const String& text) override; 193 193 void setFilesToSelectForFileUpload(Inspector::ErrorString&, const String& browsingContextHandle, const JSON::Array& filenames, const JSON::Array* optionalFileContents) override; 194 void setFilesForInputFileUpload(const String& browsingContextHandle, const String& frameHandle, const String& nodeHandle, const JSON::Array& filenames, Ref<SetFilesForInputFileUploadCallback>&&) override; 194 195 void getAllCookies(const String& browsingContextHandle, Ref<GetAllCookiesCallback>&&) override; 195 196 void deleteSingleCookie(const String& browsingContextHandle, const String& cookieName, Ref<DeleteSingleCookieCallback>&&) override; -
trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.cpp
r252539 r253030 45 45 #include <WebCore/DOMRectList.h> 46 46 #include <WebCore/DOMWindow.h> 47 #include <WebCore/File.h> 48 #include <WebCore/FileList.h> 47 49 #include <WebCore/Frame.h> 48 50 #include <WebCore/FrameTree.h> … … 50 52 #include <WebCore/HTMLFrameElement.h> 51 53 #include <WebCore/HTMLIFrameElement.h> 54 #include <WebCore/HTMLInputElement.h> 52 55 #include <WebCore/HTMLOptGroupElement.h> 53 56 #include <WebCore/HTMLOptionElement.h> … … 683 686 } 684 687 688 void WebAutomationSessionProxy::setFilesForInputFileUpload(WebCore::PageIdentifier pageID, Optional<WebCore::FrameIdentifier> frameID, String nodeHandle, Vector<String>&& filenames, CompletionHandler<void(Optional<String>)>&& completionHandler) 689 { 690 WebPage* page = WebProcess::singleton().webPage(pageID); 691 if (!page) { 692 String windowNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound); 693 completionHandler(windowNotFoundErrorType); 694 return; 695 } 696 697 WebFrame* frame = frameID ? WebProcess::singleton().webFrame(*frameID) : page->mainWebFrame(); 698 if (!frame || !frame->coreFrame() || !frame->coreFrame()->view()) { 699 String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound); 700 completionHandler(frameNotFoundErrorType); 701 return; 702 } 703 704 WebCore::Element* coreElement = elementForNodeHandle(*frame, nodeHandle); 705 if (!coreElement || !is<WebCore::HTMLInputElement>(coreElement) || !downcast<WebCore::HTMLInputElement>(*coreElement).isFileUpload()) { 706 String nodeNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::NodeNotFound); 707 completionHandler(nodeNotFoundErrorType); 708 return; 709 } 710 711 auto& inputElement = downcast<WebCore::HTMLInputElement>(*coreElement); 712 Vector<Ref<WebCore::File>> fileObjects; 713 if (inputElement.multiple()) { 714 if (auto* files = inputElement.files()) { 715 for (auto& file : files->files()) 716 fileObjects.append(file.copyRef()); 717 } 718 } 719 for (const auto& path : filenames) 720 fileObjects.append(File::create(path)); 721 inputElement.setFiles(FileList::create(WTFMove(fileObjects))); 722 723 completionHandler(WTF::nullopt); 724 } 725 685 726 static WebCore::IntRect snapshotRectForScreenshot(WebPage& page, WebCore::Element* element, bool clipToViewport) 686 727 { -
trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.h
r251361 r253030 72 72 void computeElementLayout(WebCore::PageIdentifier, Optional<WebCore::FrameIdentifier>, String nodeHandle, bool scrollIntoViewIfNeeded, CoordinateSystem, CompletionHandler<void(Optional<String>, WebCore::IntRect, Optional<WebCore::IntPoint>, bool)>&&); 73 73 void selectOptionElement(WebCore::PageIdentifier, Optional<WebCore::FrameIdentifier>, String nodeHandle, CompletionHandler<void(Optional<String>)>&&); 74 void setFilesForInputFileUpload(WebCore::PageIdentifier, Optional<WebCore::FrameIdentifier>, String nodeHandle, Vector<String>&& filenames, CompletionHandler<void(Optional<String>)>&&); 74 75 void takeScreenshot(WebCore::PageIdentifier, Optional<WebCore::FrameIdentifier>, String nodeHandle, bool scrollIntoViewIfNeeded, bool clipToViewport, uint64_t callbackID); 75 76 void getCookiesForFrame(WebCore::PageIdentifier, Optional<WebCore::FrameIdentifier>, CompletionHandler<void(Optional<String>, Vector<WebCore::Cookie>)>&&); -
trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.messages.in
r252655 r253030 35 35 SelectOptionElement(WebCore::PageIdentifier pageID, Optional<WebCore::FrameIdentifier> frameID, String nodeHandle) -> (Optional<String> errorType) Async 36 36 37 SetFilesForInputFileUpload(WebCore::PageIdentifier pageID, Optional<WebCore::FrameIdentifier> frameID, String nodeHandle, Vector<String> filenames) -> (Optional<String> errorType) Async 38 37 39 TakeScreenshot(WebCore::PageIdentifier pageID, Optional<WebCore::FrameIdentifier> frameID, String nodeHandle, bool scrollIntoViewIfNeeded, bool clipToViewport, uint64_t callbackID) 38 40 -
trunk/WebDriverTests/ChangeLog
r253029 r253030 1 2019-12-03 Carlos Garcia Campos <cgarcia@igalia.com> 2 3 WebDriver: handle elements of type file in send keys command 4 https://bugs.webkit.org/show_bug.cgi?id=188514 5 6 Reviewed by Brian Burg. 7 8 Remove expectations for tests that are now passing. 9 10 * TestExpectations.json: 11 1 12 2019-12-03 Carlos Garcia Campos <cgarcia@igalia.com> 2 13 -
trunk/WebDriverTests/TestExpectations.json
r253029 r253030 407 407 } 408 408 }, 409 "imported/w3c/webdriver/tests/element_click/file_upload.py": {410 "subtests": {411 "test_file_upload_state": {412 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/188513"}}413 }414 }415 },416 409 "imported/w3c/webdriver/tests/element_click/interactability.py": { 417 410 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/188545"}}, … … 501 494 "imported/w3c/webdriver/tests/element_send_keys/events.py": { 502 495 "subtests": { 503 "test_file_upload": {504 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/188514"}}505 },506 496 "test_not_blurred[input]": { 507 497 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/188118"}} … … 512 502 } 513 503 }, 514 "imported/w3c/webdriver/tests/element_send_keys/file_upload.py": {515 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/188514"}},516 "subtests": {517 "test_empty_text": {518 "expected": {"all": {"status": ["PASS"]}}519 }520 }521 },522 504 "imported/w3c/webdriver/tests/element_send_keys/form_controls.py": { 523 505 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/182331"}}, … … 609 591 "test_get_current_url_matches_location": { 610 592 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/180405"}} 611 }612 }613 },614 "imported/w3c/webdriver/tests/new_session/create_alwaysMatch.py": {615 "subtests": {616 "test_valid[strictFileInteractability-True]": {617 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/188514"}}618 },619 "test_valid[strictFileInteractability-False]": {620 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/188514"}}621 }622 }623 },624 "imported/w3c/webdriver/tests/new_session/create_firstMatch.py": {625 "subtests": {626 "test_valid[strictFileInteractability-True]": {627 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/188514"}}628 },629 "test_valid[strictFileInteractability-False]": {630 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/188514"}}631 }632 }633 },634 "imported/w3c/webdriver/tests/new_session/response.py": {635 "subtests": {636 "test_capability_type[strictFileInteractability-bool]": {637 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/188514"}}638 },639 "test_capability_default_value[strictFileInteractability-False]": {640 "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/188514"}}641 593 } 642 594 }
Note: See TracChangeset
for help on using the changeset viewer.