org.openqa.selenium.remote.http.W3CHttpCommandCodec.java Source code

Java tutorial

Introduction

Here is the source code for org.openqa.selenium.remote.http.W3CHttpCommandCodec.java

Source

// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

package org.openqa.selenium.remote.http;

import static org.openqa.selenium.remote.DriverCommand.ACCEPT_ALERT;
import static org.openqa.selenium.remote.DriverCommand.DISMISS_ALERT;
import static org.openqa.selenium.remote.DriverCommand.EXECUTE_ASYNC_SCRIPT;
import static org.openqa.selenium.remote.DriverCommand.EXECUTE_SCRIPT;
import static org.openqa.selenium.remote.DriverCommand.FIND_CHILD_ELEMENT;
import static org.openqa.selenium.remote.DriverCommand.FIND_CHILD_ELEMENTS;
import static org.openqa.selenium.remote.DriverCommand.FIND_ELEMENT;
import static org.openqa.selenium.remote.DriverCommand.FIND_ELEMENTS;
import static org.openqa.selenium.remote.DriverCommand.GET_ALERT_TEXT;
import static org.openqa.selenium.remote.DriverCommand.GET_CURRENT_WINDOW_HANDLE;
import static org.openqa.selenium.remote.DriverCommand.GET_CURRENT_WINDOW_POSITION;
import static org.openqa.selenium.remote.DriverCommand.GET_CURRENT_WINDOW_SIZE;
import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_ATTRIBUTE;
import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_LOCATION;
import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_LOCATION_ONCE_SCROLLED_INTO_VIEW;
import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_RECT;
import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_SIZE;
import static org.openqa.selenium.remote.DriverCommand.GET_PAGE_SOURCE;
import static org.openqa.selenium.remote.DriverCommand.GET_WINDOW_HANDLES;
import static org.openqa.selenium.remote.DriverCommand.IS_ELEMENT_DISPLAYED;
import static org.openqa.selenium.remote.DriverCommand.MAXIMIZE_CURRENT_WINDOW;
import static org.openqa.selenium.remote.DriverCommand.SET_ALERT_VALUE;
import static org.openqa.selenium.remote.DriverCommand.SET_CURRENT_WINDOW_POSITION;
import static org.openqa.selenium.remote.DriverCommand.SET_CURRENT_WINDOW_SIZE;
import static org.openqa.selenium.remote.DriverCommand.SUBMIT_ELEMENT;

import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.io.Resources;

import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.remote.internal.WebElementToJsonConverter;

import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * A command codec that adheres to the W3C's WebDriver wire protocol.
 *
 * @see <a href="https://w3.org/tr/webdriver">W3C WebDriver spec</a>
 */
public class W3CHttpCommandCodec extends AbstractHttpCommandCodec {

    public W3CHttpCommandCodec() {
        alias(GET_ELEMENT_ATTRIBUTE, EXECUTE_SCRIPT);
        alias(GET_ELEMENT_LOCATION, GET_ELEMENT_RECT);
        alias(GET_ELEMENT_LOCATION_ONCE_SCROLLED_INTO_VIEW, EXECUTE_SCRIPT);
        alias(GET_ELEMENT_SIZE, GET_ELEMENT_RECT);
        alias(IS_ELEMENT_DISPLAYED, EXECUTE_SCRIPT);
        alias(SUBMIT_ELEMENT, EXECUTE_SCRIPT);

        defineCommand(EXECUTE_SCRIPT, post("/session/:sessionId/execute/sync"));
        defineCommand(EXECUTE_ASYNC_SCRIPT, post("/session/:sessionId/execute/async"));

        alias(GET_PAGE_SOURCE, EXECUTE_SCRIPT);

        defineCommand(MAXIMIZE_CURRENT_WINDOW, post("/session/:sessionId/window/maximize"));
        alias(GET_CURRENT_WINDOW_POSITION, EXECUTE_SCRIPT);
        alias(SET_CURRENT_WINDOW_POSITION, EXECUTE_SCRIPT);
        defineCommand(GET_CURRENT_WINDOW_SIZE, get("/session/:sessionId/window/size"));
        defineCommand(SET_CURRENT_WINDOW_SIZE, post("/session/:sessionId/window/size"));
        defineCommand(GET_CURRENT_WINDOW_HANDLE, get("/session/:sessionId/window"));
        defineCommand(GET_WINDOW_HANDLES, get("/session/:sessionId/window/handles"));

        defineCommand(ACCEPT_ALERT, post("/session/:sessionId/alert/accept"));
        defineCommand(DISMISS_ALERT, post("/session/:sessionId/alert/dismiss"));
        defineCommand(GET_ALERT_TEXT, get("/session/:sessionId/alert/text"));
        defineCommand(SET_ALERT_VALUE, post("/session/:sessionId/alert/text"));
    }

    @Override
    protected Map<String, ?> amendParameters(String name, Map<String, ?> parameters) {
        switch (name) {
        case FIND_CHILD_ELEMENT:
        case FIND_CHILD_ELEMENTS:
        case FIND_ELEMENT:
        case FIND_ELEMENTS:
            String using = (String) parameters.get("using");
            String value = (String) parameters.get("value");

            Map<String, Object> toReturn = new HashMap<>();
            toReturn.putAll(parameters);

            switch (using) {
            case "class name":
                toReturn.put("using", "css selector");
                toReturn.put("value", "." + cssEscape(value));
                break;

            case "id":
                toReturn.put("using", "css selector");
                toReturn.put("value", "#" + cssEscape(value));
                break;

            case "link text":
                // Do nothing
                break;

            case "name":
                toReturn.put("using", "css selector");
                toReturn.put("value", "*[name='" + value + "']");
                break;

            case "partial link text":
                // Do nothing
                break;

            case "tag name":
                toReturn.put("using", "css selector");
                toReturn.put("value", "#" + cssEscape(value));
                break;

            case "xpath":
                // Do nothing
                break;
            }
            return toReturn;

        case GET_ELEMENT_ATTRIBUTE:
            // Read the atom, wrap it, execute it.
            return executeAtom("getAttribute.js", asElement(parameters.get("id")), parameters.get("name"));

        case GET_ELEMENT_LOCATION_ONCE_SCROLLED_INTO_VIEW:
            return toScript("return arguments[0].getBoundingClientRect()", asElement(parameters.get("id")));

        case GET_PAGE_SOURCE:
            return toScript("var source = document.documentElement.outerHTML; \n"
                    + "if (!source) { source = new XMLSerializer().serializeToString(document); }\n"
                    + "return source;");

        case GET_CURRENT_WINDOW_POSITION:
            return toScript("return {x: window.screenX, y: window.screenY}");

        case IS_ELEMENT_DISPLAYED:
            return executeAtom("isDisplayed.js", asElement(parameters.get("id")));

        case SET_CURRENT_WINDOW_POSITION:
            return toScript("window.screenX = arguments[0]; window.screenY = arguments[1]", parameters.get("x"),
                    parameters.get("y"));

        case SUBMIT_ELEMENT:
            return toScript("var form = arguments[0];\n"
                    + "while (form.nodeName != \"FORM\" && form.parentNode) {\n" + "  form = form.parentNode;\n"
                    + "}\n" + "if (form == null) { throw Error('Unable to find containing form element'); }\n"
                    + "var e = form.ownerDocument.createEvent('Event');\n" + "e.initEvent('submit', true, true);\n"
                    + "if (form.dispatchEvent(e)) { form.submit() }\n", asElement(parameters.get("id")));

        default:
            return parameters;
        }
    }

    private Map<String, ?> executeAtom(String atomFileName, Object... args) {
        try {
            String scriptName = "/org/openqa/selenium/remote/" + atomFileName;
            URL url = getClass().getResource(scriptName);

            String rawFunction = Resources.toString(url, Charsets.UTF_8);
            String script = String.format("return (%s).apply(null, arguments);", rawFunction);
            return toScript(script, args);
        } catch (IOException | NullPointerException e) {
            throw new WebDriverException(e);
        }
    }

    private Map<String, ?> toScript(String script, Object... args) {
        // Escape the quote marks
        script = script.replaceAll("\"", "\\\"");

        Iterable<Object> convertedArgs = Iterables.transform(Lists.newArrayList(args),
                new WebElementToJsonConverter());

        return ImmutableMap.of("script", script, "args", Lists.newArrayList(convertedArgs));
    }

    private Map<String, String> asElement(Object id) {
        return ImmutableMap.of("element-6066-11e4-a52e-4f735466cecf", (String) id);
    }

    private String cssEscape(String using) {
        using = using.replaceAll("(['\"\\\\#.:;,!?+<>=~*^$|%&@`{}\\-\\/\\[\\]\\(\\)])", "\\\\$1");
        if (using.length() > 0 && Character.isDigit(using.charAt(0))) {
            using = "\\" + Integer.toString(30 + Integer.parseInt(using.substring(0, 1))) + " "
                    + using.substring(1);
        }
        return using;
    }
}