blob: 27096a196841ac9a038abea0d8814b357f5d7e7d [file] [log] [blame] [edit]
// 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.
/**
* @fileoverview Atoms-based implementation of the webelement interface.
*/
goog.provide('webdriver.atoms.element');
goog.require('bot.Keyboard.Keys');
goog.require('bot.action');
goog.require('bot.dom');
goog.require('goog.array');
goog.require('goog.math.Coordinate');
goog.require('goog.style');
goog.require('webdriver.Key');
goog.require('webdriver.atoms.element.attribute');
/**
* @param {!Element} element The element to use.
* @return {boolean} Whether the element is checked or selected.
*/
webdriver.atoms.element.isSelected = function(element) {
// Although this method looks unloved, its compiled form is used by
// Chrome.
if (!bot.dom.isSelectable(element)) {
return false;
}
return bot.dom.isSelected(element);
};
/**
* @const
* @deprecated Use webdriver.atoms.element.attribute.get() instead.
*/
webdriver.atoms.element.getAttribute = webdriver.atoms.element.attribute.get;
/**
* Get the location of the element in page space, if it's displayed.
*
* @param {!Element} element The element to get the location for.
* @return {?goog.math.Rect} The bounding rectangle of the element.
*/
webdriver.atoms.element.getLocation = function(element) {
if (!bot.dom.isShown(element)) {
return null;
}
return goog.style.getBounds(element);
};
/**
* Scrolls the element into the client's view and returns its position
* relative to the client viewport. If the element or region is too
* large to fit in the view, it will be aligned to the top-left of the
* container.
*
* The element should be attached to the current document.
*
* @param {!Element} elem The element to use.
* @param {!goog.math.Rect=} opt_elemRegion The region relative to the element
* to be scrolled into view.
* @return {!goog.math.Coordinate} The coordinate of the element in client
* space.
*/
webdriver.atoms.element.getLocationInView = function(elem, opt_elemRegion) {
bot.action.scrollIntoView(elem, opt_elemRegion);
var region = bot.dom.getClientRegion(elem, opt_elemRegion);
return new goog.math.Coordinate(region.left, region.top);
};
/**
* @param {?Node} element The element to use.
* @return {boolean} Whether the element is in the HEAD tag.
* @private
* @suppress {reportUnknownTypes}
*/
webdriver.atoms.element.isInHead_ = function(element) {
while (element) {
if (element.tagName && element.tagName.toLowerCase() == 'head') {
return true;
}
try {
element = element.parentNode;
} catch (e) {
// Fine. the DOM has dispeared from underneath us
return false;
}
}
return false;
};
/**
* @param {!Element} element The element to get the text from.
* @return {string} The visible text or an empty string.
*/
webdriver.atoms.element.getText = function(element) {
return bot.dom.getVisibleText(element);
};
/**
* Types keys on the given `element` with a virtual keyboard. Converts
* special characters from the WebDriver JSON wire protocol to the appropriate
* {@link bot.Keyboard.Key} value.
*
* @param {!Element} element The element to type upon.
* @param {!Array.<string>} keys The keys to type on the element.
* @param {?bot.Keyboard=} opt_keyboard Keyboard to use; if not provided,
* constructs one.
* @param {boolean=} opt_persistModifiers Whether modifier keys should remain
* pressed when this function ends.
* @see bot.action.type
* @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol
* @suppress {reportUnknownTypes}
*/
webdriver.atoms.element.type = function(
element, keys, opt_keyboard, opt_persistModifiers) {
var persistModifierKeys = !!opt_persistModifiers;
function createSequenceRecord() {
return {persist: persistModifierKeys, keys: []};
}
/**
* @type {!Array.<{persist: boolean,
* keys: !Array.<(string|!bot.Keyboard.Key)>}>}
*/
var convertedSequences = [];
/**
* @type {{persist: boolean,
* keys: !Array.<(string|!bot.Keyboard.Key)>}}
*/
var current = createSequenceRecord();
convertedSequences.push(current);
goog.array.forEach(keys, function(sequence) {
goog.array.forEach(sequence.split(''), function(key) {
if (isWebDriverKey(key)) {
var webdriverKey = webdriver.atoms.element.type.JSON_TO_KEY_MAP_[key];
// goog.isNull uses ==, which accepts undefined.
if (webdriverKey === null) {
// bot.action.type does not support a "null" key, so we have to
// terminate the entire sequence to release modifier keys. If
// we currently allow modifier key state to persist across key
// sequences, we need to inject a dummy sequence that does not
// persist state so every modifier key gets released.
convertedSequences.push(current = createSequenceRecord());
if (persistModifierKeys) {
current.persist = false;
convertedSequences.push(current = createSequenceRecord());
}
} else if (goog.isDef(webdriverKey)) {
current.keys.push(webdriverKey);
} else {
throw Error('Unsupported WebDriver key: \\u' +
key.charCodeAt(0).toString(16));
}
} else {
// Handle common aliases.
switch (key) {
case '\n':
current.keys.push(bot.Keyboard.Keys.ENTER);
break;
case '\t':
current.keys.push(bot.Keyboard.Keys.TAB);
break;
case '\b':
current.keys.push(bot.Keyboard.Keys.BACKSPACE);
break;
default:
current.keys.push(key);
break;
}
}
});
});
goog.array.forEach(convertedSequences, function(sequence) {
bot.action.type(element, sequence.keys, opt_keyboard,
sequence.persist);
});
/**
* @param {!string|!bot.Keyboard.Key} c
* @returns {!boolean}
*/
function isWebDriverKey(c) {
return '\uE000' <= c && c <= '\uE03D';
}
};
/**
* Maps JSON wire protocol values to their {@link bot.Keyboard.Key} counterpart.
* @private {!Object.<?bot.Keyboard.Key>}
* @const
*/
webdriver.atoms.element.type.JSON_TO_KEY_MAP_ = {};
goog.scope(function() {
var map = webdriver.atoms.element.type.JSON_TO_KEY_MAP_;
var key = webdriver.Key;
var botKey = bot.Keyboard.Keys;
map[key.NULL] = null;
map[key.BACK_SPACE] = botKey.BACKSPACE;
map[key.TAB] = botKey.TAB;
map[key.RETURN] = botKey.ENTER;
// This not correct, but most browsers will do the right thing.
map[key.ENTER] = botKey.ENTER;
map[key.SHIFT] = botKey.SHIFT;
map[key.CONTROL] = botKey.CONTROL;
map[key.ALT] = botKey.ALT;
map[key.PAUSE] = botKey.PAUSE;
map[key.ESCAPE] = botKey.ESC;
map[key.SPACE] = botKey.SPACE;
map[key.PAGE_UP] = botKey.PAGE_UP;
map[key.PAGE_DOWN] = botKey.PAGE_DOWN;
map[key.END] = botKey.END;
map[key.HOME] = botKey.HOME;
map[key.LEFT] = botKey.LEFT;
map[key.UP] = botKey.UP;
map[key.RIGHT] = botKey.RIGHT;
map[key.DOWN] = botKey.DOWN;
map[key.INSERT] = botKey.INSERT;
map[key.DELETE] = botKey.DELETE;
map[key.SEMICOLON] = botKey.SEMICOLON;
map[key.EQUALS] = botKey.EQUALS;
map[key.NUMPAD0] = botKey.NUM_ZERO;
map[key.NUMPAD1] = botKey.NUM_ONE;
map[key.NUMPAD2] = botKey.NUM_TWO;
map[key.NUMPAD3] = botKey.NUM_THREE;
map[key.NUMPAD4] = botKey.NUM_FOUR;
map[key.NUMPAD5] = botKey.NUM_FIVE;
map[key.NUMPAD6] = botKey.NUM_SIX;
map[key.NUMPAD7] = botKey.NUM_SEVEN;
map[key.NUMPAD8] = botKey.NUM_EIGHT;
map[key.NUMPAD9] = botKey.NUM_NINE;
map[key.MULTIPLY] = botKey.NUM_MULTIPLY;
map[key.ADD] = botKey.NUM_PLUS;
map[key.SUBTRACT] = botKey.NUM_MINUS;
map[key.DECIMAL] = botKey.NUM_PERIOD;
map[key.DIVIDE] = botKey.NUM_DIVISION;
map[key.SEPARATOR] = botKey.SEPARATOR;
map[key.F1] = botKey.F1;
map[key.F2] = botKey.F2;
map[key.F3] = botKey.F3;
map[key.F4] = botKey.F4;
map[key.F5] = botKey.F5;
map[key.F6] = botKey.F6;
map[key.F7] = botKey.F7;
map[key.F8] = botKey.F8;
map[key.F9] = botKey.F9;
map[key.F10] = botKey.F10;
map[key.F11] = botKey.F11;
map[key.F12] = botKey.F12;
map[key.META] = botKey.META;
}); // goog.scope