Diff
Modified: trunk/Source/WebDriver/Capabilities.h (220314 => 220315)
--- trunk/Source/WebDriver/Capabilities.h 2017-08-05 08:11:11 UTC (rev 220314)
+++ trunk/Source/WebDriver/Capabilities.h 2017-08-05 09:27:15 UTC (rev 220315)
@@ -26,19 +26,28 @@
#pragma once
#include <wtf/Forward.h>
+#include <wtf/Seconds.h>
#include <wtf/Vector.h>
#include <wtf/text/WTFString.h>
namespace WebDriver {
+struct Timeouts {
+ std::optional<Seconds> script;
+ std::optional<Seconds> pageLoad;
+ std::optional<Seconds> implicit;
+};
+
struct Capabilities {
- String browserName;
- String browserVersion;
- String platform;
+ std::optional<String> browserName;
+ std::optional<String> browserVersion;
+ std::optional<String> platformName;
+ std::optional<bool> acceptInsecureCerts;
+ std::optional<Timeouts> timeouts;
#if PLATFORM(GTK)
- String browserBinary;
- Vector<String> browserArguments;
- bool useOverlayScrollbars { true };
+ std::optional<String> browserBinary;
+ std::optional<Vector<String>> browserArguments;
+ std::optional<bool> useOverlayScrollbars;
#endif
};
Modified: trunk/Source/WebDriver/ChangeLog (220314 => 220315)
--- trunk/Source/WebDriver/ChangeLog 2017-08-05 08:11:11 UTC (rev 220314)
+++ trunk/Source/WebDriver/ChangeLog 2017-08-05 09:27:15 UTC (rev 220315)
@@ -1,5 +1,46 @@
2017-08-05 Carlos Garcia Campos <cgar...@igalia.com>
+ WebDriver: properly handle capabilities and process firstMatch too
+ https://bugs.webkit.org/show_bug.cgi?id=174618
+
+ Reviewed by Brian Burg.
+
+ Implement processing of capabilities following the spec. This patch adds validation, merging and matching of
+ capabilities.
+
+ 7.2 Processing Capabilities.
+ https://w3c.github.io/webdriver/webdriver-spec.html#processing-capabilities
+
+ * Capabilities.h: Make all capabilities optional and move Timeouts struct here.
+ * Session.h:
+ * WebDriverService.cpp:
+ (WebDriver::deserializeTimeouts): Helper to extract timeouts from JSON object.
+ (WebDriver::WebDriverService::parseCapabilities const): At this point capabilities have already been validated,
+ so we just need to get them without checking the value type.
+ (WebDriver::WebDriverService::validatedCapabilities const): Validate the given capabilities, ensuring types of
+ values are the expected one.
+ (WebDriver::WebDriverService::mergeCapabilities const): Merge the alwaysMatch and firstMatch capabilities into a
+ single object ensuring that the same capability is not in both.
+ (WebDriver::WebDriverService::matchCapabilities const): Try to match the merged capabilities againt the platform
+ expected capabilities.
+ (WebDriver::WebDriverService::processCapabilities const): Validate, merge and match the capabilities.
+ (WebDriver::WebDriverService::newSession): Use processCapabilities(). Also initialize the timeouts from
+ capabilities and add all capabilities to the command result.
+ (WebDriver::WebDriverService::setTimeouts): Use deserializeTimeouts().
+ * WebDriverService.h:
+ * glib/SessionHostGlib.cpp:
+ (WebDriver::SessionHost::launchBrowser): Updated to properly access the capabilities that are now optional.
+ (WebDriver::SessionHost::startAutomationSession): Add FIXME.
+ * gtk/WebDriverServiceGtk.cpp:
+ (WebDriver::WebDriverService::platformCapabilities): Return the Capabilities with the known required ones filled.
+ (WebDriver::WebDriverService::platformValidateCapability const): Validate webkitgtk:browserOptions.
+ (WebDriver::WebDriverService::platformMatchCapability const): This does nothing for now.
+ (WebDriver::WebDriverService::platformCompareBrowserVersions): Compare the given browser versions.
+ (WebDriver::WebDriverService::platformParseCapabilities const): Updated now that capabilites have already been
+ validated before.
+
+2017-08-05 Carlos Garcia Campos <cgar...@igalia.com>
+
WebDriver: use in-view center point for clicks instead of bounding box center point
https://bugs.webkit.org/show_bug.cgi?id=174863
Modified: trunk/Source/WebDriver/Session.h (220314 => 220315)
--- trunk/Source/WebDriver/Session.h 2017-08-05 08:11:11 UTC (rev 220314)
+++ trunk/Source/WebDriver/Session.h 2017-08-05 09:27:15 UTC (rev 220315)
@@ -25,11 +25,11 @@
#pragma once
+#include "Capabilities.h"
#include <wtf/Forward.h>
#include <wtf/Function.h>
#include <wtf/OptionSet.h>
#include <wtf/RefCounted.h>
-#include <wtf/Seconds.h>
#include <wtf/Vector.h>
#include <wtf/text/WTFString.h>
@@ -41,7 +41,6 @@
namespace WebDriver {
-class Capabilities;
class CommandResult;
class SessionHost;
@@ -60,12 +59,6 @@
enum class ExecuteScriptMode { Sync, Async };
enum class Timeout { Script, PageLoad, Implicit };
- struct Timeouts {
- std::optional<Seconds> script;
- std::optional<Seconds> pageLoad;
- std::optional<Seconds> implicit;
- };
-
void waitForNavigationToComplete(Function<void (CommandResult&&)>&&);
void createTopLevelBrowsingContext(Function<void (CommandResult&&)>&&);
void close(Function<void (CommandResult&&)>&&);
Modified: trunk/Source/WebDriver/WebDriverService.cpp (220314 => 220315)
--- trunk/Source/WebDriver/WebDriverService.cpp 2017-08-05 08:11:11 UTC (rev 220314)
+++ trunk/Source/WebDriver/WebDriverService.cpp 2017-08-05 09:27:15 UTC (rev 220315)
@@ -251,25 +251,53 @@
replyHandler({ result.httpStatusCode(), responseObject->toJSONString().utf8(), ASCIILiteral("application/json; charset=utf-8") });
}
-bool WebDriverService::parseCapabilities(InspectorObject& desiredCapabilities, Capabilities& capabilities, Function<void (CommandResult&&)>& completionHandler)
+static std::optional<Timeouts> deserializeTimeouts(InspectorObject& timeoutsObject)
{
- RefPtr<InspectorValue> value;
- if (desiredCapabilities.getValue(ASCIILiteral("browserName"), value) && !value->asString(capabilities.browserName)) {
- completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("browserName parameter is invalid in capabilities")));
- return false;
+ // §8.5 Set Timeouts.
+ // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-deserialize-as-a-timeout
+ Timeouts timeouts;
+ auto end = timeoutsObject.end();
+ for (auto it = timeoutsObject.begin(); it != end; ++it) {
+ if (it->key == "sessionId")
+ continue;
+
+ int timeoutMS;
+ if (!it->value->asInteger(timeoutMS) || timeoutMS < 0 || timeoutMS > INT_MAX)
+ return std::nullopt;
+
+ if (it->key == "script")
+ timeouts.script = Seconds::fromMilliseconds(timeoutMS);
+ else if (it->key == "pageLoad")
+ timeouts.pageLoad = Seconds::fromMilliseconds(timeoutMS);
+ else if (it->key == "implicit")
+ timeouts.implicit = Seconds::fromMilliseconds(timeoutMS);
+ else
+ return std::nullopt;
}
- if (desiredCapabilities.getValue(ASCIILiteral("version"), value) && !value->asString(capabilities.browserVersion)) {
- completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("version parameter is invalid in capabilities")));
- return false;
- }
- if (desiredCapabilities.getValue(ASCIILiteral("platform"), value) && !value->asString(capabilities.platform)) {
- completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("platform parameter is invalid in capabilities")));
- return false;
- }
- // FIXME: parse all other well-known capabilities: acceptInsecureCerts, pageLoadStrategy, proxy, setWindowRect, timeouts, unhandledPromptBehavior.
- return platformParseCapabilities(desiredCapabilities, capabilities, completionHandler);
+ return timeouts;
}
+void WebDriverService::parseCapabilities(const InspectorObject& matchedCapabilities, Capabilities& capabilities) const
+{
+ // Matched capabilities have already been validated.
+ bool acceptInsecureCerts;
+ if (matchedCapabilities.getBoolean(ASCIILiteral("acceptInsecureCerts"), acceptInsecureCerts))
+ capabilities.acceptInsecureCerts = acceptInsecureCerts;
+ String browserName;
+ if (matchedCapabilities.getString(ASCIILiteral("browserName"), browserName))
+ capabilities.browserName = browserName;
+ String browserVersion;
+ if (matchedCapabilities.getString(ASCIILiteral("browserVersion"), browserVersion))
+ capabilities.browserVersion = browserVersion;
+ String platformName;
+ if (matchedCapabilities.getString(ASCIILiteral("platformName"), platformName))
+ capabilities.platformName = platformName;
+ RefPtr<InspectorObject> timeouts;
+ if (matchedCapabilities.getObject(ASCIILiteral("timeouts"), timeouts))
+ capabilities.timeouts = deserializeTimeouts(*timeouts);
+ platformParseCapabilities(matchedCapabilities, capabilities);
+}
+
RefPtr<Session> WebDriverService::findSessionOrCompleteWithError(InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler)
{
String sessionID;
@@ -287,29 +315,208 @@
return session;
}
-void WebDriverService::newSession(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
+RefPtr<InspectorObject> WebDriverService::validatedCapabilities(const InspectorObject& capabilities) const
{
- // §8.1 New Session.
- // https://www.w3.org/TR/webdriver/#new-session
+ // §7.2 Processing Capabilities.
+ // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-validate-capabilities
+ auto result = InspectorObject::create();
+ auto end = capabilities.end();
+ for (auto it = capabilities.begin(); it != end; ++it) {
+ if (it->value->isNull())
+ result->setValue(it->key, RefPtr<InspectorValue>(it->value));
+ else if (it->key == "acceptInsecureCerts") {
+ bool acceptInsecureCerts;
+ if (!it->value->asBoolean(acceptInsecureCerts))
+ return nullptr;
+ result->setBoolean(it->key, acceptInsecureCerts);
+ } else if (it->key == "browserName" || it->key == "browserVersion" || it->key == "platformName") {
+ String stringValue;
+ if (!it->value->asString(stringValue))
+ return nullptr;
+ result->setString(it->key, stringValue);
+ } else if (it->key == "pageLoadStrategy") {
+ String pageLoadStrategy;
+ if (!it->value->asString(pageLoadStrategy))
+ return nullptr;
+ // FIXME: implement pageLoadStrategy.
+ result->setString(it->key, pageLoadStrategy);
+ } else if (it->key == "proxy") {
+ // FIXME: implement proxy support.
+ } else if (it->key == "timeouts") {
+ RefPtr<InspectorObject> timeouts;
+ if (!it->value->asObject(timeouts) || !deserializeTimeouts(*timeouts))
+ return nullptr;
+ result->setValue(it->key, RefPtr<InspectorValue>(it->value));
+ } else if (it->key == "unhandledPromptBehavior") {
+ String unhandledPromptBehavior;
+ if (!it->value->asString(unhandledPromptBehavior))
+ return nullptr;
+ // FIXME: implement prompts support.
+ result->setString(it->key, unhandledPromptBehavior);
+ } else if (it->key.find(":") != notFound) {
+ if (!platformValidateCapability(it->key, it->value))
+ return nullptr;
+ result->setValue(it->key, RefPtr<InspectorValue>(it->value));
+ }
+ }
+ return result;
+}
+
+RefPtr<InspectorObject> WebDriverService::mergeCapabilities(const InspectorObject& requiredCapabilities, const InspectorObject& firstMatchCapabilities) const
+{
+ // §7.2 Processing Capabilities.
+ // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-merging-capabilities
+ auto result = InspectorObject::create();
+ auto requiredEnd = requiredCapabilities.end();
+ for (auto it = requiredCapabilities.begin(); it != requiredEnd; ++it)
+ result->setValue(it->key, RefPtr<InspectorValue>(it->value));
+
+ auto firstMatchEnd = firstMatchCapabilities.end();
+ for (auto it = firstMatchCapabilities.begin(); it != firstMatchEnd; ++it) {
+ if (requiredCapabilities.find(it->key) != requiredEnd)
+ return nullptr;
+
+ result->setValue(it->key, RefPtr<InspectorValue>(it->value));
+ }
+
+ return result;
+}
+
+std::optional<String> WebDriverService::matchCapabilities(const InspectorObject& mergedCapabilities) const
+{
+ // §7.2 Processing Capabilities.
+ // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-matching-capabilities
+ Capabilities matchedCapabilities = platformCapabilities();
+
+ // Some capabilities like browser name and version might need to launch the browser,
+ // so we only reject the known capabilities that don't match.
+ auto end = mergedCapabilities.end();
+ for (auto it = mergedCapabilities.begin(); it != end; ++it) {
+ if (it->key == "browserName" && matchedCapabilities.browserName) {
+ String browserName;
+ it->value->asString(browserName);
+ if (!equalIgnoringASCIICase(matchedCapabilities.browserName.value(), browserName))
+ return makeString("expected browserName ", matchedCapabilities.browserName.value(), " but got ", browserName);
+ } else if (it->key == "browserVersion" && matchedCapabilities.browserVersion) {
+ String browserVersion;
+ it->value->asString(browserVersion);
+ if (!platformCompareBrowserVersions(browserVersion, matchedCapabilities.browserVersion.value()))
+ return makeString("requested browserVersion is ", browserVersion, " but actual version is ", matchedCapabilities.browserVersion.value());
+ } else if (it->key == "platformName" && matchedCapabilities.platformName) {
+ String platformName;
+ it->value->asString(platformName);
+ if (!equalLettersIgnoringASCIICase(platformName, "any") && !equalIgnoringASCIICase(matchedCapabilities.platformName.value(), platformName))
+ return makeString("expected platformName ", matchedCapabilities.platformName.value(), " but got ", platformName);
+ } else if (it->key == "acceptInsecureCerts" && matchedCapabilities.acceptInsecureCerts) {
+ bool acceptInsecureCerts;
+ it->value->asBoolean(acceptInsecureCerts);
+ if (acceptInsecureCerts && !matchedCapabilities.acceptInsecureCerts.value())
+ return String("browser doesn't accept insecure TLS certificates");
+ } else if (it->key == "proxy") {
+ // FIXME: implement proxy support.
+ } else if (auto errorString = platformMatchCapability(it->key, it->value))
+ return errorString;
+ }
+
+ return std::nullopt;
+}
+
+RefPtr<InspectorObject> WebDriverService::processCapabilities(const InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler) const
+{
+ // §7.2 Processing Capabilities.
+ // https://w3c.github.io/webdriver/webdriver-spec.html#processing-capabilities
+
+ // 1. Let capabilities request be the result of getting the property "capabilities" from parameters.
RefPtr<InspectorObject> capabilitiesObject;
- if (!parameters->getObject(ASCIILiteral("capabilities"), capabilitiesObject)) {
+ if (!parameters.getObject(ASCIILiteral("capabilities"), capabilitiesObject)) {
completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated));
- return;
+ return nullptr;
}
+
+ // 2. Let required capabilities be the result of getting the property "alwaysMatch" from capabilities request.
RefPtr<InspectorValue> requiredCapabilitiesValue;
RefPtr<InspectorObject> requiredCapabilities;
if (!capabilitiesObject->getValue(ASCIILiteral("alwaysMatch"), requiredCapabilitiesValue))
+ // 2.1. If required capabilities is undefined, set the value to an empty JSON Object.
requiredCapabilities = InspectorObject::create();
else if (!requiredCapabilitiesValue->asObject(requiredCapabilities)) {
completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("alwaysMatch is invalid in capabilities")));
- return;
+ return nullptr;
}
- // FIXME: process firstMatch capabilities.
- Capabilities capabilities;
- if (!parseCapabilities(*requiredCapabilities, capabilities, completionHandler))
+ // 2.2. Let required capabilities be the result of trying to validate capabilities with argument required capabilities.
+ requiredCapabilities = validatedCapabilities(*requiredCapabilities);
+ if (!requiredCapabilities) {
+ completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("Invalid alwaysMatch capabilities")));
+ return nullptr;
+ }
+
+ // 3. Let all first match capabilities be the result of getting the property "firstMatch" from capabilities request.
+ RefPtr<InspectorValue> firstMatchCapabilitiesValue;
+ RefPtr<InspectorArray> firstMatchCapabilitiesList;
+ if (!capabilitiesObject->getValue(ASCIILiteral("firstMatch"), firstMatchCapabilitiesValue)) {
+ // 3.1. If all first match capabilities is undefined, set the value to a JSON List with a single entry of an empty JSON Object.
+ firstMatchCapabilitiesList = InspectorArray::create();
+ firstMatchCapabilitiesList->pushObject(InspectorObject::create());
+ } else if (!firstMatchCapabilitiesValue->asArray(firstMatchCapabilitiesList)) {
+ // 3.2. If all first match capabilities is not a JSON List, return error with error code invalid argument.
+ completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("firstMatch is invalid in capabilities")));
+ return nullptr;
+ }
+
+ // 4. Let validated first match capabilities be an empty JSON List.
+ Vector<RefPtr<InspectorObject>> validatedFirstMatchCapabilitiesList;
+ auto firstMatchCapabilitiesListLength = firstMatchCapabilitiesList->length();
+ validatedFirstMatchCapabilitiesList.reserveInitialCapacity(firstMatchCapabilitiesListLength);
+ // 5. For each first match capabilities corresponding to an indexed property in all first match capabilities.
+ for (unsigned i = 0; i < firstMatchCapabilitiesListLength; ++i) {
+ RefPtr<InspectorValue> firstMatchCapabilitiesValue = firstMatchCapabilitiesList->get(i);
+ RefPtr<InspectorObject> firstMatchCapabilities;
+ if (!firstMatchCapabilitiesValue->asObject(firstMatchCapabilities)) {
+ completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("Invalid capabilities found in firstMatch")));
+ return nullptr;
+ }
+ // 5.1. Let validated capabilities be the result of trying to validate capabilities with argument first match capabilities.
+ firstMatchCapabilities = validatedCapabilities(*firstMatchCapabilities);
+ if (!firstMatchCapabilities) {
+ completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("Invalid firstMatch capabilities")));
+ return nullptr;
+ }
+ // 5.2. Append validated capabilities to validated first match capabilities.
+ validatedFirstMatchCapabilitiesList.uncheckedAppend(WTFMove(firstMatchCapabilities));
+ }
+
+ // 6. For each first match capabilities corresponding to an indexed property in validated first match capabilities.
+ std::optional<String> errorString;
+ for (auto& validatedFirstMatchCapabilies : validatedFirstMatchCapabilitiesList) {
+ // 6.1. Let merged capabilities be the result of trying to merge capabilities with required capabilities and first match capabilities as arguments.
+ auto mergedCapabilities = mergeCapabilities(*requiredCapabilities, *validatedFirstMatchCapabilies);
+ if (!mergedCapabilities) {
+ completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("Same capability found in firstMatch and alwaysMatch")));
+ return nullptr;
+ }
+ // 6.2. Let matched capabilities be the result of trying to match capabilities with merged capabilities as an argument.
+ errorString = matchCapabilities(*mergedCapabilities);
+ if (!errorString) {
+ // 6.3. If matched capabilities is not null return matched capabilities.
+ return mergedCapabilities;
+ }
+ }
+
+ completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, errorString ? errorString.value() : String("Invalid capabilities")));
+ return nullptr;
+}
+
+void WebDriverService::newSession(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
+{
+ // §8.1 New Session.
+ // https://www.w3.org/TR/webdriver/#new-session
+ auto matchedCapabilities = processCapabilities(*parameters, completionHandler);
+ if (!matchedCapabilities)
return;
+ Capabilities capabilities;
+ parseCapabilities(*matchedCapabilities, capabilities);
auto sessionHost = std::make_unique<SessionHost>(WTFMove(capabilities));
auto* sessionHostPtr = sessionHost.get();
sessionHostPtr->connectToBrowser([this, sessionHost = WTFMove(sessionHost), completionHandler = WTFMove(completionHandler)](SessionHost::Succeeded succeeded) mutable {
@@ -321,19 +528,39 @@
RefPtr<Session> session = Session::create(WTFMove(sessionHost));
session->createTopLevelBrowsingContext([this, session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
if (result.isError()) {
- completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, result.errorString()));
+ completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, result.errorMessage()));
return;
}
m_activeSession = session.get();
m_sessions.add(session->id(), session);
+
+ const auto& capabilities = session->capabilities();
+ if (capabilities.timeouts)
+ session->setTimeouts(capabilities.timeouts.value(), [](CommandResult&&) { });
+
RefPtr<InspectorObject> resultObject = InspectorObject::create();
resultObject->setString(ASCIILiteral("sessionId"), session->id());
- RefPtr<InspectorObject> capabilities = InspectorObject::create();
- capabilities->setString(ASCIILiteral("browserName"), session->capabilities().browserName);
- capabilities->setString(ASCIILiteral("version"), session->capabilities().browserVersion);
- capabilities->setString(ASCIILiteral("platform"), session->capabilities().platform);
- resultObject->setObject(ASCIILiteral("value"), WTFMove(capabilities));
+ RefPtr<InspectorObject> capabilitiesObject = InspectorObject::create();
+ if (capabilities.browserName)
+ capabilitiesObject->setString(ASCIILiteral("browserName"), capabilities.browserName.value());
+ if (capabilities.browserVersion)
+ capabilitiesObject->setString(ASCIILiteral("browserVersion"), capabilities.browserVersion.value());
+ if (capabilities.platformName)
+ capabilitiesObject->setString(ASCIILiteral("platformName"), capabilities.platformName.value());
+ if (capabilities.acceptInsecureCerts)
+ capabilitiesObject->setBoolean(ASCIILiteral("acceptInsecureCerts"), capabilities.acceptInsecureCerts.value());
+ if (capabilities.timeouts) {
+ RefPtr<InspectorObject> timeoutsObject = InspectorObject::create();
+ if (capabilities.timeouts.value().script)
+ timeoutsObject->setInteger(ASCIILiteral("script"), capabilities.timeouts.value().script.value().millisecondsAs<int>());
+ if (capabilities.timeouts.value().pageLoad)
+ timeoutsObject->setInteger(ASCIILiteral("pageLoad"), capabilities.timeouts.value().pageLoad.value().millisecondsAs<int>());
+ if (capabilities.timeouts.value().implicit)
+ timeoutsObject->setInteger(ASCIILiteral("implicit"), capabilities.timeouts.value().implicit.value().millisecondsAs<int>());
+ capabilitiesObject->setObject(ASCIILiteral("timeouts"), WTFMove(timeoutsObject));
+ }
+ resultObject->setObject(ASCIILiteral("value"), WTFMove(capabilitiesObject));
completionHandler(CommandResult::success(WTFMove(resultObject)));
});
});
@@ -369,31 +596,13 @@
if (!session)
return;
- Session::Timeouts timeouts;
- auto end = parameters->end();
- for (auto it = parameters->begin(); it != end; ++it) {
- if (it->key == "sessionId")
- continue;
-
- int timeoutMS;
- if (!it->value->asInteger(timeoutMS) || timeoutMS < 0 || timeoutMS > INT_MAX) {
- completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
- return;
- }
-
- if (it->key == "script")
- timeouts.script = Seconds::fromMilliseconds(timeoutMS);
- else if (it->key == "pageLoad")
- timeouts.pageLoad = Seconds::fromMilliseconds(timeoutMS);
- else if (it->key == "implicit")
- timeouts.implicit = Seconds::fromMilliseconds(timeoutMS);
- else {
- completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
- return;
- }
+ auto timeouts = deserializeTimeouts(*parameters);
+ if (!timeouts) {
+ completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
+ return;
}
- session->setTimeouts(timeouts, WTFMove(completionHandler));
+ session->setTimeouts(timeouts.value(), WTFMove(completionHandler));
}
void WebDriverService::go(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
Modified: trunk/Source/WebDriver/WebDriverService.h (220314 => 220315)
--- trunk/Source/WebDriver/WebDriverService.h 2017-08-05 08:11:11 UTC (rev 220314)
+++ trunk/Source/WebDriver/WebDriverService.h 2017-08-05 09:27:15 UTC (rev 220315)
@@ -50,6 +50,8 @@
int run(int argc, char** argv);
void quit();
+ static bool platformCompareBrowserVersions(const String&, const String&);
+
private:
enum class HTTPMethod { Get, Post, Delete };
typedef void (WebDriverService::*CommandHandler)(RefPtr<Inspector::InspectorObject>&&, Function<void (CommandResult&&)>&&);
@@ -100,8 +102,15 @@
void executeScript(RefPtr<Inspector::InspectorObject>&&, Function<void (CommandResult&&)>&&);
void executeAsyncScript(RefPtr<Inspector::InspectorObject>&&, Function<void (CommandResult&&)>&&);
- bool parseCapabilities(Inspector::InspectorObject& desiredCapabilities, Capabilities&, Function<void (CommandResult&&)>&);
- bool platformParseCapabilities(Inspector::InspectorObject& desiredCapabilities, Capabilities&, Function<void (CommandResult&&)>&);
+ static Capabilities platformCapabilities();
+ RefPtr<Inspector::InspectorObject> processCapabilities(const Inspector::InspectorObject&, Function<void (CommandResult&&)>&) const;
+ RefPtr<Inspector::InspectorObject> validatedCapabilities(const Inspector::InspectorObject&) const;
+ RefPtr<Inspector::InspectorObject> mergeCapabilities(const Inspector::InspectorObject&, const Inspector::InspectorObject&) const;
+ std::optional<String> matchCapabilities(const Inspector::InspectorObject&) const;
+ bool platformValidateCapability(const String&, const RefPtr<Inspector::InspectorValue>&) const;
+ std::optional<String> platformMatchCapability(const String&, const RefPtr<Inspector::InspectorValue>&) const;
+ void parseCapabilities(const Inspector::InspectorObject& desiredCapabilities, Capabilities&) const;
+ void platformParseCapabilities(const Inspector::InspectorObject& desiredCapabilities, Capabilities&) const;
RefPtr<Session> findSessionOrCompleteWithError(Inspector::InspectorObject&, Function<void (CommandResult&&)>&);
void handleRequest(HTTPRequestHandler::Request&&, Function<void (HTTPRequestHandler::Response&&)>&& replyHandler) override;
Modified: trunk/Source/WebDriver/glib/SessionHostGlib.cpp (220314 => 220315)
--- trunk/Source/WebDriver/glib/SessionHostGlib.cpp 2017-08-05 08:11:11 UTC (rev 220314)
+++ trunk/Source/WebDriver/glib/SessionHostGlib.cpp 2017-08-05 09:27:15 UTC (rev 220315)
@@ -135,13 +135,14 @@
GUniquePtr<char> inspectorAddress(g_strdup_printf("127.0.0.1:%u", port));
g_subprocess_launcher_setenv(launcher.get(), "WEBKIT_INSPECTOR_SERVER", inspectorAddress.get(), TRUE);
#if PLATFORM(GTK)
- g_subprocess_launcher_setenv(launcher.get(), "GTK_OVERLAY_SCROLLING", m_capabilities.useOverlayScrollbars ? "1" : "0", TRUE);
+ g_subprocess_launcher_setenv(launcher.get(), "GTK_OVERLAY_SCROLLING", m_capabilities.useOverlayScrollbars.value() ? "1" : "0", TRUE);
#endif
- GUniquePtr<char*> args(g_new0(char*, m_capabilities.browserArguments.size() + 2));
- args.get()[0] = g_strdup(m_capabilities.browserBinary.utf8().data());
- for (unsigned i = 0; i < m_capabilities.browserArguments.size(); ++i)
- args.get()[i + 1] = g_strdup(m_capabilities.browserArguments[i].utf8().data());
+ const auto& browserArguments = m_capabilities.browserArguments.value();
+ GUniquePtr<char*> args(g_new0(char*, browserArguments.size() + 2));
+ args.get()[0] = g_strdup(m_capabilities.browserBinary.value().utf8().data());
+ for (unsigned i = 0; i < browserArguments.size(); ++i)
+ args.get()[i + 1] = g_strdup(browserArguments[i].utf8().data());
m_browser = adoptGRef(g_subprocess_launcher_spawnv(launcher.get(), args.get(), nullptr));
g_subprocess_wait_async(m_browser.get(), m_cancellable.get(), [](GObject* browser, GAsyncResult* result, gpointer userData) {
@@ -223,6 +224,7 @@
{
ASSERT(m_dbusConnection);
ASSERT(!m_startSessionCompletionHandler);
+ // FIXME: Make StartAutomationSession return browser information and we use it to match capabilities.
m_startSessionCompletionHandler = WTFMove(completionHandler);
g_dbus_connection_call(m_dbusConnection.get(), nullptr,
INSPECTOR_DBUS_OBJECT_PATH,
Modified: trunk/Source/WebDriver/gtk/WebDriverServiceGtk.cpp (220314 => 220315)
--- trunk/Source/WebDriver/gtk/WebDriverServiceGtk.cpp 2017-08-05 08:11:11 UTC (rev 220314)
+++ trunk/Source/WebDriver/gtk/WebDriverServiceGtk.cpp 2017-08-05 09:27:15 UTC (rev 220315)
@@ -34,47 +34,81 @@
namespace WebDriver {
-bool WebDriverService::platformParseCapabilities(InspectorObject& desiredCapabilities, Capabilities& capabilities, Function<void (CommandResult&&)>& completionHandler)
+Capabilities WebDriverService::platformCapabilities()
{
- RefPtr<InspectorValue> value;
+ Capabilities capabilities;
+ capabilities.platformName = String("linux");
+ capabilities.acceptInsecureCerts = false;
+ return capabilities;
+}
+
+bool WebDriverService::platformValidateCapability(const String& name, const RefPtr<InspectorValue>& value) const
+{
+ if (name != "webkitgtk:browserOptions")
+ return true;
+
RefPtr<InspectorObject> browserOptions;
- if (desiredCapabilities.getValue(ASCIILiteral("webkitgtk:browserOptions"), value) && !value->asObject(browserOptions)) {
- completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("webkitgtk:browserOptions is invalid in capabilities")));
+ if (!value->asObject(browserOptions))
return false;
- }
- if (browserOptions->isNull()) {
- capabilities.browserBinary = LIBEXECDIR "/webkit2gtk-" WEBKITGTK_API_VERSION_STRING "/MiniBrowser";
- capabilities.browserArguments = { ASCIILiteral("--automation") };
+
+ if (browserOptions->isNull())
return true;
- }
- if (!browserOptions->getString(ASCIILiteral("binary"), capabilities.browserBinary)) {
- completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("binary parameter is invalid or missing in webkitgtk:browserOptions")));
+ // If browser options are provided, binary is required.
+ String binary;
+ if (!browserOptions->getString(ASCIILiteral("binary"), binary))
return false;
- }
+ RefPtr<InspectorValue> useOverlayScrollbarsValue;
+ bool useOverlayScrollbars;
+ if (browserOptions->getValue(ASCIILiteral("useOverlayScrollbars"), useOverlayScrollbarsValue) && !useOverlayScrollbarsValue->asBoolean(useOverlayScrollbars))
+ return false;
+
+ RefPtr<InspectorValue> browserArgumentsValue;
RefPtr<InspectorArray> browserArguments;
- if (browserOptions->getValue(ASCIILiteral("args"), value) && !value->asArray(browserArguments)) {
- completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("args parameter is invalid in webkitgtk:browserOptions")));
+ if (browserOptions->getValue(ASCIILiteral("args"), browserArgumentsValue) && !browserArgumentsValue->asArray(browserArguments))
return false;
- }
+
unsigned browserArgumentsLength = browserArguments->length();
- if (!browserArgumentsLength)
- return true;
- capabilities.browserArguments.reserveInitialCapacity(browserArgumentsLength);
for (unsigned i = 0; i < browserArgumentsLength; ++i) {
RefPtr<InspectorValue> value = browserArguments->get(i);
String argument;
- if (!value->asString(argument)) {
- capabilities.browserArguments.clear();
- completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("Failed to extract arguments from webkitgtk:browserOptions::args")));
+ if (!value->asString(argument))
return false;
- }
- capabilities.browserArguments.uncheckedAppend(WTFMove(argument));
}
- if (browserOptions->getValue(ASCIILiteral("useOverlayScrollbars"), value) && !value->asBoolean(capabilities.useOverlayScrollbars)) {
- completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("useOverlayScrollbars parameter is invalid in webkitgtk:browserOptions")));
+ return true;
+}
+
+std::optional<String> WebDriverService::platformMatchCapability(const String&, const RefPtr<InspectorValue>&) const
+{
+ return std::nullopt;
+}
+
+static bool parseVersion(const String& version, uint64_t& major, uint64_t& minor, uint64_t& micro)
+{
+ major = minor = micro = 0;
+
+ Vector<String> tokens;
+ version.split(".", false, tokens);
+ bool ok;
+ switch (tokens.size()) {
+ case 3:
+ micro = tokens[2].toInt64(&ok);
+ if (!ok)
+ return false;
+ FALLTHROUGH;
+ case 2:
+ minor = tokens[1].toInt64(&ok);
+ if (!ok)
+ return false;
+ FALLTHROUGH;
+ case 1:
+ major = tokens[0].toInt64(&ok);
+ if (!ok)
+ return false;
+ break;
+ default:
return false;
}
@@ -81,4 +115,56 @@
return true;
}
+bool WebDriverService::platformCompareBrowserVersions(const String& requiredVersion, const String& proposedVersion)
+{
+ // We require clients to use format major.micro.minor as version string.
+ uint64_t requiredMajor, requiredMinor, requiredMicro;
+ if (!parseVersion(requiredVersion, requiredMajor, requiredMinor, requiredMicro))
+ return false;
+
+ uint64_t proposedMajor, proposedMinor, proposedMicro;
+ if (!parseVersion(proposedVersion, proposedMajor, proposedMinor, proposedMicro))
+ return false;
+
+ return proposedMajor > requiredMajor
+ || (proposedMajor == requiredMajor && proposedMinor > requiredMinor)
+ || (proposedMajor == requiredMajor && proposedMinor == requiredMinor && proposedMicro >= requiredMicro);
+}
+
+void WebDriverService::platformParseCapabilities(const InspectorObject& matchedCapabilities, Capabilities& capabilities) const
+{
+ RefPtr<InspectorObject> browserOptions;
+ if (!matchedCapabilities.getObject(ASCIILiteral("webkitgtk:browserOptions"), browserOptions)) {
+ capabilities.browserBinary = String(LIBEXECDIR "/webkit2gtk-" WEBKITGTK_API_VERSION_STRING "/MiniBrowser");
+ capabilities.browserArguments = Vector<String> { ASCIILiteral("--automation") };
+ capabilities.useOverlayScrollbars = true;
+ return;
+ }
+
+ String browserBinary;
+ browserOptions->getString(ASCIILiteral("binary"), browserBinary);
+ ASSERT(!browserBinary.isNull());
+ capabilities.browserBinary = browserBinary;
+
+ capabilities.browserArguments = Vector<String>();
+ RefPtr<InspectorArray> browserArguments;
+ if (browserOptions->getArray(ASCIILiteral("args"), browserArguments)) {
+ unsigned browserArgumentsLength = browserArguments->length();
+ capabilities.browserArguments->reserveInitialCapacity(browserArgumentsLength);
+ for (unsigned i = 0; i < browserArgumentsLength; ++i) {
+ RefPtr<InspectorValue> value = browserArguments->get(i);
+ String argument;
+ value->asString(argument);
+ ASSERT(!argument.isNull());
+ capabilities.browserArguments->uncheckedAppend(WTFMove(argument));
+ }
+ }
+
+ bool useOverlayScrollbars;
+ if (browserOptions->getBoolean(ASCIILiteral("useOverlayScrollbars"), useOverlayScrollbars))
+ capabilities.useOverlayScrollbars = useOverlayScrollbars;
+ else
+ capabilities.useOverlayScrollbars = true;
+}
+
} // namespace WebDriver