Diff
Modified: trunk/Source/WebCore/ChangeLog (253029 => 253030)
--- trunk/Source/WebCore/ChangeLog 2019-12-03 09:32:14 UTC (rev 253029)
+++ trunk/Source/WebCore/ChangeLog 2019-12-03 10:03:32 UTC (rev 253030)
@@ -1,5 +1,16 @@
2019-12-03 Carlos Garcia Campos <cgar...@igalia.com>
+ WebDriver: handle elements of type file in send keys command
+ https://bugs.webkit.org/show_bug.cgi?id=188514
+
+ Reviewed by Brian Burg.
+
+ Export symbols now used by WebKit::WebAutomationSessionProxy.
+
+ * html/HTMLInputElement.h:
+
+2019-12-03 Carlos Garcia Campos <cgar...@igalia.com>
+
[PSON] Tooltips from previous page shown on new page
https://bugs.webkit.org/show_bug.cgi?id=204735
Modified: trunk/Source/WebCore/html/HTMLInputElement.h (253029 => 253030)
--- trunk/Source/WebCore/html/HTMLInputElement.h 2019-12-03 09:32:14 UTC (rev 253029)
+++ trunk/Source/WebCore/html/HTMLInputElement.h 2019-12-03 10:03:32 UTC (rev 253030)
@@ -118,7 +118,7 @@
WEBCORE_EXPORT bool isText() const;
WEBCORE_EXPORT bool isEmailField() const;
- bool isFileUpload() const;
+ WEBCORE_EXPORT bool isFileUpload() const;
bool isImageButton() const;
WEBCORE_EXPORT bool isNumberField() const;
bool isSubmitButton() const;
@@ -235,7 +235,7 @@
unsigned effectiveMaxLength() const;
- bool multiple() const;
+ WEBCORE_EXPORT bool multiple() const;
bool isAutoFilled() const { return m_isAutoFilled; }
WEBCORE_EXPORT void setAutoFilled(bool = true);
Modified: trunk/Source/WebDriver/Capabilities.h (253029 => 253030)
--- trunk/Source/WebDriver/Capabilities.h 2019-12-03 09:32:14 UTC (rev 253029)
+++ trunk/Source/WebDriver/Capabilities.h 2019-12-03 10:03:32 UTC (rev 253030)
@@ -70,6 +70,7 @@
Optional<String> browserVersion;
Optional<String> platformName;
Optional<bool> acceptInsecureCerts;
+ Optional<bool> strictFileInteractability;
Optional<bool> setWindowRect;
Optional<Timeouts> timeouts;
Optional<PageLoadStrategy> pageLoadStrategy;
Modified: trunk/Source/WebDriver/ChangeLog (253029 => 253030)
--- trunk/Source/WebDriver/ChangeLog 2019-12-03 09:32:14 UTC (rev 253029)
+++ trunk/Source/WebDriver/ChangeLog 2019-12-03 10:03:32 UTC (rev 253030)
@@ -1,5 +1,32 @@
2019-12-03 Carlos Garcia Campos <cgar...@igalia.com>
+ WebDriver: handle elements of type file in send keys command
+ https://bugs.webkit.org/show_bug.cgi?id=188514
+
+ Reviewed by Brian Burg.
+
+ Handle the case of inpout element being a file upload in send keys command.
+
+ * Capabilities.h: Add strictFileInteractability.
+ * Session.cpp:
+ (WebDriver::Session::elementIsFileUpload): Helper to check if the given element is a file upload and whether
+ it's multiple or not.
+ (WebDriver::Session::parseElementIsFileUploadResult): Parse the result of elementIsFileUpload().
+ (WebDriver::Session::elementClick): If the element is a file upload, fail with invalid argument error.
+ (WebDriver::Session::setInputFileUploadFiles): Send setFilesForInputFileUpload message to the browser with the
+ selected files.
+ (WebDriver::Session::elementSendKeys): If the element is a file upload, call setInputFileUploadFiles()
+ instead. Also handle the strictFileInteractability capability to decide whether to focus and check
+ interactability on the input element or not.
+ * Session.h:
+ * WebDriverService.cpp:
+ (WebDriver::WebDriverService::parseCapabilities const): Handle strictFileInteractability.
+ (WebDriver::WebDriverService::validatedCapabilities const): Ditto.
+ (WebDriver::WebDriverService::matchCapabilities const): Ditto.
+ (WebDriver::WebDriverService::createSession): Ditto.
+
+2019-12-03 Carlos Garcia Campos <cgar...@igalia.com>
+
WebDriver: most of the clear tests are failing
https://bugs.webkit.org/show_bug.cgi?id=180404
Modified: trunk/Source/WebDriver/Session.cpp (253029 => 253030)
--- trunk/Source/WebDriver/Session.cpp 2019-12-03 09:32:14 UTC (rev 253029)
+++ trunk/Source/WebDriver/Session.cpp 2019-12-03 10:03:32 UTC (rev 253030)
@@ -30,6 +30,7 @@
#include "SessionHost.h"
#include "WebDriverAtoms.h"
#include <wtf/CryptographicallyRandomNumber.h>
+#include <wtf/FileSystem.h>
#include <wtf/HashSet.h>
#include <wtf/HexNumber.h>
#include <wtf/NeverDestroyed.h>
@@ -1528,6 +1529,60 @@
});
}
+void Session::elementIsFileUpload(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
+{
+ RefPtr<JSON::Array> arguments = JSON::Array::create();
+ arguments->pushString(createElement(elementID)->toJSONString());
+
+ static const char isFileUploadScript[] =
+ "function(element) {"
+ " if (element.tagName.toLowerCase() === 'input' && element.type === 'file')"
+ " return { 'fileUpload': true, 'multiple': element.hasAttribute('multiple') };"
+ " return { 'fileUpload': false };"
+ "}";
+
+ RefPtr<JSON::Object> parameters = JSON::Object::create();
+ parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
+ if (m_currentBrowsingContext)
+ parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
+ parameters->setString("function"_s, isFileUploadScript);
+ parameters->setArray("arguments"_s, WTFMove(arguments));
+ m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
+ if (response.isError || !response.responseObject) {
+ completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
+ return;
+ }
+ String valueString;
+ if (!response.responseObject->getString("result"_s, valueString)) {
+ completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
+ return;
+ }
+ RefPtr<JSON::Value> resultValue;
+ if (!JSON::Value::parseJSON(valueString, resultValue)) {
+ completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
+ return;
+ }
+ completionHandler(CommandResult::success(WTFMove(resultValue)));
+ });
+}
+
+Optional<Session::FileUploadType> Session::parseElementIsFileUploadResult(const RefPtr<JSON::Value>& resultValue)
+{
+ RefPtr<JSON::Object> result;
+ if (!resultValue->asObject(result))
+ return WTF::nullopt;
+
+ bool isFileUpload;
+ if (!result->getBoolean("fileUpload"_s, isFileUpload) || !isFileUpload)
+ return WTF::nullopt;
+
+ bool multiple;
+ if (!result->getBoolean("multiple"_s, multiple) || !multiple)
+ return FileUploadType::Single;
+
+ return FileUploadType::Multiple;
+}
+
void Session::selectOptionElement(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
{
RefPtr<JSON::Object> parameters = JSON::Object::create();
@@ -1555,41 +1610,52 @@
completionHandler(WTFMove(result));
return;
}
- OptionSet<ElementLayoutOption> options = { ElementLayoutOption::ScrollIntoViewIfNeeded, ElementLayoutOption::UseViewportCoordinates };
- computeElementLayout(elementID, options, [this, protectedThis = protectedThis.copyRef(), elementID, completionHandler = WTFMove(completionHandler)](Optional<Rect>&& rect, Optional<Point>&& inViewCenter, bool isObscured, RefPtr<JSON::Object>&& error) mutable {
- if (!rect || error) {
- completionHandler(CommandResult::fail(WTFMove(error)));
+ elementIsFileUpload(elementID, [this, protectedThis = protectedThis.copyRef(), elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
+ if (result.isError()) {
+ completionHandler(WTFMove(result));
return;
}
- if (isObscured) {
- completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementClickIntercepted));
+
+ if (parseElementIsFileUploadResult(result.result())) {
+ completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
return;
}
- if (!inViewCenter) {
- completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementNotInteractable));
- return;
- }
-
- getElementTagName(elementID, [this, elementID, inViewCenter = WTFMove(inViewCenter), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
- bool isOptionElement = false;
- if (!result.isError()) {
- String tagName;
- if (result.result()->asString(tagName))
- isOptionElement = tagName == "option";
+ OptionSet<ElementLayoutOption> options = { ElementLayoutOption::ScrollIntoViewIfNeeded, ElementLayoutOption::UseViewportCoordinates };
+ computeElementLayout(elementID, options, [this, protectedThis = protectedThis.copyRef(), elementID, completionHandler = WTFMove(completionHandler)](Optional<Rect>&& rect, Optional<Point>&& inViewCenter, bool isObscured, RefPtr<JSON::Object>&& error) mutable {
+ if (!rect || error) {
+ completionHandler(CommandResult::fail(WTFMove(error)));
+ return;
}
+ if (isObscured) {
+ completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementClickIntercepted));
+ return;
+ }
+ if (!inViewCenter) {
+ completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementNotInteractable));
+ return;
+ }
- Function<void (CommandResult&&)> continueAfterClickFunction = [this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
- if (result.isError()) {
- completionHandler(WTFMove(result));
- return;
+ getElementTagName(elementID, [this, elementID, inViewCenter = WTFMove(inViewCenter), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
+ bool isOptionElement = false;
+ if (!result.isError()) {
+ String tagName;
+ if (result.result()->asString(tagName))
+ isOptionElement = tagName == "option";
}
- waitForNavigationToComplete(WTFMove(completionHandler));
- };
- if (isOptionElement)
- selectOptionElement(elementID, WTFMove(continueAfterClickFunction));
- else
- performMouseInteraction(inViewCenter.value().x, inViewCenter.value().y, MouseButton::Left, MouseInteraction::SingleClick, WTFMove(continueAfterClickFunction));
+ Function<void (CommandResult&&)> continueAfterClickFunction = [this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
+ if (result.isError()) {
+ completionHandler(WTFMove(result));
+ return;
+ }
+
+ waitForNavigationToComplete(WTFMove(completionHandler));
+ };
+ if (isOptionElement)
+ selectOptionElement(elementID, WTFMove(continueAfterClickFunction));
+ else
+ performMouseInteraction(inViewCenter.value().x, inViewCenter.value().y, MouseButton::Left, MouseInteraction::SingleClick, WTFMove(continueAfterClickFunction));
+ });
});
});
});
@@ -1698,6 +1764,43 @@
});
}
+void Session::setInputFileUploadFiles(const String& elementID, const String& text, bool multiple, Function<void (CommandResult&&)>&& completionHandler)
+{
+ Vector<String> files = text.split('\n');
+ if (files.isEmpty()) {
+ completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
+ return;
+ }
+
+ if (!multiple && files.size() != 1) {
+ completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
+ return;
+ }
+
+ RefPtr<JSON::Array> filenames = JSON::Array::create();
+ for (const auto& file : files) {
+ if (!FileSystem::fileExists(file)) {
+ completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
+ return;
+ }
+ filenames->pushString(file);
+ }
+
+ RefPtr<JSON::Object> parameters = JSON::Object::create();
+ parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
+ parameters->setString("frameHandle"_s, m_currentBrowsingContext.valueOr(emptyString()));
+ parameters->setString("nodeHandle"_s, elementID);
+ parameters->setArray("filenames"_s, WTFMove(filenames));
+ m_host->sendCommandToBackend("setFilesForInputFileUpload"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), elementID, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
+ if (response.isError) {
+ completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
+ return;
+ }
+
+ completionHandler(CommandResult::success());
+ });
+}
+
String Session::virtualKeyForKey(UChar key, KeyModifier& modifier)
{
// ยง17.4.2 Keyboard Actions.
@@ -1848,74 +1951,92 @@
completionHandler(WTFMove(result));
return;
}
- // FIXME: move this to an atom.
- static const char focusScript[] =
- "function focus(element) {"
- " let doc = element.ownerDocument || element;"
- " let prevActiveElement = doc.activeElement;"
- " if (element != prevActiveElement && prevActiveElement)"
- " prevActiveElement.blur();"
- " element.focus();"
- " let tagName = element.tagName.toUpperCase();"
- " if (tagName === 'BODY' || element === document.documentElement)"
- " return;"
- " let isTextElement = tagName === 'TEXTAREA' || (tagName === 'INPUT' && element.type === 'text');"
- " if (isTextElement && element.selectionEnd == 0)"
- " element.setSelectionRange(element.value.length, element.value.length);"
- " if (element != doc.activeElement)"
- " throw {name: 'ElementNotInteractable', message: 'Element is not focusable.'};"
- "}";
-
- RefPtr<JSON::Array> arguments = JSON::Array::create();
- arguments->pushString(createElement(elementID)->toJSONString());
- RefPtr<JSON::Object> parameters = JSON::Object::create();
- parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
- if (m_currentBrowsingContext)
- parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
- parameters->setString("function"_s, focusScript);
- parameters->setArray("arguments"_s, WTFMove(arguments));
- m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), text, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
- if (response.isError || !response.responseObject) {
- completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
+ elementIsFileUpload(elementID, [this, protectedThis = protectedThis.copyRef(), elementID, text, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
+ if (result.isError()) {
+ completionHandler(WTFMove(result));
return;
}
- unsigned stickyModifiers = 0;
- auto textLength = text.length();
- Vector<KeyboardInteraction> interactions;
- interactions.reserveInitialCapacity(textLength);
- for (unsigned i = 0; i < textLength; ++i) {
- auto key = text[i];
- KeyboardInteraction interaction;
- KeyModifier modifier;
- auto virtualKey = virtualKeyForKey(key, modifier);
- if (!virtualKey.isNull()) {
- interaction.key = virtualKey;
- if (modifier != KeyModifier::None) {
- stickyModifiers ^= modifier;
- if (stickyModifiers & modifier)
- interaction.type = KeyboardInteractionType::KeyPress;
- else
- interaction.type = KeyboardInteractionType::KeyRelease;
+ auto fileUploadType = parseElementIsFileUploadResult(result.result());
+ if (!fileUploadType || capabilities().strictFileInteractability.valueOr(false)) {
+ // FIXME: move this to an atom.
+ static const char focusScript[] =
+ "function focus(element) {"
+ " let doc = element.ownerDocument || element;"
+ " let prevActiveElement = doc.activeElement;"
+ " if (element != prevActiveElement && prevActiveElement)"
+ " prevActiveElement.blur();"
+ " element.focus();"
+ " let tagName = element.tagName.toUpperCase();"
+ " if (tagName === 'BODY' || element === document.documentElement)"
+ " return;"
+ " let isTextElement = tagName === 'TEXTAREA' || (tagName === 'INPUT' && element.type === 'text');"
+ " if (isTextElement && element.selectionEnd == 0)"
+ " element.setSelectionRange(element.value.length, element.value.length);"
+ " if (element != doc.activeElement)"
+ " throw {name: 'ElementNotInteractable', message: 'Element is not focusable.'};"
+ "}";
+
+ RefPtr<JSON::Array> arguments = JSON::Array::create();
+ arguments->pushString(createElement(elementID)->toJSONString());
+ RefPtr<JSON::Object> parameters = JSON::Object::create();
+ parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
+ if (m_currentBrowsingContext)
+ parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
+ parameters->setString("function"_s, focusScript);
+ parameters->setArray("arguments"_s, WTFMove(arguments));
+ m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), fileUploadType, elementID, text, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
+ if (response.isError || !response.responseObject) {
+ completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
+ return;
}
- } else
- interaction.text = String(&key, 1);
- interactions.uncheckedAppend(WTFMove(interaction));
- }
- // Reset sticky modifiers if needed.
- if (stickyModifiers) {
- if (stickyModifiers & KeyModifier::Shift)
- interactions.append({ KeyboardInteractionType::KeyRelease, WTF::nullopt, Optional<String>("Shift"_s) });
- if (stickyModifiers & KeyModifier::Control)
- interactions.append({ KeyboardInteractionType::KeyRelease, WTF::nullopt, Optional<String>("Control"_s) });
- if (stickyModifiers & KeyModifier::Alternate)
- interactions.append({ KeyboardInteractionType::KeyRelease, WTF::nullopt, Optional<String>("Alternate"_s) });
- if (stickyModifiers & KeyModifier::Meta)
- interactions.append({ KeyboardInteractionType::KeyRelease, WTF::nullopt, Optional<String>("Meta"_s) });
+ if (fileUploadType) {
+ setInputFileUploadFiles(elementID, text, fileUploadType.value() == FileUploadType::Multiple, WTFMove(completionHandler));
+ return;
+ }
+
+ unsigned stickyModifiers = 0;
+ auto textLength = text.length();
+ Vector<KeyboardInteraction> interactions;
+ interactions.reserveInitialCapacity(textLength);
+ for (unsigned i = 0; i < textLength; ++i) {
+ auto key = text[i];
+ KeyboardInteraction interaction;
+ KeyModifier modifier;
+ auto virtualKey = virtualKeyForKey(key, modifier);
+ if (!virtualKey.isNull()) {
+ interaction.key = virtualKey;
+ if (modifier != KeyModifier::None) {
+ stickyModifiers ^= modifier;
+ if (stickyModifiers & modifier)
+ interaction.type = KeyboardInteractionType::KeyPress;
+ else
+ interaction.type = KeyboardInteractionType::KeyRelease;
+ }
+ } else
+ interaction.text = String(&key, 1);
+ interactions.uncheckedAppend(WTFMove(interaction));
+ }
+
+ // Reset sticky modifiers if needed.
+ if (stickyModifiers) {
+ if (stickyModifiers & KeyModifier::Shift)
+ interactions.append({ KeyboardInteractionType::KeyRelease, WTF::nullopt, Optional<String>("Shift"_s) });
+ if (stickyModifiers & KeyModifier::Control)
+ interactions.append({ KeyboardInteractionType::KeyRelease, WTF::nullopt, Optional<String>("Control"_s) });
+ if (stickyModifiers & KeyModifier::Alternate)
+ interactions.append({ KeyboardInteractionType::KeyRelease, WTF::nullopt, Optional<String>("Alternate"_s) });
+ if (stickyModifiers & KeyModifier::Meta)
+ interactions.append({ KeyboardInteractionType::KeyRelease, WTF::nullopt, Optional<String>("Meta"_s) });
+ }
+
+ performKeyboardInteractions(WTFMove(interactions), WTFMove(completionHandler));
+ });
+ } else {
+ setInputFileUploadFiles(elementID, text, fileUploadType.value() == FileUploadType::Multiple, WTFMove(completionHandler));
+ return;
}
-
- performKeyboardInteractions(WTFMove(interactions), WTFMove(completionHandler));
});
});
}
Modified: trunk/Source/WebDriver/Session.h (253029 => 253030)
--- trunk/Source/WebDriver/Session.h 2019-12-03 09:32:14 UTC (rev 253029)
+++ trunk/Source/WebDriver/Session.h 2019-12-03 10:03:32 UTC (rev 253030)
@@ -169,7 +169,12 @@
};
void computeElementLayout(const String& elementID, OptionSet<ElementLayoutOption>, Function<void (Optional<Rect>&&, Optional<Point>&&, bool, RefPtr<JSON::Object>&&)>&&);
+ void elementIsFileUpload(const String& elementID, Function<void (CommandResult&&)>&&);
+ enum class FileUploadType { Single, Multiple };
+ Optional<FileUploadType> parseElementIsFileUploadResult(const RefPtr<JSON::Value>&);
void selectOptionElement(const String& elementID, Function<void (CommandResult&&)>&&);
+ void setInputFileUploadFiles(const String& elementID, const String& text, bool multiple, Function<void (CommandResult&&)>&&);
+ void didSetInputFileUploadFiles(bool wasCancelled);
enum class MouseInteraction { Move, Down, Up, SingleClick, DoubleClick };
void performMouseInteraction(int x, int y, MouseButton, MouseInteraction, Function<void (CommandResult&&)>&&);
Modified: trunk/Source/WebDriver/WebDriverService.cpp (253029 => 253030)
--- trunk/Source/WebDriver/WebDriverService.cpp 2019-12-03 09:32:14 UTC (rev 253029)
+++ trunk/Source/WebDriver/WebDriverService.cpp 2019-12-03 10:03:32 UTC (rev 253030)
@@ -482,6 +482,9 @@
RefPtr<JSON::Object> proxy;
if (matchedCapabilities.getObject("proxy"_s, proxy))
capabilities.proxy = deserializeProxy(*proxy);
+ bool strictFileInteractability;
+ if (matchedCapabilities.getBoolean("strictFileInteractability"_s, strictFileInteractability))
+ capabilities.strictFileInteractability = strictFileInteractability;
RefPtr<JSON::Object> timeouts;
if (matchedCapabilities.getObject("timeouts"_s, timeouts))
capabilities.timeouts = deserializeTimeouts(*timeouts);
@@ -539,6 +542,11 @@
if (!it->value->asObject(proxy) || !deserializeProxy(*proxy))
return nullptr;
result->setValue(it->key, RefPtr<JSON::Value>(it->value));
+ } else if (it->key == "strictFileInteractability") {
+ bool strictFileInteractability;
+ if (!it->value->asBoolean(strictFileInteractability))
+ return nullptr;
+ result->setBoolean(it->key, strictFileInteractability);
} else if (it->key == "timeouts") {
RefPtr<JSON::Object> timeouts;
if (!it->value->asObject(timeouts) || !deserializeTimeouts(*timeouts))
@@ -592,6 +600,8 @@
matchedCapabilities->setString("platformName"_s, platformCapabilities.platformName.value());
if (platformCapabilities.acceptInsecureCerts)
matchedCapabilities->setBoolean("acceptInsecureCerts"_s, platformCapabilities.acceptInsecureCerts.value());
+ if (platformCapabilities.strictFileInteractability)
+ matchedCapabilities->setBoolean("strictFileInteractability"_s, platformCapabilities.strictFileInteractability.value());
if (platformCapabilities.setWindowRect)
matchedCapabilities->setBoolean("setWindowRect"_s, platformCapabilities.setWindowRect.value());
@@ -800,6 +810,7 @@
capabilitiesObject->setString("browserVersion"_s, capabilities.browserVersion.valueOr(emptyString()));
capabilitiesObject->setString("platformName"_s, capabilities.platformName.valueOr(emptyString()));
capabilitiesObject->setBoolean("acceptInsecureCerts"_s, capabilities.acceptInsecureCerts.valueOr(false));
+ capabilitiesObject->setBoolean("strictFileInteractability"_s, capabilities.strictFileInteractability.valueOr(false));
capabilitiesObject->setBoolean("setWindowRect"_s, capabilities.setWindowRect.valueOr(true));
switch (capabilities.unhandledPromptBehavior.valueOr(UnhandledPromptBehavior::DismissAndNotify)) {
case UnhandledPromptBehavior::Dismiss:
Modified: trunk/Source/WebKit/ChangeLog (253029 => 253030)
--- trunk/Source/WebKit/ChangeLog 2019-12-03 09:32:14 UTC (rev 253029)
+++ trunk/Source/WebKit/ChangeLog 2019-12-03 10:03:32 UTC (rev 253030)
@@ -1,5 +1,27 @@
2019-12-03 Carlos Garcia Campos <cgar...@igalia.com>
+ WebDriver: handle elements of type file in send keys command
+ https://bugs.webkit.org/show_bug.cgi?id=188514
+
+ Reviewed by Brian Burg.
+
+ Add setFilesForInputFileUpload method to Automation. It's like setFilesToSelectForFileUpload, but it works
+ differently, so I'm keeping both to not break safaridriver. The new one simply sends the file list to the web
+ process to be set on the input element, instead of saving the file list, wait for the driver to trigger the open
+ panel, intercept and complete the open panel request and send a dismiss open panel event to the driver.
+
+ * UIProcess/Automation/Automation.json: Add setFilesForInputFileUpload.
+ * UIProcess/Automation/WebAutomationSession.cpp:
+ (WebKit::WebAutomationSession::setFilesForInputFileUpload): Send SetFilesForInputFileUpload message to the web process.
+ * UIProcess/Automation/WebAutomationSession.h:
+ * WebProcess/Automation/WebAutomationSessionProxy.cpp:
+ (WebKit::WebAutomationSessionProxy::setFilesForInputFileUpload): Create a FileList with the received paths and
+ call HTMLInputElement::setFiles() on the given element.
+ * WebProcess/Automation/WebAutomationSessionProxy.h:
+ * WebProcess/Automation/WebAutomationSessionProxy.messages.in: Add SetFilesForInputFileUpload message.
+
+2019-12-03 Carlos Garcia Campos <cgar...@igalia.com>
+
WebDriver: most of the clear tests are failing
https://bugs.webkit.org/show_bug.cgi?id=180404
Modified: trunk/Source/WebKit/UIProcess/Automation/Automation.json (253029 => 253030)
--- trunk/Source/WebKit/UIProcess/Automation/Automation.json 2019-12-03 09:32:14 UTC (rev 253029)
+++ trunk/Source/WebKit/UIProcess/Automation/Automation.json 2019-12-03 10:03:32 UTC (rev 253030)
@@ -624,6 +624,17 @@
]
},
{
+ "name": "setFilesForInputFileUpload",
+ "description": "Sets the choosen files for the given input file upload element.",
+ "parameters": [
+ { "name": "browsingContextHandle", "$ref": "BrowsingContextHandle", "description": "The handle for the browsing context." },
+ { "name": "frameHandle", "$ref": "FrameHandle", "description": "The handle for the frame that contains the input element." },
+ { "name": "nodeHandle", "$ref": "NodeHandle", "description": "The handle of the input element." },
+ { "name": "filenames", "type": "array", "items": { "type": "string" }, "description": "Absolute paths to the choosen files." }
+ ],
+ "async": true
+ },
+ {
"name": "getAllCookies",
"description": "Returns all cookies visible to the specified browsing context.",
"parameters": [
Modified: trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.cpp (253029 => 253030)
--- trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.cpp 2019-12-03 09:32:14 UTC (rev 253029)
+++ trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.cpp 2019-12-03 10:03:32 UTC (rev 253030)
@@ -1239,6 +1239,39 @@
m_filesToSelectForFileUpload.swap(newFileList);
}
+void WebAutomationSession::setFilesForInputFileUpload(const String& browsingContextHandle, const String& frameHandle, const String& nodeHandle, const JSON::Array& filenames, Ref<SetFilesForInputFileUploadCallback>&& callback)
+{
+ WebPageProxy* page = webPageProxyForHandle(browsingContextHandle);
+ if (!page)
+ ASYNC_FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
+
+ bool frameNotFound = false;
+ auto frameID = webFrameIDForHandle(frameHandle, frameNotFound);
+ if (frameNotFound)
+ ASYNC_FAIL_WITH_PREDEFINED_ERROR(FrameNotFound);
+
+ Vector<String> newFileList;
+ newFileList.reserveInitialCapacity(filenames.length());
+ for (size_t i = 0; i < filenames.length(); ++i) {
+ String filename;
+ if (!filenames.get(i)->asString(filename))
+ ASYNC_FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InternalError, "The parameter 'filenames' contains a non-string value.");
+
+ newFileList.append(filename);
+ }
+
+ CompletionHandler<void(Optional<String>)> completionHandler = [callback = callback.copyRef()](Optional<String> errorType) mutable {
+ if (errorType) {
+ callback->sendFailure(STRING_FOR_PREDEFINED_ERROR_MESSAGE(*errorType));
+ return;
+ }
+
+ callback->sendSuccess();
+ };
+
+ page->process().sendWithAsyncReply(Messages::WebAutomationSessionProxy::SetFilesForInputFileUpload(page->webPageID(), frameID, nodeHandle, WTFMove(newFileList)), WTFMove(completionHandler));
+}
+
static Ref<Inspector::Protocol::Automation::Cookie> buildObjectForCookie(const WebCore::Cookie& cookie)
{
return Inspector::Protocol::Automation::Cookie::create()
Modified: trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.h (253029 => 253030)
--- trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.h 2019-12-03 09:32:14 UTC (rev 253029)
+++ trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.h 2019-12-03 10:03:32 UTC (rev 253030)
@@ -191,6 +191,7 @@
void messageOfCurrentJavaScriptDialog(Inspector::ErrorString&, const String& browsingContextHandle, String* text) override;
void setUserInputForCurrentJavaScriptPrompt(Inspector::ErrorString&, const String& browsingContextHandle, const String& text) override;
void setFilesToSelectForFileUpload(Inspector::ErrorString&, const String& browsingContextHandle, const JSON::Array& filenames, const JSON::Array* optionalFileContents) override;
+ void setFilesForInputFileUpload(const String& browsingContextHandle, const String& frameHandle, const String& nodeHandle, const JSON::Array& filenames, Ref<SetFilesForInputFileUploadCallback>&&) override;
void getAllCookies(const String& browsingContextHandle, Ref<GetAllCookiesCallback>&&) override;
void deleteSingleCookie(const String& browsingContextHandle, const String& cookieName, Ref<DeleteSingleCookieCallback>&&) override;
void addSingleCookie(const String& browsingContextHandle, const JSON::Object& cookie, Ref<AddSingleCookieCallback>&&) override;
Modified: trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.cpp (253029 => 253030)
--- trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.cpp 2019-12-03 09:32:14 UTC (rev 253029)
+++ trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.cpp 2019-12-03 10:03:32 UTC (rev 253030)
@@ -44,11 +44,14 @@
#include <WebCore/DOMRect.h>
#include <WebCore/DOMRectList.h>
#include <WebCore/DOMWindow.h>
+#include <WebCore/File.h>
+#include <WebCore/FileList.h>
#include <WebCore/Frame.h>
#include <WebCore/FrameTree.h>
#include <WebCore/FrameView.h>
#include <WebCore/HTMLFrameElement.h>
#include <WebCore/HTMLIFrameElement.h>
+#include <WebCore/HTMLInputElement.h>
#include <WebCore/HTMLOptGroupElement.h>
#include <WebCore/HTMLOptionElement.h>
#include <WebCore/HTMLSelectElement.h>
@@ -682,6 +685,44 @@
completionHandler(WTF::nullopt);
}
+void WebAutomationSessionProxy::setFilesForInputFileUpload(WebCore::PageIdentifier pageID, Optional<WebCore::FrameIdentifier> frameID, String nodeHandle, Vector<String>&& filenames, CompletionHandler<void(Optional<String>)>&& completionHandler)
+{
+ WebPage* page = WebProcess::singleton().webPage(pageID);
+ if (!page) {
+ String windowNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound);
+ completionHandler(windowNotFoundErrorType);
+ return;
+ }
+
+ WebFrame* frame = frameID ? WebProcess::singleton().webFrame(*frameID) : page->mainWebFrame();
+ if (!frame || !frame->coreFrame() || !frame->coreFrame()->view()) {
+ String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound);
+ completionHandler(frameNotFoundErrorType);
+ return;
+ }
+
+ WebCore::Element* coreElement = elementForNodeHandle(*frame, nodeHandle);
+ if (!coreElement || !is<WebCore::HTMLInputElement>(coreElement) || !downcast<WebCore::HTMLInputElement>(*coreElement).isFileUpload()) {
+ String nodeNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::NodeNotFound);
+ completionHandler(nodeNotFoundErrorType);
+ return;
+ }
+
+ auto& inputElement = downcast<WebCore::HTMLInputElement>(*coreElement);
+ Vector<Ref<WebCore::File>> fileObjects;
+ if (inputElement.multiple()) {
+ if (auto* files = inputElement.files()) {
+ for (auto& file : files->files())
+ fileObjects.append(file.copyRef());
+ }
+ }
+ for (const auto& path : filenames)
+ fileObjects.append(File::create(path));
+ inputElement.setFiles(FileList::create(WTFMove(fileObjects)));
+
+ completionHandler(WTF::nullopt);
+}
+
static WebCore::IntRect snapshotRectForScreenshot(WebPage& page, WebCore::Element* element, bool clipToViewport)
{
auto* frameView = page.mainFrameView();
Modified: trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.h (253029 => 253030)
--- trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.h 2019-12-03 09:32:14 UTC (rev 253029)
+++ trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.h 2019-12-03 10:03:32 UTC (rev 253030)
@@ -71,6 +71,7 @@
void focusFrame(WebCore::PageIdentifier, Optional<WebCore::FrameIdentifier>);
void computeElementLayout(WebCore::PageIdentifier, Optional<WebCore::FrameIdentifier>, String nodeHandle, bool scrollIntoViewIfNeeded, CoordinateSystem, CompletionHandler<void(Optional<String>, WebCore::IntRect, Optional<WebCore::IntPoint>, bool)>&&);
void selectOptionElement(WebCore::PageIdentifier, Optional<WebCore::FrameIdentifier>, String nodeHandle, CompletionHandler<void(Optional<String>)>&&);
+ void setFilesForInputFileUpload(WebCore::PageIdentifier, Optional<WebCore::FrameIdentifier>, String nodeHandle, Vector<String>&& filenames, CompletionHandler<void(Optional<String>)>&&);
void takeScreenshot(WebCore::PageIdentifier, Optional<WebCore::FrameIdentifier>, String nodeHandle, bool scrollIntoViewIfNeeded, bool clipToViewport, uint64_t callbackID);
void getCookiesForFrame(WebCore::PageIdentifier, Optional<WebCore::FrameIdentifier>, CompletionHandler<void(Optional<String>, Vector<WebCore::Cookie>)>&&);
void deleteCookie(WebCore::PageIdentifier, Optional<WebCore::FrameIdentifier>, String cookieName, CompletionHandler<void(Optional<String>)>&&);
Modified: trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.messages.in (253029 => 253030)
--- trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.messages.in 2019-12-03 09:32:14 UTC (rev 253029)
+++ trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.messages.in 2019-12-03 10:03:32 UTC (rev 253030)
@@ -34,6 +34,8 @@
SelectOptionElement(WebCore::PageIdentifier pageID, Optional<WebCore::FrameIdentifier> frameID, String nodeHandle) -> (Optional<String> errorType) Async
+ SetFilesForInputFileUpload(WebCore::PageIdentifier pageID, Optional<WebCore::FrameIdentifier> frameID, String nodeHandle, Vector<String> filenames) -> (Optional<String> errorType) Async
+
TakeScreenshot(WebCore::PageIdentifier pageID, Optional<WebCore::FrameIdentifier> frameID, String nodeHandle, bool scrollIntoViewIfNeeded, bool clipToViewport, uint64_t callbackID)
GetCookiesForFrame(WebCore::PageIdentifier pageID, Optional<WebCore::FrameIdentifier> frameID) -> (Optional<String> errorType, Vector<WebCore::Cookie> cookies) Async
Modified: trunk/WebDriverTests/ChangeLog (253029 => 253030)
--- trunk/WebDriverTests/ChangeLog 2019-12-03 09:32:14 UTC (rev 253029)
+++ trunk/WebDriverTests/ChangeLog 2019-12-03 10:03:32 UTC (rev 253030)
@@ -1,5 +1,16 @@
2019-12-03 Carlos Garcia Campos <cgar...@igalia.com>
+ WebDriver: handle elements of type file in send keys command
+ https://bugs.webkit.org/show_bug.cgi?id=188514
+
+ Reviewed by Brian Burg.
+
+ Remove expectations for tests that are now passing.
+
+ * TestExpectations.json:
+
+2019-12-03 Carlos Garcia Campos <cgar...@igalia.com>
+
WebDriver: most of the clear tests are failing
https://bugs.webkit.org/show_bug.cgi?id=180404
Modified: trunk/WebDriverTests/TestExpectations.json (253029 => 253030)
--- trunk/WebDriverTests/TestExpectations.json 2019-12-03 09:32:14 UTC (rev 253029)
+++ trunk/WebDriverTests/TestExpectations.json 2019-12-03 10:03:32 UTC (rev 253030)
@@ -406,13 +406,6 @@
}
}
},
- "imported/w3c/webdriver/tests/element_click/file_upload.py": {
- "subtests": {
- "test_file_upload_state": {
- "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/188513"}}
- }
- }
- },
"imported/w3c/webdriver/tests/element_click/interactability.py": {
"expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/188545"}},
"subtests": {
@@ -500,9 +493,6 @@
},
"imported/w3c/webdriver/tests/element_send_keys/events.py": {
"subtests": {
- "test_file_upload": {
- "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/188514"}}
- },
"test_not_blurred[input]": {
"expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/188118"}}
},
@@ -511,14 +501,6 @@
}
}
},
- "imported/w3c/webdriver/tests/element_send_keys/file_upload.py": {
- "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/188514"}},
- "subtests": {
- "test_empty_text": {
- "expected": {"all": {"status": ["PASS"]}}
- }
- }
- },
"imported/w3c/webdriver/tests/element_send_keys/form_controls.py": {
"expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/182331"}},
"subtests": {
@@ -611,36 +593,6 @@
}
}
},
- "imported/w3c/webdriver/tests/new_session/create_alwaysMatch.py": {
- "subtests": {
- "test_valid[strictFileInteractability-True]": {
- "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/188514"}}
- },
- "test_valid[strictFileInteractability-False]": {
- "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/188514"}}
- }
- }
- },
- "imported/w3c/webdriver/tests/new_session/create_firstMatch.py": {
- "subtests": {
- "test_valid[strictFileInteractability-True]": {
- "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/188514"}}
- },
- "test_valid[strictFileInteractability-False]": {
- "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/188514"}}
- }
- }
- },
- "imported/w3c/webdriver/tests/new_session/response.py": {
- "subtests": {
- "test_capability_type[strictFileInteractability-bool]": {
- "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/188514"}}
- },
- "test_capability_default_value[strictFileInteractability-False]": {
- "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/188514"}}
- }
- }
- },
"imported/w3c/webdriver/tests/new_session/default_values.py": {
"subtests": {
"test_valid_but_unmatchable_key": {