All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.uiautomation.ios.server.DOMContext Maven / Gradle / Ivy

The newest version!
/*
 * 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.server;

import org.json.JSONArray;
import org.json.JSONObject;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriverException;
import org.uiautomation.ios.context.BaseWebInspector;
import org.uiautomation.ios.wkrdp.RemoteExceptionException;
import org.uiautomation.ios.wkrdp.events.ChildNodeRemoved;
import org.uiautomation.ios.wkrdp.events.Event;
import org.uiautomation.ios.wkrdp.events.EventHistory;
import org.uiautomation.ios.wkrdp.events.inserted.ChildIframeInserted;
import org.uiautomation.ios.wkrdp.message.WebkitPage;
import org.uiautomation.ios.wkrdp.model.NodeId;
import org.uiautomation.ios.wkrdp.model.RemoteWebElement;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;


// TODO freynaud revisit pageLoad, reset, setFrame and newContext and expose only 1 thing.
public class DOMContext {

  private static final Logger log = Logger.getLogger(DOMContext.class.getName());

  private volatile boolean pageLoaded = false;

  private volatile boolean isReady = true;
  private NodeId parent;

  private final BaseWebInspector inspector;

  private RemoteWebElement window;
  private RemoteWebElement document;
  private RemoteWebElement iframe;

  private boolean isOnMainFrame = true;
  private RemoteWebElement mainDocument;
  private RemoteWebElement mainWindow;

  private final EventHistory eventHistory = new EventHistory();
  private List windowHandles;
  private String windowHandle;
  private String id;

  private Lock eventsLock = new ReentrantLock();
  private Condition pageLoadEvent = eventsLock.newCondition();

  public DOMContext(BaseWebInspector inspector) {
    this.inspector = inspector;
    id = UUID.randomUUID().toString();
  }

  public RemoteWebElement getDocument() {
    int cpt = 0;
    while (!isReady) {
      cpt++;
      if (cpt > 20) {
        isReady = true;
        throw new TimeoutException("doc not ready.");
      }
      try {
        Thread.sleep(250);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    return document;
  }

  public RemoteWebElement getWindow() {
    return window;
  }

  public void newContext() {
    log.fine("newContext was called.");
    id = UUID.randomUUID().toString();
    window = null;
    document = null;
    iframe = null;
    mainDocument = null;
  }

  public String toString() {
    StringBuilder b = new StringBuilder();
    b.append("window " + window);
    b.append("document " + document);
    b.append("iframe " + iframe);
    b.append("mainDocument " + mainDocument);
    return b.toString();
  }

  // TODO freynaud reset() != pageLoad
  public void reset() {
    log.fine("reset called on " + toString());
    RemoteWebElement newDocument = null;
    RemoteWebElement newWindow = null;

    // check is what changed was the context for the current frame.
    if (iframe != null) {
      log.info("iframe was null");
      try {
        newDocument = iframe.getContentDocument();
        newWindow = iframe.getContentWindow();
        log.fine("newDoc=" + newDocument + " newWindow=" + newWindow);
        setCurrentFrame(iframe, newDocument, newWindow);

        return;
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    log.fine(
        "iframe ==null , no iframe selected, the page load must be from the main page,newContext()");
    // couldn't update the current frame. Reseting everything.
    newContext();
  }

  // TODO freynaud. Cleanup. A reference to the main document of the page needs
  // to be kept.
  // calling getDocument again to have the document after siwtching to an iframe
  // breaks the nodeId reference.
  public void setCurrentFrame(RemoteWebElement iframe, RemoteWebElement document,
                              RemoteWebElement window) {
    this.iframe = iframe;
    this.document = document;
    this.window = window;

    if (iframe != null) {
      isOnMainFrame = false;
    } else {
      isOnMainFrame = true;
    }

    // switchToDefaultContent. revert to main document if it was set.
    if (iframe == null && document == null) {
      this.document = mainDocument;
      this.window = mainWindow;
    }

    // setting the main document for the first time
    if (iframe == null && document != null) {
      mainDocument = document;
      mainWindow = window;
    }
    isReady = true;

  }

  public boolean isOnMainFrame() {
    return isOnMainFrame;
  }

  public RemoteWebElement getCurrentFrame() {
    return iframe;
  }


  public void onPageLoad() {
    pageLoaded = true;
    reset();
  }

  public void waitForPageToLoad(long timeout) {
    long start = System.currentTimeMillis();
    long deadLine = start + timeout;
    while (!pageLoaded) {
      if (System.currentTimeMillis() > deadLine) {
        // TODO freynaud check for alert while page loads.
        throw new TimeoutException("failed to load the page after " + timeout + " ms.");
      }
      try {
        Thread.sleep(50);
      } catch (InterruptedException e) {
        // ignore
      }
    }

    pageLoaded = false;
    waitForDocumentReady(deadLine);
    return;
  }

  public boolean isLoading() {
    return !isReady();
  }

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

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

  private void waitForDocumentReady(long deadLine) {
    while (!isReady()) {
      if (System.currentTimeMillis() > deadLine) {
        throw new TimeoutException("failed to load the page");
      }
    }
  }


  public synchronized void domHasChanged(Event e) {
    try {

      if (e instanceof ChildNodeRemoved) {
        ChildNodeRemoved removed = (ChildNodeRemoved) e;
        if (iframe != null ? removed.getNode().equals(iframe.getNodeId()) : false) {
          isReady = false;
          parent = removed.getParent();
          log.fine("current frame " + iframe.getNodeId() + " is gone.Parent = " + parent);
          List newOnes = eventHistory.getInsertedFrames(parent);
          if (newOnes.size() == 0) {
            return;
          } else if (newOnes.size() == 1) {
            Event newFrame = newOnes.get(0);
            assignNewFrameFromEvent((ChildIframeInserted) newFrame);
            eventHistory.removeEvent(newFrame);
          } else {
            log.warning(
                "there should be only 1 newly created frame with parent =" + parent + ". Found "
                + newOnes.size());
          }
        }
        return;
      }

      if (e instanceof ChildIframeInserted) {
        ChildIframeInserted newFrame = (ChildIframeInserted) e;
        // are we waiting for something ?
        if (isReady) {
          eventHistory.add(newFrame);
          return;
        } else {
          // is it the new node we're looking for ?
          if (parent.equals(newFrame.getParent())) {
            log.fine("the new node is here :" + newFrame.getNode());
            assignNewFrameFromEvent(newFrame);
          }
        }
      }
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }

  private void assignNewFrameFromEvent(ChildIframeInserted newFrameEvent) throws Exception {
    RemoteWebElement frame = new RemoteWebElement(newFrameEvent.getNode(), inspector);
    RemoteWebElement document = new RemoteWebElement(newFrameEvent.getContentDocument(), inspector);
    RemoteWebElement window = frame.getContentWindow();
    setCurrentFrame(frame, document, window);
    isReady = true;
  }

  public void frameDied(JSONObject message) {
    // if that's the one we're working on, deselect it.
    if (iframe != null) {
      if (!iframe.exists()) {
        log.fine(
            "the current frame is dead. Will need to switch to default content or another frame before being able to do anything.");
        isReady = true;
      }
    }

  }

  public void setWindowHandles(List handles) {
    this.windowHandles = handles;

    for (WebkitPage page : handles) {
      if (page.getConnection() != null) {
        windowHandle = "" + page.getPageId();
        return;
      }
    }
    windowHandle = null;
  }

  public List getWindowHandles() {
    return windowHandles;
  }

  public String getWindowHandle() {
    if (windowHandle == null) {
      throw new WebDriverException("don't know the current window.");
    }
    return windowHandle;
  }

  public void setWindow(String pageId) throws Exception {
    newContext();
    //session.getRemoteWebDriver().getProtocol().switchTo(pageId);
    //session.getRemoteWebDriver().enablePageEvent();
  }

  public String getId() {
    return id;
  }

  public void waitForLoadEvent() {
    try {
      eventsLock.lock();
      pageLoadEvent.await(30, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
      throw new TimeoutException("timeout waiting for page load event.");
    } finally {
      eventsLock.unlock();
    }
  }


  public Lock eventsLock() {
    return eventsLock;
  }

  // needs to be static ?
  public void signallNewPageLoadRecieved() {
    try {
      eventsLock.lock();
      reset();
      pageLoadEvent.signalAll();
    } finally {
      eventsLock.unlock();
    }

  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy