Package org.uiautomation.ios.wkrdp

Source Code of org.uiautomation.ios.wkrdp.BaseWebInspector

/*
* Copyright 2012-2013 eBay Software Foundation and ios-driver committers
*
* Licensed 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.uiautomation.ios.wkrdp;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;
import org.uiautomation.ios.communication.WebDriverLikeCommand;
import org.uiautomation.ios.command.uiautomation.SetScriptTimeoutNHandler;
import org.uiautomation.ios.ServerSideSession;
import org.uiautomation.ios.wkrdp.command.DOM;
import org.uiautomation.ios.wkrdp.command.Page;
import org.uiautomation.ios.wkrdp.events.ChildNodeRemoved;
import org.uiautomation.ios.wkrdp.events.Event;
import org.uiautomation.ios.wkrdp.events.EventFactory;
import org.uiautomation.ios.wkrdp.events.inserted.ChildIframeInserted;
import org.uiautomation.ios.wkrdp.internal.IosAtoms;
import org.uiautomation.ios.wkrdp.message.ApplicationDataMessage;
import org.uiautomation.ios.wkrdp.message.ApplicationSentListingMessage;
import org.uiautomation.ios.wkrdp.message.IOSMessage;
import org.uiautomation.ios.wkrdp.model.NodeId;
import org.uiautomation.ios.wkrdp.model.RemoteObject;
import org.uiautomation.ios.wkrdp.model.RemoteObjectArray;
import org.uiautomation.ios.wkrdp.model.RemoteWebElement;

import sun.net.dns.ResolverConfiguration;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

public abstract class BaseWebInspector implements MessageListener, ConnectListener {

  private static final Logger log = Logger.getLogger(BaseWebInspector.class.getName());
  protected final ServerSideSession session;
  private boolean newPage = true;
  public static final Long defaultPageLoadTimeoutInMs = 60000L;
  private final DOMContext context;

  protected BaseWebInspector(ServerSideSession session) {
    this.session = session;
    this.context = new DOMContext(this);
  }

  public abstract JSONObject sendCommand(JSONObject command);


  public abstract int getPageIdentifier();

  public RemoteWebElement getDocument() {
    long deadline = System.currentTimeMillis() + defaultPageLoadTimeoutInMs;
    return getDocument(deadline);
  }

  public RemoteWebElement getDocument(long deadline) {
    RemoteWebElement result = context.getDocument();
    if (result == null) {
      result = retrieveDocumentAndCheckReady(deadline);
      RemoteWebElement window = getMainWindow();
      context.setCurrentFrame(null, result, window);
    }
    return result;
  }


  public RemoteWebElement getMainWindow() {
    return new RemoteWebElement(new NodeId(0), this);
  }


  private RemoteWebElement retrieveDocumentAndCheckReady(long deadline) {
    RemoteWebElement element = null;
    String readyState = "";
    while (!readyState.equals("complete")) {
      if (deadline > 0 && System.currentTimeMillis() > deadline) {
        throw new TimeoutException("Timeout waiting to get the document.");
      }
      try {
        log.fine("trying to get the document");
        element = retrieveDocument();
        log.fine("got it");
        readyState = element.getRemoteObject().call(".readyState");
        log.fine("ready ? " + readyState);
      } catch (Exception e) {
        log.warning("Exception waiting for ready state, nodeId=" +
            ((element != null) ? element.getNodeId() : "null") + ": " + e +
            ". Retrying ...");
        throw new WebKitSeemsCorruptedException();
      }
    }
    return element;
  }


  private RemoteWebElement retrieveDocument() throws Exception {
    JSONObject result = sendCommand(DOM.getDocument());
    JSONObject root = result.getJSONObject("root");
    RemoteWebElement rme = new RemoteWebElement(new NodeId(root.getInt("nodeId")), this);
    return rme;
  }


  public void get(String url) {
    try {
      context.eventsLock().lock();
      JSONObject command = Page.navigate(url);
      sendCommand(command);
      context.newContext();
      checkForPageLoad();
      context.waitForLoadEvent();
      // wait for everything to be ready by fetching the doc.
      getDocument();
    } finally {
      context.eventsLock().unlock();
    }
  }


  public String getCurrentUrl() {
    long deadline = System.currentTimeMillis() + getTimeout();
    while (System.currentTimeMillis() < deadline) {
      try {
        return getCurrentUrlOnce();
      } catch (RemoteExceptionException e) {
        if (!e.getMessage().contains("Inspected frame has gone")) {
          throw e;
        }
        log.severe("current url not ready :" + e.getMessage());
      }
    }
    throw new WebDriverException("timeout waiting for the URL to be accessible.");
  }

  private String getCurrentUrlOnce() {

    try {
      RemoteWebElement document = getDocument();
      String f = "(function(arg) { var url=this.URL;return url;})";
      JSONObject cmd = new JSONObject();

      cmd.put("method", "Runtime.callFunctionOn");

      JSONArray args = new JSONArray();

      cmd.put("params", new JSONObject().put("objectId", document.getRemoteObject().getId())
          .put("functionDeclaration", f).put("arguments", args).put("returnByValue", true));

      JSONObject response = sendCommand(cmd);
      return cast(response);
    } catch (JSONException e) {
      throw new WebDriverException(e);
    }


  }


  public String getTitle() {
    try {
      JSONObject cmd = new JSONObject();
      cmd.put("method", "Runtime.evaluate");
      cmd.put("params",
              new JSONObject().put("expression", "document.title;").put("returnByValue", true));
      JSONObject response = sendCommand(cmd);
      return cast(response);
    } catch (JSONException e) {
      throw new WebDriverException(e);
    }
    /*String title = (String) executeScript("var state = document.title; return state",new JSONArray());
    return title;*/
  }


  public List<WebElement> findElements(By by) {
    return null//To change body of implemented methods use File | Settings | File Templates.
  }


  public WebElement findElement(By by) {
    return null//To change body of implemented methods use File | Settings | File Templates.
  }


  public String getPageSource() {
    try {
      JSONObject cmd = new JSONObject();
      cmd.put("method", "Runtime.evaluate");
      cmd.put("params", new JSONObject()
          .put("expression", "new window.XMLSerializer().serializeToString(document);")
          .put("returnByValue", true));
      JSONObject response = sendCommand(cmd);
      return cast(response);
    } catch (Exception e) {
      throw new WebDriverException(e);
    }

  }


  public void close() {
    //To change body of implemented methods use File | Settings | File Templates.
  }


  public void quit() {
    //To change body of implemented methods use File | Settings | File Templates.
  }


  public WebDriver.TargetLocator switchTo() {
    return null//To change body of implemented methods use File | Settings | File Templates.
  }


  public WebDriver.Navigation navigate() {
    return null//To change body of implemented methods use File | Settings | File Templates.
  }


  public ResolverConfiguration.Options manage() {
    return null//To change body of implemented methods use File | Settings | File Templates.
  }

  // TODO freynaud fix the element swapping.
  public Object executeScript(String script, JSONArray args) {

    try {
      JSONArray arguments = processScriptArguments(args);
      JSONObject response = getScriptResponse(script, arguments);
      Object o = cast(response);
      return o;
    } catch (JSONException e) {
      throw new WebDriverException(e);
    }

  }

  public JSONObject getScriptResponse(String script) throws JSONException {
    return getScriptResponse(script, new JSONArray());
  }

  public JSONObject getScriptResponse(String script, JSONArray arguments) throws JSONException {
    RemoteWebElement document = getDocument();

    if (!context.isOnMainFrame()) {
      arguments.put(new JSONObject().put("objectId", document.getRemoteObject().getId()));
      arguments.put(new JSONObject().put("objectId", context.getWindow().getRemoteObject().getId()));

      String contextObject = "{'document': arguments[" + (arguments.length() - 2) + "], 'window': arguments[" + (arguments.length() - 1) + "]}";
      script = "with (" + contextObject + ") {" + script + "}";

    }

    JSONObject cmd = new JSONObject();
    cmd.put("method", "Runtime.callFunctionOn");
    cmd.put(
        "params",
        new JSONObject().put("objectId", document.getRemoteObject().getId())
            .put("functionDeclaration", "(function() { " + script + "})")
            .put("arguments", arguments)
            .put("returnByValue", false));
    JSONObject response = sendCommand(cmd);
    checkForJSErrors(response);
    return response;
  }

  public void checkForJSErrors(JSONObject response) throws JSONException {
    if (response.optBoolean("wasThrown")) {
      JSONObject details = response.getJSONObject("result");
      String desc = details.optString("description");
      throw new WebDriverException("JS error :" + desc);
    }
  }

  public JSONArray processScriptArguments(JSONArray args) throws JSONException {
    JSONArray arguments = new JSONArray();
    int nbParam = args.length();
    for (int i = 0; i < nbParam; i++) {
      Object arg = args.get(i);
      if (arg instanceof JSONObject) {
        JSONObject jsonArg = (JSONObject) arg;
        if (jsonArg.optString("ELEMENT") != null) {
          // TODO use driver factory to check the  pageId
          NodeId n = new NodeId(Integer.parseInt(jsonArg.optString("ELEMENT").split("_")[1]));
          RemoteWebElement rwep = new RemoteWebElement(n, this);
          arguments.put(new JSONObject().put("objectId", rwep.getRemoteObject().getId()));
        }
      } else if (arg instanceof JSONArray) {
        JSONArray jsonArr = (JSONArray) arg;
        JSONObject array = getScriptResponse("return " + jsonArr.toString() + ";");

        arguments.put(new JSONObject().put("objectId", getResponseBody(array).getString("objectId")));
      } else {
        arguments.put(new JSONObject().put("value", arg));
      }

    }

    return arguments;
  }

  public JSONObject getResponseBody(JSONObject response) {
    JSONObject body = null;
    try {
      body = response.has("result") ? response.getJSONObject("result")
                                    : response.getJSONObject("object");
    } catch (JSONException e) {
      throw new WebDriverException(e);
    }

    if (body == null) {
      throw new RuntimeException("Error parsting " + response);
    }

    return body;
  }

  public <T> T cast(JSONObject response) {
    JSONObject body = getResponseBody(response);

    try {
      return cast_(body);
    } catch (JSONException e) {
      throw new WebDriverException(e);
    }


  }

  private <T> T cast_(JSONObject body) throws JSONException {
    List<String> primitives = new ArrayList<String>();
    primitives.add("boolean");
    primitives.add("number");
    primitives.add("string");

    String type = body.getString("type");
    // handle null return
    if ("undefined".equals(type)) {
      return (T) null;
    }

    // handle primitive types.
    if (primitives.contains(type)) { // primitive type.
      Object value = body.get("value");
      return (T) value;
    }

    // handle objects
    if ("object".equals(type)) {
      if (body.has("value") && body.isNull("value")) {
        return (T) null;
      }

      if ("array".equals(body.optString("subtype"))) {
        RemoteObject array = new RemoteObject(body.getString("objectId"), this);
        RemoteObjectArray a = new RemoteObjectArray(array);
        ArrayList<Object> res = new ArrayList<Object>();
        for (Object ro : a) {
          res.add(ro);
        }
        return (T) res;
      }

      if (body.has("objectId")) {
        if ("node".equals(body.optString("subtype")) || "Window"
            .equals(body.optString("className"))) {
          return (T) new RemoteObject(body.getString("objectId"), this);
        } else {
          RemoteObject ro = new RemoteObject(body.getString("objectId"), this);
          JSONObject o = new JSONObject(ro.stringify());
          return (T) o;
        }

      }
      return (T) new RemoteObject(body.getString("objectId"), this);

    }
    throw new RuntimeException("NI " + body);
  }


  public Object executeAsyncScript(String script, JSONArray args) {
    try {

      // These are arrays so they can be passed back and forth as references with objectIds
      // The relevant information is always going to be on index 0
      String resultReadyObjectId = getResponseBody(getScriptResponse("return [];")).getString("objectId");
      String resultObjectId = getResponseBody(getScriptResponse("return [];")).getString("objectId");

      JSONArray arguments = processScriptArguments(args);
      Boolean realResultFound = false;
      Object realResult = null;
      long whenToTimeout = System.currentTimeMillis() + SetScriptTimeoutNHandler.TIMEOUT;

      JSONObject callbackFunction = getScriptResponse(
        "var async_results = arguments[0]," +
        "    async_results_ready = arguments[1];" +
        "return function(result) {" +
        " async_results_ready[0] = true;" +
        " async_results[0] = result;" +
        "};",
      new JSONArray()
        .put(new JSONObject().put("objectId", resultObjectId))
        .put(new JSONObject().put("objectId", resultReadyObjectId)));

      arguments.put(new JSONObject().put("objectId", getResponseBody(callbackFunction).getString("objectId")));
      getScriptResponse(script, arguments);

      while (!realResultFound) {
        Thread.sleep(10);

        realResultFound = (Boolean) cast(getScriptResponse(
          "return !! arguments[0][0];",
          new JSONArray().put(new JSONObject().put("objectId", resultReadyObjectId))
        ));

        if (realResultFound) {
          realResult = cast(getScriptResponse(
            "return arguments[0][0];",
            new JSONArray().put(new JSONObject().put("objectId", resultObjectId))
          ));
        } else {
          if (System.currentTimeMillis() > whenToTimeout) {
            throw new TimeoutException("Timeout waiting for async script callback.");
          }
        }
      }

      return realResult;
    } catch (JSONException e) {
      throw new WebDriverException(e);
    } catch (TimeoutException e) {
      throw new WebDriverException(e);
    } catch (InterruptedException e) {
      throw new WebDriverException(e);
    }
  }

  public int getInnerWidth() throws JSONException {
    JSONObject cmd = new JSONObject();
    cmd.put("method", "Runtime.evaluate");
    cmd.put("params", new JSONObject().put("expression", "document.body.clientWidth;"));

    JSONObject response = sendCommand(cmd);
    return (Integer) cast(response);
  }

  public Dimension getSize() throws Exception {
    String
        f =
        "(function(element) { var result = " + IosAtoms.GET_INTERACTABLE_SIZE + "(window.top);"
        + "var res = " + IosAtoms.STRINGIFY + "(result);"
        + "return  res;  })";
    JSONObject cmd = new JSONObject();

    cmd.put("method", "Runtime.callFunctionOn");

    cmd.put("params",
            new JSONObject()
                .put("objectId", getDocument().getRemoteObject().getId())
                .put("functionDeclaration", f)
                .put("returnByValue", false));

    JSONObject response = sendCommand(cmd);
    String s = cast(response);
    JSONObject o = new JSONObject(s);
    Dimension dim = new Dimension(o.getInt("width"), o.getInt("height"));
    return dim;

  }

  public RemoteWebElement findElementByCSSSelector(String cssSelector) {
    RemoteWebElement document = getDocument();
    return document.findElementByCSSSelector(cssSelector);
  }

  public List<RemoteWebElement> findElementsByCSSSelector(String cssSelector) {
    RemoteWebElement document = getDocument();
    return document.findElementsByCSSSelector(cssSelector);
  }

  private void flagPageLoaded() {
    try {
      JSONObject cmd = new JSONObject();
      cmd.put("method", "Runtime.evaluate");
      cmd.put("params",
              new JSONObject().put("expression", "window.top.iosdriver='" + context.getId() + "'"));
      sendCommand(cmd);
    } catch (JSONException e) {
      throw new WebDriverException(e);
    }

  }

  public String getLoadedFlag() {
    JSONObject cmd = new JSONObject();
    try {
      cmd.put("method", "Runtime.evaluate");
      cmd.put("params", new JSONObject().put("expression", "window.top.iosdriver"));
    } catch (JSONException e) {
      throw new WebDriverException(e);
    }
    JSONObject response = sendCommand(cmd);
    return cast(response);
  }

  public boolean isReady() {
    return "complete".equals(getDocumentReadyState());
  }

  private String getDocumentReadyState() {
    String state = null;
    try {
      state = (String) executeScript("var state = document.readyState; return state",
                                     new JSONArray());
    } catch (Exception e) {
      // Arguments should belong to the same JavaScript world as the target object.
      System.err.println("error, reseting because " + e.getMessage());
      context.reset();
      return "unknown";
    }
    return state;
  }

  public void checkForPageLoad() {
    // a new page appeared.
    /*String id = getLoadedFlag();
    //System.out.println("on a page with id =" + id + " - " + context.getId());
    if (!context.getId().equals(id)) {

      long
          timeout = getTimeout();

      long deadline = System.currentTimeMillis() + timeout;
      //context.newContext();

      /*for (int i=0;i<100;i++){
        try {
          retrieveDocument().findElementByCSSSelector("#v4-1");
          //System.out.println(doc.getRemoteObject().call(".readyState"));
          } catch (Exception e) {
          System.err.println(e.getMessage()); //To change body of catch statement use File | Settings | File Templates.
        }
      }
      flagPageLoaded();
        */
    //System.out.println("on a page with id =" + getLoadedFlag());
    //}
  }

  private String getMainPageReadyState() {
    try {
      JSONObject cmd = new JSONObject();
      cmd.put("method", "Runtime.evaluate");
      cmd.put("params",
              new JSONObject().put("expression", "document.readyState;")
                  .put("returnByValue", true));
      JSONObject response = sendCommand(cmd);
      return cast(response);
    } catch (JSONException e) {
      throw new WebDriverException(e);
    }
  }

  private long getTimeout() {
    if (session == null) {
      return defaultPageLoadTimeoutInMs;
    } else {
      long timeout =
          (Long) session.configure(WebDriverLikeCommand.URL)
              .opt("page load", defaultPageLoadTimeoutInMs);
      if (timeout < 0) {
        return defaultPageLoadTimeoutInMs;
      }
      return timeout;

    }
  }

  @Override
  public void onMessage(IOSMessage message) {
    // a page was loaded.
    if (message instanceof ApplicationSentListingMessage) {

    }

    if (message instanceof ApplicationDataMessage) {
      ApplicationDataMessage m = (ApplicationDataMessage) message;
      EventFactory EventFactory = new EventFactory();
      Event e = EventFactory.createEvent(m.getMessage());
      if ((e instanceof ChildIframeInserted || e instanceof ChildNodeRemoved)) {
        context.domHasChanged(e);
      }
      if ("Page.frameDetached".equals(m.getMessage().optString("method"))) {
        context.frameDied(m.getMessage());
      }
      if ("Page.loadEventFired".equals(m.getMessage().optString("method"))) {
        context.signallNewPageLoadRecieved();
      }
      if ("Profiler.resetProfiles".equals(m.getMessage().optString("method"))) {

      }
    }
  }

  @Override
  public void onConnect(WebInspector inspector) {
    if (inspector == this) {
      // We are being connected. We want to get our own Page events.
      sendCommand(Page.enable());
    }
  }

  public DOMContext getContext() {
    return context;
  }

  public void back() throws JSONException {
    try {
      String f = "(function() { var f=" + IosAtoms.BACK + "();})()";
      JSONObject cmd = new JSONObject();
      cmd.put("method", "Runtime.evaluate");
      cmd.put("params",
              new JSONObject().put("expression", f).put("returnByValue", true));
      JSONObject response = sendCommand(cmd);
      cast(response);
    } catch (JSONException e) {
      throw new WebDriverException(e);
    }
  }

  public void refresh() throws Exception {
    JSONObject cmd = new JSONObject();
    cmd.put("method", "Page.reload");
    JSONObject response = sendCommand(cmd);
  }

  public void forward() throws Exception {
    try {
      String f = "(function() { var f=" + IosAtoms.FORWARD + "();})()";
      JSONObject cmd = new JSONObject();
      cmd.put("method", "Runtime.evaluate");
      cmd.put("params",
              new JSONObject().put("expression", f).put("returnByValue", true));
      JSONObject response = sendCommand(cmd);
      cast(response);
    } catch (JSONException e) {
      throw new WebDriverException(e);
    }
  }

  public void highlightNode(NodeId nodeId) {
    sendCommand(DOM.highlightNode(nodeId));
  }

  public void deleteCookie(String name, String url) {
    sendCommand(Page.deleteCookie(name, url));
  }

  public List<Cookie> getCookies() {

    List<Cookie> res = new ArrayList<>();
    JSONObject o = sendCommand(Page.getCookies());
    JSONArray cookies = o.optJSONArray("cookies");
    if (cookies != null) {
      for (int i = 0; i < cookies.length(); i++) {
        JSONObject cookie = cookies.optJSONObject(i);
        String name = cookie.optString("name");
        String value = cookie.optString("value");
        String domain = cookie.optString("domain");
        String path = cookie.optString("path");
        Date expiry = new Date(cookie.optLong("expires"));
        boolean isSecure = cookie.optBoolean("secure");
        Cookie c = new Cookie(name, value, domain, path, expiry, isSecure);
        res.add(c);
      }
      return res;
    } else {
      // TODO
    }
    return null;
  }
}
TOP

Related Classes of org.uiautomation.ios.wkrdp.BaseWebInspector

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.