...30import org.openqa.selenium.remote.Command;31import org.openqa.selenium.remote.CommandCodec;32import org.openqa.selenium.remote.CommandExecutor;33import org.openqa.selenium.remote.DriverCommand;34import org.openqa.selenium.remote.HttpSessionId;35import org.openqa.selenium.remote.RemoteWebDriver;36import org.openqa.selenium.remote.RemoteWebElement;37import org.openqa.selenium.remote.Response;38import org.openqa.selenium.remote.ResponseCodec;39import org.openqa.selenium.remote.codec.w3c.W3CHttpCommandCodec;40import org.openqa.selenium.remote.codec.w3c.W3CHttpResponseCodec;41import org.openqa.selenium.remote.http.AddSeleniumUserAgent;42import org.openqa.selenium.remote.http.Contents;43import org.openqa.selenium.remote.http.HttpHandler;44import org.openqa.selenium.remote.http.HttpMethod;45import org.openqa.selenium.remote.http.HttpRequest;46import org.openqa.selenium.remote.http.HttpResponse;47import org.openqa.selenium.remote.http.Routable;48import org.openqa.selenium.remote.http.UrlTemplate;49import org.openqa.selenium.remote.locators.CustomLocator;50import java.io.IOException;51import java.io.UncheckedIOException;52import java.util.Map;53import java.util.Set;54import java.util.function.Function;55import java.util.stream.Collectors;56import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;57import static org.openqa.selenium.json.Json.MAP_TYPE;58class CustomLocatorHandler implements Routable {59 private static final Json JSON = new Json();60 private static final UrlTemplate FIND_ELEMENT = new UrlTemplate("/session/{sessionId}/element");61 private static final UrlTemplate FIND_ELEMENTS = new UrlTemplate("/session/{sessionId}/elements");62 private static final UrlTemplate FIND_CHILD_ELEMENT = new UrlTemplate("/session/{sessionId}/element/{elementId}/element");63 private static final UrlTemplate FIND_CHILD_ELEMENTS = new UrlTemplate("/session/{sessionId}/element/{elementId}/elements");64 private final HttpHandler toNode;65 private final Map<String, Function<Object, By>> extraLocators;66 // These are derived from the w3c webdriver spec67 private static final Set<String> W3C_STRATEGIES = ImmutableSet.of(68 "css selector",69 "link text",70 "partial link text",71 "tag name",72 "xpath");73 @VisibleForTesting74 CustomLocatorHandler(Node node, Secret registrationSecret, Set<CustomLocator> extraLocators) {75 Require.nonNull("Node", node);76 Require.nonNull("Registration secret", registrationSecret);77 Require.nonNull("Extra locators", extraLocators);78 HttpHandler nodeHandler = node::executeWebDriverCommand;79 this.toNode = nodeHandler.with(new AddSeleniumUserAgent())80 .with(new AddWebDriverSpecHeaders())81 .with(new AddSecretFilter(registrationSecret));82 this.extraLocators = extraLocators.stream()83 .collect(Collectors.toMap(CustomLocator::getLocatorName, locator -> locator::createBy));84 }85 @Override86 public boolean matches(HttpRequest req) {87 if (req.getMethod() != HttpMethod.POST) {88 return false;89 }90 return FIND_ELEMENT.match(req.getUri()) != null ||91 FIND_ELEMENTS.match(req.getUri()) != null ||92 FIND_CHILD_ELEMENT.match(req.getUri()) != null ||93 FIND_CHILD_ELEMENTS.match(req.getUri()) != null;94 }95 @Override96 public HttpResponse execute(HttpRequest req) throws UncheckedIOException {97 String originalContents = Contents.string(req);98 // There has to be a nicer way of doing this.99 Map<String, Object> contents = JSON.toType(originalContents, MAP_TYPE);100 Object using = contents.get("using");101 if (!(using instanceof String)) {102 return new HttpResponse()103 .setStatus(HTTP_BAD_REQUEST)104 .setContent(Contents.asJson(ImmutableMap.of(105 "value", ImmutableMap.of(106 "error", "invalid argument",107 "message", "Unable to determine element locating strategy",108 "stacktrace", ""))));109 }110 if (W3C_STRATEGIES.contains(using)) {111 // TODO: recreate the original request112 return toNode.execute(req);113 }114 Object value = contents.get("value");115 if (value == null) {116 return new HttpResponse()117 .setStatus(HTTP_BAD_REQUEST)118 .setContent(Contents.asJson(ImmutableMap.of(119 "value", ImmutableMap.of(120 "error", "invalid argument",121 "message", "Unable to determine element locator arguments",122 "stacktrace", ""))));123 }124 Function<Object, By> customLocator = extraLocators.get(using);125 if (customLocator == null) {126 return new HttpResponse()127 .setStatus(HTTP_BAD_REQUEST)128 .setContent(Contents.asJson(ImmutableMap.of(129 "value", ImmutableMap.of(130 "error", "invalid argument",131 "message", "Unable to determine element locating strategy for " + using,132 "stacktrace", ""))));133 }134 CommandExecutor executor = new NodeWrappingExecutor(toNode);135 RemoteWebDriver driver = new CustomWebDriver(136 executor,137 HttpSessionId.getSessionId(req.getUri())138 .orElseThrow(() -> new IllegalArgumentException("Cannot locate session ID from " + req.getUri())));139 SearchContext context = null;140 RemoteWebElement element;141 boolean findMultiple = false;142 UrlTemplate.Match match = FIND_ELEMENT.match(req.getUri());143 if (match != null) {144 element = new RemoteWebElement();145 element.setParent(driver);146 element.setId(match.getParameters().get("elementId"));147 context = driver;148 }149 match = FIND_ELEMENTS.match(req.getUri());150 if (match != null) {151 element = new RemoteWebElement();...