public void sendKeys(CharSequence... keysToSend) {
    verifyCanInteractWithElement();

    // Handle special input types
    String typeAttribute = getAttribute("type").toLowerCase();

    if (getTagName().equals("INPUT") && specialInputs.contains(typeAttribute)) {
      if (typeAttribute.equals("file")) {
        File localFile = fileDetector.getLocalFile(keysToSend);

        if (localFile != null) {
          debugger.setFormElementValue(objectId, localFile.getAbsolutePath());
        }
      } else {
        debugger.setFormElementValue(objectId, Joiner.on("").join(keysToSend));
      }

      return;
    }

    parent.getScopeServices().captureOperaIdle();

    switchFocusToThisIfNeeded();
    parent.getKeyboard().sendKeys(keysToSend);

    try {
      parent.waitForLoadToComplete();
    } catch (ResponseNotReceivedException e) {
      // return control to user
    }
  }
  /**
   * Click the element at the given X,Y offset from the top left.
   *
   * @param x the distance from the left border of the element to click
   * @param y the distance from the top border of the element to click
   */
  public void click(int x, int y) {
    parent.getScopeServices().captureOperaIdle();
    parent.actionHandler.click(this, x, y);

    try {
      parent.waitForLoadToComplete();
    } catch (ResponseNotReceivedException e) {
      // This might be expected
      logger.fine("Response not received, returning control to user");
    }
  }
  public void submit() {
    parent.getScopeServices().captureOperaIdle();

    evaluateMethod("return " + OperaAtom.SUBMIT + "(locator)");

    try {
      parent.waitForLoadToComplete();
    } catch (ResponseNotReceivedException e) {
      // This might be expected
      logger.fine("Response not received, returning control to user");
    }
  }
  // TODO(andreastt): OPDRV-199
  public void click() {
    assertElementNotStale();
    assertElementDisplayed();

    parent.getScopeServices().captureOperaIdle();

    if (getTagName().equals("OPTION")) {
      callMethod("return " + OperaAtom.CLICK + "(locator)");
    } else {
      parent.getMouse().click(getCoordinates());
    }

    try {
      parent.waitForLoadToComplete();
    } catch (ResponseNotReceivedException e) {
      // This might be expected
      logger.fine("Response not received, returning control to user");
    }
  }
  public void click() {
    assertElementDisplayed("Cannot click an element that is not displayed");

    parent.getScopeServices().captureOperaIdle();

    if (getTagName().equals("OPTION")) {
      assertElementEnabled("Cannot select disabled element");
      callMethod("return " + OperaAtoms.CLICK + "(locator)");
    } else {
      parent.actionHandler.click(this, "");
    }

    try {
      parent.waitForLoadToComplete();
    } catch (ResponseNotReceivedException e) {
      // This might be expected
      logger.fine("Response not received, returning control to user");
    }
  }
  public void sendKeys(CharSequence... keysToSend) {
    // A list of keys that should be held down, instead of pressed
    ArrayList<String> holdKeys = new ArrayList<String>();
    holdKeys.add(OperaKeys.SHIFT.getValue());
    holdKeys.add(OperaKeys.CONTROL.getValue());
    // Keys that have been held down, and need to be released
    ArrayList<String> heldKeys = new ArrayList<String>();

    if (OperaFlags.ENABLE_CHECKS) {
      long start = System.currentTimeMillis();
      boolean isDisplayed;

      while (true) {
        isDisplayed = isDisplayed();

        if (!isDisplayed && parent.hasTimeRemaining(start)) {
          sleep(OperaIntervals.EXEC_SLEEP.getValue());
        } else {
          break;
        }
      }

      assertElementDisplayed("Cannot type on an element that is not displayed");
      assertElementEnabled("Cannot type on an element that is disabled");
    }

    if (getTagName().equalsIgnoreCase("input")
        && (hasAttribute("type") && getAttribute("type").equals("file"))) {
      click();
    } else {
      executeMethod("locator.focus()");
      // When focused textareas return the cursor to the last position it was at. Inputs place the
      // cursor at the beginning, and so we need to move it to the end. We do this by pre-pending an
      // "End" key to the keys to send (in a round-about way).
      if (getTagName().equalsIgnoreCase("input")) {
        // Javascript from webdriver_session.cc in ChromeDriver
        executeMethod(
            "function(elem) {"
                + "  var doc = elem.ownerDocument || elem;"
                + "  var prevActiveElem = doc.activeElement;"
                + "  if (elem != prevActiveElem && prevActiveElem)"
                + "    prevActiveElem.blur();"
                + "  elem.focus();"
                + "  if (elem != prevActiveElem && elem.value && elem.value.length &&"
                + "      elem.setSelectionRange) {"
                + "    elem.setSelectionRange(elem.value.length, elem.value.length);"
                + "  }"
                + "  if (elem != doc.activeElement)"
                + "    throw new Error('Failed to send keys because cannot focus element');"
                + "}(locator)");
      }
    }

    // This code is a bit ugly. Because "special" keys can be sent either as an individual
    // argument, or in the middle of a string of "normal" characters, we have to loop through the
    // string and check each against a list of special keys.

    parent.getScopeServices().captureOperaIdle();
    for (CharSequence seq : keysToSend) {
      if (seq instanceof Keys) {
        String key = OperaKeys.get(((Keys) seq).name());
        // Check if this is a key we hold down, and haven't already pressed, and press, but don't
        // release it. That's done at the end of this method.
        if (holdKeys.contains(key) && !heldKeys.contains(key) && !execService.keyIsPressed(key)) {
          execService.key(key, false);
          heldKeys.add(key);
        } else if (key.equals("null")) {
          for (String hkey : heldKeys) {
            execService.key(hkey, true);
          }
        } else {
          execService.key(key);
        }
      } else if (seq.toString().equals("\n")) {
        execService.key("enter");
      } else {
        // We need to check each character to see if it is a "special" key
        for (int i = 0; i < seq.length(); i++) {
          Character c = seq.charAt(i);
          String keyName = charToKeyName(c);

          // Buffer normal keys for a single type() call
          if (keyName == null) {
            execService.type(c.toString());
          } else {
            String key = OperaKeys.get(keyName);
            // TODO: Code repeated from above
            if (holdKeys.contains(key)
                && !heldKeys.contains(key)
                && !execService.keyIsPressed(key)) {
              execService.key(key, false);
              heldKeys.add(key);
            } else if (key.equals("null")) {
              for (String hkey : heldKeys) {
                execService.key(hkey, true);
              }
            } else {
              execService.key(key);
            }
          }
        }
      }
    }

    if (heldKeys.size() > 0) {
      for (String key : heldKeys) {
        execService.key(key, true);
      }
    }

    try {
      parent.waitForLoadToComplete();
    } catch (ResponseNotReceivedException e) {
      // This might be expected
      logger.fine("Response not received, returning control to user");
    }

    // executeMethod("locator.blur()");
  }