...22import static com.google.common.net.HttpHeaders.CONTENT_TYPE;23import static com.google.common.net.MediaType.JSON_UTF_8;24import static java.nio.charset.StandardCharsets.UTF_8;25import static org.openqa.selenium.json.Json.MAP_TYPE;26import static org.openqa.selenium.remote.DriverCommand.ADD_COOKIE;27import static org.openqa.selenium.remote.DriverCommand.CLEAR_ELEMENT;28import static org.openqa.selenium.remote.DriverCommand.CLICK_ELEMENT;29import static org.openqa.selenium.remote.DriverCommand.CLOSE;30import static org.openqa.selenium.remote.DriverCommand.DELETE_ALL_COOKIES;31import static org.openqa.selenium.remote.DriverCommand.DELETE_COOKIE;32import static org.openqa.selenium.remote.DriverCommand.ELEMENT_EQUALS;33import static org.openqa.selenium.remote.DriverCommand.ELEMENT_SCREENSHOT;34import static org.openqa.selenium.remote.DriverCommand.FIND_CHILD_ELEMENT;35import static org.openqa.selenium.remote.DriverCommand.FIND_CHILD_ELEMENTS;36import static org.openqa.selenium.remote.DriverCommand.FIND_ELEMENT;37import static org.openqa.selenium.remote.DriverCommand.FIND_ELEMENTS;38import static org.openqa.selenium.remote.DriverCommand.FULLSCREEN_CURRENT_WINDOW;39import static org.openqa.selenium.remote.DriverCommand.GET;40import static org.openqa.selenium.remote.DriverCommand.GET_ALL_COOKIES;41import static org.openqa.selenium.remote.DriverCommand.GET_ALL_SESSIONS;42import static org.openqa.selenium.remote.DriverCommand.GET_APP_CACHE_STATUS;43import static org.openqa.selenium.remote.DriverCommand.GET_AVAILABLE_LOG_TYPES;44import static org.openqa.selenium.remote.DriverCommand.GET_CAPABILITIES;45import static org.openqa.selenium.remote.DriverCommand.GET_CONTEXT_HANDLES;46import static org.openqa.selenium.remote.DriverCommand.GET_COOKIE;47import static org.openqa.selenium.remote.DriverCommand.GET_CURRENT_CONTEXT_HANDLE;48import static org.openqa.selenium.remote.DriverCommand.GET_CURRENT_URL;49import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_LOCATION;50import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_PROPERTY;51import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_RECT;52import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_SIZE;53import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_TAG_NAME;54import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_TEXT;55import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_VALUE_OF_CSS_PROPERTY;56import static org.openqa.selenium.remote.DriverCommand.GET_LOCATION;57import static org.openqa.selenium.remote.DriverCommand.GET_LOG;58import static org.openqa.selenium.remote.DriverCommand.GET_NETWORK_CONNECTION;59import static org.openqa.selenium.remote.DriverCommand.GET_SCREEN_ORIENTATION;60import static org.openqa.selenium.remote.DriverCommand.GET_SCREEN_ROTATION;61import static org.openqa.selenium.remote.DriverCommand.GET_SESSION_LOGS;62import static org.openqa.selenium.remote.DriverCommand.GET_TITLE;63import static org.openqa.selenium.remote.DriverCommand.GO_BACK;64import static org.openqa.selenium.remote.DriverCommand.GO_FORWARD;65import static org.openqa.selenium.remote.DriverCommand.IME_ACTIVATE_ENGINE;66import static org.openqa.selenium.remote.DriverCommand.IME_DEACTIVATE;67import static org.openqa.selenium.remote.DriverCommand.IME_GET_ACTIVE_ENGINE;68import static org.openqa.selenium.remote.DriverCommand.IME_GET_AVAILABLE_ENGINES;69import static org.openqa.selenium.remote.DriverCommand.IME_IS_ACTIVATED;70import static org.openqa.selenium.remote.DriverCommand.IMPLICITLY_WAIT;71import static org.openqa.selenium.remote.DriverCommand.IS_BROWSER_ONLINE;72import static org.openqa.selenium.remote.DriverCommand.IS_ELEMENT_ENABLED;73import static org.openqa.selenium.remote.DriverCommand.IS_ELEMENT_SELECTED;74import static org.openqa.selenium.remote.DriverCommand.NEW_SESSION;75import static org.openqa.selenium.remote.DriverCommand.QUIT;76import static org.openqa.selenium.remote.DriverCommand.REFRESH;77import static org.openqa.selenium.remote.DriverCommand.SCREENSHOT;78import static org.openqa.selenium.remote.DriverCommand.SEND_KEYS_TO_ELEMENT;79import static org.openqa.selenium.remote.DriverCommand.SET_ALERT_CREDENTIALS;80import static org.openqa.selenium.remote.DriverCommand.SET_BROWSER_ONLINE;81import static org.openqa.selenium.remote.DriverCommand.SET_LOCATION;82import static org.openqa.selenium.remote.DriverCommand.SET_NETWORK_CONNECTION;83import static org.openqa.selenium.remote.DriverCommand.SET_SCREEN_ORIENTATION;84import static org.openqa.selenium.remote.DriverCommand.SET_SCREEN_ROTATION;85import static org.openqa.selenium.remote.DriverCommand.SET_SCRIPT_TIMEOUT;86import static org.openqa.selenium.remote.DriverCommand.SET_TIMEOUT;87import static org.openqa.selenium.remote.DriverCommand.STATUS;88import static org.openqa.selenium.remote.DriverCommand.SWITCH_TO_CONTEXT;89import static org.openqa.selenium.remote.DriverCommand.SWITCH_TO_FRAME;90import static org.openqa.selenium.remote.DriverCommand.SWITCH_TO_PARENT_FRAME;91import static org.openqa.selenium.remote.DriverCommand.SWITCH_TO_WINDOW;92import static org.openqa.selenium.remote.DriverCommand.UPLOAD_FILE;93import static org.openqa.selenium.remote.DriverCommand.NETWORK_LOG_CAPTURE;94import static org.openqa.selenium.remote.DriverCommand.GET_MONITOR_STATS;95import com.google.common.base.Objects;96import com.google.common.base.Splitter;97import com.google.common.base.Strings;98import com.google.common.collect.ImmutableList;99import org.openqa.selenium.UnsupportedCommandException;100import org.openqa.selenium.json.Json;101import org.openqa.selenium.net.Urls;102import org.openqa.selenium.remote.Command;103import org.openqa.selenium.remote.CommandCodec;104import org.openqa.selenium.remote.SessionId;105import java.util.HashMap;106import java.util.Map;107import java.util.concurrent.ConcurrentHashMap;108/**109 * A command codec that adheres to the W3C's WebDriver wire protocol.110 *111 * @see <a href="https://w3.org/tr/webdriver">W3C WebDriver spec</a>112 */113public abstract class AbstractHttpCommandCodec implements CommandCodec<HttpRequest> {114 private static final Splitter PATH_SPLITTER = Splitter.on('/').omitEmptyStrings();115 private static final String SESSION_ID_PARAM = "sessionId";116 private final ConcurrentHashMap<String, CommandSpec> nameToSpec = new ConcurrentHashMap<>();117 private final Map<String, String> aliases = new HashMap<>();118 private final Json json = new Json();119 public AbstractHttpCommandCodec() {120 defineCommand(STATUS, get("/status"));121 defineCommand(GET_ALL_SESSIONS, get("/sessions"));122 defineCommand(NEW_SESSION, post("/session"));123 defineCommand(GET_CAPABILITIES, get("/session/:sessionId"));124 defineCommand(QUIT, delete("/session/:sessionId"));125 defineCommand(GET_SESSION_LOGS, post("/logs"));126 defineCommand(GET_LOG, post("/session/:sessionId/log"));127 defineCommand(GET_AVAILABLE_LOG_TYPES, get("/session/:sessionId/log/types"));128 defineCommand(SWITCH_TO_FRAME, post("/session/:sessionId/frame"));129 defineCommand(SWITCH_TO_PARENT_FRAME, post("/session/:sessionId/frame/parent"));130 defineCommand(CLOSE, delete("/session/:sessionId/window"));131 defineCommand(SWITCH_TO_WINDOW, post("/session/:sessionId/window"));132 defineCommand(FULLSCREEN_CURRENT_WINDOW, post("/session/:sessionId/window/fullscreen"));133 defineCommand(GET_CURRENT_URL, get("/session/:sessionId/url"));134 defineCommand(GET, post("/session/:sessionId/url"));135 defineCommand(GO_BACK, post("/session/:sessionId/back"));136 defineCommand(GO_FORWARD, post("/session/:sessionId/forward"));137 defineCommand(REFRESH, post("/session/:sessionId/refresh"));138 defineCommand(SET_ALERT_CREDENTIALS, post("/session/:sessionId/alert/credentials"));139 defineCommand(UPLOAD_FILE, post("/session/:sessionId/file"));140 defineCommand(SCREENSHOT, get("/session/:sessionId/screenshot"));141 defineCommand(ELEMENT_SCREENSHOT, get("/session/:sessionId/element/:id/screenshot"));142 defineCommand(GET_TITLE, get("/session/:sessionId/title"));143 defineCommand(FIND_ELEMENT, post("/session/:sessionId/element"));144 defineCommand(FIND_ELEMENTS, post("/session/:sessionId/elements"));145 defineCommand(GET_ELEMENT_PROPERTY, get("/session/:sessionId/element/:id/property/:name"));146 defineCommand(CLICK_ELEMENT, post("/session/:sessionId/element/:id/click"));147 defineCommand(CLEAR_ELEMENT, post("/session/:sessionId/element/:id/clear"));148 defineCommand(149 GET_ELEMENT_VALUE_OF_CSS_PROPERTY,150 get("/session/:sessionId/element/:id/css/:propertyName"));151 defineCommand(FIND_CHILD_ELEMENT, post("/session/:sessionId/element/:id/element"));152 defineCommand(FIND_CHILD_ELEMENTS, post("/session/:sessionId/element/:id/elements"));153 defineCommand(IS_ELEMENT_ENABLED, get("/session/:sessionId/element/:id/enabled"));154 defineCommand(ELEMENT_EQUALS, get("/session/:sessionId/element/:id/equals/:other"));155 defineCommand(GET_ELEMENT_RECT, get("/session/:sessionId/element/:id/rect"));156 defineCommand(GET_ELEMENT_LOCATION, get("/session/:sessionId/element/:id/location"));157 defineCommand(GET_ELEMENT_TAG_NAME, get("/session/:sessionId/element/:id/name"));158 defineCommand(IS_ELEMENT_SELECTED, get("/session/:sessionId/element/:id/selected"));159 defineCommand(GET_ELEMENT_SIZE, get("/session/:sessionId/element/:id/size"));160 defineCommand(GET_ELEMENT_TEXT, get("/session/:sessionId/element/:id/text"));161 defineCommand(SEND_KEYS_TO_ELEMENT, post("/session/:sessionId/element/:id/value"));162 defineCommand(GET_ALL_COOKIES, get("/session/:sessionId/cookie"));163 defineCommand(GET_COOKIE, get("/session/:sessionId/cookie/:name"));164 defineCommand(ADD_COOKIE, post("/session/:sessionId/cookie"));165 defineCommand(DELETE_ALL_COOKIES, delete("/session/:sessionId/cookie"));166 defineCommand(DELETE_COOKIE, delete("/session/:sessionId/cookie/:name"));167 defineCommand(SET_TIMEOUT, post("/session/:sessionId/timeouts"));168 defineCommand(SET_SCRIPT_TIMEOUT, post("/session/:sessionId/timeouts/async_script"));169 defineCommand(IMPLICITLY_WAIT, post("/session/:sessionId/timeouts/implicit_wait"));170 defineCommand(GET_APP_CACHE_STATUS, get("/session/:sessionId/application_cache/status"));171 defineCommand(IS_BROWSER_ONLINE, get("/session/:sessionId/browser_connection"));172 defineCommand(SET_BROWSER_ONLINE, post("/session/:sessionId/browser_connection"));173 defineCommand(GET_LOCATION, get("/session/:sessionId/location"));174 defineCommand(SET_LOCATION, post("/session/:sessionId/location"));175 defineCommand(GET_SCREEN_ORIENTATION, get("/session/:sessionId/orientation"));176 defineCommand(SET_SCREEN_ORIENTATION, post("/session/:sessionId/orientation"));177 defineCommand(GET_SCREEN_ROTATION, get("/session/:sessionId/rotation"));178 defineCommand(SET_SCREEN_ROTATION, post("/session/:sessionId/rotation"));179 defineCommand(IME_GET_AVAILABLE_ENGINES, get("/session/:sessionId/ime/available_engines"));180 defineCommand(IME_GET_ACTIVE_ENGINE, get("/session/:sessionId/ime/active_engine"));181 defineCommand(IME_IS_ACTIVATED, get("/session/:sessionId/ime/activated"));182 defineCommand(IME_DEACTIVATE, post("/session/:sessionId/ime/deactivate"));183 defineCommand(IME_ACTIVATE_ENGINE, post("/session/:sessionId/ime/activate"));184 // Mobile Spec185 defineCommand(GET_NETWORK_CONNECTION, get("/session/:sessionId/network_connection"));186 defineCommand(SET_NETWORK_CONNECTION, post("/session/:sessionId/network_connection"));187 defineCommand(SWITCH_TO_CONTEXT, post("/session/:sessionId/context"));188 defineCommand(GET_CURRENT_CONTEXT_HANDLE, get("/session/:sessionId/context"));189 defineCommand(GET_CONTEXT_HANDLES, get("/session/:sessionId/contexts"));190 defineCommand(NETWORK_LOG_CAPTURE, post("/session/:sessionId/networklog"));191 defineCommand(GET_MONITOR_STATS, post("/session/:sessionId/monitorstats"));192 }193 @Override194 public HttpRequest encode(Command command) {195 String name = aliases.getOrDefault(command.getName(), command.getName());196 //System.out.println(" [DEBUG] "+String.format("name = %s , Command Name = %s ",name,command.getName()));197 CommandSpec spec = nameToSpec.get(name);198 //System.out.println(" [DEBUG] "+String.format("spec = %s ",spec));199 if (spec == null) {200 throw new UnsupportedCommandException(command.getName());201 }202 Map<String, ?> parameters = amendParameters(command.getName(), command.getParameters());203 String uri = buildUri(name, command.getSessionId(), parameters, spec);204 HttpRequest request = new HttpRequest(spec.method, uri);205 if (HttpMethod.POST == spec.method) {206 String content = json.toJson(parameters);207 byte[] data = content.getBytes(UTF_8);208 request.setHeader(CONTENT_LENGTH, String.valueOf(data.length));209 request.setHeader(CONTENT_TYPE, JSON_UTF_8.toString());210 request.setContent(data);211 }212 if (HttpMethod.GET == spec.method) {213 request.setHeader(CACHE_CONTROL, "no-cache");214 }215 return request;216 }217 protected abstract Map<String,?> amendParameters(String name, Map<String, ?> parameters);218 @Override219 public Command decode(final HttpRequest encodedCommand) {220 final String path = Strings.isNullOrEmpty(encodedCommand.getUri())221 ? "/" : encodedCommand.getUri();222 final ImmutableList<String> parts = ImmutableList.copyOf(PATH_SPLITTER.split(path));223 int minPathLength = Integer.MAX_VALUE;224 CommandSpec spec = null;225 String name = null;226 for (Map.Entry<String, CommandSpec> nameValue : nameToSpec.entrySet()) {227 if ((nameValue.getValue().pathSegments.size() < minPathLength)228 && nameValue.getValue().isFor(encodedCommand.getMethod(), parts)) {229 name = nameValue.getKey();230 spec = nameValue.getValue();231 }232 }233 if (name == null) {234 throw new UnsupportedCommandException(235 encodedCommand.getMethod() + " " + encodedCommand.getUri());236 }237 Map<String, Object> parameters = new HashMap<>();238 spec.parsePathParameters(parts, parameters);239 String content = encodedCommand.getContentString();240 if (!content.isEmpty()) {241 @SuppressWarnings("unchecked")242 Map<String, Object> tmp = json.toType(content, MAP_TYPE);243 parameters.putAll(tmp);244 }245 SessionId sessionId = null;246 if (parameters.containsKey(SESSION_ID_PARAM)) {247 String id = (String) parameters.remove(SESSION_ID_PARAM);248 if (id != null) {249 sessionId = new SessionId(id);250 }251 }252 return new Command(sessionId, name, parameters);253 }254 /**255 * Defines a new command mapping.256 *257 * @param name The command name.258 * @param method The HTTP method to use for the command.259 * @param pathPattern The URI path pattern for the command. When encoding a command, each260 * path segment prefixed with a ":" will be replaced with the corresponding parameter261 * from the encoded command.262 */263 public void defineCommand(String name, HttpMethod method, String pathPattern) {264 defineCommand(name, new CommandSpec(method, pathPattern));265 }266 @Override267 public void alias(String commandName, String isAnAliasFor) {268 aliases.put(commandName, isAnAliasFor);269 }270 protected void defineCommand(String name, CommandSpec spec) {271 //System.out.println("[ DEBUG ] Command Spec Initiated for String Name ="+name);272 checkNotNull(name, "null name");273 //System.out.println("[ DEBUG ] Command Spec = "+spec+" String Name ="+name);274 nameToSpec.put(name, spec);275 }276 protected static CommandSpec delete(String path) {277 return new CommandSpec(HttpMethod.DELETE, path);278 }279 protected static CommandSpec get(String path) {280 return new CommandSpec(HttpMethod.GET, path);281 }282 protected static CommandSpec post(String path) {283 return new CommandSpec(HttpMethod.POST, path);284 }285 private String buildUri(286 String commandName,287 SessionId sessionId,288 Map<String, ?> parameters,289 CommandSpec spec) {290 StringBuilder builder = new StringBuilder();291 //System.out.println("[DEBUG] Path segments "+spec.pathSegments);292 for (String part : spec.pathSegments) {293 if (part.isEmpty()) {294 continue;295 }296 builder.append("/");297 if (part.startsWith(":")) {298 builder.append(getParameter(part.substring(1), commandName, sessionId, parameters));299 } else {300 builder.append(part);301 }302 }303 return builder.toString();304 }305 private String getParameter(306 String parameterName,307 String commandName,308 SessionId sessionId,309 Map<String, ?> parameters) {310 if ("sessionId".equals(parameterName)) {311 SessionId id = sessionId;312 checkArgument(id != null, "Session ID may not be null for command %s", commandName);313 return id.toString();314 }315 Object value = parameters.get(parameterName);316 checkArgument(value != null,317 "Missing required parameter \"%s\" for command %s", parameterName, commandName);318 return Urls.urlEncode(String.valueOf(value));319 }320 protected static class CommandSpec {321 private final HttpMethod method;322 private final String path;323 private final ImmutableList<String> pathSegments;324 private CommandSpec(HttpMethod method, String path) {325 this.method = checkNotNull(method, "null method");326 this.path = path;327 this.pathSegments = ImmutableList.copyOf(PATH_SPLITTER.split(path));328 }329 @Override330 public boolean equals(Object o) {331 if (o instanceof CommandSpec) {332 CommandSpec that = (CommandSpec) o;333 return this.method.equals(that.method)334 && this.path.equals(that.path);335 }336 return false;337 }338 @Override339 public int hashCode() {340 return Objects.hashCode(method, path);341 }342 /**343 * Returns whether this instance matches the provided HTTP request.344 *345 * @param method The request method.346 * @param parts The parsed request path segments....