// 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. goog.module('webdriver.atoms.element.attribute'); goog.module.declareLegacyNamespace(); var TagName = goog.require('goog.dom.TagName'); var array = goog.require('goog.array'); var domCore = goog.require('bot.dom.core'); var utils = goog.require('goog.utils'); /** * Common aliases for properties. This maps names that users use to the correct * property name. * * @const {!Object} */ var PROPERTY_ALIASES = { 'class': 'className', 'readonly': 'readOnly' }; /** * Used to determine whether we should return a boolean value from getAttribute. * These are all extracted from the WHATWG spec: * * http://www.whatwg.org/specs/web-apps/current-work/ * * These must all be lower-case. * * @const {!Array} */ var BOOLEAN_PROPERTIES = [ 'allowfullscreen', 'allowpaymentrequest', 'allowusermedia', 'async', 'autofocus', 'autoplay', 'checked', 'compact', 'complete', 'controls', 'declare', 'default', 'defaultchecked', 'defaultselected', 'defer', 'disabled', 'ended', 'formnovalidate', 'hidden', 'indeterminate', 'iscontenteditable', 'ismap', 'itemscope', 'loop', 'multiple', 'muted', 'nohref', 'nomodule', 'noresize', 'noshade', 'novalidate', 'nowrap', 'open', 'paused', 'playsinline', 'pubdate', 'readonly', 'required', 'reversed', 'scoped', 'seamless', 'seeking', 'selected', 'truespeed', 'typemustmatch', 'willvalidate' ]; /** * Get the value of the given property or attribute. If the "attribute" is for * a boolean property, we return null in the case where the value is false. If * the attribute name is "style" an attempt to convert that style into a string * is done. * * @param {!Element} element The element to use. * @param {string} attribute The name of the attribute to look up. * @return {?string} The string value of the attribute or property, or null. * @suppress {reportUnknownTypes} */ exports.get = function(element, attribute) { var value = null; var name = attribute.toLowerCase(); if ('style' == name) { value = element.style; if (value && typeof value !== 'string') { value = value.cssText; } return /** @type {?string} */ (value); } if (('selected' == name || 'checked' == name) && domCore.isSelectable(element)) { return domCore.isSelected(element) ? 'true' : null; } // Our tests suggest that returning the attribute is desirable for // the href attribute of tags and the src attribute of tags, // but we normally attempt to get the property value before the attribute. var isLink = domCore.isElement(element, TagName.A); var isImg = domCore.isElement(element, TagName.IMG); // Although the attribute matters, the property is consistent. Return that in // preference to the attribute for links and images. if ((isImg && name == 'src') || (isLink && name == 'href')) { value = domCore.getAttribute(element, name); if (value) { // We want the full URL if present value = domCore.getProperty(element, name); } return /** @type {?string} */ (value); } if ('spellcheck' == name) { value = domCore.getAttribute(element, name); if (value !== null) { if (value.toLowerCase() == 'false') { return 'false'; } else if (value.toLowerCase() == 'true') { return 'true'; } } // coerce the property value to a string return domCore.getProperty(element, name) + ''; } var propName = PROPERTY_ALIASES[attribute] || attribute; if (array.contains(BOOLEAN_PROPERTIES, name)) { value = domCore.getAttribute(element, attribute) !== null || domCore.getProperty(element, propName); return value ? 'true' : null; } var property; try { property = domCore.getProperty(element, propName); } catch (e) { // Leaves property undefined or null } // 1- Call getAttribute if getProperty fails, // i.e. property is null or undefined. // This happens for event handlers in Firefox. // For example, calling getProperty for 'onclick' would // fail while getAttribute for 'onclick' will succeed and // return the JS code of the handler. // // 2- When property is an object we fall back to the // actual attribute instead. // See issue http://code.google.com/p/selenium/issues/detail?id=966 if (property == null || utils.isObject(property)) { value = domCore.getAttribute(element, attribute); } else { value = property; } // The empty string is a valid return value. return value != null ? value.toString() : null; };