DanielWagnerHall on behalf of SeveralGooglers: Pushing out a bunch of Google javascript changes
* Added support for new IE10 pointer events
* Moved attribute/property conflating from atoms to webdriver atoms
* Lots of linting and type fixing
* Made bot.action.getInteractableSize public
* Added ability to force events to fire on non-interactable elements
* Added HTML5 doctype declarations to all test pages
* Always use webdriver.atoms.inject not bot.inject, as this is required by the
android driver
* Updated closure library and compiler to the latest release
git-svn-id: http://selenium.googlecode.com/svn/trunk@18101 07704840-8298-11de-bf8c-fd130f914ac9
diff --git a/cpp/prebuilt/amd64/libnoblur64.so b/cpp/prebuilt/amd64/libnoblur64.so
index 916e530..8f96aea 100755
--- a/cpp/prebuilt/amd64/libnoblur64.so
+++ b/cpp/prebuilt/amd64/libnoblur64.so
Binary files differ
diff --git a/cpp/prebuilt/i386/libnoblur.so b/cpp/prebuilt/i386/libnoblur.so
index 8e7db8d..ae7dcee 100755
--- a/cpp/prebuilt/i386/libnoblur.so
+++ b/cpp/prebuilt/i386/libnoblur.so
Binary files differ
diff --git a/java/client/src/org/openqa/selenium/firefox/build.desc b/java/client/src/org/openqa/selenium/firefox/build.desc
index 28f8370..aa4d6b9 100644
--- a/java/client/src/org/openqa/selenium/firefox/build.desc
+++ b/java/client/src/org/openqa/selenium/firefox/build.desc
@@ -13,9 +13,9 @@
"//third_party/java/commons-exec",
],
embedded = [
- "//javascript/firefox-driver:webdriver",
- "//javascript/firefox-driver:webdriver_prefs",
":amd64",
+ ":firefox_extension",
+ ":webdriver_prefs",
":x86",
])
@@ -39,6 +39,20 @@
"x86/libibushandler.so",
])
+rename(
+ name = "webdriver_prefs",
+ out = "webdriver_prefs.json",
+ deps = [
+ "//javascript/firefox-driver:webdriver_prefs",
+ ])
+
+rename(
+ name = "firefox_extension",
+ out = "webdriver.xpi",
+ deps = [
+ "//javascript/firefox-driver:webdriver",
+ ])
+
# TODO(dawagner): These tmp folders are a horrible hack, and should disappear post haste
rename(name = "noblur",
deps = [
diff --git a/javascript/atoms/action.js b/javascript/atoms/action.js
index 873d892..1985a58 100644
--- a/javascript/atoms/action.js
+++ b/javascript/atoms/action.js
@@ -469,7 +469,7 @@
bot.action.multiTouchAction_ = function(element, transformStart, transformHalf,
opt_coords, opt_touchscreen) {
var center = bot.action.prepareToInteractWith_(element, opt_coords);
- var size = bot.action.getInteractableSize_(element);
+ var size = bot.action.getInteractableSize(element);
var offsetVec = new goog.math.Vec2(
Math.min(center.x, size.width - center.x),
Math.min(center.y, size.height - center.y));
@@ -528,7 +528,7 @@
if (opt_coords) {
return goog.math.Vec2.fromCoordinate(opt_coords);
} else {
- var size = bot.action.getInteractableSize_(element);
+ var size = bot.action.getInteractableSize(element);
return new goog.math.Vec2(size.width / 2, size.height / 2);
}
};
@@ -539,12 +539,11 @@
*
* @param {!Element} elem Element.
* @return {!goog.math.Size} size Size of the element.
- * @private
*/
-bot.action.getInteractableSize_ = function(elem) {
+bot.action.getInteractableSize = function(elem) {
var size = goog.style.getSize(elem);
return ((size.width > 0 && size.height > 0) || !elem.offsetParent) ? size :
- bot.action.getInteractableSize_(elem.offsetParent);
+ bot.action.getInteractableSize(elem.offsetParent);
};
diff --git a/javascript/atoms/bot.js b/javascript/atoms/bot.js
index c94ea31..5eb25fa 100644
--- a/javascript/atoms/bot.js
+++ b/javascript/atoms/bot.js
@@ -29,6 +29,8 @@
* @type {!Window}
* @private
*/
+bot.window_;
+
try {
bot.window_ = window;
} catch (ignored) {
diff --git a/javascript/atoms/color.js b/javascript/atoms/color.js
index 8d78de1..0808741 100644
--- a/javascript/atoms/color.js
+++ b/javascript/atoms/color.js
@@ -20,8 +20,8 @@
goog.provide('bot.color');
+goog.require('goog.array');
goog.require('goog.color.names');
-goog.require('goog.math');
/**
diff --git a/javascript/atoms/device.js b/javascript/atoms/device.js
index 1a5e684..6e1a228 100644
--- a/javascript/atoms/device.js
+++ b/javascript/atoms/device.js
@@ -31,6 +31,7 @@
goog.require('goog.userAgent.product');
+
/**
* A Device class that provides common functionality for input devices.
* @param {bot.Device.ModifiersState=} opt_modifiersState state of modifier
@@ -132,15 +133,16 @@
* @param {!goog.math.Coordinate} coord The coordinate where event will fire.
* @param {number} button The mouse button value for the event.
* @param {Element=} opt_related The related element of this event.
- * @param {number=} opt_wheelDelta The wheel delta value for the event.
+ * @param {?number=} opt_wheelDelta The wheel delta value for the event.
+ * @param {boolean=} opt_force Whether the event should be fired even if the
+ * element is not interactable, such as the case of a mousemove or
+ * mouseover event that immediately follows a mouseout.
* @return {boolean} Whether the event fired successfully; false if cancelled.
* @protected
*/
bot.Device.prototype.fireMouseEvent = function(type, coord, button,
- opt_related, opt_wheelDelta) {
- // TODO(user): Event if the element is not interactable, the mouse event
- // should still fire on another element (offset parent?).
- if (!bot.dom.isInteractable(this.element_)) {
+ opt_related, opt_wheelDelta, opt_force) {
+ if (!opt_force && !bot.dom.isInteractable(this.element_)) {
return false;
}
@@ -227,6 +229,63 @@
/**
+ * Fires a MSPointer event given the state of the device and the given
+ * arguments.
+ *
+ * @param {bot.events.EventType} type MSPointer event type.
+ * @param {!goog.math.Coordinate} coord The coordinate where event will fire.
+ * @param {number} button The mouse button value for the event.
+ * @param {number} pointerId The pointer id for this event.
+ * @param {number} device The device type used for this event.
+ * @param {boolean} isPrimary Whether the pointer represents the primary point
+ * of contact.
+ * @param {Element=} opt_related The related element of this event.
+ * @param {boolean=} opt_force Whether the event should be fired even if the
+ * element is not interactable, such as the case of a mousemove or
+ * mouseover event that immediately follows a mouseout.
+ * @return {boolean} Whether the event fired successfully; false if cancelled.
+ * @protected
+ */
+bot.Device.prototype.fireMSPointerEvent = function(type, coord, button,
+ pointerId, device, isPrimary, opt_related, opt_force) {
+ if (!opt_force && !bot.dom.isInteractable(this.element_)) {
+ return false;
+ }
+
+ if (opt_related &&
+ !(bot.events.EventType.MSPOINTEROVER == type ||
+ bot.events.EventType.MSPOINTEROUT == type)) {
+ throw new bot.Error(bot.ErrorCode.INVALID_ELEMENT_STATE,
+ 'Event type does not allow related target: ' + type);
+ }
+
+ var args = {
+ clientX: coord.x,
+ clientY: coord.y,
+ button: button,
+ altKey: false,
+ ctrlKey: false,
+ shiftKey: false,
+ metaKey: false,
+ relatedTarget: opt_related || null,
+ width: 0,
+ height: 0,
+ pressure: 0, // Pressure is only given when a stylus is used.
+ rotation: 0,
+ pointerId: pointerId,
+ tiltX: 0,
+ tiltY: 0,
+ pointerType: device,
+ isPrimary: isPrimary
+ };
+
+ var target = this.select_ ?
+ this.getTargetOfOptionMouseEvent_(type) : this.element_;
+ return target ? bot.events.fire(target, type, args) : true;
+};
+
+
+/**
* A mouse event fired "on" an <option> element, doesn't always fire on the
* <option> element itself. Sometimes it fires on the parent <select> element
* and sometimes not at all, depending on the browser and event type. This
@@ -241,9 +300,11 @@
if (goog.userAgent.IE) {
switch (type) {
case bot.events.EventType.MOUSEOVER:
+ case bot.events.EventType.MSPOINTEROVER:
return null;
case bot.events.EventType.CONTEXTMENU:
case bot.events.EventType.MOUSEMOVE:
+ case bot.events.EventType.MSPOINTERMOVE:
return this.select_.multiple ? this.select_ : null;
default:
return this.select_;
@@ -427,7 +488,7 @@
*/
bot.Device.ALWAYS_FOLLOWS_LINKS_ON_CLICK_ =
goog.userAgent.WEBKIT || goog.userAgent.OPERA ||
- (bot.userAgent.FIREFOX_EXTENSION && bot.userAgent.isProductVersion(3.6));
+ (bot.userAgent.FIREFOX_EXTENSION && bot.userAgent.isProductVersion(3.6));
/**
@@ -691,6 +752,7 @@
};
+
/**
* Stores the state of modifier keys
*
diff --git a/javascript/atoms/dom.js b/javascript/atoms/dom.js
index 2e75331..b30b045 100644
--- a/javascript/atoms/dom.js
+++ b/javascript/atoms/dom.js
@@ -43,7 +43,8 @@
* @return {Element} The active element, if any.
*/
bot.dom.getActiveElement = function(nodeOrWindow) {
- return goog.dom.getOwnerDocument(nodeOrWindow).activeElement;
+ return goog.dom.getActiveElement(
+ goog.dom.getOwnerDocument(nodeOrWindow));
};
@@ -53,7 +54,7 @@
* is an element, regardless of the tag name.h
*
* @param {Node} node The node to test.
- * @param {goog.dom.TagName=} opt_tagName Tag name to test the node for.
+ * @param {string=} opt_tagName Tag name to test the node for.
* @return {boolean} Whether the node is an element with the given tag name.
*/
bot.dom.isElement = function(node, opt_tagName) {
@@ -74,11 +75,23 @@
bot.dom.isInteractable = function(element) {
return bot.dom.isShown(element, /*ignoreOpacity=*/true) &&
bot.dom.isEnabled(element) &&
- // check pointer-style isn't 'none'
- // Although IE, Opera, FF < 3.6 don't care about this property.
- (goog.userAgent.IE || goog.userAgent.OPERA ||
- (bot.userAgent.FIREFOX_EXTENSION && bot.userAgent.isProductVersion(3.6)) ||
- bot.dom.getEffectiveStyle(element, 'pointer-events') != 'none');
+ !bot.dom.hasPointerEventsDisabled_(element);
+};
+
+
+/**
+ * @param {!Element} element Element.
+ * @return {boolean} Whether element is set by the CSS pointer-events property
+ * not to be interactable.
+ * @private
+ */
+bot.dom.hasPointerEventsDisabled_ = function(element) {
+ if (goog.userAgent.IE || goog.userAgent.OPERA ||
+ (goog.userAgent.GECKO && !bot.userAgent.isEngineVersion('1.9.2'))) {
+ // Don't support pointer events
+ return false;
+ }
+ return bot.dom.getEffectiveStyle(element, 'pointer-events') == 'none';
};
@@ -157,127 +170,27 @@
/**
- * Common aliases for properties. This maps names that users use to the correct
- * property name.
- *
- * @const
- * @private
- */
-bot.dom.PROPERTY_ALIASES_ = {
- 'class': 'className',
- 'readonly': 'readOnly'
-};
-
-
-/**
- * A list of boolean properties that are defined for all elements
- * according to the HTML5 spec. If any of these are missing when
- * calling 'getProperty' they default to false.
- *
- * http://dev.w3.org/html5/spec/Overview.html#elements-in-the-dom
- *
- * @const
- * @private
- */
-bot.dom.BOOLEAN_PROPERTIES_ = [
- 'checked',
- 'disabled',
- 'draggable',
- 'hidden'
-];
-
-
-/**
* Looks up the given property (not to be confused with an attribute) on the
- * given element. The following properties are aliased so that they return the
- * values expected by users:
- *
- * <ul>
- * <li>class - as "className"
- * <li>readonly - as "readOnly"
- * </ul>
+ * given element.
*
* @param {!Element} element The element to use.
* @param {string} propertyName The name of the property.
* @return {*} The value of the property.
*/
bot.dom.getProperty = function(element, propertyName) {
- var key = bot.dom.PROPERTY_ALIASES_[propertyName] || propertyName;
-
- var value = element[key];
- if (!goog.isDef(value) &&
- goog.array.contains(bot.dom.BOOLEAN_PROPERTIES_, key)) {
- return false;
- }
-
- if (propertyName == 'value' &&
+ // When an <option>'s value attribute is not set, its value property should be
+ // its text content, but IE < 8 does not adhere to that behavior, so fix it.
+ // http://www.w3.org/TR/1999/REC-html401-19991224/interact/forms.html#adef-value-OPTION
+ if (bot.userAgent.IE_DOC_PRE8 && propertyName == 'value' &&
bot.dom.isElement(element, goog.dom.TagName.OPTION) &&
- !bot.dom.hasAttribute(element, propertyName)) {
- // IE does not adhere to this behaviour, so we hack it in. See:
- // http://www.w3.org/TR/1999/REC-html401-19991224/interact/forms.html#adef-value-OPTION
- value = goog.dom.getRawTextContent(element);
+ goog.isNull(bot.dom.getAttribute(element, 'value'))) {
+ return goog.dom.getRawTextContent(element);
}
- return value;
+ return element[propertyName];
};
/**
- * 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
- * @private
- */
-bot.dom.BOOLEAN_ATTRIBUTES_ = [
- 'async',
- 'autofocus',
- 'autoplay',
- 'checked',
- 'compact',
- 'complete',
- 'controls',
- 'declare',
- 'defaultchecked',
- 'defaultselected',
- 'defer',
- 'disabled',
- 'draggable',
- 'ended',
- 'formnovalidate',
- 'hidden',
- 'indeterminate',
- 'iscontenteditable',
- 'ismap',
- 'itemscope',
- 'loop',
- 'multiple',
- 'muted',
- 'nohref',
- 'noresize',
- 'noshade',
- 'novalidate',
- 'nowrap',
- 'open',
- 'paused',
- 'pubdate',
- 'readonly',
- 'required',
- 'reversed',
- 'scoped',
- 'seamless',
- 'seeking',
- 'selected',
- 'spellcheck',
- 'truespeed',
- 'willvalidate'
-];
-
-
-/**
* Regex to split on semicolons, but not when enclosed in parens or quotes.
* Helper for {@link bot.dom.standardizeStyleAttribute_}.
* If the style attribute ends with a semicolon this will include an empty
@@ -294,15 +207,6 @@
/**
- * @param {string} attributeName The name of the attribute to check.
- * @return {boolean} Whether the specified attribute is a boolean attribute.
- */
-bot.dom.isBooleanAttribute = function(attributeName) {
- return goog.array.contains(bot.dom.BOOLEAN_ATTRIBUTES_, attributeName);
-};
-
-
-/**
* Standardize a style attribute value, which includes:
* (1) converting all property names lowercase
* (2) ensuring it ends in a trailing semi-colon
@@ -333,23 +237,26 @@
/**
* Get the user-specified value of the given attribute of the element, or null
- * if no such value. This method endeavours to return consistent values between
- * browsers. For boolean attributes such as "selected" or "checked", it returns
- * the string "true" if it is present and null if it is not. For the style
- * attribute, it standardizes the value by lower-casing the property names
- * and always including a trailing semi-colon.
+ * if the attribute is not present.
+ *
+ * <p>For boolean attributes such as "selected" or "checked", this method
+ * returns the value of element.getAttribute(attributeName) cast to a String
+ * when attribute is present. For modern browsers, this will be the string the
+ * attribute is given in the HTML, but for IE8 it will be the name of the
+ * attribute, and for IE7, it will be the string "true". To test whether a
+ * boolean attribute is present, test whether the return value is non-null, the
+ * same as one would for non-boolean attributes. Specifically, do *not* test
+ * whether the boolean evaluation of the return value is true, because the value
+ * of a boolean attribute that is present will often be the empty string.
+ *
+ * <p>For the style attribute, it standardizes the value by lower-casing the
+ * property names and always including a trailing semi-colon.
*
* @param {!Element} element The element to use.
* @param {string} attributeName The name of the attribute to return.
* @return {?string} The value of the attribute or "null" if entirely missing.
*/
bot.dom.getAttribute = function(element, attributeName) {
- // Protect ourselves from the case where documentElementsByTagName also
- // returns comments in IE.
- if (goog.dom.NodeType.COMMENT == element.nodeType) {
- return null;
- }
-
attributeName = attributeName.toLowerCase();
// The style attribute should be a css text string that includes only what
@@ -360,58 +267,27 @@
return bot.dom.standardizeStyleAttribute_(element.style.cssText);
}
+ // In IE doc mode < 8, the "value" attribute of an <input> is only accessible
+ // as a property.
+ if (bot.userAgent.IE_DOC_PRE8 && attributeName == 'value' &&
+ bot.dom.isElement(element, goog.dom.TagName.INPUT)) {
+ return element['value'];
+ }
+
+ // In IE < 9, element.getAttributeNode will return null for some boolean
+ // attributes that are present, such as the selected attribute on <option>
+ // elements. This if-statement is sufficient if these cases are restricted
+ // to boolean attributes whose reflected property names are all lowercase
+ // (as attributeName is by this point), like "selected". We have not
+ // found a boolean attribute for which this does not work.
+ if (bot.userAgent.IE_DOC_PRE9 && element[attributeName] === true) {
+ return String(element.getAttribute(attributeName));
+ }
+
+ // When the attribute is not present, either attr will be null or
+ // attr.specified will be false.
var attr = element.getAttributeNode(attributeName);
-
- // IE8/9 in standards mode handles boolean attributes differently (of
- // course!). This if-statement is nested so the compiler can easily strip it
- // out when compiled for non-IE browsers.
- if (goog.userAgent.IE) {
- if (!attr && goog.userAgent.isVersion(8) &&
- goog.array.contains(bot.dom.BOOLEAN_ATTRIBUTES_, attributeName)) {
- attr = element[attributeName];
- }
- }
-
- if (!attr) {
- return null;
- }
-
- // Attempt to always return either true or null for boolean attributes.
- // In IE, attributes will sometimes be present even when not user-specified.
- // We would like to rely on the 'specified' property of attribute nodes, but
- // that is sometimes false for user-specified boolean attributes.
- // IE does consistently yield 'true' or 'false' strings for boolean attribute
- // values, and so we know 'false' attribute values were not user-specified.
- if (goog.array.contains(bot.dom.BOOLEAN_ATTRIBUTES_, attributeName)) {
- return bot.userAgent.IE_DOC_PRE9 && attr.value == 'false' ? null : 'true';
- }
-
- // For non-boolean attributes, we compensate for IE's extra attributes by
- // returning null if the 'specified' property of the attributes node is false.
- return attr.specified ? attr.value : null;
-};
-
-
-/**
- * Check if the DOM element has a particular attribute.
- * Convenience method since IE6/7 do not supply it.
- *
- * @param {!Element} element The element to use.
- * @param {string} attributeName The name of the attribute.
- * @return {boolean} Whether the node has the attribute, regardless of whether
- * it is the default value or user defined.
- */
-bot.dom.hasAttribute = function(element, attributeName) {
- attributeName = attributeName.toLowerCase();
- if (element.hasAttribute) {
- return element.hasAttribute(attributeName);
- } else {
- try {
- return element.attributes[attributeName].specified;
- } catch (e) {
- return false;
- }
- }
+ return (attr && attr.specified) ? attr.value : null;
};
@@ -806,6 +682,12 @@
if (size.height > 0 && size.width > 0) {
return true;
}
+ // A vertical or horizontal SVG Path element will report zero width or
+ // height but is "shown" if it has a positive stroke-width.
+ if (bot.dom.isElement(e, 'PATH') && (size.height > 0 || size.width > 0)) {
+ var strokeWidth = bot.dom.getEffectiveStyle(e, 'stroke-width');
+ return !!strokeWidth && (parseInt(strokeWidth, 10) > 0);
+ }
// Zero-sized elements should still be considered to have positive size
// if they have a child element or text node with positive size.
return goog.array.some(e.childNodes, function(n) {
@@ -822,8 +704,8 @@
// size of the parent
function isOverflowHiding(e) {
var parent = goog.style.getOffsetParent(e);
- var parentNode = goog.userAgent.GECKO || goog.userAgent.IE || goog.userAgent.OPERA ?
- bot.dom.getParentElement(e) : parent;
+ var parentNode = goog.userAgent.GECKO || goog.userAgent.IE ||
+ goog.userAgent.OPERA ? bot.dom.getParentElement(e) : parent;
// Gecko will skip the BODY tag when calling getOffsetParent. However, the
// combination of the overflow values on the BODY _and_ HTML tags determine
@@ -1081,7 +963,7 @@
*/
bot.dom.getOpacity = function(elem) {
// TODO(bsilverberg): Does this need to deal with rgba colors?
- if (!goog.userAgent.IE) {
+ if (!bot.userAgent.IE_DOC_PRE10) {
return bot.dom.getOpacityNonIE_(elem);
} else {
if (bot.dom.getEffectiveStyle(elem, 'position') == 'relative') {
diff --git a/javascript/atoms/events.js b/javascript/atoms/events.js
index 763fb49..a1911a5 100644
--- a/javascript/atoms/events.js
+++ b/javascript/atoms/events.js
@@ -22,15 +22,19 @@
goog.provide('bot.events.EventArgs');
goog.provide('bot.events.EventType');
goog.provide('bot.events.KeyboardArgs');
+goog.provide('bot.events.MSGestureArgs');
+goog.provide('bot.events.MSPointerArgs');
goog.provide('bot.events.MouseArgs');
goog.provide('bot.events.Touch');
goog.provide('bot.events.TouchArgs');
+goog.require('bot');
goog.require('bot.Error');
goog.require('bot.ErrorCode');
goog.require('bot.userAgent');
goog.require('goog.array');
goog.require('goog.dom');
+goog.require('goog.style');
goog.require('goog.userAgent');
goog.require('goog.userAgent.product');
@@ -42,9 +46,10 @@
* @type {boolean}
*/
bot.events.SUPPORTS_TOUCH_EVENTS = !(goog.userAgent.IE &&
- !bot.userAgent.isEngineVersion(10)) &&
+ !bot.userAgent.isEngineVersion(10)) &&
!goog.userAgent.OPERA;
+
/**
* Whether the browser supports a native touch api.
*
@@ -62,9 +67,20 @@
/**
+ * Whether the browser supports the construction of MSPointer events.
+ *
+ * @const
+ * @type {boolean}
+ */
+bot.events.SUPPORTS_MSPOINTER_EVENTS =
+ goog.userAgent.IE && bot.getWindow().navigator.msPointerEnabled;
+
+
+/**
* Arguments to initialize an event.
*
- * @typedef {bot.events.MouseArgs|bot.events.KeyboardArgs|bot.events.TouchArgs}
+ * @typedef {bot.events.MouseArgs|bot.events.KeyboardArgs|bot.events.TouchArgs|
+ bot.events.MSGestureArgs|bot.events.MSPointerArgs}
*/
bot.events.EventArgs;
@@ -128,6 +144,49 @@
bot.events.Touch;
+/**
+ * Arguments to initialize an MSGesture event.
+ *
+ * @typedef {{clientX: number,
+ * clientY: number,
+ * translationX: number,
+ * translationY: number,
+ * scale: number,
+ * expansion: number,
+ * rotation: number,
+ * velocityX: number,
+ * velocityY: number,
+ * velocityExpansion: number,
+ * velocityAngular: number,
+ * relatedTarget: Element}}
+ */
+bot.events.MSGestureArgs;
+
+
+/**
+ * Arguments to initialize an MSPointer event.
+ *
+ * @typedef {{clientX: number,
+ * clientY: number,
+ * button: number,
+ * altKey: boolean,
+ * ctrlKey: boolean,
+ * shiftKey: boolean,
+ * metaKey: boolean,
+ * relatedTarget: Element,
+ * width: number,
+ * height: number,
+ * pressure: number,
+ * rotation: number,
+ * pointerId: number,
+ * tiltX: number,
+ * tiltY: number,
+ * pointerType: number,
+ * isPrimary: boolean}}
+ */
+bot.events.MSPointerArgs;
+
+
/**
* Factory for event objects of a specific type.
@@ -210,7 +269,7 @@
/**
- * @inheritDoc
+ * @override
*/
bot.events.MouseEventFactory_.prototype.create = function(target, opt_args) {
// Only Gecko supports the mouse pixel scroll event.
@@ -308,31 +367,27 @@
args.ctrlKey, args.altKey, args.shiftKey, args.metaKey, args.button,
args.relatedTarget);
- // Lifted from jquery-ui tests/jquery.simulate.js
- // IE 9+ creates events with pageX and pageY set to 0.
// Trying to modify the properties throws an error,
// so we define getters to return the correct values.
if (goog.userAgent.IE &&
event.pageX === 0 && event.pageY === 0 && Object.defineProperty) {
- var doc = bot.getDocument().documentElement;
- var body = bot.getDocument().body;
+ var scrollElem = goog.dom.getDomHelper(target).getDocumentScrollElement();
+ var clientElem = goog.style.getClientViewportElement(target);
+ var pageX = args.clientX + scrollElem.scrollLeft - clientElem.clientLeft;
+ var pageY = args.clientY + scrollElem.scrollTop - clientElem.clientTop;
- Object.defineProperty( event, "pageX", {
+ Object.defineProperty(event, 'pageX', {
get: function() {
- return args.clientX +
- ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
- ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+ return pageX;
}
});
- Object.defineProperty( event, "pageY", {
+ Object.defineProperty(event, 'pageY', {
get: function() {
- return args.clientY +
- ( doc && doc.scrollTop || body && body.scrollTop || 0 ) -
- ( doc && doc.clientTop || body && body.clientTop || 0 );
- }
+ return pageY;
+ }
});
}
-}
+ }
return event;
};
@@ -356,7 +411,7 @@
/**
- * @inheritDoc
+ * @override
*/
bot.events.KeyboardEventFactory_.prototype.create = function(target, opt_args) {
var args = (/** @type {!bot.events.KeyboardArgs} */ opt_args);
@@ -414,7 +469,7 @@
/**
- * @inheritDoc
+ * @override
*/
bot.events.TouchEventFactory_.prototype.create = function(target, opt_args) {
if (!bot.events.SUPPORTS_TOUCH_EVENTS) {
@@ -504,6 +559,99 @@
};
+
+/**
+ * Factory for MSGesture event objects of a specific type.
+ *
+ * @constructor
+ * @param {string} type Type of the created events.
+ * @param {boolean} bubbles Whether the created events bubble.
+ * @param {boolean} cancelable Whether the created events are cancelable.
+ * @extends {bot.events.EventFactory_}
+ * @private
+ */
+bot.events.MSGestureEventFactory_ = function(type, bubbles, cancelable) {
+ goog.base(this, type, bubbles, cancelable);
+};
+goog.inherits(bot.events.MSGestureEventFactory_, bot.events.EventFactory_);
+
+
+/**
+ * @override
+ */
+bot.events.MSGestureEventFactory_.prototype.create = function(target,
+ opt_args) {
+ if (!bot.events.SUPPORTS_MSPOINTER_EVENTS) {
+ throw new bot.Error(bot.ErrorCode.UNSUPPORTED_OPERATION,
+ 'Browser does not support MSGesture events.');
+ }
+
+ var args = (/** @type {!bot.events.MSGestureArgs} */ opt_args);
+ var doc = goog.dom.getOwnerDocument(target);
+ var view = goog.dom.getWindow(doc);
+ var event = doc.createEvent('MSGestureEvent');
+ var timestamp = (new Date).getTime();
+
+ // See http://msdn.microsoft.com/en-us/library/windows/apps/hh441187.aspx
+ event.initGestureEvent(this.type_, this.bubbles_, this.cancelable_, view,
+ /*detail*/ 1, /*screenX*/ 0, /*screenY*/ 0,
+ args.clientX, args.clientY, /*offsetX*/ 0,
+ /*offsetY*/ 0, args.translationX, args.translationY,
+ args.scale, args.expansion, args.rotation,
+ args.velocityX, args.velocityY, args.velocityExpansion,
+ args.velocityAngular, timestamp, args.relatedTarget);
+ return event;
+};
+
+
+
+/**
+ * Factory for MSPointer event objects of a specific type.
+ *
+ * @constructor
+ * @param {string} type Type of the created events.
+ * @param {boolean} bubbles Whether the created events bubble.
+ * @param {boolean} cancelable Whether the created events are cancelable.
+ * @extends {bot.events.EventFactory_}
+ * @private
+ */
+bot.events.MSPointerEventFactory_ = function(type, bubbles, cancelable) {
+ goog.base(this, type, bubbles, cancelable);
+};
+goog.inherits(bot.events.MSPointerEventFactory_, bot.events.EventFactory_);
+
+
+/**
+ * @override
+ * @suppress {checkTypes} Closure compiler externs don't know about pointer
+ * events
+ */
+bot.events.MSPointerEventFactory_.prototype.create = function(target,
+ opt_args) {
+ if (!bot.events.SUPPORTS_MSPOINTER_EVENTS) {
+ throw new bot.Error(bot.ErrorCode.UNSUPPORTED_OPERATION,
+ 'Browser does not support MSPointer events.');
+ }
+
+ var args = (/** @type {!bot.events.MSPointerArgs} */ opt_args);
+ var doc = goog.dom.getOwnerDocument(target);
+ var view = goog.dom.getWindow(doc);
+ var event = doc.createEvent('MSPointerEvent');
+
+ // See http://msdn.microsoft.com/en-us/library/ie/hh772109(v=vs.85).aspx
+ event.initPointerEvent(this.type_, this.bubbles_, this.cancelable_, view,
+ /*detail*/ 0, /*screenX*/ 0, /*screenY*/ 0,
+ args.clientX, args.clientY, args.ctrlKey, args.altKey,
+ args.shiftKey, args.metaKey, args.button,
+ args.relatedTarget, /*offsetX*/ 0, /*offsetY*/ 0,
+ args.width, args.height, args.pressure, args.rotation,
+ args.tiltX, args.tiltY, args.pointerId,
+ args.pointerType, /*hwTimeStamp*/ 0, args.isPrimary);
+
+ return event;
+};
+
+
/**
* The types of events this modules supports firing.
*
@@ -546,7 +694,33 @@
// Touch events.
TOUCHEND: new bot.events.TouchEventFactory_('touchend', true, true),
TOUCHMOVE: new bot.events.TouchEventFactory_('touchmove', true, true),
- TOUCHSTART: new bot.events.TouchEventFactory_('touchstart', true, true)
+ TOUCHSTART: new bot.events.TouchEventFactory_('touchstart', true, true),
+
+ // MSGesture events
+ MSGESTURECHANGE: new bot.events.MSGestureEventFactory_(
+ 'MSGestureChange', true, true),
+ MSGESTUREEND: new bot.events.MSGestureEventFactory_(
+ 'MSGestureEnd', true, true),
+ MSGESTUREHOLD: new bot.events.MSGestureEventFactory_(
+ 'MSGestureHold', true, true),
+ MSGESTURESTART: new bot.events.MSGestureEventFactory_(
+ 'MSGestureStart', true, true),
+ MSGESTURETAP: new bot.events.MSGestureEventFactory_(
+ 'MSGestureTap', true, true),
+ MSINERTIASTART: new bot.events.MSGestureEventFactory_(
+ 'MSInertiaStart', true, true),
+
+ // MSPointer events
+ MSPOINTERDOWN: new bot.events.MSPointerEventFactory_(
+ 'MSPointerDown', true, true),
+ MSPOINTERMOVE: new bot.events.MSPointerEventFactory_(
+ 'MSPointerMove', true, true),
+ MSPOINTEROVER: new bot.events.MSPointerEventFactory_(
+ 'MSPointerOver', true, true),
+ MSPOINTEROUT: new bot.events.MSPointerEventFactory_(
+ 'MSPointerOut', true, true),
+ MSPOINTERUP: new bot.events.MSPointerEventFactory_(
+ 'MSPointerUp', true, true)
};
@@ -559,7 +733,7 @@
* @return {boolean} Whether the event fired successfully or was cancelled.
*/
bot.events.fire = function(target, type, opt_args) {
- var factory = /** @type {!bot.events.EventFactory_} */ type;
+ var factory = /** @type {!bot.events.EventFactory_} */ (type);
var event = factory.create(target, opt_args);
// Ensure the event's isTrusted property is set to false, so that
diff --git a/javascript/atoms/keyboard.js b/javascript/atoms/keyboard.js
index 80cade4..d0e7e91 100644
--- a/javascript/atoms/keyboard.js
+++ b/javascript/atoms/keyboard.js
@@ -30,7 +30,6 @@
goog.require('goog.array');
goog.require('goog.dom.TagName');
goog.require('goog.dom.selection');
-goog.require('goog.events.KeyCodes');
goog.require('goog.structs.Map');
goog.require('goog.structs.Set');
goog.require('goog.userAgent');
@@ -276,10 +275,10 @@
// Punctuation keys
EQUALS: bot.Keyboard.newKey_(
{gecko: 107, ieWebkit: 187, opera: 61}, '=', '+'),
+ SEPARATOR: bot.Keyboard.newKey_(108, ','),
HYPHEN: bot.Keyboard.newKey_(
{gecko: 109, ieWebkit: 189, opera: 109}, '-', '_'),
COMMA: bot.Keyboard.newKey_(188, ',', '<'),
- SEPARATOR: bot.Keyboard.newKey_(188, ','),
PERIOD: bot.Keyboard.newKey_(190, '.', '>'),
SLASH: bot.Keyboard.newKey_(191, '/', '?'),
BACKTICK: bot.Keyboard.newKey_(192, '`', '~'),
@@ -382,8 +381,8 @@
*/
bot.Keyboard.prototype.setKeyPressed_ = function(key, isPressed) {
if (goog.array.contains(bot.Keyboard.MODIFIERS, key)) {
- var modifier = /** @type {bot.Device.Modifier}*/
- bot.Keyboard.KEY_TO_MODIFIER_.get(key.code);
+ var modifier = /** @type {bot.Device.Modifier}*/ (
+ bot.Keyboard.KEY_TO_MODIFIER_.get(key.code));
this.modifiersState.setPressed(modifier, isPressed);
}
@@ -882,5 +881,5 @@
* @return {bot.Device.ModifiersState} Modifiers state.
*/
bot.Keyboard.prototype.getModifiersState = function() {
- return this.modifiersState
+ return this.modifiersState;
};
diff --git a/javascript/atoms/locators/css.js b/javascript/atoms/locators/css.js
index d83e673..c16757e 100644
--- a/javascript/atoms/locators/css.js
+++ b/javascript/atoms/locators/css.js
@@ -18,10 +18,7 @@
goog.provide('bot.locators.css');
goog.require('bot.userAgent');
-goog.require('goog.array');
-goog.require('goog.dom');
goog.require('goog.dom.NodeType');
-goog.require('goog.object');
goog.require('goog.string');
goog.require('goog.userAgent');
diff --git a/javascript/atoms/locators/xpath.js b/javascript/atoms/locators/xpath.js
index 88b77f4..9525ba5 100644
--- a/javascript/atoms/locators/xpath.js
+++ b/javascript/atoms/locators/xpath.js
@@ -36,13 +36,12 @@
goog.require('bot');
goog.require('bot.Error');
goog.require('bot.ErrorCode');
-goog.require('bot.userAgent');
goog.require('goog.array');
goog.require('goog.dom');
-goog.require('goog.dom.NodeType');
-goog.require('goog.userAgent');
-goog.require('wgxpath');
+goog.require('goog.dom.NodeType');
+goog.require('goog.userAgent');
goog.require('goog.userAgent.product');
+goog.require('wgxpath');
/**
@@ -90,47 +89,25 @@
* @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathEvaluator-evaluate
*/
bot.locators.xpath.evaluate_ = function(node, path, resultType) {
- var doc = goog.dom.getOwnerDocument(node);
- if (goog.userAgent.IE) {
+ var doc = goog.dom.getOwnerDocument(node);
+
+ // Let the wgxpath library be compiled away unless we are on IE or Android.
+ // TODO(gdennis): Restrict this to just IE when we drop support for Froyo.
+ if (goog.userAgent.IE || goog.userAgent.product.ANDROID) {
wgxpath.install(goog.dom.getWindow(doc));
- } else {
- try {
- if (!doc.implementation ||
- !doc.implementation.hasFeature('XPath', '3.0')) {
- return null;
- }
- } catch (ex) {
- // If the document isn't ready yet, Firefox may throw NS_ERROR_UNEXPECTED on
- // accessing doc.implementation
- return null;
- }
}
+
try {
- // On Android 2.2 and earlier, the evaluate function is only defined on the
- // top-level document.
- var docForEval;
- if (goog.userAgent.product.ANDROID &&
- !bot.userAgent.isProductVersion(2.3)) {
- try {
- docForEval = goog.dom.getWindow(doc).top.document;
- } catch (e) {
- // Crossed domains trying to find the evaluate function.
- // Return null to indicate the element could not be found.
- return null;
- }
- } else {
- docForEval = doc;
- }
var resolver = doc.createNSResolver ?
- doc.createNSResolver(doc.documentElement) :
- bot.locators.xpath.DEFAULT_RESOLVER_;
- if (goog.userAgent.IE && !goog.userAgent.isVersion(7)) {
- // IE6, and only IE6, has an issue where calling a custom function
+ doc.createNSResolver(doc.documentElement) :
+ bot.locators.xpath.DEFAULT_RESOLVER_;
+ if (goog.userAgent.IE && !goog.userAgent.isVersion(7)) {
+ // IE6, and only IE6, has an issue where calling a custom function
// directly attached to the document object does not correctly propagate
// thrown errors. So in that case *only* we will use apply().
- return docForEval.evaluate.apply(null, [path, node, resolver, resultType, null]);
+ return doc.evaluate.call(doc, path, node, resolver, resultType, null);
} else {
- return docForEval.evaluate(path, node, resolver, resultType, null);
+ return doc.evaluate(path, node, resolver, resultType, null);
}
} catch (ex) {
// The Firefox XPath evaluator can throw an exception if the document is
@@ -169,7 +146,7 @@
*/
bot.locators.xpath.single = function(target, root) {
- function selectSingleNode() {
+ function selectSingleNode() {
var result = bot.locators.xpath.evaluate_(root, target,
bot.locators.XPathResult_.FIRST_ORDERED_NODE_TYPE);
if (result) {
diff --git a/javascript/atoms/mouse.js b/javascript/atoms/mouse.js
index ee99227..7b50534 100644
--- a/javascript/atoms/mouse.js
+++ b/javascript/atoms/mouse.js
@@ -20,6 +20,7 @@
goog.provide('bot.Mouse');
goog.provide('bot.Mouse.Button');
+goog.provide('bot.Mouse.State');
goog.require('bot');
goog.require('bot.Device');
@@ -96,7 +97,7 @@
this.hasEverInteracted_ = opt_state.hasEverInteracted;
try {
- if(bot.dom.isElement(opt_state.element)) {
+ if (bot.dom.isElement(opt_state.element)) {
this.setElement((/** @type {!Element} */opt_state.element));
}
} catch (ignored) {
@@ -178,6 +179,18 @@
buttonValueMap[bot.events.EventType.MOUSEMOVE] = [0, 0, 0, 0];
}
+ if (bot.userAgent.IE_DOC_10) {
+ buttonValueMap[bot.events.EventType.MSPOINTERDOWN] =
+ buttonValueMap[bot.events.EventType.MOUSEUP];
+ buttonValueMap[bot.events.EventType.MSPOINTERUP] =
+ buttonValueMap[bot.events.EventType.MOUSEUP];
+ buttonValueMap[bot.events.EventType.MSPOINTERMOVE] = [-1, -1, -1, -1];
+ buttonValueMap[bot.events.EventType.MSPOINTEROUT] =
+ buttonValueMap[bot.events.EventType.MSPOINTERMOVE];
+ buttonValueMap[bot.events.EventType.MSPOINTEROVER] =
+ buttonValueMap[bot.events.EventType.MSPOINTERMOVE];
+ }
+
buttonValueMap[bot.events.EventType.DBLCLICK] =
buttonValueMap[bot.events.EventType.CLICK];
buttonValueMap[bot.events.EventType.MOUSEDOWN] =
@@ -189,6 +202,20 @@
/**
+ * Maps mouse events to corresponding MSPointer event.
+ * @type {!Object.<bot.events.EventType, bot.events.EventType>}
+ * @private
+ */
+bot.Mouse.MOUSE_EVENT_MAP_ = {
+ mousedown: bot.events.EventType.MSPOINTERDOWN,
+ mousemove: bot.events.EventType.MSPOINTERMOVE,
+ mouseout: bot.events.EventType.MSPOINTEROUT,
+ mouseover: bot.events.EventType.MSPOINTEROVER,
+ mouseup: bot.events.EventType.MSPOINTERUP
+};
+
+
+/**
* Attempts to fire a mousedown event and then returns whether or not the
* element should receive focus as a result of the mousedown.
*
@@ -295,6 +322,10 @@
* @param {!goog.math.Coordinate} coords Mouse position related to the target.
*/
bot.Mouse.prototype.move = function(element, coords) {
+ // If the element is interactable at the start of the move, it receives the
+ // full event sequence, even if hidden by an element mid sequence.
+ var toElemWasInteractable = bot.dom.isInteractable(element);
+
var pos = goog.style.getClientPosition(element);
this.clientXY_.x = coords.x + pos.x;
this.clientXY_.y = coords.y + pos.y;
@@ -331,15 +362,18 @@
// All browsers except IE fire the mouseover before the mousemove.
if (!goog.userAgent.IE) {
- this.fireMouseEvent_(bot.events.EventType.MOUSEOVER, fromElement);
+ this.fireMouseEvent_(bot.events.EventType.MOUSEOVER, fromElement, null,
+ toElemWasInteractable);
}
}
- this.fireMouseEvent_(bot.events.EventType.MOUSEMOVE);
+ this.fireMouseEvent_(bot.events.EventType.MOUSEMOVE, null, null,
+ toElemWasInteractable);
// IE fires the mouseover event after the mousemove.
if (goog.userAgent.IE && element != fromElement) {
- this.fireMouseEvent_(bot.events.EventType.MOUSEOVER, fromElement);
+ this.fireMouseEvent_(bot.events.EventType.MOUSEOVER, fromElement, null,
+ toElemWasInteractable);
}
this.nextClickIsDoubleClick_ = false;
@@ -380,15 +414,30 @@
*
* @param {bot.events.EventType} type Event type.
* @param {Element=} opt_related The related element of this event.
- * @param {number=} opt_wheelDelta The wheel delta value for the event.
+ * @param {?number=} opt_wheelDelta The wheel delta value for the event.
+ * @param {boolean=} opt_force Whether the event should be fired even if the
+ * element is not interactable.
* @return {boolean} Whether the event fired successfully or was cancelled.
* @private
*/
bot.Mouse.prototype.fireMouseEvent_ = function(type, opt_related,
- opt_wheelDelta) {
+ opt_wheelDelta, opt_force) {
this.hasEverInteracted_ = true;
+ if (bot.userAgent.IE_DOC_10) {
+ var msPointerEvent = bot.Mouse.MOUSE_EVENT_MAP_[type];
+ if (msPointerEvent) {
+ // The pointerId for mouse events is always 1 and the mouse event is never
+ // fired if the MSPointer event fails.
+ if (!this.fireMSPointerEvent(msPointerEvent, this.clientXY_,
+ this.getButtonValue_(msPointerEvent), /* pointerId */ 1,
+ MSPointerEvent.MSPOINTER_TYPE_MOUSE, /* isPrimary */ true,
+ opt_related, opt_force)) {
+ return false;
+ }
+ }
+ }
return this.fireMouseEvent(type, this.clientXY_,
- this.getButtonValue_(type), opt_related, opt_wheelDelta);
+ this.getButtonValue_(type), opt_related, opt_wheelDelta, opt_force);
};
@@ -416,11 +465,12 @@
return buttonValue;
};
+
/**
* Serialize the current state of the mouse.
* @return {!bot.Mouse.State} The current mouse state.
*/
-bot.Mouse.prototype.getState = function () {
+bot.Mouse.prototype.getState = function() {
var state = {};
state.buttonPressed = this.buttonPressed_;
state.elementPressed = this.elementPressed_;
diff --git a/javascript/atoms/test/attribute_test.html b/javascript/atoms/test/attribute_test.html
index 50cd9e6..aa94036 100644
--- a/javascript/atoms/test/attribute_test.html
+++ b/javascript/atoms/test/attribute_test.html
@@ -25,91 +25,90 @@
goog.require('goog.events.EventType');
goog.require('goog.testing.jsunit');
</script>
-
<script type="text/javascript">
function testCanFindNamedAttributes() {
var e = bot.locators.findElement({id: 'cheddar'});
-
- assertEquals('cheese1', bot.dom.getAttribute(e, 'name'));
+ assertAttributeEquals('cheese', e, 'name');
}
function testCanFindAttributesOnTheExpando() {
var e = bot.locators.findElement({id: 'cheddar'});
-
- assertEquals('lovely', bot.dom.getAttribute(e, 'unknown'));
- }
-
- function testReturnsNullWhenSelectedAttributeCannotBeSet() {
- var e = bot.locators.findElement({id: 'cheddar'});
-
- assertEquals(null, bot.dom.getAttribute(e, 'selected'));
+ assertAttributeEquals('lovely', e, 'unknown');
}
function testShouldReturnClassAttribute() {
- var e = bot.locators.findElement({id: 'brie'});
-
- assertEquals('tasty', bot.dom.getAttribute(e, 'class'));
+ var e = bot.locators.findElement({id: 'cheddar'});
+ assertAttributeEquals('tasty', e, 'class');
}
function testShouldReturnNullForMissingAttributes() {
- var e = bot.locators.findElement({id: 'gouda'});
-
- assertNull(bot.dom.getAttribute(e, 'never_there'));
- assertNull(bot.dom.getAttribute(e, 'class'));
+ var e = bot.locators.findElement({id: 'checky'});
+ assertAttributeEquals(null, e, 'never_there');
+ assertAttributeEquals(null, e, 'class');
}
function testShouldReturnAnEmptyStringWhenAttributesValueIsAnEmptyString() {
- var e = bot.locators.findElement({id: 'gouda'});
-
- assertEquals('', bot.dom.getAttribute(e, 'empty'));
+ var e = bot.locators.findElement({id: 'cheddar'});
+ assertAttributeEquals('', e, 'empty');
}
- function testReturnsTrueWhenBooleanAttributesPresent() {
- var e1 = bot.locators.findElement({id: 'peas'});
- var e2 = bot.locators.findElement({name: 'checky'});
- var e3 = bot.locators.findElement({id: 'checked'});
-
- assertReturnsTrue(e1, 'selected');
- assertReturnsTrue(e2, 'checked');
- assertReturnsTrue(e2, 'readonly');
- assertReturnsTrue(e3, 'checked');
-
- function assertReturnsTrue(element, attribute) {
- assertEquals('Should return "true" for attribute "' + attribute + '"',
- 'true', bot.dom.getAttribute(element, attribute));
+ function testReturnAttributeStringForPresentBooleanAttributes() {
+ function assertPresentBooleanAttr(elem, attr) {
+ var value = bot.dom.getAttribute(elem, attr);
+ assertEquals(String(elem.getAttribute(attr)), value);
}
+
+ var e = bot.locators.findElement({id: 'selecty'});
+ assertPresentBooleanAttr(e, 'selected');
+
+ var e = bot.locators.findElement({id: 'checky'});
+ assertPresentBooleanAttr(e, 'disabled');
+ assertPresentBooleanAttr(e, 'readonly');
+ assertPresentBooleanAttr(e, 'checked');
+ }
+
+ function testReturnNullForAbsentBooleanAttributes() {
+ var e = bot.locators.findElement({id: 'unselecty'});
+ assertAttributeEquals(null, e, 'selected');
+
+ var e = bot.locators.findElement({id: 'unchecky'});
+ assertAttributeEquals(null, e, 'disabled');
+ assertAttributeEquals(null, e, 'readonly');
+ assertAttributeEquals(null, e, 'checked');
+ }
+
+ function testCanGetValueAttributeFromInput() {
+ var input = bot.locators.findElement({id: 'unchecky'});
+ var option = bot.locators.findElement({id: 'unselecty'});
+
+ assertAttributeEquals('unchecky', input, 'value');
+ assertAttributeEquals('unselecty', option, 'value');
}
function testAttributeMatchesAreCaseInsensitive() {
- var e = bot.locators.findElement({name: 'checky'});
+ var e = bot.locators.findElement({id: 'checky'});
+ assertAttributeEquals(bot.dom.getAttribute(e, 'readonly'), e, 'readOnly');
+ assertAttributeEquals(bot.dom.getAttribute(e, 'name'), e, 'NaMe');
+ }
- assertEquals(bot.dom.getAttribute(e, 'readonly'),
- bot.dom.getAttribute(e, 'readOnly'));
- assertEquals(bot.dom.getAttribute(e, 'name'),
- bot.dom.getAttribute(e, 'NaMe'));
+ function assertAttributeEquals(expected, elem, attr) {
+ assertEquals('Expected attribute "' + attr + '" to equal "' +
+ expected + '"', expected, bot.dom.getAttribute(elem, attr));
}
</script>
</head>
<body>
- <div name="cheese1" id="cheddar" unknown="lovely">Cheddar</div>
- <div name="cheese2" id="brie" class="tasty">Brie</div>
- <div name="cheese3" id="gouda" empty="">Gouda</div>
+ <div id="cheddar" name="cheese" class="tasty" unknown="lovely" empty="">Cheddar</div>
- <form action="#" method="get" name="myform">
- <select name="fish_supper">
- <option value="fish">Fish</option>
- <option id="chips" value="chips">Chips</option>
- <option id="peas" value="peas" selected>Mushy Peas</option>
- <option value="gravy">Gravy</option>
+ <form>
+ <select>
+ <option id="selecty" selected>selecty</option>
+ <option id="unselecty" value="unselecty">unselecty</option>
</select>
-
- <!-- Setting checked="false" but getAttribute should return true. -->
- <input name="checky" type="checkbox" checked="false" readonly="readonly"/>
- <input name="radio" id="unchecked" type="radio" value="me">Me
- <input name="radio" id="checked" type="radio" value="myself"
- checked="checked">Myself
- <input name="radio" type="radio" value="i">And I
+ <!-- Setting checked="false" but getAttribute should be non-null. -->
+ <input id="checky" type="checkbox" disabled readonly="readonly" checked="false"/>
+ <input id="unchecky" type="checkbox" value="unchecky"/>
</form>
</body>
</html>
diff --git a/javascript/atoms/test/child_locator_test.html b/javascript/atoms/test/child_locator_test.html
index 1477ce3..0b3cde3 100644
--- a/javascript/atoms/test/child_locator_test.html
+++ b/javascript/atoms/test/child_locator_test.html
@@ -7,7 +7,6 @@
goog.require('goog.dom');
goog.require('goog.events.EventType');
goog.require('goog.testing.jsunit');
- goog.require('goog.userAgent');
goog.require('bot');
goog.require('bot.dom');
goog.require('bot.locators');
@@ -104,9 +103,6 @@
}
function testCanFindChildElementByXpath() {
- if (!document['evaluate'] && !goog.userAgent.IE) {
- return; // Skip this until we get xpath working on all browsers
- }
var parent = bot.locators.findElement({id: 'parent'});
var child = bot.locators.findElement({xpath: './/*[@id="child"]'}, parent);
@@ -114,9 +110,6 @@
}
function testCanFindChildElementsByXpath() {
- if (!document['evaluate'] && !goog.userAgent.IE) {
- return; // Skip this until we get xpath working on all browsers
- }
var parent = bot.locators.findElement({id: 'parent'});
var children = bot.locators.findElements({xpath: './/*[@id="child"]'}, parent);
@@ -206,7 +199,6 @@
<p name="child">Me either</p>
-
<div id="link-parent-a">
<a id="link" href="#">this is a link</a>
<a name="fishsticks">this is a link</a>
diff --git a/javascript/atoms/test/click_link_test.html b/javascript/atoms/test/click_link_test.html
index 7197483..b6f0185 100644
--- a/javascript/atoms/test/click_link_test.html
+++ b/javascript/atoms/test/click_link_test.html
@@ -560,11 +560,14 @@
<script>
asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
// Android needs a longer timeout due to emulator slowness.
- // Opera 12 is sometimes slow to load new iframe pages.
+ // Opera 12, IE10, Firefox 10 are sometimes slow to load new iframe pages.
if (goog.userAgent.product.ANDROID) {
asyncTestCase.stepTimeout = 5000;
- } else if (goog.userAgent.product.OPERA &&
- bot.userAgent.isEngineVersion(12)) {
+ } else if ((goog.userAgent.product.OPERA &&
+ bot.userAgent.isEngineVersion(12)) ||
+ bot.userAgent.IE_DOC_10 ||
+ (goog.userAgent.GECKO &&
+ bot.userAgent.isEngineVersion(10))) {
asyncTestCase.stepTimeout = 2000;
}
</script>
diff --git a/javascript/atoms/test/click_test.html b/javascript/atoms/test/click_test.html
index 0398c49..fb0f1b4 100644
--- a/javascript/atoms/test/click_test.html
+++ b/javascript/atoms/test/click_test.html
@@ -8,6 +8,7 @@
goog.require('bot.action');
goog.require('bot.locators');
goog.require('bot.test');
+ goog.require('bot.userAgent');
goog.require('goog.Uri');
goog.require('goog.debug.DivConsole');
goog.require('goog.debug.Logger');
@@ -223,8 +224,7 @@
}
// http://code.google.com/p/selenium/issues/detail?id=1207
- function shouldRespondCorrectlyIfElementIsHiddenMidClickSequence(
- actionEvents, action) {
+ function shouldRespondCorrectlyIfElementIsHiddenMidClickSequence(action) {
if (!checkActionCompatibility(action)) {
return;
}
@@ -233,20 +233,20 @@
return;
}
+ var clickEvents = [
+ goog.events.EventType.MOUSEMOVE,
+ goog.events.EventType.MOUSEDOWN,
+ goog.events.EventType.FOCUS,
+ goog.events.EventType.MOUSEUP,
+ goog.events.EventType.CLICK
+ ];
+
function runTest(hideOn, expectedEvents) {
resetClicker();
var events = [];
- goog.events.listen(clicker,
- [
- goog.events.EventType.MOUSEOVER,
- goog.events.EventType.MOUSEMOVE,
- goog.events.EventType.MOUSEDOWN,
- goog.events.EventType.FOCUS,
- goog.events.EventType.MOUSEUP,
- goog.events.EventType.CLICK],
- function(e) {
- events.push(e.type);
- });
+ goog.events.listen(clicker, clickEvents, function(e) {
+ events.push(e.type);
+ });
hideClickerOn(hideOn);
assertTrue('Should start shown', bot.dom.isShown(clicker));
@@ -257,9 +257,9 @@
}
// Test hiding on each of the expected events in the click sequence.
- for (var i = 0; i < actionEvents.length; i++) {
- var hideOn = actionEvents[i];
- var expectedEvents = goog.array.slice(actionEvents, 0, i + 1);
+ for (var i = 0; i < clickEvents.length; i++) {
+ var hideOn = clickEvents[i];
+ var expectedEvents = goog.array.slice(clickEvents, 0, i + 1);
// Opera fires a focus event when the element is hidden on mousedown.
if (goog.userAgent.OPERA && hideOn == goog.events.EventType.MOUSEDOWN) {
@@ -274,25 +274,13 @@
}
}
- var expectedTapHiddenMidClickSequenceEvents = [
- goog.events.EventType.MOUSEMOVE,
- goog.events.EventType.MOUSEDOWN,
- goog.events.EventType.FOCUS,
- goog.events.EventType.MOUSEUP,
- goog.events.EventType.CLICK];
-
- var expectedClickHiddenMidClickSequenceEvents =
- [goog.events.EventType.MOUSEOVER].
- concat(expectedTapHiddenMidClickSequenceEvents);
-
var testClickShouldRespondCorrectlyIfElementIsHiddenMidClickSequence =
goog.partial(shouldRespondCorrectlyIfElementIsHiddenMidClickSequence,
- expectedClickHiddenMidClickSequenceEvents,
bot.action.click);
var testTapShouldRespondCorrectlyIfElementIsHiddenMidClickSequence =
goog.partial(shouldRespondCorrectlyIfElementIsHiddenMidClickSequence,
- expectedTapHiddenMidClickSequenceEvents, bot.action.tap);
+ bot.action.tap);
function cancelledMousedownDoesNotFocus(expectedEvents, action) {
@@ -306,7 +294,8 @@
goog.events.EventType.MOUSEDOWN,
goog.events.EventType.FOCUS,
goog.events.EventType.MOUSEUP,
- goog.events.EventType.CLICK],
+ goog.events.EventType.CLICK,
+ goog.events.EventType.MOUSEOUT],
function(e) {
events.push(e.type);
});
@@ -323,6 +312,14 @@
goog.events.EventType.MOUSEUP,
goog.events.EventType.CLICK];
+ var expectedIETouchEvents = [
+ goog.events.EventType.MOUSEMOVE,
+ goog.events.EventType.MOUSEOVER,
+ goog.events.EventType.MOUSEDOWN,
+ goog.events.EventType.MOUSEUP,
+ goog.events.EventType.CLICK,
+ goog.events.EventType.MOUSEOUT];
+
var expectedClickEvents = [
FIRST_EVENT_OF_CLICK,
SECOND_EVENT_OF_CLICK,
@@ -336,14 +333,16 @@
bot.action.click);
var testTapCancelledMousedownDoesNotFocus = goog.partial(
- cancelledMousedownDoesNotFocus, expectedTapEvents, bot.action.tap);
+ cancelledMousedownDoesNotFocus,
+ bot.userAgent.IE_DOC_10 ? expectedIETouchEvents : expectedTapEvents,
+ bot.action.tap);
function shouldBeAbleToClickOnElementsWithOpacityZero(action) {
if (!checkActionCompatibility(action)) {
return;
}
var clickJacker = findElement({id: 'clickJacker'});
- if (goog.userAgent.IE) {
+ if (bot.userAgent.IE_DOC_PRE10) {
clickJacker.style.filter = 'alpha(opacity=0)';
} else {
clickJacker.style.opacity = 0;
@@ -360,10 +359,9 @@
var testTapShouldBeAbleToClickOnElementsWithOpacityZero = goog.partial(
shouldBeAbleToClickOnElementsWithOpacityZero, bot.action.tap);
-
function shouldNotBeAbleToClickOnElementWithPointerEventsNone(action) {
if (!checkActionCompatibility(action) || goog.userAgent.IE || goog.userAgent.OPERA ||
- (bot.userAgent.FIREFOX_EXTENSION && bot.userAgent.isProductVersion(3.6))) {
+ (goog.userAgent.GECKO && !bot.userAgent.isEngineVersion('1.9.2'))) {
return;
}
var el = findElement({id: 'pointerEvents'});
@@ -377,8 +375,6 @@
var testTapShouldNotBeAbleToClickOnElementWithPointerEventsNone = goog.partial(
shouldNotBeAbleToClickOnElementWithPointerEventsNone, bot.action.tap);
-
-
function testClickShouldNotScrollWhenElementInView() {
if (bot.userAgent.MOBILE) {
return;
@@ -432,7 +428,7 @@
<script type="text/javascript">
var clickJacker = document.getElementById('clickJacker');
function setOpacity(opacity) {
- if (goog.userAgent.IE) {
+ if (bot.userAgent.IE_DOC_PRE10) {
clickJacker.style.filter = 'alpha(opacity=' + (opacity * 100) + ')';
} else {
clickJacker.style.opacity = opacity;
diff --git a/javascript/atoms/test/dom_test.html b/javascript/atoms/test/dom_test.html
index 5caf0c5..c553618 100644
--- a/javascript/atoms/test/dom_test.html
+++ b/javascript/atoms/test/dom_test.html
@@ -126,7 +126,7 @@
}
function testStandardizeStyleAttributeReturnsIdenticalStringWithLowercasedPropertyNames() {
- toTest = [
+ var toTest = [
{input: "Left: 0px; Text-align: center;",
expected: "left: 0px; text-align: center;"},
{input: "background-image: url('http://www.google.ca/Test.gif');",
@@ -136,7 +136,7 @@
expected: "-ms-filter: 'progid:DXImageTransform(strength=50)," +
" progid:DXImageTransform.(mirror=1)';"}
];
- for (var i in toTest) {
+ for (var i = 0; i < toTest.length; i++) {
assertObjectEquals(toTest[i].expected,
bot.dom.standardizeStyleAttribute_(toTest[i].input));
}
@@ -150,7 +150,7 @@
}
function testStandardizeStyleAttributeShouldWorkWithQuotesAndParens() {
- toTest = [
+ var toTest = [
{input: "key:value", expected: "key:value;"},
{input: "key:value;", expected: "key:value;"},
{input: "key1:value1; key2: value2",
@@ -173,7 +173,7 @@
expected: "key1:\"double;quoted;string!\";" +
" key2:'single;quoted;string;'; key3:it(is;in;parens);"}
];
- for (var i in toTest) {
+ for (var i = 0; i < toTest.length; i++) {
assertObjectEquals(toTest[i].expected,
bot.dom.standardizeStyleAttribute_(toTest[i].input));
}
diff --git a/javascript/atoms/test/events_test.html b/javascript/atoms/test/events_test.html
index 3711fc1..41da735 100644
--- a/javascript/atoms/test/events_test.html
+++ b/javascript/atoms/test/events_test.html
@@ -121,6 +121,50 @@
});
}
+ function testCanFireMSGestureEvent() {
+ if (!bot.events.SUPPORTS_MSPOINTER_EVENTS) {
+ return;
+ }
+ fireAndTest(bot.events.EventType.MSGESTURESTART, {
+ clientX: 0,
+ clientY: 0,
+ translationX: 0,
+ translationY: 0,
+ scale: 0,
+ expansion: 0,
+ rotation: 0,
+ velocityX: 0,
+ velocityY: 0,
+ velocityExpansion: 0,
+ velocityAngular: 0,
+ relatedTarget: null
+ });
+ }
+
+ function testCanFireMSPointerEvent() {
+ if (!bot.events.SUPPORTS_MSPOINTER_EVENTS) {
+ return;
+ }
+ fireAndTest(bot.events.EventType.MSPOINTERMOVE, {
+ clientX: 0,
+ clientY: 0,
+ button: 0,
+ altKey: false,
+ ctrlKey: false,
+ metaKey: false,
+ relatedTarget: null,
+ width: 1,
+ height: 1,
+ pressure: 10,
+ rotation: 0,
+ pointerIdArg: 1,
+ tiltX: 0,
+ tiltY: 0,
+ pointerType: MSPointerEvent.MSPOINTER_TYPE_MOUSE,
+ isPrimary: 2
+ });
+ }
+
function testIsSynthentic() {
fireAndTest(bot.events.EventType.CHANGE, {}, function(event) {
assertTrue(bot.events.isSynthetic(event));
diff --git a/javascript/atoms/test/focus_test.html b/javascript/atoms/test/focus_test.html
index e03f09e..f7d4e07 100644
--- a/javascript/atoms/test/focus_test.html
+++ b/javascript/atoms/test/focus_test.html
@@ -61,7 +61,7 @@
}
function assertElementIsActiveElement(expected) {
- var actual = document.activeElement;
+ var actual = goog.dom.getActiveElement(document);
if (actual) {
// NOTE(jleyba): Using assertEquals(expected, document.activeElement)
// causes an error in IE when the assertion fails (from trying to
diff --git a/javascript/atoms/test/gestures_test.html b/javascript/atoms/test/gestures_test.html
index e84556b..6c66d6e 100644
--- a/javascript/atoms/test/gestures_test.html
+++ b/javascript/atoms/test/gestures_test.html
@@ -18,10 +18,10 @@
function setUp() {
elem = goog.dom.getElement('multitouch');
goog.events.removeAll();
- startCoords = null;
- firstMoveCoords = null;
- secondMoveCoords = null;
- endCoords = null;
+ startCoords = [];
+ firstMoveCoords = [];
+ secondMoveCoords = [];
+ endCoords = [];
function handleCoords(event, touchListName) {
event.preventDefault();
@@ -29,20 +29,20 @@
var touches = e[touchListName];
assertEquals(2, touches.length);
var touch0 = touches[0], touch1 = touches[1];
- return {
- coord0: new goog.math.Coordinate(touch0.clientX, touch0.clientY),
- coord1: new goog.math.Coordinate(touch1.clientX, touch1.clientY)
- };
+ return [
+ new goog.math.Coordinate(touch0.clientX, touch0.clientY),
+ new goog.math.Coordinate(touch1.clientX, touch1.clientY)
+ ];
}
goog.events.listen(elem, goog.events.EventType.TOUCHSTART, function(e) {
- assert('touchstart already fired', !startCoords);
+ assert('touchstart already fired', !startCoords.length);
startCoords = handleCoords(e, 'touches');
});
goog.events.listen(elem, goog.events.EventType.TOUCHMOVE, function(e) {
- assert('two touchmoves already fired', !secondMoveCoords);
- if (!firstMoveCoords) {
+ assert('two touchmoves already fired', !secondMoveCoords.length);
+ if (!firstMoveCoords.length) {
firstMoveCoords = handleCoords(e, 'touches');
} else {
secondMoveCoords = handleCoords(e, 'touches');
@@ -50,12 +50,39 @@
});
goog.events.listen(elem, goog.events.EventType.TOUCHEND, function(e) {
- assert('touchend already fired', !endCoords);
+ assert('touchend already fired', !endCoords.length);
endCoords = handleCoords(e, 'changedTouches');
});
+
+ function handlePointerCoords(event) {
+ event.preventDefault();
+ var e = event.getBrowserEvent();
+ return new goog.math.Coordinate(e.clientX, e.clientY);
+ }
+
+ goog.events.listen(elem, goog.events.EventType.MSPOINTERDOWN,
+ function(e) {
+ assert('MSPointerDown already fired', startCoords.length < 2);
+ startCoords.push(handlePointerCoords(e));
+ });
+
+ goog.events.listen(elem, goog.events.EventType.MSPOINTERMOVE,
+ function(e) {
+ assert('MSPointerMoves already fired', secondMoveCoords.length < 2);
+ if (firstMoveCoords.length < 2) {
+ firstMoveCoords.push(handlePointerCoords(e));
+ } else {
+ secondMoveCoords.push(handlePointerCoords(e));
+ }
+ });
+
+ goog.events.listen(elem, goog.events.EventType.MSPOINTERUP, function(e) {
+ assert('MSPointerUp already fired', endCoords.length < 2);
+ endCoords.push(handlePointerCoords(e));
+ });
}
- var EPSILON = 0.000000000001;
+ var EPSILON = 0.01;
function assertApproxEquals(comment, expected, actual) {
// Android 4+ coerces real numbered coordinates to their floor values.
@@ -72,25 +99,25 @@
function assertMultitouchPoints(start0X, start0Y, start1X, start1Y,
mid0X, mid0Y, mid1X, mid1Y, end0X, end0Y, end1X, end1Y) {
- assertApproxEquals('start 0 x', start0X, startCoords.coord0.x);
- assertApproxEquals('start 0 y', start0Y, startCoords.coord0.y);
- assertApproxEquals('start 1 x', start1X, startCoords.coord1.x);
- assertApproxEquals('start 1 y', start1Y, startCoords.coord1.y);
+ assertApproxEquals('start 0 x', start0X, startCoords[0].x);
+ assertApproxEquals('start 0 y', start0Y, startCoords[0].y);
+ assertApproxEquals('start 1 x', start1X, startCoords[1].x);
+ assertApproxEquals('start 1 y', start1Y, startCoords[1].y);
- assertApproxEquals('first move 0 x', mid0X, firstMoveCoords.coord0.x);
- assertApproxEquals('first move 0 y', mid0Y, firstMoveCoords.coord0.y);
- assertApproxEquals('first move 1 x', mid1X, firstMoveCoords.coord1.x);
- assertApproxEquals('first move 1 y', mid1Y, firstMoveCoords.coord1.y);
+ assertApproxEquals('first move 0 x', mid0X, firstMoveCoords[0].x);
+ assertApproxEquals('first move 0 y', mid0Y, firstMoveCoords[0].y);
+ assertApproxEquals('first move 1 x', mid1X, firstMoveCoords[1].x);
+ assertApproxEquals('first move 1 y', mid1Y, firstMoveCoords[1].y);
- assertApproxEquals('second move 0 x', end0X, secondMoveCoords.coord0.x);
- assertApproxEquals('second move 0 y', end0Y, secondMoveCoords.coord0.y);
- assertApproxEquals('second move 1 x', end1X, secondMoveCoords.coord1.x);
- assertApproxEquals('second move 1 y', end1Y, secondMoveCoords.coord1.y);
+ assertApproxEquals('second move 0 x', end0X, secondMoveCoords[0].x);
+ assertApproxEquals('second move 0 y', end0Y, secondMoveCoords[0].y);
+ assertApproxEquals('second move 1 x', end1X, secondMoveCoords[1].x);
+ assertApproxEquals('second move 1 y', end1Y, secondMoveCoords[1].y);
- assertApproxEquals('end 0 x', end0X, endCoords.coord0.x);
- assertApproxEquals('end 0 y', end0Y, endCoords.coord0.y);
- assertApproxEquals('end 1 x', end1X, endCoords.coord1.x);
- assertApproxEquals('end 1 y', end1Y, endCoords.coord1.y);
+ assertApproxEquals('end 0 x', end0X, endCoords[0].x);
+ assertApproxEquals('end 0 y', end0Y, endCoords[0].y);
+ assertApproxEquals('end 1 x', end1X, endCoords[1].x);
+ assertApproxEquals('end 1 y', end1Y, endCoords[1].y);
}
function testPinchInward() {
diff --git a/javascript/atoms/test/history_test.html b/javascript/atoms/test/history_test.html
index db107b5..93979ad 100644
--- a/javascript/atoms/test/history_test.html
+++ b/javascript/atoms/test/history_test.html
@@ -36,11 +36,13 @@
// this page when the popup has loaded (b/3504107). On Opera 11.5, the popup
// blocker is currently enabled (b/5746540). On Webview (Android),
// window.open will cause a dialog prompt that will cause load failure.
+ // On IE 10, the popup block is enabled (b/7126679).
// For these browsers, we test history in an iframe instead of a popup,
// which limits the history properties we can reliably check.
// TODO (gdennis): Use popups on all browsers once these bugs are fixed.
if (bot.test.isSeleniumBacked() || goog.userAgent.product.ANDROID ||
- (goog.userAgent.OPERA && bot.userAgent.isEngineVersion(11.5))) {
+ (goog.userAgent.OPERA && bot.userAgent.isEngineVersion(11.5)) ||
+ bot.userAgent.IE_DOC_10) {
// A bug in prior versions of WebKit (persisting into Safari 5) causes the
// initial page of an iframe to not enter the browser history, so we
// initialize the iframe to a dummy page and the test cases here do not
diff --git a/javascript/atoms/test/html5/appcache_test.html b/javascript/atoms/test/html5/appcache_test.html
index 1834fcc..768f791 100644
--- a/javascript/atoms/test/html5/appcache_test.html
+++ b/javascript/atoms/test/html5/appcache_test.html
@@ -19,13 +19,11 @@
// WebDriver does not enable application cache for Android-Froyo (b/5478400).
// WebDriver does not enable application cache for Firefox (b/5787180).
// Selenium breaks application cache on Opera (b/578165).
- // Chrome/Linux does not always cache (b/6658688).
var APPCACHE_NOT_WORKING =
!bot.html5.isSupported(bot.html5.API.APPCACHE) ||
goog.userAgent.product.ANDROID ||
goog.userAgent.product.FIREFOX ||
- (goog.userAgent.OPERA && bot.test.isSeleniumBacked()) ||
- (goog.userAgent.product.CHROME && goog.userAgent.LINUX);
+ (goog.userAgent.OPERA && bot.test.isSeleniumBacked());
function testGetStatusWithHtmlManifest() {
diff --git a/javascript/atoms/test/locator_test.html b/javascript/atoms/test/locator_test.html
index 9f31f5a..8752849 100644
--- a/javascript/atoms/test/locator_test.html
+++ b/javascript/atoms/test/locator_test.html
@@ -22,19 +22,13 @@
<script type="text/javascript">
goog.require('bot');
goog.require('bot.ErrorCode');
- goog.require('bot.locators');
- goog.require('goog.events.EventType');
- goog.require('goog.userAgent');
+ goog.require('bot.locators');
+ goog.require('goog.events.EventType');
goog.require('goog.testing.PropertyReplacer');
goog.require('goog.testing.jsunit');
</script>
<script type="text/javascript">
- var CAN_SELECT_SINGLE_NODE =
- document.documentElement['selectSingleNode'] || document['evaluate'];
- var CAN_SELECT_MANY_NODES =
- document.documentElement['selectNodes'] || document['evaluate'];
-
function testCanFindById() {
var e = bot.locators.findElement({id: 'x'});
@@ -161,10 +155,6 @@
}
function testCanLocateElementsUsingXPath() {
- if (!CAN_SELECT_SINGLE_NODE && !goog.userAgent.IE) {
- return; // Skip this until we get xpath working on all browsers
- }
-
var doggies = bot.locators.findElement({xpath: "//*[@id = 'after']"});
assertNotNull(doggies);
@@ -172,30 +162,17 @@
}
function testCanLocateElementsUsingXPathInIframe() {
- if (!CAN_SELECT_SINGLE_NODE && !goog.userAgent.IE) {
- return; // Skip this until we get xpath working on all browsers
- }
-
var frameDoc = window.frames[0].document;
var frameElement = bot.locators.findElement({xpath: '//body/h1'}, frameDoc);
assertNotNull(frameElement);
assertEquals('H1', frameElement.tagName);
}
-
function testWillReturnNullIfNoMatchUsingXPathIsFound() {
- if (!CAN_SELECT_SINGLE_NODE && !goog.userAgent.IE) {
- return; // Skip this until we get xpath working on all browsers
- }
-
assertNull(bot.locators.findElement({xpath: '//fish'}));
}
function testShouldThrowInvalidSelectorErrorWhenXPathIsSyntacticallyInvalidInSingle() {
- if (!CAN_SELECT_SINGLE_NODE && !goog.userAgent.IE) {
- return; // Skip this until we get xpath working on all browsers
- }
-
try {
bot.locators.findElement({xpath: 'this][isnot][valid'});
fail('Should not have succeeded because the xpath expression is ' +
@@ -208,10 +185,6 @@
}
function testShouldThrowInvalidSelectorErrorWhenXPathReturnsAWrongTypeInSingle() {
- if (!CAN_SELECT_SINGLE_NODE && !goog.userAgent.IE) {
- return; // Skip this until we get xpath working on all browsers
- }
-
try {
bot.locators.findElement({xpath: 'count(//fish)'});
fail('Should not have succeeded because the xpath expression does ' +
@@ -252,20 +225,12 @@
}
function testCanFindManyElementsViaXPath() {
- if (!CAN_SELECT_MANY_NODES && !goog.userAgent.IE) {
- return; // Skip this until we get xpath working on all browsers
- }
-
var bad = bot.locators.findElements({xpath: '//*[@name = "after"]'});
assertEquals(2, bad.length);
}
function testShouldThrowInvalidSelectorErrorWhenXPathIsSyntacticallyInvalidInMany() {
- if (!CAN_SELECT_MANY_NODES && !goog.userAgent.IE) {
- return; // Skip this until we get xpath working on all browsers
- }
-
try {
bot.locators.findElements({xpath: 'this][isnot][valid'});
fail('Should not have succeeded because the xpath expression is ' +
@@ -278,10 +243,6 @@
}
function testShouldThrowInvalidSelectorErrorWhenXPathReturnsAWrongTypeInMany() {
- if (!CAN_SELECT_MANY_NODES && !goog.userAgent.IE) {
- return; // Skip this until we get xpath working on all browsers
- }
-
try {
bot.locators.findElements({xpath: 'count(//fish)'});
fail('Should not have succeeded because the xpath expression does ' +
@@ -440,5 +401,5 @@
<a href="#" id="comma-in-alt" alt="has, a comma">has, a comma</a>
<iframe src="./testdata/iframe_page.html"></iframe>
- </body>
+</body>
</html>
diff --git a/javascript/atoms/test/mouse_test.html b/javascript/atoms/test/mouse_test.html
index 69d7b9f..24bb9b6 100644
--- a/javascript/atoms/test/mouse_test.html
+++ b/javascript/atoms/test/mouse_test.html
@@ -12,8 +12,9 @@
goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('goog.math.Coordinate');
- goog.require('goog.testing.jsunit');
+ goog.require('goog.style');
goog.require('goog.testing.AsyncTestCase');
+ goog.require('goog.testing.jsunit');
goog.require('goog.userAgent');
</script>
<body>
@@ -42,7 +43,12 @@
goog.events.EventType.MOUSEPIXELSCROLL,
goog.events.EventType.CLICK,
goog.events.EventType.CONTEXTMENU,
- goog.events.EventType.DBLCLICK
+ goog.events.EventType.DBLCLICK,
+ goog.events.EventType.MSPOINTERDOWN,
+ goog.events.EventType.MSPOINTERMOVE,
+ goog.events.EventType.MSPOINTEROVER,
+ goog.events.EventType.MSPOINTEROUT,
+ goog.events.EventType.MSPOINTERUP
];
function setUpPage() {
@@ -79,14 +85,46 @@
events = [];
}
- function mouseoverSequence(elem, button) {
+ function mousedownEvents(elem, button) {
+ var events = [goog.events.EventType.MOUSEDOWN, elem, button];
+ return !bot.userAgent.IE_DOC_10 ? events :
+ [goog.events.EventType.MSPOINTERDOWN, elem, button].concat(events);
+ }
+
+ function mousemoveEvents(elem, button) {
+ var events = [goog.events.EventType.MOUSEMOVE, elem, button];
+ return bot.userAgent.IE_DOC_10 ?
+ [goog.events.EventType.MSPOINTERMOVE, elem, -1].concat(events) :
+ events;
+ }
+
+ function mouseoutEvents(elem, button) {
+ var events = [goog.events.EventType.MOUSEOUT, elem, button];
+ return bot.userAgent.IE_DOC_10 ?
+ [goog.events.EventType.MSPOINTEROUT, elem, -1].concat(events) :
+ events;
+ }
+
+ function mouseupEvents(elem, button) {
+ var events = [goog.events.EventType.MOUSEUP, elem, button];
+ return bot.userAgent.IE_DOC_10 ?
+ [goog.events.EventType.MSPOINTERUP, elem, button].concat(events) :
+ events;
+ }
+
+ function mouseoverAndMoveEvents(elem, button) {
+ function mouseoverEvents(elem, button) {
+ var events = [goog.events.EventType.MOUSEOVER, elem, button];
+ return bot.userAgent.IE_DOC_10 ?
+ [goog.events.EventType.MSPOINTEROVER, elem, -1].concat(events) :
+ events;
+ }
// IE fires the movemove *before* the mouseover event, and
// IE < 9 always supplies a mouseover button value of 0.
- return goog.userAgent.IE ? [goog.events.EventType.MOUSEMOVE, elem, button,
- goog.events.EventType.MOUSEOVER, elem,
- bot.userAgent.IE_DOC_9 ? button : 0] :
- [goog.events.EventType.MOUSEOVER, elem, button,
- goog.events.EventType.MOUSEMOVE, elem, button];
+ return goog.userAgent.IE ?
+ mousemoveEvents(elem, button).concat(
+ mouseoverEvents(elem, bot.userAgent.IE_DOC_9 ? button : 0)) :
+ mouseoverEvents(elem, button).concat(mousemoveEvents(elem, button));
}
function testNoClickWhenPressHiddenElement() {
@@ -97,7 +135,7 @@
mouse.pressButton(bot.Mouse.Button.LEFT);
mouse.releaseButton();
- assertEvents(mouseoverSequence(greenDiv, 0));
+ assertEvents(mouseoverAndMoveEvents(greenDiv, 0));
goog.style.showElement(greenDiv, true);
}
@@ -109,9 +147,9 @@
mouse.releaseButton();
assertEvents(
- mouseoverSequence(redDiv, 0),
- goog.events.EventType.MOUSEDOWN, redDiv, b({ie: 1, wk: 0, ff: 0}),
- goog.events.EventType.MOUSEUP, redDiv, b({ie: 1, wk: 0, ff: 0}),
+ mouseoverAndMoveEvents(redDiv, 0),
+ mousedownEvents(redDiv, b({ie: 1, wk: 0, ff: 0})),
+ mouseupEvents(redDiv, b({ie: 1, wk: 0, ff: 0})),
goog.events.EventType.CLICK, redDiv, 0
);
}
@@ -126,11 +164,11 @@
// No click if we released on another element.
assertEvents(
- mouseoverSequence(greenDiv, 0),
- goog.events.EventType.MOUSEDOWN, greenDiv, b({ie: 1, wk: 0, ff: 0}),
- goog.events.EventType.MOUSEOUT, greenDiv, 0,
- mouseoverSequence(redDiv, b({ie: 1, wk: 0, ff: 0})),
- goog.events.EventType.MOUSEUP, redDiv, b({ie: 1, wk: 0, ff: 0})
+ mouseoverAndMoveEvents(greenDiv, 0),
+ mousedownEvents(greenDiv, b({ie: 1, wk: 0, ff: 0})),
+ mouseoutEvents(greenDiv, 0),
+ mouseoverAndMoveEvents(redDiv, b({ie: 1, wk: 0, ff: 0})),
+ mouseupEvents(redDiv, b({ie: 1, wk: 0, ff: 0}))
);
}
@@ -144,11 +182,11 @@
// Right click triggers contextmenu even when released over another element.
assertEvents(
- mouseoverSequence(greenDiv, 0),
- goog.events.EventType.MOUSEDOWN, greenDiv, 2,
- goog.events.EventType.MOUSEOUT, greenDiv, b({ie: 0, wk: 2, ff: 0}),
- mouseoverSequence(redDiv, b({ie: 2, wk: 2, ff: 0})),
- goog.events.EventType.MOUSEUP, redDiv, 2,
+ mouseoverAndMoveEvents(greenDiv, 0),
+ mousedownEvents(greenDiv, 2),
+ mouseoutEvents(greenDiv, b({ie: 0, wk: 2, ff: 0})),
+ mouseoverAndMoveEvents(redDiv, b({ie: 2, wk: 2, ff: 0})),
+ mouseupEvents(redDiv, 2),
goog.events.EventType.CONTEXTMENU, redDiv, b({ie: 0, wk: 2, ff: 2})
);
}
@@ -163,12 +201,12 @@
mouse.releaseButton();
assertEvents(
- mouseoverSequence(redDiv, 0),
- goog.events.EventType.MOUSEDOWN, redDiv, b({ie: 1, wk: 0, ff: 0}),
- goog.events.EventType.MOUSEUP, redDiv, b({ie: 1, wk: 0, ff: 0}),
+ mouseoverAndMoveEvents(redDiv, 0),
+ mousedownEvents(redDiv, b({ie: 1, wk: 0, ff: 0})),
+ mouseupEvents(redDiv, b({ie: 1, wk: 0, ff: 0})),
goog.events.EventType.CLICK, redDiv, 0,
- goog.events.EventType.MOUSEDOWN, redDiv, b({ie: 1, wk: 0, ff: 0}),
- goog.events.EventType.MOUSEUP, redDiv, b({ie: 1, wk: 0, ff: 0}),
+ mousedownEvents(redDiv, b({ie: 1, wk: 0, ff: 0})),
+ mouseupEvents(redDiv, b({ie: 1, wk: 0, ff: 0})),
goog.events.EventType.CLICK, redDiv, 0,
goog.events.EventType.DBLCLICK, redDiv, 0
);
@@ -185,14 +223,14 @@
mouse.releaseButton();
assertEvents(
- mouseoverSequence(greenDiv, 0),
- goog.events.EventType.MOUSEDOWN, greenDiv, b({ie: 1, wk: 0, ff: 0}),
- goog.events.EventType.MOUSEUP, greenDiv, b({ie: 1, wk: 0, ff: 0}),
+ mouseoverAndMoveEvents(greenDiv, 0),
+ mousedownEvents(greenDiv, b({ie: 1, wk: 0, ff: 0})),
+ mouseupEvents(greenDiv, b({ie: 1, wk: 0, ff: 0})),
goog.events.EventType.CLICK, greenDiv, 0,
- goog.events.EventType.MOUSEOUT, greenDiv, 0,
- mouseoverSequence(redDiv, 0),
- goog.events.EventType.MOUSEDOWN, redDiv, b({ie: 1, wk: 0, ff: 0}),
- goog.events.EventType.MOUSEUP, redDiv, b({ie: 1, wk: 0, ff: 0}),
+ mouseoutEvents(greenDiv, 0),
+ mouseoverAndMoveEvents(redDiv, 0),
+ mousedownEvents(redDiv, b({ie: 1, wk: 0, ff: 0})),
+ mouseupEvents(redDiv, b({ie: 1, wk: 0, ff: 0})),
goog.events.EventType.CLICK, redDiv, 0
);
}
@@ -205,8 +243,8 @@
mouse.move(greenDiv, coords2);
assertEvents(
- mouseoverSequence(greenDiv, 0),
- goog.events.EventType.MOUSEMOVE, greenDiv, 0
+ mouseoverAndMoveEvents(greenDiv, 0),
+ mousemoveEvents(greenDiv, 0)
);
}
@@ -217,9 +255,9 @@
mouse.move(redDiv, coords);
assertEvents(
- mouseoverSequence(greenDiv, 0),
- goog.events.EventType.MOUSEOUT, greenDiv, 0,
- mouseoverSequence(redDiv, 0)
+ mouseoverAndMoveEvents(greenDiv, 0),
+ mouseoutEvents(greenDiv, 0),
+ mouseoverAndMoveEvents(redDiv, 0)
);
}
@@ -263,7 +301,7 @@
function testMoveMouseFromClosedWindowDoesNotError() {
var mouse = new bot.Mouse();
- var coord = new goog.math.Coordinate(0, 0);
+ var coord = new goog.math.Coordinate(0, 0);
var iframe = document.createElement('iframe');
goog.events.listenOnce(iframe, 'load', function() {
@@ -284,6 +322,23 @@
document.body.appendChild(iframe);
}
+ function testMoveMouseFiresAllEventsOnElementHiddenMidSequence() {
+ var coords = new goog.math.Coordinate(5, 5);
+ var mouse = new bot.Mouse();
+ mouse.move(greenDiv, coords);
+ goog.events.listen(greenDiv, 'mouseout', function() {
+ goog.style.showElement(redDiv, false);
+ });
+ mouse.move(redDiv, coords);
+ goog.style.showElement(redDiv, true);
+
+ assertEvents(
+ mouseoverAndMoveEvents(greenDiv, 0),
+ mouseoutEvents(greenDiv, 0),
+ mouseoverAndMoveEvents(redDiv, 0)
+ );
+ }
+
function testScrollMouseZeroTicksThrowsError() {
var mouse = new bot.Mouse();
mouse.move(greenDiv, new goog.math.Coordinate(5, 5));
@@ -299,13 +354,13 @@
if (goog.userAgent.GECKO) {
assertEvents(
- mouseoverSequence(greenDiv, 0),
+ mouseoverAndMoveEvents(greenDiv, 0),
goog.events.EventType.MOUSEWHEEL, greenDiv, 0,
goog.events.EventType.MOUSEPIXELSCROLL, greenDiv, 0
);
} else {
assertEvents(
- mouseoverSequence(greenDiv, 0),
+ mouseoverAndMoveEvents(greenDiv, 0),
goog.events.EventType.MOUSEWHEEL, greenDiv, 0
);
}
@@ -318,7 +373,7 @@
if (goog.userAgent.GECKO) {
assertEvents(
- mouseoverSequence(greenDiv, 0),
+ mouseoverAndMoveEvents(greenDiv, 0),
goog.events.EventType.MOUSEWHEEL, greenDiv, 0,
goog.events.EventType.MOUSEPIXELSCROLL, greenDiv, 0,
goog.events.EventType.MOUSEWHEEL, greenDiv, 0,
@@ -326,7 +381,7 @@
);
} else {
assertEvents(
- mouseoverSequence(greenDiv, 0),
+ mouseoverAndMoveEvents(greenDiv, 0),
goog.events.EventType.MOUSEWHEEL, greenDiv, 0,
goog.events.EventType.MOUSEWHEEL, greenDiv, 0
);
diff --git a/javascript/atoms/test/opacity_test.html b/javascript/atoms/test/opacity_test.html
index 28e8242..8650aa8 100644
--- a/javascript/atoms/test/opacity_test.html
+++ b/javascript/atoms/test/opacity_test.html
@@ -36,7 +36,7 @@
function testOpacity() {
var suite = buildTestSuites();
- if(!goog.userAgent.IE) {
+ if(!bot.userAgent.IE_DOC_PRE10) {
runTestSuite(suite, 'others');
} else if(bot.userAgent.isEngineVersion(8)) {
runTestSuite(suite, 'ie8');
diff --git a/javascript/atoms/test/property_test.html b/javascript/atoms/test/property_test.html
index 50634d3..5e1e8f5 100644
--- a/javascript/atoms/test/property_test.html
+++ b/javascript/atoms/test/property_test.html
@@ -50,18 +50,8 @@
assertTrue(result);
}
- function testShouldAliasClassNameProperty() {
- var e = bot.locators.findElement({id: 'brie'});
-
- assertEquals('tasty', bot.dom.getProperty(e, 'class'));
- assertEquals('tasty', bot.dom.getProperty(e, 'className'));
- }
-
- function testShouldHaveDefaultValueOfFalseForDisabledProperty() {
- var working = bot.locators.findElement({id: 'not_disabled'});
+ function testShouldReturnDisabledProperty() {
var disabled = bot.locators.findElement({id: 'is_disabled'});
-
- assertFalse(bot.dom.getProperty(working, 'disabled'));
assertTrue(bot.dom.getProperty(disabled, 'disabled'));
}
@@ -110,6 +100,5 @@
<input id="is_disabled" name="foo" disabled />
</form>
- <p id="not_disabled">This paragraph can check for the disabled property.</p>
</body>
</html>
diff --git a/javascript/atoms/test/scrolled_into_view_test.html b/javascript/atoms/test/scrolled_into_view_test.html
index f982404..fb05d0f 100644
--- a/javascript/atoms/test/scrolled_into_view_test.html
+++ b/javascript/atoms/test/scrolled_into_view_test.html
@@ -1,16 +1,14 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<!DOCTYPE html>
<html>
<head>
<title>scrolling_test.html</title>
<script src="test_bootstrap.js"></script>
<script type="text/javascript">
- goog.require('bot');
- goog.require('bot.action');
goog.require('bot.dom');
+ goog.require('bot.locators');
+ goog.require('bot.userAgent');
goog.require('goog.events');
goog.require('goog.testing.jsunit');
- goog.require('goog.userAgent');
- goog.require('goog.userAgent.product');
</script>
</head>
<body>
@@ -130,6 +128,10 @@
}
function testNestedIframeWhereMiddleNeedsScrollingIsNotInView() {
+ // Android Froyo zooms out to auto-fit the page and make the element visible.
+ if (bot.userAgent.ANDROID_PRE_GINGERBREAD) {
+ return;
+ }
var iframe = findElement({id: 'nested-iframe-parent'});
var frameDocument = iframe.contentWindow.document;
var innerIframe = findElement({id: 'middle'}, frameDocument);
diff --git a/javascript/atoms/test/style_test.html b/javascript/atoms/test/style_test.html
index 1005059..6fe5eb9 100644
--- a/javascript/atoms/test/style_test.html
+++ b/javascript/atoms/test/style_test.html
@@ -243,7 +243,8 @@
function testEffectiveStyleReturnsQuotedShortMsFilterAsFilter() {
var e = bot.locators.findElement({id: 'msFilterQuotedFilterShort'});
- if (goog.userAgent.IE && bot.userAgent.isEngineVersion(8)) {
+ if (goog.userAgent.IE && bot.userAgent.isEngineVersion(8) &&
+ !bot.userAgent.isEngineVersion(10)) {
assertEquals('alpha(opacity=0)', bot.dom.getInlineStyle(e, 'filter'));
} else {
// -ms-filters make sense only on IE.
@@ -253,7 +254,8 @@
function testEffectiveStyleReturnsQuotedLongMsFilterAsFilter() {
var e = bot.locators.findElement({id: 'msFilterQuotedFilterLong'});
- if (goog.userAgent.IE && bot.userAgent.isEngineVersion(8)) {
+ if (goog.userAgent.IE && bot.userAgent.isEngineVersion(8) &&
+ !bot.userAgent.isEngineVersion(10)) {
assertEquals('progid:DXImageTransform.Microsoft.Alpha(Opacity=20)',
bot.dom.getInlineStyle(e, 'filter'));
} else {
diff --git a/javascript/atoms/test/svg_test.html b/javascript/atoms/test/svg_test.html
index a37b165..92b908b 100644
--- a/javascript/atoms/test/svg_test.html
+++ b/javascript/atoms/test/svg_test.html
@@ -1,10 +1,5 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE html
- PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink">
+<!DOCTYPE html>
+<html>
<head>
<title>svg_test.html</title>
<script src="test_bootstrap.js"></script>
@@ -12,77 +7,51 @@
goog.require('bot.action');
goog.require('bot.dom');
goog.require('bot.locators');
+ goog.require('bot.test');
goog.require('goog.testing.jsunit');
goog.require('goog.userAgent');
</script>
<script type="text/javascript">
- // Skip this test on IE and Android until SVG is natively supported.
- var SUPPORTS_SVG = !goog.userAgent.IE && !goog.userAgent.product.ANDROID;
-
function testSvgElementsShouldBeVisible() {
- if (!SUPPORTS_SVG) {
+ if (!bot.test.SUPPORTS_INLINE_SVG) {
return;
}
- var doc = window.frames['chart'].document.documentElement;
- var element = bot.locators.findElement({id: 'title'}, doc);
+ var element = bot.locators.findElement({id: 'rect'});
assertTrue(bot.dom.isShown(element));
}
function testFocusingOnSvgElementDoesNotThrow() {
- if (!SUPPORTS_SVG) {
+ if (!bot.test.SUPPORTS_INLINE_SVG) {
return;
}
- var doc = window.frames['chart'].document.documentElement;
- var element = bot.locators.findElement({id: 'title'}, doc);
- var otherElement = bot.locators.findElement({id: 'someText'});
-
+ var element = bot.locators.findElement({id: 'rect'});
+ var otherElement = bot.locators.findElement({id: 'text'});
bot.action.focusOnElement(otherElement);
bot.action.focusOnElement(element);
}
- function testCanFindElementsWithinFramedSvg() {
- if (!SUPPORTS_SVG) {
- return;
- }
- var doc = window.frames['chart'].document.documentElement;
- var element = bot.locators.findElement({tagName: 'text'}, doc);
- assertEquals('title', element.id);
- }
-
- function testLocatingSvgElementsByTagNameIsCaseSensitive() {
- if (!SUPPORTS_SVG) {
- return;
- }
- // Just documenting this for posterity...
- var doc = window.frames['chart'].document.documentElement;
- assertNull(bot.locators.findElement({tagName: 'TEXT'}, doc));
- assertNotNull(bot.locators.findElement({tagName: 'text'}, doc));
- }
-
function testGettingTextOfSvgElementDoesNotThrow() {
- if (!SUPPORTS_SVG) {
+ if (!bot.test.SUPPORTS_INLINE_SVG) {
return;
}
+ var element = bot.locators.findElement({id: 'text'});
+ bot.dom.getVisibleText(element);
+ }
- var doc = window.frames['chart'].document.documentElement;
- bot.dom.getVisibleText(doc);
+ function testSvgLineWithZeroSizeBBoxIsShown() {
+ if (!bot.test.SUPPORTS_INLINE_SVG) {
+ return;
+ }
+ var element = bot.locators.findElement({id: 'path'});
+ assertTrue(bot.dom.isShown(element));
}
</script>
</head>
<body>
- <div id="someText">
- Some text for the chart.
- </div>
- <div id="result">Nothing.</div>
- <div id="chart_container" style="width: 400px; height: 220px; border: 1px solid #808080;">
- <script type="text/javascript">
- if (SUPPORTS_SVG) {
- document.write('<iframe name="chart" src="testdata/chart.svg" ' +
- 'width="400" height="220" ' +
- 'type="image/svg+xml"></iframe>');
- }
- </script>
- </div>
- <div style="display: none; position: absolute; top: 230px; left: 410px; white-space: nowrap; font-family: Arial; font-size: 12px;">WOrange</div>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 1000 50">
+ <rect id="rect" fill="red" stroke="none" height="12" width="12" y="20" x="100"></rect>
+ <text id="text" fill="black" font-size="12" font-family="Arial" y="30" x="115">Apple</text>
+ <path id="path" d="M 200 26 L 600 26" stroke="red" stroke-width="1em"/>
+</svg>
</body>
</html>
diff --git a/javascript/atoms/test/test_util.js b/javascript/atoms/test/test_util.js
index 5a620d0..df2ca36 100644
--- a/javascript/atoms/test/test_util.js
+++ b/javascript/atoms/test/test_util.js
@@ -19,10 +19,22 @@
goog.provide('bot.test');
+goog.require('bot.userAgent');
goog.require('goog.userAgent');
/**
+ * Whether the browser supports SVG element inline with HTML.
+ *
+ * @const
+ * @type {boolean}
+ */
+bot.test.SUPPORTS_INLINE_SVG = !bot.userAgent.IE_DOC_PRE9 &&
+ !bot.userAgent.ANDROID_PRE_GINGERBREAD &&
+ !(goog.userAgent.GECKO && !bot.userAgent.isEngineVersion(2));
+
+
+/**
* @return {boolean} Whether the window under test has focus.
*/
bot.test.isWindowFocused = function() {
diff --git a/javascript/atoms/test/testdata/nested_scrolling_iframe_parent.html b/javascript/atoms/test/testdata/nested_scrolling_iframe_parent.html
index 91bdfcb..9301cfa 100644
--- a/javascript/atoms/test/testdata/nested_scrolling_iframe_parent.html
+++ b/javascript/atoms/test/testdata/nested_scrolling_iframe_parent.html
@@ -1,3 +1,4 @@
+<!DOCTYPE html>
<html>
<head>
<title>Nested scrollable</title>
diff --git a/javascript/atoms/test/testdata/simple.html b/javascript/atoms/test/testdata/simple.html
index 259c613..7377200 100644
--- a/javascript/atoms/test/testdata/simple.html
+++ b/javascript/atoms/test/testdata/simple.html
@@ -1,3 +1,4 @@
+<!DOCTYPE html>
<html>
<body>
<a href="#" id="in-offscreen-in-view-iframe">Click</a>
diff --git a/javascript/atoms/test/touchscreen_test.html b/javascript/atoms/test/touchscreen_test.html
index c940cb1..887d7f2 100644
--- a/javascript/atoms/test/touchscreen_test.html
+++ b/javascript/atoms/test/touchscreen_test.html
@@ -23,8 +23,15 @@
goog.events.EventType.TOUCHMOVE,
goog.events.EventType.MOUSEMOVE,
goog.events.EventType.MOUSEDOWN,
+ goog.events.EventType.MOUSEOUT,
+ goog.events.EventType.MOUSEOVER,
goog.events.EventType.MOUSEUP,
- goog.events.EventType.CLICK
+ goog.events.EventType.CLICK,
+ goog.events.EventType.MSPOINTERDOWN,
+ goog.events.EventType.MSPOINTERMOVE,
+ goog.events.EventType.MSPOINTEROVER,
+ goog.events.EventType.MSPOINTEROUT,
+ goog.events.EventType.MSPOINTERUP
];
function setUpPage() {
@@ -53,7 +60,29 @@
events = [];
}
- function assertEvents(expectedEvents) {
+ function msPointerDownEvents(elem) {
+ return [goog.events.EventType.MOUSEMOVE, target].concat(
+ [goog.events.EventType.MSPOINTEROVER, target],
+ [goog.events.EventType.MOUSEOVER, target],
+ [goog.events.EventType.MSPOINTERDOWN, target],
+ [goog.events.EventType.MOUSEDOWN, target]);
+ }
+
+ function msPointerMoveEvents(elem) {
+ return [goog.events.EventType.MSPOINTERMOVE, target].concat(
+ [goog.events.EventType.MOUSEMOVE, target]);
+ }
+
+ function msPointerUpEvents(elem) {
+ return [goog.events.EventType.MSPOINTERUP, target].concat(
+ [goog.events.EventType.MOUSEUP, target],
+ [goog.events.EventType.CLICK, target],
+ [goog.events.EventType.MSPOINTEROUT, target],
+ [goog.events.EventType.MOUSEOUT, target]);
+ }
+
+ function assertEvents(var_args) {
+ var expectedEvents = goog.array.concat.apply(null, arguments);
assertArrayEquals(expectedEvents, events);
events = [];
}
@@ -65,7 +94,12 @@
touchscreen.move(target, new goog.math.Coordinate(0, 0));
touchscreen.press();
assertTrue(touchscreen.isPressed());
- assertEvents([goog.events.EventType.TOUCHSTART, target, 1]);
+
+ if (bot.userAgent.IE_DOC_10) {
+ assertEvents(msPointerDownEvents(target));
+ } else {
+ assertEvents([goog.events.EventType.TOUCHSTART, target, 1]);
+ }
}
function testTouchScreen2FingerPress() {
@@ -77,7 +111,12 @@
new goog.math.Coordinate(10, 10));
touchscreen.press(/* opt_press2 */ true);
assertTrue(touchscreen.isPressed());
- assertEvents([goog.events.EventType.TOUCHSTART, target, 2]);
+ if (bot.userAgent.IE_DOC_10) {
+ assertEvents(msPointerDownEvents(target),
+ msPointerDownEvents(target));
+ } else {
+ assertEvents([goog.events.EventType.TOUCHSTART, target, 2]);
+ }
}
function testTouchScreen2FingerPinch() {
@@ -92,9 +131,18 @@
new goog.math.Coordinate(0, 10),
new goog.math.Coordinate(0, 40));
touchscreen.release();
- assertEvents([goog.events.EventType.TOUCHSTART, target, 2,
- goog.events.EventType.TOUCHMOVE, target, 2,
- goog.events.EventType.TOUCHEND, target, 2]);
+ if (bot.userAgent.IE_DOC_10) {
+ assertEvents(msPointerDownEvents(target),
+ msPointerDownEvents(target),
+ msPointerMoveEvents(target),
+ msPointerMoveEvents(target),
+ msPointerUpEvents(target),
+ msPointerUpEvents(target));
+ } else {
+ assertEvents([goog.events.EventType.TOUCHSTART, target, 2,
+ goog.events.EventType.TOUCHMOVE, target, 2,
+ goog.events.EventType.TOUCHEND, target, 2]);
+ }
}
function testTouchScreen2FingerRotate() {
@@ -108,10 +156,21 @@
touchscreen.move(target, new goog.math.Coordinate(10, 40));
touchscreen.move(target, new goog.math.Coordinate(50, 50));
touchscreen.release();
- assertEvents([goog.events.EventType.TOUCHSTART, target, 2,
- goog.events.EventType.TOUCHMOVE, target, 2,
- goog.events.EventType.TOUCHMOVE, target, 2,
- goog.events.EventType.TOUCHEND, target, 2]);
+ if (bot.userAgent.IE_DOC_10) {
+ assertEvents(msPointerDownEvents(target),
+ msPointerDownEvents(target),
+ msPointerMoveEvents(target),
+ msPointerMoveEvents(target),
+ msPointerMoveEvents(target),
+ msPointerMoveEvents(target),
+ msPointerUpEvents(target),
+ msPointerUpEvents(target));
+ } else {
+ assertEvents([goog.events.EventType.TOUCHSTART, target, 2,
+ goog.events.EventType.TOUCHMOVE, target, 2,
+ goog.events.EventType.TOUCHMOVE, target, 2,
+ goog.events.EventType.TOUCHEND, target, 2]);
+ }
}
function testTouchScreenRelease() {
@@ -122,12 +181,16 @@
touchscreen.press();
touchscreen.release();
assertFalse(touchscreen.isPressed());
- assertEvents([goog.events.EventType.TOUCHSTART, target, 1,
- goog.events.EventType.TOUCHEND, target, 1,
- goog.events.EventType.MOUSEMOVE, target,
- goog.events.EventType.MOUSEDOWN, target,
- goog.events.EventType.MOUSEUP, target,
- goog.events.EventType.CLICK, target]);
+ if (bot.userAgent.IE_DOC_10) {
+ assertEvents(msPointerDownEvents(target), msPointerUpEvents(target));
+ } else {
+ assertEvents([goog.events.EventType.TOUCHSTART, target, 1,
+ goog.events.EventType.TOUCHEND, target, 1,
+ goog.events.EventType.MOUSEMOVE, target,
+ goog.events.EventType.MOUSEDOWN, target,
+ goog.events.EventType.MOUSEUP, target,
+ goog.events.EventType.CLICK, target]);
+ }
}
function testTouchScreenReleaseAfterMove() {
@@ -139,9 +202,15 @@
// After a move, the mouseclick should not happen.
touchscreen.move(target, new goog.math.Coordinate(10, 10));
touchscreen.release();
- assertEvents([goog.events.EventType.TOUCHSTART, target, 1,
- goog.events.EventType.TOUCHMOVE, target, 1,
- goog.events.EventType.TOUCHEND, target, 1]);
+ if (bot.userAgent.IE_DOC_10) {
+ assertEvents(msPointerDownEvents(target),
+ msPointerMoveEvents(target),
+ msPointerUpEvents(target));
+ } else {
+ assertEvents([goog.events.EventType.TOUCHSTART, target, 1,
+ goog.events.EventType.TOUCHMOVE, target, 1,
+ goog.events.EventType.TOUCHEND, target, 1]);
+ }
}
function testTouchScreenReleaseAfterNewElement() {
@@ -154,9 +223,15 @@
// events should fire on the element where the touch started.
touchscreen.move(lowerTarget, new goog.math.Coordinate(0, 0));
touchscreen.release();
- assertEvents([goog.events.EventType.TOUCHSTART, target, 1,
- goog.events.EventType.TOUCHMOVE, target, 1,
- goog.events.EventType.TOUCHEND, target, 1]);
+ if (bot.userAgent.IE_DOC_10) {
+ assertEvents(msPointerDownEvents(target),
+ msPointerMoveEvents(target),
+ msPointerUpEvents(lowerTarget));
+ } else {
+ assertEvents([goog.events.EventType.TOUCHSTART, target, 1,
+ goog.events.EventType.TOUCHMOVE, target, 1,
+ goog.events.EventType.TOUCHEND, target, 1]);
+ }
}
function testTouchScreenMoveWithoutPress() {
@@ -175,8 +250,13 @@
touchscreen.press();
touchscreen.move(target, new goog.math.Coordinate(20, 20));
assertTrue(touchscreen.isPressed());
- assertEvents([goog.events.EventType.TOUCHSTART, target, 1,
- goog.events.EventType.TOUCHMOVE, target, 1]);
+ if (bot.userAgent.IE_DOC_10) {
+ assertEvents(msPointerDownEvents(target),
+ msPointerMoveEvents(target));
+ } else {
+ assertEvents([goog.events.EventType.TOUCHSTART, target, 1,
+ goog.events.EventType.TOUCHMOVE, target, 1]);
+ }
}
function testExceptionReleasingWhenNotPressed() {
@@ -193,7 +273,7 @@
}
touchscreen.move(target, new goog.math.Coordinate(0, 0));
touchscreen.press();
- assertThrows('Pressing when alread pressed should throw an exception',
+ assertThrows('Pressing when already pressed should throw an exception',
goog.bind(touchscreen.press, touchscreen));
}
</script>
diff --git a/javascript/atoms/test/useragent_quirks_test.html b/javascript/atoms/test/useragent_quirks_test.html
new file mode 100644
index 0000000..aae45de
--- /dev/null
+++ b/javascript/atoms/test/useragent_quirks_test.html
@@ -0,0 +1,14 @@
+<html>
+<head>
+ <title>useragent_quirks_test</title>
+ <script src="test_bootstrap.js"></script>
+ <script type="text/javascript">
+ goog.require('bot.userAgent');
+ goog.require('goog.dom');
+ goog.require('goog.testing.jsunit');
+ goog.require('goog.userAgent');
+ goog.require('goog.userAgent.product');
+ </script>
+ <script src="useragent_test.js" type="text/javascript"></script>
+</head>
+</html>
diff --git a/javascript/atoms/test/useragent_test.html b/javascript/atoms/test/useragent_test.html
new file mode 100644
index 0000000..ef295e8
--- /dev/null
+++ b/javascript/atoms/test/useragent_test.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>useragent_test</title>
+ <script src="test_bootstrap.js"></script>
+ <script type="text/javascript">
+ goog.require('bot.userAgent');
+ goog.require('goog.dom');
+ goog.require('goog.testing.jsunit');
+ goog.require('goog.userAgent');
+ goog.require('goog.userAgent.product');
+ </script>
+ <script src="useragent_test.js" type="text/javascript"></script>
+</head>
+</html>
diff --git a/javascript/atoms/test/useragent_test.js b/javascript/atoms/test/useragent_test.js
new file mode 100644
index 0000000..a69349b
--- /dev/null
+++ b/javascript/atoms/test/useragent_test.js
@@ -0,0 +1,61 @@
+/**
+ * @fileoverview Common user agent tests.
+ * @author joonlee@google.com (Joon Lee)
+ */
+
+goog.require('goog.dom');
+goog.require('goog.userAgent');
+goog.require('goog.userAgent.product');
+
+var productVersion = parseFloat(goog.userAgent.product.ANDROID ?
+ bot.userAgent.ANDROID_VERSION_ : goog.userAgent.product.VERSION);
+
+var engineVersion = parseFloat(goog.userAgent.VERSION);
+if (goog.userAgent.IE && !goog.dom.isCss1CompatMode() && engineVersion < 10) {
+ engineVersion = 5;
+}
+
+function testIsEngineVersion() {
+ assertTrue(bot.userAgent.isEngineVersion(engineVersion));
+}
+
+function testIsEngineVersionLower() {
+ assertTrue(bot.userAgent.isEngineVersion(engineVersion - 1));
+}
+
+function testIsEngineVersionLittleHigher() {
+ assertFalse(bot.userAgent.isEngineVersion(engineVersion + 0.111));
+}
+
+function testIsEngineVersionHigher() {
+ assertFalse(bot.userAgent.isEngineVersion(engineVersion + 1));
+}
+
+function testIsEngineVersionLetters() {
+ assertTrue(bot.userAgent.isEngineVersion(engineVersion + 'a'));
+}
+
+function testIsProductVersion() {
+ assertTrue(bot.userAgent.isProductVersion(productVersion));
+}
+
+function testIsProductVersionLower() {
+ assertTrue(bot.userAgent.isProductVersion(productVersion - 1));
+}
+
+function testIsProductVersionHigher() {
+ assertFalse(bot.userAgent.isProductVersion(productVersion + 1));
+}
+
+function testProductVersionAtLeastEngineVersion_IE() {
+ if (goog.userAgent.IE) {
+ assertTrue(bot.userAgent.isProductVersion(engineVersion));
+ }
+}
+
+function testEngineVersionIsMajorProductVersionInStandardsMode_IE() {
+ if (goog.userAgent.IE && goog.dom.isCss1CompatMode()) {
+ var majorProductVersion = Math.floor(productVersion);
+ assertTrue(bot.userAgent.isEngineVersion(majorProductVersion));
+ }
+}
diff --git a/javascript/atoms/touchscreen.js b/javascript/atoms/touchscreen.js
index 4da27fd..677357d 100644
--- a/javascript/atoms/touchscreen.js
+++ b/javascript/atoms/touchscreen.js
@@ -106,7 +106,11 @@
this.touchIdentifier2_ = this.touchCounter_++;
}
- this.fireTouchEvent_(bot.events.EventType.TOUCHSTART);
+ if (bot.userAgent.IE_DOC_10) {
+ this.firePointerEvents_(bot.Touchscreen.fireSinglePressPointer_);
+ } else {
+ this.fireTouchEvent_(bot.events.EventType.TOUCHSTART);
+ }
};
@@ -120,26 +124,10 @@
'Cannot release touchscreen when not already pressed.');
}
- this.fireTouchEvent_(bot.events.EventType.TOUCHEND);
-
- // If no movement occurred since press, TouchScreen.Release will fire the
- // legacy mouse events: mousemove, mousedown, mouseup, and click
- // after the touch events have been fired. The click button should be zero
- // and only one mousemove should fire.
- if (!this.hasMovedAfterPress_) {
- this.fireMouseEvent(bot.events.EventType.MOUSEMOVE, this.clientXY_, 0);
- var performFocus = this.fireMouseEvent(bot.events.EventType.MOUSEDOWN,
- this.clientXY_, 0);
- // Element gets focus after the mousedown event only if the mousedown was
- // not cancelled.
- if (performFocus) {
- this.focusOnElement();
- }
-
- this.fireMouseEvent(bot.events.EventType.MOUSEUP, this.clientXY_, 0);
-
- // Special click logic to follow links and to perform form actions.
- this.clickElement(this.clientXY_, /* button value */ 0);
+ if (bot.userAgent.IE_DOC_10) {
+ this.firePointerEvents_(bot.Touchscreen.fireSingleReleasePointer_);
+ } else {
+ this.fireTouchReleaseEvents_();
}
this.touchIdentifier_ = 0;
this.touchIdentifier2_ = 0;
@@ -158,7 +146,8 @@
bot.Touchscreen.prototype.move = function(element, coords, opt_coords2) {
// The target element for touch actions is the original element. Hence, the
// element is set only when the touchscreen is not currently being pressed.
- if (!this.isPressed()) {
+ // The exception is IE10 which fire events on the moved to element.
+ if (!this.isPressed() || bot.userAgent.IE_DOC_10) {
this.setElement(element);
}
@@ -173,7 +162,11 @@
if (this.isPressed()) {
this.hasMovedAfterPress_ = true;
- this.fireTouchEvent_(bot.events.EventType.TOUCHMOVE);
+ if (bot.userAgent.IE_DOC_10) {
+ this.firePointerEvents_(bot.Touchscreen.fireSingleMovePointer_);
+ } else {
+ this.fireTouchEvent_(bot.events.EventType.TOUCHMOVE);
+ }
}
};
@@ -192,7 +185,6 @@
* A helper function to fire touch events.
*
* @param {bot.events.EventType} type Event type.
- * @return {boolean} Whether the event fired successfully or was cancelled.
* @private
*/
bot.Touchscreen.prototype.fireTouchEvent_ = function(type) {
@@ -206,6 +198,127 @@
touchIdentifier2 = this.touchIdentifier2_;
coords2 = this.clientXY2_;
}
- return this.fireTouchEvent(type, this.touchIdentifier_, this.clientXY_,
- touchIdentifier2, coords2);
+ this.fireTouchEvent(type, this.touchIdentifier_, this.clientXY_,
+ touchIdentifier2, coords2);
+};
+
+
+/**
+ * A helper function to fire touch events that occur on a release.
+ *
+ * @private
+ */
+bot.Touchscreen.prototype.fireTouchReleaseEvents_ = function() {
+ this.fireTouchEvent_(bot.events.EventType.TOUCHEND);
+
+ // If no movement occurred since press, TouchScreen.Release will fire the
+ // legacy mouse events: mousemove, mousedown, mouseup, and click
+ // after the touch events have been fired. The click button should be zero
+ // and only one mousemove should fire.
+ if (!this.hasMovedAfterPress_) {
+ this.fireMouseEvent(bot.events.EventType.MOUSEMOVE, this.clientXY_, 0);
+ var performFocus = this.fireMouseEvent(bot.events.EventType.MOUSEDOWN,
+ this.clientXY_, 0);
+ // Element gets focus after the mousedown event only if the mousedown was
+ // not cancelled.
+ if (performFocus) {
+ this.focusOnElement();
+ }
+ this.fireMouseEvent(bot.events.EventType.MOUSEUP, this.clientXY_, 0);
+
+ // Special click logic to follow links and to perform form actions.
+ this.clickElement(this.clientXY_, /* button value */ 0);
+ }
+};
+
+
+/**
+ * A helper function to fire a sequence of Pointer events.
+ * @param {function(!bot.Touchscreen, !goog.math.Coordinate, number, boolean)}
+ * fireSinglePointer A function that fires a set of events for one finger.
+ * @private
+ */
+bot.Touchscreen.prototype.firePointerEvents_ = function(fireSinglePointer) {
+ fireSinglePointer(this, this.clientXY_, this.touchIdentifier_, true);
+ if (this.touchIdentifier2_) {
+ fireSinglePointer(this, this.clientXY2_, this.touchIdentifier2_, false);
+ }
+};
+
+
+/**
+ * A helper function to fire Pointer events related to a press.
+ *
+ * @param {!bot.Touchscreen} ts A touchscreen object.
+ * @param {!goog.math.Coordinate} coords Coordinates relative to
+ * currentElement.
+ * @param {number} id The touch identifier.
+ * @param {boolean} isPrimary Whether the pointer represents the primary point
+ * of contact.
+ * @private
+ */
+bot.Touchscreen.fireSinglePressPointer_ = function(ts, coords, id, isPrimary) {
+ // Fire a mousemove event.
+ ts.fireMouseEvent(bot.events.EventType.MOUSEMOVE, coords, 0);
+
+ // Fire a MSPointerOver and mouseover events.
+ ts.fireMSPointerEvent(bot.events.EventType.MSPOINTEROVER, coords, 0, id,
+ MSPointerEvent.MSPOINTER_TYPE_TOUCH, isPrimary);
+ ts.fireMouseEvent(bot.events.EventType.MOUSEOVER, coords, 0);
+
+ // Fire a MSPointerDown and mousedown events.
+ ts.fireMSPointerEvent(bot.events.EventType.MSPOINTERDOWN, coords, 0, id,
+ MSPointerEvent.MSPOINTER_TYPE_TOUCH, isPrimary);
+
+ // Element gets focus after the mousedown event.
+ if (ts.fireMouseEvent(bot.events.EventType.MOUSEDOWN, coords, 0)) {
+ ts.focusOnElement();
+ }
+};
+
+
+/**
+ * A helper function to fire Pointer events related to a release.
+ *
+ * @param {!bot.Touchscreen} ts A touchscreen object.
+ * @param {!goog.math.Coordinate} coords Coordinates relative to
+ * currentElement.
+ * @param {number} id The touch identifier.
+ * @param {boolean} isPrimary Whether the pointer represents the primary point
+ * of contact.
+ * @private
+ */
+bot.Touchscreen.fireSingleReleasePointer_ = function(ts, coords, id,
+ isPrimary) {
+ // Fire a MSPointerUp and mouseup events.
+ ts.fireMSPointerEvent(bot.events.EventType.MSPOINTERUP, coords, 0, id,
+ MSPointerEvent.MSPOINTER_TYPE_TOUCH, isPrimary);
+ ts.fireMouseEvent(bot.events.EventType.MOUSEUP, coords, 0);
+
+ // Fire a click.
+ ts.clickElement(coords, 0);
+
+ // Fire a MSPointerOut and mouseout events.
+ ts.fireMSPointerEvent(bot.events.EventType.MSPOINTEROUT, coords, -1, id,
+ MSPointerEvent.MSPOINTER_TYPE_TOUCH, isPrimary);
+ ts.fireMouseEvent(bot.events.EventType.MOUSEOUT, coords, 0);
+};
+
+
+/**
+ * A helper function to fire Pointer events related to a move.
+ *
+ * @param {!bot.Touchscreen} ts A touchscreen object.
+ * @param {!goog.math.Coordinate} coords Coordinates relative to
+ * currentElement.
+ * @param {number} id The touch identifier.
+ * @param {boolean} isPrimary Whether the pointer represents the primary point
+ * of contact.
+ * @private
+ */
+bot.Touchscreen.fireSingleMovePointer_ = function(ts, coords, id, isPrimary) {
+ // Fire a MSPointerMove and mousemove events.
+ ts.fireMSPointerEvent(bot.events.EventType.MSPOINTERMOVE, coords, -1, id,
+ MSPointerEvent.MSPOINTER_TYPE_TOUCH, isPrimary);
+ ts.fireMouseEvent(bot.events.EventType.MOUSEMOVE, coords, 0);
};
diff --git a/javascript/atoms/userAgent.js b/javascript/atoms/userAgent.js
index 04ea0bb..a808ef1 100644
--- a/javascript/atoms/userAgent.js
+++ b/javascript/atoms/userAgent.js
@@ -43,7 +43,8 @@
if (bot.userAgent.FIREFOX_EXTENSION) {
return bot.userAgent.FIREFOX_EXTENSION_IS_ENGINE_VERSION_(version);
} else if (goog.userAgent.IE) {
- return goog.string.compareVersions(document.documentMode, version) >= 0;
+ return goog.string.compareVersions(
+ /** @type {number} */ (goog.userAgent.DOCUMENT_MODE), version) >= 0;
} else {
return goog.userAgent.isVersion(version);
}
@@ -183,11 +184,20 @@
/**
+ * Whether the current document is IE in a documentMode older than 8.
+ * @type {boolean}
+ * @const
+ */
+bot.userAgent.IE_DOC_PRE8 = goog.userAgent.IE &&
+ !goog.userAgent.isDocumentMode(8);
+
+
+/**
* Whether the current document is IE in IE9 (or newer) standards mode.
* @type {boolean}
* @const
*/
-bot.userAgent.IE_DOC_9 = goog.userAgent.IE && document.documentMode >= 9;
+bot.userAgent.IE_DOC_9 = goog.userAgent.isDocumentMode(9);
/**
@@ -195,4 +205,31 @@
* @type {boolean}
* @const
*/
-bot.userAgent.IE_DOC_PRE9 = goog.userAgent.IE && !bot.userAgent.IE_DOC_9;
+bot.userAgent.IE_DOC_PRE9 = goog.userAgent.IE &&
+ !goog.userAgent.isDocumentMode(9);
+
+
+/**
+ * Whether the current document is IE in IE10 (or newer) standards mode.
+ * @type {boolean}
+ * @const
+ */
+bot.userAgent.IE_DOC_10 = goog.userAgent.isDocumentMode(10);
+
+
+/**
+ * Whether the current document is IE in a documentMode older than 10.
+ * @type {boolean}
+ * @const
+ */
+bot.userAgent.IE_DOC_PRE10 = goog.userAgent.IE &&
+ !goog.userAgent.isDocumentMode(10);
+
+
+/**
+ * Whether the current browser is Android pre-gingerbread.
+ * @type {boolean}
+ * @const
+ */
+bot.userAgent.ANDROID_PRE_GINGERBREAD = goog.userAgent.product.ANDROID &&
+ !bot.userAgent.isProductVersion(2.3);
diff --git a/javascript/firefox-driver/extension/content/server.js b/javascript/firefox-driver/extension/content/server.js
index 5fe60a3..33565c1 100644
--- a/javascript/firefox-driver/extension/content/server.js
+++ b/javascript/firefox-driver/extension/content/server.js
@@ -34,8 +34,8 @@
// This will configure a FirefoxDriver and DomMessenger for each
// _browser window_ (not chrome window). Multiple tabs in the same window will
// share a FirefoxDriver and DomMessenger instance.
-window.addEventListener("load", function(e) {
- handle = Components.classes["@googlecode.com/webdriver/fxdriver;1"].createInstance();
+window.addEventListener('load', function(e) {
+ handle = Components.classes['@googlecode.com/webdriver/fxdriver;1'].createInstance();
var server = handle.wrappedJSObject;
if (!domMessenger) {
diff --git a/javascript/firefox-driver/js/badCertListener.js b/javascript/firefox-driver/js/badCertListener.js
index da949a9..28e54e1 100644
--- a/javascript/firefox-driver/js/badCertListener.js
+++ b/javascript/firefox-driver/js/badCertListener.js
@@ -25,14 +25,14 @@
goog.provide('WdCertOverrideService');
+goog.require('bot.userAgent');
goog.require('fxdriver.logging');
goog.require('fxdriver.moz');
-goog.require('bot.userAgent');
function getPreferenceFromProfile(prefName, prefDefaultValue) {
var prefs =
- CC["@mozilla.org/preferences-service;1"].getService(CI["nsIPrefBranch"]);
+ CC['@mozilla.org/preferences-service;1'].getService(CI['nsIPrefBranch']);
if (!prefs.prefHasUserValue(prefName)) {
fxdriver.logging.info(prefName + ' not set; defaulting to ' + prefDefaultValue);
@@ -40,7 +40,7 @@
}
var prefValue = prefs.getBoolPref(prefName);
- fxdriver.logging.info("Found preference for " + prefName + ": " + prefValue);
+ fxdriver.logging.info('Found preference for ' + prefName + ': ' + prefValue);
return prefValue;
}
@@ -49,11 +49,11 @@
// Defaults to true - accepting untrusted certificates.
// This puts the module into effect - setting it to false
// will delegate all calls to the original service.
- return getPreferenceFromProfile("webdriver_accept_untrusted_certs", true);
+ return getPreferenceFromProfile('webdriver_accept_untrusted_certs', true);
}
function shouldAssumeUntrustedIssuer() {
- return getPreferenceFromProfile("webdriver_assume_untrusted_issuer", true);
+ return getPreferenceFromProfile('webdriver_assume_untrusted_issuer', true);
}
WdCertOverrideService = function() {
@@ -69,10 +69,10 @@
this.default_bits = 0;
}
- fxdriver.logging.info("Accept untrusted certificates: " + shouldAcceptUntrustedCerts());
+ fxdriver.logging.info('Accept untrusted certificates: ' + shouldAcceptUntrustedCerts());
// UUID of the original implementor of this service.
- var ORIGINAL_OVERRIDE_SERVICE_ID = "{67ba681d-5485-4fff-952c-2ee337ffdcd6}";
+ var ORIGINAL_OVERRIDE_SERVICE_ID = '{67ba681d-5485-4fff-952c-2ee337ffdcd6}';
// Keep a reference to the original bad certificate listener.
var originalService = Components.classesByID[ORIGINAL_OVERRIDE_SERVICE_ID].
@@ -92,10 +92,10 @@
};
// Returns the bit needed to mask if the certificate has expired, 0 otherwise.
-WdCertOverrideService.prototype.certificateExpiredBit_ = function
- (theCert, verification_result) {
+WdCertOverrideService.prototype.certificateExpiredBit_ =
+ function(theCert, verification_result) {
if ((verification_result & theCert.CERT_EXPIRED) != 0) {
- fxdriver.logging.info("Certificate expired.");
+ fxdriver.logging.info('Certificate expired.');
return this.ERROR_TIME;
}
@@ -104,17 +104,17 @@
// Returns the bit needed to mask untrusted issuers, 0 otherwise.
// Note that this bit is already set by default in default_bits
-WdCertOverrideService.prototype.certificateIssuerUntrusted_ = function
- (theCert, verification_result) {
+WdCertOverrideService.prototype.certificateIssuerUntrusted_ =
+ function(theCert, verification_result) {
if (((verification_result & theCert.ISSUER_UNKNOWN) != 0) ||
((verification_result & theCert.ISSUER_NOT_TRUSTED) != 0) ||
((verification_result & theCert.CERT_NOT_TRUSTED) != 0) ||
((verification_result & theCert.INVALID_CA) != 0)) {
- fxdriver.logging.info("Certificate issuer unknown.");
- fxdriver.logging.info("Unknown: " + (theCert.ISSUER_UNKNOWN & verification_result));
- fxdriver.logging.info("Issuer not trusted: " + (theCert.ISSUER_NOT_TRUSTED & verification_result));
- fxdriver.logging.info("Cert not trusted: " + (theCert.CERT_NOT_TRUSTED & verification_result));
- fxdriver.logging.info("Invalid CA: " + (theCert.INVALID_CA & verification_result));
+ fxdriver.logging.info('Certificate issuer unknown.');
+ fxdriver.logging.info('Unknown: ' + (theCert.ISSUER_UNKNOWN & verification_result));
+ fxdriver.logging.info('Issuer not trusted: ' + (theCert.ISSUER_NOT_TRUSTED & verification_result));
+ fxdriver.logging.info('Cert not trusted: ' + (theCert.CERT_NOT_TRUSTED & verification_result));
+ fxdriver.logging.info('Invalid CA: ' + (theCert.INVALID_CA & verification_result));
return this.ERROR_UNTRUSTED;
}
@@ -124,11 +124,11 @@
// Returns the bit needed to mask mismatch between actual hostname
// and the hostname the certificate was issued for, 0 otherwise.
-WdCertOverrideService.prototype.certificateHostnameMismatch_ = function
- (theCert, aHost) {
- var commonNameRE = new RegExp("^" + theCert.commonName.replace('*', '[\\w|\-]+') + "$", "i");
+WdCertOverrideService.prototype.certificateHostnameMismatch_ =
+ function(theCert, aHost) {
+ var commonNameRE = new RegExp('^' + theCert.commonName.replace('*', '[\\w|\-]+') + '$', 'i');
if (aHost.match(commonNameRE) === null) {
- fxdriver.logging.info("Host name mismatch: cert: " + theCert.commonName + " get: " + aHost);
+ fxdriver.logging.info('Host name mismatch: cert: ' + theCert.commonName + ' get: ' + aHost);
return this.ERROR_MISMATCH;
}
@@ -136,7 +136,7 @@
};
// Given a certificate and the host it was received for, fill in the bits
-// needed to accept this certificate for this host, even though the
+// needed to accept this certificate for this host, even though the
// certificate is invalid.
//
// Note that the bitmask has to be accurate: At the moment, Firefox expects
@@ -147,7 +147,7 @@
var verification_bits = aCert.verifyForUsage(aCert.CERT_USAGE_SSLClient);
var return_bits = this.default_bits;
- fxdriver.logging.info("Certificate verification results: " + verification_bits);
+ fxdriver.logging.info('Certificate verification results: ' + verification_bits);
return_bits = return_bits | this.certificateExpiredBit_(
aCert, verification_bits);
@@ -159,12 +159,12 @@
// It has been observed that if there's a host name mismatch then it
// may not be required to check the trust status of the certificate issuer.
if (return_bits == 0) {
- fxdriver.logging.info("Checking issuer since certificate has not expired or has a host name mismatch.");
+ fxdriver.logging.info('Checking issuer since certificate has not expired or has a host name mismatch.');
return_bits = return_bits | this.certificateIssuerUntrusted_(
aCert, verification_bits);
}
- fxdriver.logging.info("return_bits now: " + return_bits);
+ fxdriver.logging.info('return_bits now: ' + return_bits);
return return_bits;
};
@@ -175,12 +175,12 @@
var acceptAll = shouldAcceptUntrustedCerts();
if (acceptAll === true) {
- fxdriver.logging.info("Allowing certificate from site: " + aHostName + ":" + aPort);
+ fxdriver.logging.info('Allowing certificate from site: ' + aHostName + ':' + aPort);
retval = true;
aIsTemporary.value = false;
aOverrideBits.value = this.fillNeededBits(aCert, aHostName);
- fxdriver.logging.info("Override Bits: " + aOverrideBits.value);
+ fxdriver.logging.info('Override Bits: ' + aOverrideBits.value);
} else {
retval = this.origListener_.hasMatchingOverride(aHostName, aPort,
aCert, aOverrideBits, aIsTemporary);
@@ -230,7 +230,7 @@
};
// Service contract ID which we override
-/** @const */ var CERTOVERRIDE_CONTRACT_ID = "@mozilla.org/security/certoverride;1";
+/** @const */ var CERTOVERRIDE_CONTRACT_ID = '@mozilla.org/security/certoverride;1';
// UUID for this instance specifically.
/** @const */ var DUMMY_CERTOVERRIDE_SERVICE_CLASS_ID =
Components.ID('{c8fffaba-9b7a-41aa-872d-7e7366c16715}');
@@ -238,7 +238,7 @@
var service = undefined;
var WDCertOverrideFactory = {
- createInstance: function (aOuter, aIID) {
+ createInstance: function(aOuter, aIID) {
if (aOuter != null)
throw CR['NS_ERROR_NO_AGGREGATION'];
if (service == undefined) {
@@ -260,17 +260,17 @@
throw CR['NS_ERROR_FACTORY_REGISTER_AGAIN'];
}
- fxdriver.logging.info("Registering Override Certificate service.");
+ fxdriver.logging.info('Registering Override Certificate service.');
aCompMgr = aCompMgr.QueryInterface(
CI['nsIComponentRegistrar']);
aCompMgr.registerFactoryLocation(
- DUMMY_CERTOVERRIDE_SERVICE_CLASS_ID, "badCertListener",
+ DUMMY_CERTOVERRIDE_SERVICE_CLASS_ID, 'badCertListener',
CERTOVERRIDE_CONTRACT_ID, aFileSpec, aLocation, aType);
};
WDBadCertListenerModule.prototype.unregisterSelf = function(
aCompMgr, aLocation, aType) {
- fxdriver.logging.info("Un-registering Override Certificate service.");
+ fxdriver.logging.info('Un-registering Override Certificate service.');
aCompMgr =
aCompMgr.QueryInterface(CI['nsIComponentRegistrar']);
aCompMgr.unregisterFactoryLocation(DUMMY_CERTOVERRIDE_SERVICE_CLASS_ID, aLocation);
@@ -279,9 +279,9 @@
var FACTORY;
-if (!bot.userAgent.isProductVersion('10')){
+if (!bot.userAgent.isProductVersion('10')) {
/** @const */ FACTORY = {
- createInstance: function (aOuter, aIID) {
+ createInstance: function(aOuter, aIID) {
if (aOuter != null)
throw CR['NS_ERROR_NO_AGGREGATION'];
@@ -291,8 +291,8 @@
var raw = new WdCertOverrideService();
- var mainThread = CC["@mozilla.org/thread-manager;1"].getService().mainThread;
- var proxyManager = CC["@mozilla.org/xpcomproxy;1"]
+ var mainThread = CC['@mozilla.org/thread-manager;1'].getService().mainThread;
+ var proxyManager = CC['@mozilla.org/xpcomproxy;1']
.getService(CI.nsIProxyObjectManager);
// 5 == NS_PROXY_ALWAYS | NS_PROXY_SYNC
@@ -313,8 +313,8 @@
if (aCID.equals(DUMMY_CERTOVERRIDE_SERVICE_CLASS_ID)) {
if (bot.userAgent.isProductVersion('10')) {
- return WDCertOverrideFactory;
- } else{
+ return WDCertOverrideFactory;
+ } else {
return FACTORY;
}
}
@@ -332,7 +332,7 @@
WdCertOverrideService.prototype.classID = DUMMY_CERTOVERRIDE_SERVICE_CLASS_ID;
-fxdriver.moz.load("resource://gre/modules/XPCOMUtils.jsm");
+fxdriver.moz.load('resource://gre/modules/XPCOMUtils.jsm');
if (XPCOMUtils.generateNSGetFactory) {
/** @const */ NSGetFactory = XPCOMUtils.generateNSGetFactory([WdCertOverrideService]);
}
diff --git a/javascript/firefox-driver/js/dispatcher.js b/javascript/firefox-driver/js/dispatcher.js
index 1317deb..6bae4b6 100644
--- a/javascript/firefox-driver/js/dispatcher.js
+++ b/javascript/firefox-driver/js/dispatcher.js
@@ -19,12 +19,12 @@
goog.provide('Dispatcher');
goog.provide('Resource');
+goog.require('Request');
+goog.require('Response');
+goog.require('Utils');
goog.require('bot.ErrorCode');
goog.require('fxdriver.error');
goog.require('fxdriver.logging');
-goog.require('Response');
-goog.require('Request');
-goog.require('Utils');
/**
@@ -121,7 +121,7 @@
var url = request.getRequestUrl();
response.setStatus(Response.SEE_OTHER);
response.setHeader('Location',
- url.scheme + '://' + url.host + ":" + url.hostPort + url.path + '/' +
+ url.scheme + '://' + url.host + ':' + url.hostPort + url.path + '/' +
jsonResponse.value);
response.commit();
}
@@ -329,7 +329,7 @@
on(Request.Method.GET, Dispatcher.executeAs('isOnline'));
this.bind_('/session/:sessionId/application_cache/status').
- on(Request.Method.GET, Dispatcher.executeAs('getAppCacheStatus'))
+ on(Request.Method.GET, Dispatcher.executeAs('getAppCacheStatus'));
// --------------------------------------------------------------------------
// Firefox extensions to the wire protocol.
diff --git a/javascript/firefox-driver/js/events.js b/javascript/firefox-driver/js/events.js
index 835ae14..c0b8884 100644
--- a/javascript/firefox-driver/js/events.js
+++ b/javascript/firefox-driver/js/events.js
@@ -18,10 +18,10 @@
goog.provide('fxdriver.events');
+goog.require('Utils');
goog.require('fxdriver.logging');
goog.require('fxdriver.moz');
goog.require('goog.style');
-goog.require('Utils');
/**
@@ -43,8 +43,8 @@
if (goog.isNull(x) && element) {
var size = goog.style.getSize(element);
- x = size.width / 2;
- y = size.height / 2;
+ x = size.width / 2;
+ y = size.height / 2;
}
return {
diff --git a/javascript/firefox-driver/js/firefox-utils.js b/javascript/firefox-driver/js/firefox-utils.js
index d1eed50..00e5851 100644
--- a/javascript/firefox-driver/js/firefox-utils.js
+++ b/javascript/firefox-driver/js/firefox-utils.js
@@ -24,9 +24,9 @@
goog.provide('fxdriver.utils');
-goog.require('bot.userAgent');
goog.require('bot.Error');
goog.require('bot.ErrorCode');
+goog.require('bot.userAgent');
goog.require('fxdriver.moz');
goog.require('goog.array');
@@ -89,16 +89,16 @@
// TODO(simon): initialize this statically.
if (!fxdriver.utils._generator) {
fxdriver.utils._generator =
- fxdriver.moz.getService("@mozilla.org/uuid-generator;1", "nsIUUIDGenerator");
+ fxdriver.moz.getService('@mozilla.org/uuid-generator;1', 'nsIUUIDGenerator');
}
return fxdriver.utils._generator.generateUUID().toString();
};
/**
- * @param {!Element} element The element to use
- * @param {int} x X coordinate
- * @param {int} y Y coordinate
+ * @param {!Element} element The element to use.
+ * @param {int} x X coordinate.
+ * @param {int} y Y coordinate.
*/
fxdriver.utils.newCoordinates = function(element, x, y) {
return {
@@ -112,4 +112,4 @@
x: x,
y: y
};
-}
+};
diff --git a/javascript/firefox-driver/js/firefoxDriver.js b/javascript/firefox-driver/js/firefoxDriver.js
index 8d07bbb..48462b1 100644
--- a/javascript/firefox-driver/js/firefoxDriver.js
+++ b/javascript/firefox-driver/js/firefoxDriver.js
@@ -21,13 +21,13 @@
goog.require('Utils');
goog.require('WebLoadingListener');
goog.require('bot.ErrorCode');
+goog.require('bot.appcache');
+goog.require('bot.connection');
goog.require('bot.dom');
goog.require('bot.frame');
goog.require('bot.locators');
goog.require('bot.userAgent');
goog.require('bot.window');
-goog.require('bot.connection');
-goog.require('bot.appcache');
goog.require('fxdriver.Timer');
goog.require('fxdriver.events');
goog.require('fxdriver.io');
@@ -37,9 +37,10 @@
goog.require('fxdriver.screenshot');
goog.require('fxdriver.utils');
goog.require('goog.array');
+goog.require('goog.dom');
goog.require('goog.dom.selection');
-goog.require('goog.math');
-goog.require('goog.userAgent');
+goog.require('goog.math.Coordinate');
+goog.require('goog.math.Size');
FirefoxDriver = function(server, enableNativeEvents, win) {
@@ -56,25 +57,25 @@
// This really shouldn't be here, but the firefoxdriver isn't compiled with closure, so the atoms
// aren't exported into global scope
FirefoxDriver.prototype.dismissAlert.preconditions =
- [ function() { fxdriver.preconditions.alertPresent(this) } ];
+ [function() { fxdriver.preconditions.alertPresent(this) }];
FirefoxDriver.prototype.acceptAlert.preconditions =
- [ function() { fxdriver.preconditions.alertPresent(this) } ];
+ [function() { fxdriver.preconditions.alertPresent(this) }];
FirefoxDriver.prototype.getAlertText.preconditions =
- [ function() { fxdriver.preconditions.alertPresent(this) } ];
+ [function() { fxdriver.preconditions.alertPresent(this) }];
FirefoxDriver.prototype.setAlertValue.preconditions =
- [ function() { fxdriver.preconditions.alertPresent(this) } ];
+ [function() { fxdriver.preconditions.alertPresent(this) }];
- FirefoxDriver.listenerScript = Utils.loadUrl("resource://fxdriver/evaluate.js");
+ FirefoxDriver.listenerScript = Utils.loadUrl('resource://fxdriver/evaluate.js');
this.jsTimer = new fxdriver.Timer();
- this.mouse = Utils.newInstance("@googlecode.com/webdriver/syntheticmouse;1", "wdIMouse");
+ this.mouse = Utils.newInstance('@googlecode.com/webdriver/syntheticmouse;1', 'wdIMouse');
// Current state of modifier keys (for synthenized events).
- this.modifierKeysState = Utils.newInstance("@googlecode.com/webdriver/modifierkeys;1", "wdIModifierKeys");
+ this.modifierKeysState = Utils.newInstance('@googlecode.com/webdriver/modifierkeys;1', 'wdIModifierKeys');
this.mouse.initialize(this.modifierKeysState);
if (!bot.userAgent.isProductVersion('3.5')) {
- fxdriver.logging.info("Replacing CSS lookup mechanism with Sizzle");
+ fxdriver.logging.info('Replacing CSS lookup mechanism with Sizzle');
var cssSelectorFunction = (function() {
var sizzle = [
'var originalSizzle = window.Sizzle;',
@@ -123,7 +124,7 @@
text == target;
});
}
- }
+ };
})();
@@ -137,7 +138,7 @@
};
-FirefoxDriver.prototype.__defineGetter__("id", function() {
+FirefoxDriver.prototype.__defineGetter__('id', function() {
if (!this.id_) {
this.id_ = this.server.getNextId();
}
@@ -181,7 +182,7 @@
respond.sendError(new WebDriverError(bot.ErrorCode.TIMEOUT,
'Timed out waiting for page load.'));
} else {
- respond.value = "";
+ respond.value = '';
respond.send();
}
}, respond.session.getPageLoadTimeout(), respond.session.getWindow());
@@ -190,7 +191,7 @@
respond.session.getBrowser().loadURI(url);
if (!loadEventExpected) {
- fxdriver.logging.info("No load event expected");
+ fxdriver.logging.info('No load event expected');
respond.send();
}
};
@@ -199,13 +200,13 @@
FirefoxDriver.prototype.close = function(respond) {
// Grab all the references we'll need. Once we call close all this might go away
var wm = fxdriver.moz.getService(
- "@mozilla.org/appshell/window-mediator;1", "nsIWindowMediator");
+ '@mozilla.org/appshell/window-mediator;1', 'nsIWindowMediator');
var appService = fxdriver.moz.getService(
- "@mozilla.org/toolkit/app-startup;1", "nsIAppStartup");
+ '@mozilla.org/toolkit/app-startup;1', 'nsIAppStartup');
var forceQuit = Components.interfaces.nsIAppStartup.eForceQuit;
var numOpenWindows = 0;
- var allWindows = wm.getEnumerator("navigator:browser");
+ var allWindows = wm.getEnumerator('navigator:browser');
while (allWindows.hasMoreElements()) {
numOpenWindows += 1;
allWindows.getNext();
@@ -236,7 +237,7 @@
var browser = respond.session.getBrowser();
notifyOfCloseWindow(browser.id);
browser.contentWindow.close();
- } catch(e) {
+ } catch (e) {
fxdriver.logging.warning(e);
}
@@ -252,7 +253,7 @@
var script = parameters['script'];
var converted = Utils.unwrapParameters(parameters['args'], doc);
- if (doc.designMode && "on" == doc.designMode.toLowerCase()) {
+ if (doc.designMode && 'on' == doc.designMode.toLowerCase()) {
if (isAsync) {
respond.sendError(
'Document designMode is enabled; advanced operations, ' +
@@ -264,7 +265,7 @@
}
// See https://developer.mozilla.org/en/rich-text_editing_in_mozilla#Internet_Explorer_Differences
- fxdriver.logging.info("Window in design mode, falling back to sandbox: " + doc.designMode);
+ fxdriver.logging.info('Window in design mode, falling back to sandbox: ' + doc.designMode);
var window = respond.session.getWindow();
window = window.wrappedJSObject;
var sandbox = new Components.utils.Sandbox(window);
@@ -274,8 +275,8 @@
sandbox.__webdriverParams = converted;
try {
- var scriptSrc = "with(window) { var __webdriverFunc = function(){" + parameters.script +
- "}; __webdriverFunc.apply(null, __webdriverParams); }";
+ var scriptSrc = 'with(window) { var __webdriverFunc = function(){' + parameters.script +
+ '}; __webdriverFunc.apply(null, __webdriverParams); }';
var res = Components.utils.evalInSandbox(scriptSrc, sandbox);
respond.value = Utils.wrapResult(res, doc);
respond.send();
@@ -292,7 +293,7 @@
// The modal detection code in modals.js deals with throwing an
// exception, in the other case.
respond.sendError(new WebDriverError(bot.ErrorCode.JAVASCRIPT_ERROR,
- "waiting for doc.body failed"));
+ 'waiting for doc.body failed'));
}
};
@@ -301,7 +302,7 @@
// The modal detection code in modals.js deals with throwing an
// exception, in the other case.
respond.sendError(new WebDriverError(bot.ErrorCode.JAVASCRIPT_ERROR,
- "waiting for evaluate.js load failed"));
+ 'waiting for evaluate.js load failed'));
}
};
@@ -345,8 +346,8 @@
// Attach the listener to the DOM
var addListener = function() {
if (!doc.getUserData('webdriver-evaluate-attached')) {
- var element = doc.createElement("script");
- element.setAttribute("type", "text/javascript");
+ var element = doc.createElement('script');
+ element.setAttribute('type', 'text/javascript');
element.innerHTML = FirefoxDriver.listenerScript;
doc.body.appendChild(element);
element.parentNode.removeChild(element);
@@ -359,7 +360,7 @@
};
timer.runWhenTrue(checkDocBodyLoaded, addListener, 10000, docBodyLoadTimeOut);
-};
+}
FirefoxDriver.prototype.executeScript = function(respond, parameters) {
@@ -384,7 +385,7 @@
var docElement = win.document.documentElement;
if (!docElement) {
// empty string means no DOM element available (the page is probably rebuilding at the moment)
- respond.value = "";
+ respond.value = '';
respond.send();
return;
}
@@ -413,7 +414,7 @@
* @private
*/
FirefoxDriver.annotateInvalidSelectorError_ = function(selector, ex) {
- if(ex.code == bot.ErrorCode.INVALID_SELECTOR_ERROR) {
+ if (ex.code == bot.ErrorCode.INVALID_SELECTOR_ERROR) {
return new WebDriverError(bot.ErrorCode.INVALID_SELECTOR_ERROR,
'The given selector ' + selector +
' is either invalid or does not result' +
@@ -422,8 +423,8 @@
try {
var converted = ex.QueryInterface(Components.interfaces['nsIException']);
- fxdriver.logging.info("Converted the exception: " + converted.name);
- if ("NS_ERROR_DOM_SYNTAX_ERR" == converted.name) {
+ fxdriver.logging.info('Converted the exception: ' + converted.name);
+ if ('NS_ERROR_DOM_SYNTAX_ERR' == converted.name) {
return new WebDriverError(bot.ErrorCode.INVALID_SELECTOR_ERROR,
'The given selector ' + selector +
' is either invalid or does not result' +
@@ -617,26 +618,26 @@
var switchingToDefault = !goog.isDef(parameters.id) || goog.isNull(parameters.id);
if ((!currentWindow || currentWindow.closed) && !switchingToDefault) {
// By definition there will be no child frames.
- respond.sendError(new WebDriverError(bot.ErrorCode.NO_SUCH_FRAME, "Current window is closed"));
+ respond.sendError(new WebDriverError(bot.ErrorCode.NO_SUCH_FRAME, 'Current window is closed'));
}
var newWindow = null;
if (switchingToDefault) {
- fxdriver.logging.info("Switching to default content (topmost frame)");
+ fxdriver.logging.info('Switching to default content (topmost frame)');
newWindow = respond.session.getBrowser().contentWindow;
} else if (goog.isString(parameters.id)) {
- fxdriver.logging.info("Switching to frame with name or ID: " + parameters.id);
+ fxdriver.logging.info('Switching to frame with name or ID: ' + parameters.id);
newWindow = bot.frame.findFrameByNameOrId(parameters.id, currentWindow);
} else if (goog.isNumber(parameters.id)) {
- fxdriver.logging.info("Switching to frame by index: " + parameters.id);
+ fxdriver.logging.info('Switching to frame by index: ' + parameters.id);
newWindow = bot.frame.findFrameByIndex(parameters.id, currentWindow);
} else if (goog.isObject(parameters.id) && 'ELEMENT' in parameters.id) {
- fxdriver.logging.info("Switching to frame by element: " + parameters.id['ELEMENT']);
+ fxdriver.logging.info('Switching to frame by element: ' + parameters.id['ELEMENT']);
var element = Utils.getElementAt(parameters.id['ELEMENT'],
currentWindow.document);
- element = fxdriver.moz.unwrapFor4(element)
+ element = fxdriver.moz.unwrapFor4(element);
if (/^i?frame$/i.test(element.tagName)) {
// Each session maintains a weak reference to the window it is currently
@@ -667,7 +668,7 @@
var element = Utils.getActiveElement(respond.session.getDocument());
var id = Utils.addToKnownElements(element);
- respond.value = {'ELEMENT':id};
+ respond.value = {'ELEMENT': id};
respond.send();
};
@@ -728,7 +729,7 @@
var currDomain = currLocation.host;
if (currDomain.indexOf(cookie.domain) == -1) { // Not quite right, but close enough
throw new WebDriverError(bot.ErrorCode.INVALID_COOKIE_DOMAIN,
- "You may only set cookies for the current domain");
+ 'You may only set cookies for the current domain');
}
}
@@ -736,17 +737,17 @@
// We'll catch ip6 addresses by mistake. Since no-one uses those
// this will be okay for now.
if (cookie.domain.match(/:\d+$/)) {
- cookie.domain = cookie.domain.replace(/:\d+$/, "");
+ cookie.domain = cookie.domain.replace(/:\d+$/, '');
}
var document = respond.session.getDocument();
if (!document || !document.contentType.match(/html/i)) {
throw new WebDriverError(bot.ErrorCode.UNABLE_TO_SET_COOKIE,
- "You may only set cookies on html documents");
+ 'You may only set cookies on html documents');
}
var cookieManager =
- fxdriver.moz.getService("@mozilla.org/cookiemanager;1", "nsICookieManager2");
+ fxdriver.moz.getService('@mozilla.org/cookiemanager;1', 'nsICookieManager2');
// The signature for "add" is different in firefox 3 and 2. We should sniff
// the browser version and call the right version of the method, but for now
@@ -754,7 +755,7 @@
try {
cookieManager.add(cookie.domain, cookie.path, cookie.name, cookie.value,
cookie.secure, false, cookie.expiry);
- } catch(e) {
+ } catch (e) {
cookieManager.add(cookie.domain, cookie.path, cookie.name, cookie.value,
cookie.secure, false, false, cookie.expiry);
}
@@ -766,25 +767,25 @@
var results = [];
var currentPath = location.pathname;
- if (!currentPath) currentPath = "/";
+ if (!currentPath) currentPath = '/';
var isForCurrentPath = function(aPath) {
return currentPath.indexOf(aPath) != -1;
};
- var cm = fxdriver.moz.getService("@mozilla.org/cookiemanager;1", "nsICookieManager");
+ var cm = fxdriver.moz.getService('@mozilla.org/cookiemanager;1', 'nsICookieManager');
var e = cm.enumerator;
while (e.hasMoreElements()) {
- var cookie = e.getNext().QueryInterface(Components.interfaces["nsICookie"]);
+ var cookie = e.getNext().QueryInterface(Components.interfaces['nsICookie']);
// Take the hostname and progressively shorten it
var hostname = location.hostname;
do {
- if ((cookie.host == "." + hostname || cookie.host == hostname)
+ if ((cookie.host == '.' + hostname || cookie.host == hostname)
&& isForCurrentPath(cookie.path)) {
results.push(cookie);
break;
}
- hostname = hostname.replace(/^.*?\./, "");
- } while (hostname.indexOf(".") != -1);
+ hostname = hostname.replace(/^.*?\./, '');
+ } while (hostname.indexOf('.') != -1);
}
return results;
@@ -821,7 +822,7 @@
// doesn't always do The Right Thing
FirefoxDriver.prototype.deleteCookie = function(respond, parameters) {
var toDelete = parameters.name;
- var cm = fxdriver.moz.getService("@mozilla.org/cookiemanager;1", "nsICookieManager");
+ var cm = fxdriver.moz.getService('@mozilla.org/cookiemanager;1', 'nsICookieManager');
var cookies = getVisibleCookies(respond.session.getBrowser().
contentWindow.location);
@@ -837,7 +838,7 @@
FirefoxDriver.prototype.deleteAllCookies = function(respond) {
- var cm = fxdriver.moz.getService("@mozilla.org/cookiemanager;1", "nsICookieManager");
+ var cm = fxdriver.moz.getService('@mozilla.org/cookiemanager;1', 'nsICookieManager');
var cookies = getVisibleCookies(respond.session.getBrowser().
contentWindow.location);
@@ -889,11 +890,11 @@
var canvas = fxdriver.screenshot.grab(window);
try {
fxdriver.screenshot.save(canvas, pngFile);
- } catch(e) {
+ } catch (e) {
throw new WebDriverError(bot.ErrorCode.UNKNOWN_ERROR,
'Could not save screenshot to ' + pngFile + ' - ' + e);
}
- } catch(e) {
+ } catch (e) {
throw new WebDriverError(bot.ErrorCode.UNKNOWN_ERROR,
'Could not take screenshot of current page - ' + e);
}
@@ -942,7 +943,7 @@
fxdriver.modals.isModalPresent(
function(present) {
if (present) {
- respond.value = fxdriver.modals.getText(driver)
+ respond.value = fxdriver.modals.getText(driver);
} else {
respond.status = bot.ErrorCode.NO_MODAL_DIALOG_OPEN;
respond.value = { message: 'No alert is present' };
@@ -970,7 +971,7 @@
respond.value = returnArray;
} catch (e) {
throw new WebDriverError(bot.ErrorCode.IME_NOT_AVAILABLE,
- "IME not available on the host: " + e);
+ 'IME not available on the host: ' + e);
}
respond.send();
};
@@ -983,7 +984,7 @@
respond.value = activeEngine.value;
} catch (e) {
throw new WebDriverError(bot.ErrorCode.IME_NOT_AVAILABLE,
- "IME not available on the host: " + e);
+ 'IME not available on the host: ' + e);
}
respond.send();
};
@@ -996,7 +997,7 @@
respond.value = isActive.value;
} catch (e) {
throw new WebDriverError(bot.ErrorCode.IME_NOT_AVAILABLE,
- "IME not available on the host: " + e);
+ 'IME not available on the host: ' + e);
}
respond.send();
};
@@ -1007,7 +1008,7 @@
obj.imeDeactivate();
} catch (e) {
throw new WebDriverError(bot.ErrorCode.IME_NOT_AVAILABLE,
- "IME not available on the host: " + e);
+ 'IME not available on the host: ' + e);
}
respond.send();
@@ -1021,12 +1022,12 @@
obj.imeActivateEngine(engineToActivate, successfulActivation);
} catch (e) {
throw new WebDriverError(bot.ErrorCode.IME_NOT_AVAILABLE,
- "IME not available on the host: " + e);
+ 'IME not available on the host: ' + e);
}
if (! successfulActivation.value) {
throw new WebDriverError(bot.ErrorCode.IME_ENGINE_ACTIVATION_FAILED,
- "Activation of engine failed: " + engineToActivate);
+ 'Activation of engine failed: ' + engineToActivate);
}
respond.send();
};
@@ -1050,28 +1051,28 @@
if (mouseLocation.initialized) {
elementForNode = doc.elementFromPoint(locationX, locationY);
- fxdriver.logging.info("Element from (" + locationX + "," + locationY + ") :" + elementForNode);
+ fxdriver.logging.info('Element from (' + locationX + ',' + locationY + ') :' + elementForNode);
} else {
- fxdriver.logging.info("Mouse coordinates were not set - using body");
- elementForNode = doc.getElementsByTagName("body")[0];
+ fxdriver.logging.info('Mouse coordinates were not set - using body');
+ elementForNode = doc.getElementsByTagName('body')[0];
}
return fxdriver.moz.unwrap(elementForNode);
}
function generateErrorForNativeEvents(nativeEventsEnabled, nativeEventsObj, nodeForInteraction) {
- var nativeEventFailureCause = "Could not get node for element or native " +
- "events are not supported on the platform.";
+ var nativeEventFailureCause = 'Could not get node for element or native ' +
+ 'events are not supported on the platform.';
if (!nativeEventsEnabled) {
- nativeEventFailureCause = "native events are disabled on this platform.";
+ nativeEventFailureCause = 'native events are disabled on this platform.';
} else if (!nativeEventsObj) {
- nativeEventFailureCause = "Could not load native events component.";
+ nativeEventFailureCause = 'Could not load native events component.';
} else {
- nativeEventFailureCause = "Could not get node for element - cannot interact.";
+ nativeEventFailureCause = 'Could not get node for element - cannot interact.';
}
// TODO: use the correct error type here.
return new WebDriverError(bot.ErrorCode.INVALID_ELEMENT_STATE,
- "Cannot perform native interaction: " + nativeEventFailureCause);
+ 'Cannot perform native interaction: ' + nativeEventFailureCause);
}
FirefoxDriver.prototype.sendResponseFromSyntheticMouse_ = function(mouseReturnValue, respond) {
@@ -1102,7 +1103,7 @@
// Fast path first
if (!this.enableNativeEvents) {
var target = parameters['element'] ? Utils.getElementAt(parameters['element'], doc) : null;
- fxdriver.logging.info("Calling move with: " + parameters['xoffset'] + ', ' + parameters['yoffset'] + ", " + target);
+ fxdriver.logging.info('Calling move with: ' + parameters['xoffset'] + ', ' + parameters['yoffset'] + ', ' + target);
var result = this.mouse.move(target, parameters['xoffset'], parameters['yoffset']);
this.sendResponseFromSyntheticMouse_(result, respond);
@@ -1134,7 +1135,7 @@
} catch (ex) {
if (ex.code == bot.ErrorCode.MOVE_TARGET_OUT_OF_BOUNDS) {
respond.sendError(new WebDriverError(bot.ErrorCode.MOVE_TARGET_OUT_OF_BOUNDS,
- "Given coordinates (" + clickPoint_ownerDocumentPreScroll.x + ", " + clickPoint_ownerDocumentPreScroll.y + ") are outside the document. Error: " + ex));
+ 'Given coordinates (' + clickPoint_ownerDocumentPreScroll.x + ', ' + clickPoint_ownerDocumentPreScroll.y + ') are outside the document. Error: ' + ex));
return;
}
else {
@@ -1154,8 +1155,8 @@
if (nativeEventsEnabled && nativeMouse && node) {
var currentPosition = respond.session.getMousePosition();
var currentPosition_windowHandle = {x: currentPosition.x + browserOffset.x, y: currentPosition.y + browserOffset.y};
- fxdriver.logging.info("Moving from (" + currentPosition.x + ", " + currentPosition.y + ") to (" +
- clickPoint_ownerDocumentPostScroll.x + ", " + clickPoint_ownerDocumentPostScroll.y + ")");
+ fxdriver.logging.info('Moving from (' + currentPosition.x + ', ' + currentPosition.y + ') to (' +
+ clickPoint_ownerDocumentPostScroll.x + ', ' + clickPoint_ownerDocumentPostScroll.y + ')');
nativeMouse.mouseMove(node,
currentPosition_windowHandle.x, currentPosition_windowHandle.y,
mouseTarget_ownerDocument_windowHandle.x, mouseTarget_ownerDocument_windowHandle.y);
@@ -1241,8 +1242,8 @@
if (isMouseButtonPressed) {
upX = currentPosition.viewPortXOffset;
upY = currentPosition.viewPortYOffset;
- fxdriver.logging.info("Button pressed. Using coordiantes with viewport offset: "
- + upX + ", " + upY);
+ fxdriver.logging.info('Button pressed. Using coordiantes with viewport offset: '
+ + upX + ', ' + upY);
}
var browserOffset = Utils.getBrowserSpecificOffset(respond.session.getBrowser());
@@ -1274,7 +1275,7 @@
// The right mouse button is defined as '2' in the wire protocol
var RIGHT_MOUSE_BUTTON = 2;
var result;
- if(RIGHT_MOUSE_BUTTON == button) {
+ if (RIGHT_MOUSE_BUTTON == button) {
result = this.mouse.contextClick(null);
} else {
result = this.mouse.click(null);
@@ -1353,19 +1354,19 @@
var currentlyActiveElement = Utils.getActiveElement(respond.session.getDocument());
- if(bot.dom.isEditable(currentlyActiveElement)) {
+ if (bot.dom.isEditable(currentlyActiveElement)) {
goog.dom.selection.setCursorPosition(
currentlyActiveElement, currentlyActiveElement.value.length);
}
var useElement = currentlyActiveElement;
var tagName = useElement.tagName.toLowerCase();
- if (tagName == "body" && useElement.ownerDocument.defaultView.frameElement) {
+ if (tagName == 'body' && useElement.ownerDocument.defaultView.frameElement) {
useElement.ownerDocument.defaultView.focus();
// Turns out, this is what we should be using as the target
// to send events to
- useElement = useElement.ownerDocument.getElementsByTagName("html")[0];
+ useElement = useElement.ownerDocument.getElementsByTagName('html')[0];
}
// In case Utils.type performs non-native typing, it will modify the state of the
@@ -1422,32 +1423,32 @@
var documentWindow = respond.session.getWindow();
var chromeWindow = this.getChromeWindowFromDocumentWindow(documentWindow);
-
+
chromeWindow.maximize();
respond.send();
};
-FirefoxDriver.prototype.getChromeWindowFromDocumentWindow = function(documentWindow){
- // Find the chrome window for the requested document window.
+FirefoxDriver.prototype.getChromeWindowFromDocumentWindow = function(documentWindow) {
+ // Find the chrome window for the requested document window.
// This will ignore unfocused tabs
var wm = fxdriver.moz.getService(
- "@mozilla.org/appshell/window-mediator;1", "nsIWindowMediator");
- var allWindows = wm.getEnumerator("navigator:browser");
+ '@mozilla.org/appshell/window-mediator;1', 'nsIWindowMediator');
+ var allWindows = wm.getEnumerator('navigator:browser');
while (allWindows.hasMoreElements()) {
- var chromeWindow = allWindows.getNext()
+ var chromeWindow = allWindows.getNext();
if (chromeWindow.gBrowser.contentWindow == documentWindow.top) {
return chromeWindow;
}
- }
+ }
};
//TODO(jari): could this be made into a precondition?
FirefoxDriver.prototype.assertTargetsCurrentWindow_ = function(parameters) {
- if (parameters.windowHandle != "current") {
+ if (parameters.windowHandle != 'current') {
throw new WebDriverError(bot.ErrorCode.UNSUPPORTED_OPERATION,
'Window operations are only supported for the currently focused window.');
}
diff --git a/javascript/firefox-driver/js/logger.js b/javascript/firefox-driver/js/logger.js
index a7c6895..b71f253 100644
--- a/javascript/firefox-driver/js/logger.js
+++ b/javascript/firefox-driver/js/logger.js
@@ -21,17 +21,17 @@
goog.require('fxdriver.prefs');
goog.require('goog.array');
goog.require('goog.debug.Formatter');
-goog.require('goog.debug.LogRecord');
goog.require('goog.debug.Logger');
goog.require('goog.debug.TextFormatter');
goog.require('goog.object');
+goog.require('goog.string');
/**
* Represents the logging preferences as sent across the wire.
*
- * @typedef {{driver: (number=), profiler: (number=)}}
+ * @typedef {{driver: (number|undefined), profiler: (number|undefined)}}
*/
fxdriver.logging.LoggingPreferences;
@@ -736,23 +736,23 @@
/**
* Takes an object and attempts to discover which interfaces it implements.
*
- * @param {*} object The object to dump
+ * @param {*} object The object to dump.
*/
fxdriver.logging.dumpObject = function(element) {
- var msg = "=============\n";
+ var msg = '=============\n';
var rows = [];
- msg += "Supported interfaces: ";
+ msg += 'Supported interfaces: ';
for (var i in Components.interfaces) {
try {
var view = element.QueryInterface(Components.interfaces[i]);
- msg += i + ", ";
+ msg += i + ', ';
} catch (e) {
// Doesn't support the interface
}
}
- msg += "\n------------\n";
+ msg += '\n------------\n';
try {
fxdriver.logging.dumpProperties_(element, rows);
@@ -761,29 +761,29 @@
rows.sort();
for (var j in rows) {
- msg += rows[j] + "\n";
+ msg += rows[j] + '\n';
}
- msg += "=============\n\n\n";
+ msg += '=============\n\n\n';
fxdriver.logging.info(msg);
};
/**
- * @param {*} view The object to get the properties of
+ * @param {*} view The object to get the properties of.
* @param {!Array.<string>} rows The place to output results to.
* @private
*/
fxdriver.logging.dumpProperties_ = function(view, rows) {
for (var i in view) {
- var value = "\t" + i + ": ";
+ var value = '\t' + i + ': ';
try {
if (typeof(view[i]) == typeof(Function)) {
- value += " function()";
+ value += ' function()';
} else {
value += String(view[i]);
}
} catch (e) {
- value += " Cannot obtain value";
+ value += ' Cannot obtain value';
}
rows.push(value);
}
diff --git a/javascript/firefox-driver/js/modals.js b/javascript/firefox-driver/js/modals.js
index cbf7949..5f80bd5 100644
--- a/javascript/firefox-driver/js/modals.js
+++ b/javascript/firefox-driver/js/modals.js
@@ -16,15 +16,15 @@
*/
/**
- * @fileoverview Methods for dealing with modal dialogs
+ * @fileoverview Methods for dealing with modal dialogs.
*/
goog.provide('fxdriver.modals');
goog.require('WebDriverError');
goog.require('bot.ErrorCode');
-goog.require('fxdriver.logging');
goog.require('fxdriver.Timer');
+goog.require('fxdriver.logging');
goog.require('fxdriver.moz');
goog.require('fxdriver.utils');
@@ -43,7 +43,7 @@
fxdriver.modals.acceptAlert = function(driver) {
var modal = fxdriver.modals.find_();
- var button = fxdriver.modals.findButton_(modal, "accept");
+ var button = fxdriver.modals.findButton_(modal, 'accept');
button.click();
fxdriver.modals.clearFlag_(driver);
};
@@ -51,11 +51,11 @@
fxdriver.modals.dismissAlert = function(driver) {
var modal = fxdriver.modals.find_();
- var button = fxdriver.modals.findButton_(modal, "cancel");
+ var button = fxdriver.modals.findButton_(modal, 'cancel');
if (!button) {
fxdriver.logging.info('No cancel button Falling back to the accept button');
- button = fxdriver.modals.findButton_(modal, "accept");
+ button = fxdriver.modals.findButton_(modal, 'accept');
}
button.click();
@@ -110,7 +110,7 @@
fxdriver.modals.findButton_ = function(modal, value) {
var doc = modal.document;
- var dialog = doc.getElementsByTagName("dialog")[0];
+ var dialog = doc.getElementsByTagName('dialog')[0];
return dialog.getButton(value);
};
@@ -126,7 +126,7 @@
fxdriver.modals.findAssociatedDriver_ = function(window) {
- var ww = CC["@mozilla.org/embedcomp/window-watcher;1"].getService(CI["nsIWindowWatcher"]);
+ var ww = CC['@mozilla.org/embedcomp/window-watcher;1'].getService(CI['nsIWindowWatcher']);
var parent = window ? window : ww.activeWindow;
if (parent.wrappedJSObject) {
@@ -135,13 +135,13 @@
var top = parent.top;
// Now iterate over all open browsers to find the one we belong to
- var wm = CC["@mozilla.org/appshell/window-mediator;1"].getService(CI["nsIWindowMediator"]);
- var allWindows = wm.getEnumerator("navigator:browser");
+ var wm = CC['@mozilla.org/appshell/window-mediator;1'].getService(CI['nsIWindowMediator']);
+ var allWindows = wm.getEnumerator('navigator:browser');
while (allWindows.hasMoreElements()) {
var chrome = allWindows.getNext().QueryInterface(CI.nsIDOMWindow);
if (chrome.content == window) {
return chrome.fxdriver;
- } else if(chrome.content.top == window.top) {
+ } else if (chrome.content.top == window.top) {
return chrome.fxdriver;
}
}
@@ -152,7 +152,7 @@
};
fxdriver.modals.signalOpenModal = function(parent, text) {
- fxdriver.logging.info("signalOpenModal");
+ fxdriver.logging.info('signalOpenModal');
// Try to grab the top level window
var driver = fxdriver.modals.findAssociatedDriver_(parent);
if (driver && driver.response_) {
@@ -211,7 +211,7 @@
}
return 'dismiss';
-}
+};
/**
@@ -221,10 +221,10 @@
*/
fxdriver.modals.configure = function(unexpectedAlertBehaviour) {
var prefs = fxdriver.moz.getService(
- "@mozilla.org/preferences-service;1", "nsIPrefBranch");
+ '@mozilla.org/preferences-service;1', 'nsIPrefBranch');
var value = fxdriver.modals.asAcceptableAlertValue(unexpectedAlertBehaviour);
- prefs.setCharPref("webdriver_unexpected_alert_behaviour", value);
+ prefs.setCharPref('webdriver_unexpected_alert_behaviour', value);
};
@@ -233,12 +233,12 @@
*/
fxdriver.modals.getUnexpectedAlertBehaviour = function() {
var prefs = fxdriver.moz.getService(
- "@mozilla.org/preferences-service;1", "nsIPrefBranch");
+ '@mozilla.org/preferences-service;1', 'nsIPrefBranch');
- if (!prefs.prefHasUserValue("webdriver_unexpected_alert_behaviour")) {
+ if (!prefs.prefHasUserValue('webdriver_unexpected_alert_behaviour')) {
return 'dismiss';
}
- var raw = prefs.getCharPref("webdriver_unexpected_alert_behaviour");
+ var raw = prefs.getCharPref('webdriver_unexpected_alert_behaviour');
return fxdriver.modals.asAcceptableAlertValue(raw);
};
diff --git a/javascript/firefox-driver/js/modifierKeys.js b/javascript/firefox-driver/js/modifierKeys.js
index 0caecb5..203c23e 100644
--- a/javascript/firefox-driver/js/modifierKeys.js
+++ b/javascript/firefox-driver/js/modifierKeys.js
@@ -1,51 +1,50 @@
goog.provide('ModifierKeys');
+goog.require('Utils');
goog.require('bot.Device');
goog.require('fxdriver.moz');
-goog.require('goog.userAgent');
-goog.require('Utils');
-ModifierKeys = function () {
+ModifierKeys = function() {
this.wrappedJSObject = this;
- this.QueryInterface = fxdriver.moz.queryInterface (this, [CI.nsISupports, CI.wdIModifierKeys]);
+ this.QueryInterface = fxdriver.moz.queryInterface(this, [CI.nsISupports, CI.wdIModifierKeys]);
this.backingState_ = new bot.Device.ModifiersState();
};
ModifierKeys.prototype.isShiftPressed = function() {
return this.backingState_.isShiftPressed();
-}
+};
ModifierKeys.prototype.isControlPressed = function() {
return this.backingState_.isControlPressed();
-}
+};
ModifierKeys.prototype.isAltPressed = function() {
return this.backingState_.isAltPressed();
-}
+};
ModifierKeys.prototype.isMetaPressed = function() {
return this.backingState_.isMetaPressed();
-}
+};
ModifierKeys.prototype.setShiftPressed = function(isPressed) {
this.backingState_.setPressed(bot.Device.Modifier.SHIFT, isPressed);
-}
+};
ModifierKeys.prototype.setControlPressed = function(isPressed) {
this.backingState_.setPressed(bot.Device.Modifier.CONTROL, isPressed);
-}
+};
ModifierKeys.prototype.setAltPressed = function(isPressed) {
this.backingState_.setPressed(bot.Device.Modifier.ALT, isPressed);
-}
+};
ModifierKeys.prototype.setMetaPressed = function(isPressed) {
this.backingState_.setPressed(bot.Device.Modifier.META, isPressed);
-}
+};
ModifierKeys.prototype.classDescription = 'Keeps the state of the modifier keys (shift, alt, meta, ctrl)';
ModifierKeys.prototype.contractID = '@googlecode.com/webdriver/modifierkeys;1';
-ModifierKeys.prototype.classID = Components.ID ('{2E4B69B9-21FE-48ad-A2F6-AB355D6D2FCE}');
+ModifierKeys.prototype.classID = Components.ID('{2E4B69B9-21FE-48ad-A2F6-AB355D6D2FCE}');
/** @const */ var components = [ModifierKeys];
@@ -55,4 +54,4 @@
NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
} else {
NSGetModule = XPCOMUtils.generateNSGetModule(components);
-}
\ No newline at end of file
+}
diff --git a/javascript/firefox-driver/js/moz.js b/javascript/firefox-driver/js/moz.js
index 0d4a079..4757183 100644
--- a/javascript/firefox-driver/js/moz.js
+++ b/javascript/firefox-driver/js/moz.js
@@ -19,6 +19,7 @@
goog.require('bot.userAgent');
goog.require('fxdriver.logging');
+goog.require('goog.array');
/** @const */ var CC = Components.classes;
@@ -93,7 +94,7 @@
* Unwraps a something which is wrapped into a XPCNativeWrapper or XrayWrapper.
*
* @param {!Object} thing The "something" to unwrap.
- * @returns {!Object} The object, unwrapped if possible.
+ * @return {!Object} The object, unwrapped if possible.
*/
fxdriver.moz.unwrap = function(thing) {
// TODO(simon): This is identical to the same function in firefox-chrome
@@ -121,7 +122,7 @@
toReturn.__fxdriver_unwrapped = true;
return toReturn;
}
- } catch(e) {
+ } catch (e) {
// Unwrapping will fail for JS literals - numbers, for example. Catch
// the exception and proceed, it will eventually be returned as-is.
}
@@ -136,13 +137,13 @@
* See: https://developer.mozilla.org/en/XPCNativeWrapper
*/
fxdriver.moz.unwrapXpcOnly = function(thing) {
- if (XPCNativeWrapper && "unwrap" in XPCNativeWrapper) {
+ if (XPCNativeWrapper && 'unwrap' in XPCNativeWrapper) {
try {
return XPCNativeWrapper.unwrap(thing);
- } catch(e) {
+ } catch (e) {
//Unwrapping will fail for JS literals - numbers, for example. Catch
// the exception and proceed, it will eventually be returend as-is.
- fxdriver.logging.warning("Unwrap From XPC only failed: " + e);
+ fxdriver.logging.warning('Unwrap From XPC only failed: ' + e);
}
}
@@ -157,4 +158,3 @@
}
return doc;
};
-
diff --git a/javascript/firefox-driver/js/nsCommandProcessor.js b/javascript/firefox-driver/js/nsCommandProcessor.js
index 1c6bff2..92a6606 100644
--- a/javascript/firefox-driver/js/nsCommandProcessor.js
+++ b/javascript/firefox-driver/js/nsCommandProcessor.js
@@ -29,14 +29,13 @@
goog.require('bot.ErrorCode');
goog.require('bot.locators');
goog.require('bot.userAgent');
-goog.require('fxdriver.logging');
goog.require('fxdriver.Timer');
goog.require('fxdriver.error');
-goog.require('fxdriver.moz');
+goog.require('fxdriver.logging');
goog.require('fxdriver.modals');
+goog.require('fxdriver.moz');
goog.require('fxdriver.profiler');
-goog.require('goog.dom');
-goog.require('goog.object');
+goog.require('goog.array');
goog.require('wdSessionStoreService');
@@ -79,9 +78,9 @@
* @param {window} win The content window that the command will be executed on.
*/
startCommand: function(win) {
- this.statusBarLabel_ = win.document.getElementById("fxdriver-label");
+ this.statusBarLabel_ = win.document.getElementById('fxdriver-label');
if (this.statusBarLabel_) {
- this.statusBarLabel_.style.color = "red";
+ this.statusBarLabel_.style.color = 'red';
}
},
@@ -119,11 +118,11 @@
},
set name(name) { this.json_.name = name; },
- get name() { return this.json_.name; },
+ get name() { return this.json_.name; },
set status(newStatus) { this.json_.status = newStatus; },
- get status() { return this.json_.status; },
- set value(val) { this.json_.value = val; },
- get value() { return this.json_.value; }
+ get status() { return this.json_.status; },
+ set value(val) { this.json_.value = val; },
+ get value() { return this.json_.value; }
};
@@ -214,7 +213,7 @@
var rawRequest = requests.getNext();
try {
- request = rawRequest.QueryInterface(Components.interfaces.nsIRequest)
+ request = rawRequest.QueryInterface(Components.interfaces.nsIRequest);
} catch (e) {
// This may happen for pages that use WebSockets.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=765618
@@ -226,7 +225,7 @@
var isPending = false;
try {
isPending = request.isPending();
- } catch(e) {
+ } catch (e) {
// Normal during page load, which means we should just return "true"
return true;
}
@@ -313,7 +312,7 @@
this.response_, parameters);
var guards = goog.bind(this.checkPreconditions_, this,
driverFunction.preconditions, this.response_, parameters);
-
+
var toExecute = function() {
try {
guards();
@@ -334,7 +333,7 @@
toExecute();
} catch (e) {
if (!e.isWebDriverError) {
- fxdriver.logging.error('Exception caught by driver: ' +
+ fxdriver.logging.error('Exception caught by driver: ' +
this.command_.name + '(' + this.command_.parameters + ')');
fxdriver.logging.error(e);
}
@@ -356,7 +355,7 @@
eval(Utils.loadUrl('resource://fxdriver/json2.js'));
}
- var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces["nsIPrefBranch"]);
+ var prefs = Components.classes['@mozilla.org/preferences-service;1'].getService(Components.interfaces['nsIPrefBranch']);
if (prefs.prefHasUserValue('webdriver.load.strategy')) {
loadStrategy_ = prefs.getCharPref('webdriver.load.strategy');
@@ -469,7 +468,7 @@
}
if (driver.modalOpen) {
- if (command.name != 'getAlertText' &&
+ if (command.name != 'getAlertText' &&
command.name != 'setAlertValue' &&
command.name != 'acceptAlert' &&
command.name != 'dismissAlert') {
@@ -660,7 +659,7 @@
// See https://developer.mozilla.org/en/XPCOM_ABI
return (xulRuntime.XPCOMABI || 'unknown').split('-')[0];
} catch (ignored) {
- return 'unknown'
+ return 'unknown';
}
})(),
// See https://developer.mozilla.org/en/OS_TARGET
@@ -683,7 +682,7 @@
* @param {Response} response The object to send the command response in.
*/
nsCommandProcessor.prototype.newSession = function(response, parameters) {
- var win = this.wm.getMostRecentWindow("navigator:browser");
+ var win = this.wm.getMostRecentWindow('navigator:browser');
var driver = win.fxdriver;
if (!driver) {
response.sendError(new WebDriverError(bot.ErrorCode.UNKNOWN_ERROR,
@@ -789,7 +788,7 @@
};
-nsCommandProcessor.prototype.QueryInterface = function (aIID) {
+nsCommandProcessor.prototype.QueryInterface = function(aIID) {
if (!aIID.equals(Components.interfaces.nsICommandProcessor) &&
!aIID.equals(Components.interfaces.nsISupports)) {
throw Components.results.NS_ERROR_NO_INTERFACE;
@@ -810,7 +809,7 @@
* {@code CommandProcessor}.
*/
nsCommandProcessor.Factory = {
- instance_ : null,
+ instance_: null,
createInstance: function(aOuter, aIID) {
if (aOuter != null) {
@@ -871,7 +870,7 @@
};
nsCommandProcessor.prototype.classID = nsCommandProcessor.CLASS_ID;
-fxdriver.moz.load("resource://gre/modules/XPCOMUtils.jsm");
+fxdriver.moz.load('resource://gre/modules/XPCOMUtils.jsm');
if (XPCOMUtils.generateNSGetFactory) {
/** @const */ NSGetFactory = XPCOMUtils.generateNSGetFactory([nsCommandProcessor]);
}
diff --git a/javascript/firefox-driver/js/preconditions.js b/javascript/firefox-driver/js/preconditions.js
index 52502cc..f56fc1b 100644
--- a/javascript/firefox-driver/js/preconditions.js
+++ b/javascript/firefox-driver/js/preconditions.js
@@ -20,8 +20,8 @@
goog.provide('fxdriver.preconditions');
-goog.require('bot.dom');
goog.require('Utils');
+goog.require('bot.dom');
/**
diff --git a/javascript/firefox-driver/js/profiler.js b/javascript/firefox-driver/js/profiler.js
index 13f7e83..b132b5e 100644
--- a/javascript/firefox-driver/js/profiler.js
+++ b/javascript/firefox-driver/js/profiler.js
@@ -27,4 +27,3 @@
fxdriver.logging.log(fxdriver.logging.LogType.PROFILER,
fxdriver.logging.LogLevel.INFO, message);
};
-
diff --git a/javascript/firefox-driver/js/promptService.js b/javascript/firefox-driver/js/promptService.js
index 6064d0b..06c0857 100644
--- a/javascript/firefox-driver/js/promptService.js
+++ b/javascript/firefox-driver/js/promptService.js
@@ -26,7 +26,7 @@
try {
var queried = delegate.QueryInterface(iid);
for (var i in queried) {
- if (!goog.array.contains(EXCLUDED_NAMES, i.toString())) {
+ if (!goog.array.contains(EXCLUDED_NAMES, i.toString())) {
to[i] = queried[i];
}
}
@@ -53,7 +53,7 @@
ObservingAlert.prototype.alertCheck = function(dialogTitle, text, checkMsg, checkValue) {
fxdriver.modals.signalOpenModal(this.parentWindow_, text);
- this.delegate_.alertCheck(dialogTitle, text, checkMsg, checkValue);
+ this.delegate_.alertCheck(dialogTitle, text, checkMsg, checkValue);
};
ObservingAlert.prototype.confirm = function(dialogTitle, text) {
@@ -63,7 +63,7 @@
ObservingAlert.prototype.confirmCheck = function(dialogTitle, text, checkMsg, checkValue) {
fxdriver.modals.signalOpenModal(this.parentWindow_, text);
- return this.delegate_.confirmCheck(dialogTitle, text, checkMsg, checkValue);
+ return this.delegate_.confirmCheck(dialogTitle, text, checkMsg, checkValue);
};
ObservingAlert.prototype.confirmEx = function(dialogTitle, text,
@@ -100,17 +100,17 @@
// Spoof implementation
function DrivenPromptService() {
- fxdriver.logging.info("Spoofing prompt service");
+ fxdriver.logging.info('Spoofing prompt service');
// @mozilla.org/prompter;1
var prompters = [
- "{1c978d25-b37f-43a8-a2d6-0c7a239ead87}" // nsPrompter.js: Firefox 4 late betas onwards
+ '{1c978d25-b37f-43a8-a2d6-0c7a239ead87}' // nsPrompter.js: Firefox 4 late betas onwards
];
// @mozilla.org/embedcomp/prompt-service;
var promptServices = [
- "{7ad1b327-6dfa-46ec-9234-f2a620ea7e00}", // nsPrompter.js: Firefox 4 betas
- "{A2112D6A-0E28-421f-B46A-25C0B308CBD0}" // nsPromptService.h: Firefox 3.x
+ '{7ad1b327-6dfa-46ec-9234-f2a620ea7e00}', // nsPrompter.js: Firefox 4 betas
+ '{A2112D6A-0E28-421f-B46A-25C0B308CBD0}' // nsPromptService.h: Firefox 3.x
];
var findImplementation = function(interfaceName, cids) {
@@ -123,7 +123,7 @@
try {
var toReturn = service.QueryInterface(interfaceName);
- fxdriver.logging.info("Found implementation at: " + cids[i]);
+ fxdriver.logging.info('Found implementation at: ' + cids[i]);
return toReturn;
} catch (ignored) {}
}
@@ -136,11 +136,11 @@
var originalPrompter_ = findImplementation(CI.nsIPromptFactory, prompters);
if (!originalPromptService_) {
- fxdriver.logging.info("Unable to locate original prompt service");
+ fxdriver.logging.info('Unable to locate original prompt service');
}
if (!originalPrompter_) {
- fxdriver.logging.info("Unable to locate original prompter");
+ fxdriver.logging.info('Unable to locate original prompter');
}
this.delegate_ = originalPrompter_ ? originalPrompter_ : originalPromptService_;
@@ -151,7 +151,7 @@
this.QueryInterface = fxdriver.moz.queryInterface(this,
[CI.nsIPromptFactory, CI.nsIPromptService, CI.nsIPromptService2]);
- fxdriver.logging.info("Finished initializing spoofed prompt service");
+ fxdriver.logging.info('Finished initializing spoofed prompt service');
}
// Constants from nsIPromtService.idl
@@ -269,15 +269,15 @@
fxdriver.modals.signalOpenModal(aParent, '');
var service = this.delegate_.QueryInterface(CI.nsIPromptService2);
- return service.asyncPromptAuth(aParent, aChannel, aCallback, aContext, level, authInfo, checkboxLabel,checkValue)
+ return service.asyncPromptAuth(aParent, aChannel, aCallback, aContext, level, authInfo, checkboxLabel, checkValue);
};
// nsIPromptFactory
-DrivenPromptService.prototype.getPrompt = function (domWin, iid) {
+DrivenPromptService.prototype.getPrompt = function(domWin, iid) {
var factory = this.delegate_.QueryInterface(CI.nsIPromptFactory);
var rawPrompt = factory.getPrompt(domWin, iid);
-
+
return new ObservingAlert(domWin, rawPrompt);
};
@@ -287,8 +287,8 @@
};
-/** @const */ var PROMPT_SERVICE_CONTRACT_ID = "@mozilla.org/embedcomp/prompt-service;1";
-/** @const */ var PROMPTER_CONTRACT_ID = "@mozilla.org/prompter;1";
+/** @const */ var PROMPT_SERVICE_CONTRACT_ID = '@mozilla.org/embedcomp/prompt-service;1';
+/** @const */ var PROMPTER_CONTRACT_ID = '@mozilla.org/prompter;1';
// This is defined by us
/** @const */ var DRIVEN_PROMPT_SERVICE_CLASS_ID = Components.ID('{e26dbdcd-d3ba-4ded-88c3-6cb07ee3e9e0}');
@@ -296,7 +296,7 @@
var service = undefined;
var PromptServiceSpoofFactory = {
- createInstance: function (aOuter, aIID) {
+ createInstance: function(aOuter, aIID) {
if (aOuter != null)
throw Components.results.NS_ERROR_NO_AGGREGATION;
if (service == undefined) {
@@ -317,13 +317,13 @@
}
aCompMgr = aCompMgr.QueryInterface(CI.nsIComponentRegistrar);
aCompMgr.registerFactoryLocation(
- DRIVEN_PROMPT_SERVICE_CLASS_ID, "Driven prompt service", PROMPT_SERVICE_CONTRACT_ID, aFileSpec, aLocation, aType);
+ DRIVEN_PROMPT_SERVICE_CLASS_ID, 'Driven prompt service', PROMPT_SERVICE_CONTRACT_ID, aFileSpec, aLocation, aType);
aCompMgr.registerFactoryLocation(
- DRIVEN_PROMPT_SERVICE_CLASS_ID, "Driven prompter service", PROMPTER_CONTRACT_ID, aFileSpec, aLocation, aType);
+ DRIVEN_PROMPT_SERVICE_CLASS_ID, 'Driven prompter service', PROMPTER_CONTRACT_ID, aFileSpec, aLocation, aType);
};
PromptServiceSpoofModule.prototype.unregisterSelf = function(aCompMgr, aLocation, aType) {
- fxdriver.logging.info("Unregistering\n");
+ fxdriver.logging.info('Unregistering\n');
aCompMgr.QueryInterface(CI.nsIComponentRegistrar);
aCompMgr.unregisterFactoryLocation(DRIVEN_PROMPT_SERVICE_CLASS_ID, aLocation);
};
diff --git a/javascript/firefox-driver/js/proxy.js b/javascript/firefox-driver/js/proxy.js
index b18a0ff..5b5b35b 100644
--- a/javascript/firefox-driver/js/proxy.js
+++ b/javascript/firefox-driver/js/proxy.js
@@ -133,7 +133,7 @@
fxdriver.proxy.systemConfig_ = function(prefs, ignored) {
fxdriver.logging.info('Using system proxy to connect to the network');
- prefs.setIntPref('network.proxy.type',
+ prefs.setIntPref('network.proxy.type',
fxdriver.proxy.TYPES_['SYSTEM'].value);
};
diff --git a/javascript/firefox-driver/js/sessionstore.js b/javascript/firefox-driver/js/sessionstore.js
index 48d418a..9d8e4f8 100644
--- a/javascript/firefox-driver/js/sessionstore.js
+++ b/javascript/firefox-driver/js/sessionstore.js
@@ -22,6 +22,7 @@
goog.require('fxdriver.modals');
goog.require('fxdriver.moz');
goog.require('fxdriver.proxy');
+goog.require('goog.object');
goog.require('wdSession');
/**
@@ -115,9 +116,9 @@
};
/**
- * Extract the setting for a capability.
+ * Extract the setting for a capability.
*
- * If a capability is defined both among desired capabilities and among
+ * If a capability is defined both among desired capabilities and among
* required capabilities the required setting has priority.
*
* @private
@@ -150,7 +151,7 @@
};
/**
- * Read-write capabilities for FirefoxDriver corresponding to (boolean)
+ * Read-write capabilities for FirefoxDriver corresponding to (boolean)
* profile preferences. NB! the native events capability is not mapped to a
* Firefox preferences.
* @type {!Object.<string, string>}
@@ -218,9 +219,9 @@
fxdriver.logging.info('Setting capability ' +
key + ' (' + pref + ') to ' + value);
if (key == 'nativeEvents') {
- driver.enableNativeEvents = value;
+ driver.enableNativeEvents = value;
}
- }
+ }
});
};
@@ -319,7 +320,7 @@
};
-/** @see nsIModule.unregisterSelf */
+/** @see nsIModule.unregisterSelf */
wdSessionStoreServiceModule.prototype.unregisterSelf = function(aCompMgr, aLocation) {
aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar).
unregisterFactoryLocation(wdSessionStoreService.CLASS_ID, aLocation);
@@ -352,7 +353,7 @@
};
wdSessionStoreService.prototype.classID = wdSessionStoreService.CLASS_ID;
-fxdriver.moz.load("resource://gre/modules/XPCOMUtils.jsm");
+fxdriver.moz.load('resource://gre/modules/XPCOMUtils.jsm');
if (XPCOMUtils.generateNSGetFactory) {
/** @const */ NSGetFactory = XPCOMUtils.generateNSGetFactory([wdSessionStoreService]);
}
diff --git a/javascript/firefox-driver/js/syntheticMouse.js b/javascript/firefox-driver/js/syntheticMouse.js
index fb5b7b8..2d183c8 100644
--- a/javascript/firefox-driver/js/syntheticMouse.js
+++ b/javascript/firefox-driver/js/syntheticMouse.js
@@ -20,16 +20,18 @@
goog.require('Utils');
goog.require('bot.ErrorCode');
goog.require('bot.Mouse');
-goog.require('bot.events.EventType');
goog.require('bot.action');
goog.require('bot.dom');
goog.require('bot.events');
+goog.require('bot.events.EventType');
goog.require('bot.window');
+goog.require('fxdriver.logging');
goog.require('fxdriver.moz');
goog.require('fxdriver.utils');
-goog.require('fxdriver.logging');
-goog.require('goog.events.EventType');
+goog.require('goog.dom');
+goog.require('goog.dom.TagName');
goog.require('goog.math.Coordinate');
+goog.require('goog.style');
SyntheticMouse = function() {
@@ -48,7 +50,7 @@
// track of the viewport scroll offset in this variable, when we mouseDown,
// until we mouseUp, so that we can account for any scrolling which may have
// happened, when we fire events.
- this.viewPortOffset = new goog.math.Coordinate(0,0);
+ this.viewPortOffset = new goog.math.Coordinate(0, 0);
};
@@ -69,7 +71,7 @@
SyntheticMouse.prototype.isElementShown = function(element) {
if (!bot.dom.isShown(element, /*ignoreOpacity=*/true)) {
return SyntheticMouse.newResponse(bot.ErrorCode.ELEMENT_NOT_VISIBLE,
- 'Element is not currently visible and so may not be interacted with')
+ 'Element is not currently visible and so may not be interacted with');
}
};
@@ -126,7 +128,7 @@
mouse.move(element, coords);
- return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, "ok");
+ return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, 'ok');
};
@@ -144,18 +146,18 @@
// Check to see if this is an option element. If it is, and the parent isn't a multiple
// select, then click on the select first.
var tagName = element.tagName.toLowerCase();
- if ("option" == tagName) {
+ if ('option' == tagName) {
var parent = element;
- while (parent.parentNode != null && parent.tagName.toLowerCase() != "select") {
+ while (parent.parentNode != null && parent.tagName.toLowerCase() != 'select') {
parent = parent.parentNode;
}
- if (parent && parent.tagName.toLowerCase() == "select" && !parent.multiple) {
+ if (parent && parent.tagName.toLowerCase() == 'select' && !parent.multiple) {
bot.action.click(parent);
}
}
- fxdriver.logging.info("About to do a bot.action.click on " + element);
+ fxdriver.logging.info('About to do a bot.action.click on ' + element);
var keyboardState = new bot.Device.ModifiersState();
if (this.modifierKeys !== undefined) {
keyboardState.setPressed(bot.Device.Modifier.SHIFT, this.modifierKeys.isShiftPressed());
@@ -167,7 +169,7 @@
var mouseWithKeyboardState = new bot.Mouse(null, keyboardState);
bot.action.click(element, undefined /* coords */, mouseWithKeyboardState);
- return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, "ok");
+ return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, 'ok');
};
SyntheticMouse.prototype.contextClick = function(target) {
@@ -181,10 +183,10 @@
return error;
}
- fxdriver.logging.info("About to do a bot.action.rightClick on " + element);
+ fxdriver.logging.info('About to do a bot.action.rightClick on ' + element);
bot.action.rightClick(element);
- return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, "ok");
+ return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, 'ok');
};
SyntheticMouse.prototype.doubleClick = function(target) {
@@ -195,10 +197,10 @@
return error;
}
- fxdriver.logging.info("About to do a bot.action.doubleClick on " + element);
+ fxdriver.logging.info('About to do a bot.action.doubleClick on ' + element);
bot.action.doubleClick(element);
- return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, "ok");
+ return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, 'ok');
};
@@ -224,7 +226,7 @@
this.addEventModifierKeys(botCoords);
bot.events.fire(element, bot.events.EventType.MOUSEDOWN, botCoords);
- return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, "ok");
+ return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, 'ok');
};
@@ -250,11 +252,11 @@
this.viewPortOffset.x = 0;
this.viewPortOffset.y = 0;
- return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, "ok");
+ return SyntheticMouse.newResponse(bot.ErrorCode.SUCCESS, 'ok');
};
-SyntheticMouse.prototype.addEventModifierKeys = function (botCoords) {
+SyntheticMouse.prototype.addEventModifierKeys = function(botCoords) {
if (this.modifierKeys !== undefined) {
botCoords.altKey = this.modifierKeys.isAltPressed();
botCoords.ctrlKey = this.modifierKeys.isControlPressed();
@@ -265,7 +267,7 @@
// And finally, registering
-SyntheticMouse.prototype.classDescription = "Pure JS implementation of a mouse";
+SyntheticMouse.prototype.classDescription = 'Pure JS implementation of a mouse';
SyntheticMouse.prototype.contractID = '@googlecode.com/webdriver/syntheticmouse;1';
SyntheticMouse.prototype.classID = Components.ID('{E8F9FEFE-C513-4097-98BE-BE00A41D3645}');
diff --git a/javascript/firefox-driver/js/utils.js b/javascript/firefox-driver/js/utils.js
index 71e89b3..b5a52d6 100644
--- a/javascript/firefox-driver/js/utils.js
+++ b/javascript/firefox-driver/js/utils.js
@@ -26,9 +26,9 @@
goog.require('fxdriver.logging');
goog.require('fxdriver.moz');
goog.require('fxdriver.utils');
-goog.require('goog.dom.TagName');
-goog.require('goog.style');
+goog.require('goog.dom');
goog.require('goog.string');
+goog.require('goog.style');
/**
@@ -85,7 +85,7 @@
* @type {!boolean}
*/
this.isWebDriverError = true;
-}
+};
function notifyOfCloseWindow(windowId) {
windowId = windowId || 0;
@@ -110,7 +110,7 @@
var clazz = Components.classes[className];
if (!clazz) {
- fxdriver.logging.warning("Unable to find class: " + className);
+ fxdriver.logging.warning('Unable to find class: ' + className);
return undefined;
}
var iface = Components.interfaces[interfaceName];
@@ -118,7 +118,7 @@
try {
return clazz.createInstance(iface);
} catch (e) {
- fxdriver.logging.warning("Cannot create: " + className + " from " + interfaceName);
+ fxdriver.logging.warning('Cannot create: ' + className + ' from ' + interfaceName);
fxdriver.logging.warning(e);
throw e;
}
@@ -127,7 +127,7 @@
Utils.getServer = function() {
var handle =
- Utils.newInstance("@googlecode.com/webdriver/fxdriver;1", "nsISupports");
+ Utils.newInstance('@googlecode.com/webdriver/fxdriver;1', 'nsISupports');
return handle.wrappedJSObject;
};
@@ -136,7 +136,7 @@
var window = goog.dom.getWindow(doc);
var element;
- if (doc["activeElement"]) {
+ if (doc['activeElement']) {
element = doc.activeElement;
} else {
var topWindow = window.top;
@@ -197,29 +197,29 @@
try {
var obj = Components.classes[componentId].createInstance();
return obj.QueryInterface(componentInterface);
- } catch(e) {
- fxdriver.logging.warning("Unable to find native component: " + componentId);
+ } catch (e) {
+ fxdriver.logging.warning('Unable to find native component: ' + componentId);
fxdriver.logging.warning(e);
// Unable to retrieve native events. No biggie, because we fall back to
// synthesis later
return undefined;
}
-}
+};
Utils.getNativeEvents = function() {
- return Utils.getNativeComponent("@openqa.org/nativeevents;1", Components.interfaces.nsINativeEvents);
+ return Utils.getNativeComponent('@openqa.org/nativeevents;1', Components.interfaces.nsINativeEvents);
};
Utils.getNativeMouse = function() {
- return Utils.getNativeComponent("@openqa.org/nativemouse;1", Components.interfaces.nsINativeMouse);
+ return Utils.getNativeComponent('@openqa.org/nativemouse;1', Components.interfaces.nsINativeMouse);
};
Utils.getNativeKeyboard = function() {
- return Utils.getNativeComponent("@openqa.org/nativekeyboard;1", Components.interfaces.nsINativeKeyboard);
+ return Utils.getNativeComponent('@openqa.org/nativekeyboard;1', Components.interfaces.nsINativeKeyboard);
};
Utils.getNativeIME = function() {
- return Utils.getNativeComponent("@openqa.org/nativeime;1", Components.interfaces.nsINativeIME);
+ return Utils.getNativeComponent('@openqa.org/nativeime;1', Components.interfaces.nsINativeIME);
};
Utils.getNodeForNativeEvents = function(element) {
@@ -227,12 +227,12 @@
// This stuff changes between releases.
// Do as much up-front work in JS as possible
var retrieval = Utils.newInstance(
- "@mozilla.org/accessibleRetrieval;1", "nsIAccessibleRetrieval");
+ '@mozilla.org/accessibleRetrieval;1', 'nsIAccessibleRetrieval');
var accessible = retrieval.getAccessibleFor(element.ownerDocument);
var accessibleDoc =
accessible.QueryInterface(Components.interfaces.nsIAccessibleDocument);
return accessibleDoc.QueryInterface(Components.interfaces.nsISupports);
- } catch(e) {
+ } catch (e) {
// Unable to retrieve the accessible doc
return undefined;
}
@@ -240,10 +240,10 @@
Utils.useNativeEvents = function() {
var prefs =
- fxdriver.moz.getService("@mozilla.org/preferences-service;1", "nsIPrefBranch");
+ fxdriver.moz.getService('@mozilla.org/preferences-service;1', 'nsIPrefBranch');
var enableNativeEvents =
- prefs.prefHasUserValue("webdriver_enable_native_events") ?
- prefs.getBoolPref("webdriver_enable_native_events") : false;
+ prefs.prefHasUserValue('webdriver_enable_native_events') ?
+ prefs.getBoolPref('webdriver_enable_native_events') : false;
return !!(enableNativeEvents && Utils.getNativeEvents());
};
@@ -259,7 +259,7 @@
var obj = Utils.getNativeKeyboard();
var node = Utils.getNodeForNativeEvents(element);
- var thmgr_cls = Components.classes["@mozilla.org/thread-manager;1"];
+ var thmgr_cls = Components.classes['@mozilla.org/thread-manager;1'];
var isUsingNativeEvents = opt_useNativeEvents && obj && node && thmgr_cls;
if (isUsingNativeEvents) {
@@ -273,7 +273,7 @@
return;
}
- fxdriver.logging.info("Doing sendKeys in a non-native way...");
+ fxdriver.logging.info('Doing sendKeys in a non-native way...');
var controlKey = false;
var shiftKey = false;
var altKey = false;
@@ -297,25 +297,25 @@
if (c == '\uE000') {
if (controlKey) {
var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CONTROL;
- Utils.keyEvent(doc, element, "keyup", kCode, 0,
+ Utils.keyEvent(doc, element, 'keyup', kCode, 0,
controlKey = false, shiftKey, altKey, metaKey, false);
}
if (shiftKey) {
var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT;
- Utils.keyEvent(doc, element, "keyup", kCode, 0,
+ Utils.keyEvent(doc, element, 'keyup', kCode, 0,
controlKey, shiftKey = false, altKey, metaKey, false);
}
if (altKey) {
var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ALT;
- Utils.keyEvent(doc, element, "keyup", kCode, 0,
+ Utils.keyEvent(doc, element, 'keyup', kCode, 0,
controlKey, shiftKey, altKey = false, metaKey, false);
}
if (metaKey) {
var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_META;
- Utils.keyEvent(doc, element, "keyup", kCode, 0,
+ Utils.keyEvent(doc, element, 'keyup', kCode, 0,
controlKey, shiftKey, altKey, metaKey = false, false);
}
@@ -324,7 +324,7 @@
// otherwise decode keyCode, charCode, modifiers ...
- var modifierEvent = "";
+ var modifierEvent = '';
var charCode = 0;
var keyCode = 0;
@@ -345,19 +345,19 @@
} else if (c == '\uE008') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT;
shiftKey = !shiftKey;
- modifierEvent = shiftKey ? "keydown" : "keyup";
+ modifierEvent = shiftKey ? 'keydown' : 'keyup';
} else if (c == '\uE009') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CONTROL;
controlKey = !controlKey;
- modifierEvent = controlKey ? "keydown" : "keyup";
+ modifierEvent = controlKey ? 'keydown' : 'keyup';
} else if (c == '\uE00A') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ALT;
altKey = !altKey;
- modifierEvent = altKey ? "keydown" : "keyup";
+ modifierEvent = altKey ? 'keydown' : 'keyup';
} else if (c == '\uE03D') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_META;
metaKey = !metaKey;
- modifierEvent = metaKey ? "keydown" : "keyup";
+ modifierEvent = metaKey ? 'keydown' : 'keyup';
} else if (c == '\uE00B') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_PAUSE;
} else if (c == '\uE00C') {
@@ -509,7 +509,7 @@
if (needsShift && !shiftKey) {
var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT;
- Utils.keyEvent(doc, element, "keydown", kCode, 0,
+ Utils.keyEvent(doc, element, 'keydown', kCode, 0,
controlKey, true, altKey, metaKey, false);
Utils.shiftCount += 1;
}
@@ -540,20 +540,20 @@
}
var accepted =
- Utils.keyEvent(doc, element, "keydown", keyCode, 0,
+ Utils.keyEvent(doc, element, 'keydown', keyCode, 0,
controlKey, needsShift || shiftKey, altKey, metaKey, false);
- Utils.keyEvent(doc, element, "keypress", pressCode, charCode,
+ Utils.keyEvent(doc, element, 'keypress', pressCode, charCode,
controlKey, needsShift || shiftKey, altKey, metaKey, !accepted);
- Utils.keyEvent(doc, element, "keyup", keyCode, 0,
+ Utils.keyEvent(doc, element, 'keyup', keyCode, 0,
controlKey, needsShift || shiftKey, altKey, metaKey, false);
// shift up if needed
if (needsShift && !shiftKey) {
var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT;
- Utils.keyEvent(doc, element, "keyup", kCode, 0,
+ Utils.keyEvent(doc, element, 'keyup', kCode, 0,
controlKey, false, altKey, metaKey, false);
}
}
@@ -562,25 +562,25 @@
if (controlKey && releaseModifiers) {
var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CONTROL;
- Utils.keyEvent(doc, element, "keyup", kCode, 0,
+ Utils.keyEvent(doc, element, 'keyup', kCode, 0,
controlKey = false, shiftKey, altKey, metaKey, false);
}
if (shiftKey && releaseModifiers) {
var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT;
- Utils.keyEvent(doc, element, "keyup", kCode, 0,
+ Utils.keyEvent(doc, element, 'keyup', kCode, 0,
controlKey, shiftKey = false, altKey, metaKey, false);
}
if (altKey && releaseModifiers) {
var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ALT;
- Utils.keyEvent(doc, element, "keyup", kCode, 0,
+ Utils.keyEvent(doc, element, 'keyup', kCode, 0,
controlKey, shiftKey, altKey = false, metaKey, false);
}
if (metaKey && releaseModifiers) {
var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_META;
- Utils.keyEvent(doc, element, "keyup", kCode, 0,
+ Utils.keyEvent(doc, element, 'keyup', kCode, 0,
controlKey, shiftKey, altKey, metaKey = false, false);
}
@@ -599,7 +599,7 @@
var preventDefault = shouldPreventDefault == undefined ? false
: shouldPreventDefault;
- var keyboardEvent = doc.createEvent("KeyEvents");
+ var keyboardEvent = doc.createEvent('KeyEvents');
var currentView = doc.defaultView;
keyboardEvent.initKeyEvent(
@@ -631,7 +631,7 @@
Utils.fireHtmlEvent = function(element, eventName) {
var doc = element.ownerDocument;
- var e = doc.createEvent("HTMLEvents");
+ var e = doc.createEvent('HTMLEvents');
e.initEvent(eventName, true, true);
return element.dispatchEvent(e);
};
@@ -643,7 +643,7 @@
Utils.triggerMouseEvent = function(element, eventType, clientX, clientY) {
- var event = element.ownerDocument.createEvent("MouseEvents");
+ var event = element.ownerDocument.createEvent('MouseEvents');
var view = element.ownerDocument.defaultView;
clientX = clientX || 0;
@@ -660,7 +660,7 @@
var y = element.offsetTop;
var elementParent = element.offsetParent;
while (elementParent != null) {
- if (elementParent.tagName == "TABLE") {
+ if (elementParent.tagName == 'TABLE') {
var parentBorder = parseInt(elementParent.border);
if (isNaN(parentBorder)) {
var parentFrame = elementParent.getAttribute('frame');
@@ -687,7 +687,7 @@
Utils.getLocationViaAccessibilityInterface = function(element) {
var retrieval = Utils.newInstance(
- "@mozilla.org/accessibleRetrieval;1", "nsIAccessibleRetrieval");
+ '@mozilla.org/accessibleRetrieval;1', 'nsIAccessibleRetrieval');
var accessible = retrieval.getAccessibleFor(element);
if (! accessible) {
@@ -698,8 +698,8 @@
accessible.getBounds(x, y, width, height);
return {
- x : x.value,
- y : y.value,
+ x: x.value,
+ y: y.value,
width: width.value,
height: height.value
};
@@ -727,8 +727,8 @@
// Firefox 3.5
if (clientRect['width']) {
return {
- x : clientRect.left,
- y : clientRect.top,
+ x: clientRect.left,
+ y: clientRect.top,
width: clientRect.width,
height: clientRect.height
};
@@ -739,22 +739,22 @@
var retWidth = clientRect.right - clientRect.left;
var retHeight = clientRect.bottom - clientRect.top;
return {
- x : clientRect.left,
- y : clientRect.top,
+ x: clientRect.left,
+ y: clientRect.top,
width: retWidth,
height: retHeight
- }
+ };
}
// Firefox 3.0, but lacking client rect
- fxdriver.logging.info("Falling back to firefox3 mechanism");
+ fxdriver.logging.info('Falling back to firefox3 mechanism');
var accessibleLocation = Utils.getLocationViaAccessibilityInterface(element);
accessibleLocation.x = clientRect.left;
accessibleLocation.y = clientRect.top;
return accessibleLocation;
- } catch(e) {
+ } catch (e) {
// Element doesn't have an accessibility node
- fxdriver.logging.warning("Falling back to using closure to find the location of the element");
+ fxdriver.logging.warning('Falling back to using closure to find the location of the element');
fxdriver.logging.warning(e);
var position = goog.style.getClientPosition(element);
@@ -810,8 +810,8 @@
var rect = inBrowser.getBoundingClientRect();
browserSpecificYOffset += rect.top;
browserSpecificXOffset += rect.left;
- fxdriver.logging.info("Browser-specific offset (X,Y): " + browserSpecificXOffset
- + ", " + browserSpecificYOffset);
+ fxdriver.logging.info('Browser-specific offset (X,Y): ' + browserSpecificXOffset
+ + ', ' + browserSpecificYOffset);
}
return {x: browserSpecificXOffset, y: browserSpecificYOffset};
@@ -937,19 +937,19 @@
return result;
}
};
-
+
Utils.loadUrl = function(url) {
- fxdriver.logging.info("Loading: " + url);
- var ioService = fxdriver.moz.getService("@mozilla.org/network/io-service;1", "nsIIOService");
+ fxdriver.logging.info('Loading: ' + url);
+ var ioService = fxdriver.moz.getService('@mozilla.org/network/io-service;1', 'nsIIOService');
var channel = ioService.newChannel(url, null, null);
var channelStream = channel.open();
- var scriptableStream = Components.classes["@mozilla.org/scriptableinputstream;1"]
+ var scriptableStream = Components.classes['@mozilla.org/scriptableinputstream;1']
.createInstance(Components.interfaces.nsIScriptableInputStream);
scriptableStream.init(channelStream);
- var converter = Utils.newInstance("@mozilla.org/intl/scriptableunicodeconverter",
+ var converter = Utils.newInstance('@mozilla.org/intl/scriptableunicodeconverter',
'nsIScriptableUnicodeConverter');
converter.charset = 'UTF-8';
@@ -963,11 +963,11 @@
scriptableStream.close();
channelStream.close();
- fxdriver.logging.info("Done reading: " + url);
+ fxdriver.logging.info('Done reading: ' + url);
return text;
};
-Utils.installWindowCloseListener = function (respond) {
+Utils.installWindowCloseListener = function(respond) {
var browser = respond.session.getBrowser();
// Override the "respond.send" function to remove the observer, otherwise
@@ -990,7 +990,7 @@
if (target == source) {
- fxdriver.logging.info("Window was closed.");
+ fxdriver.logging.info('Window was closed.');
respond.send();
}
}
@@ -1036,7 +1036,7 @@
var docLoaderService = browser.webProgress;
if (!docLoaderService.isLoadingDocument) {
WebLoadingListener.removeListener(browser, clickListener);
- fxdriver.logging.info("Not loading document anymore.");
+ fxdriver.logging.info('Not loading document anymore.');
respond.send();
}
};
@@ -1045,7 +1045,7 @@
if (contentWindow.closed) {
// Nulls out the session; client will have to switch to another
// window on their own.
- fxdriver.logging.info("Content window closed.");
+ fxdriver.logging.info('Content window closed.');
respond.send();
return;
}
@@ -1053,7 +1053,7 @@
};
Utils.waitForNativeEventsProcessing = function(element, nativeEvents, pageUnloadedData, jsTimer) {
- var thmgr_cls = Components.classes["@mozilla.org/thread-manager;1"];
+ var thmgr_cls = Components.classes['@mozilla.org/thread-manager;1'];
var node = Utils.getNodeForNativeEvents(element);
var hasEvents = {};
@@ -1070,7 +1070,7 @@
var doneNativeEventWait = false;
var callback = function() {
- fxdriver.logging.info("Done native event wait.");
+ fxdriver.logging.info('Done native event wait.');
doneNativeEventWait = true;
};
@@ -1078,7 +1078,7 @@
nativeEvents.hasUnhandledEvents(node, hasEvents);
- fxdriver.logging.info("Pending native events: " + hasEvents.value);
+ fxdriver.logging.info('Pending native events: ' + hasEvents.value);
var numEventsProcessed = 0;
// Do it as long as the timeout function has not been called and the
// page has not been unloaded. If the page has been unloaded, there is no
@@ -1089,15 +1089,15 @@
thread.processNextEvent(true);
numEventsProcessed += 1;
}
- fxdriver.logging.info("Extra events processed: " + numEventsProcessed +
- " Page Unloaded: " + pageUnloadedData.wasUnloaded);
+ fxdriver.logging.info('Extra events processed: ' + numEventsProcessed +
+ ' Page Unloaded: ' + pageUnloadedData.wasUnloaded);
} while ((hasEvents.value == true) && (!pageUnloadedData.wasUnloaded));
- fxdriver.logging.info("Done main loop.");
+ fxdriver.logging.info('Done main loop.');
if (pageUnloadedData.wasUnloaded) {
- fxdriver.logging.info("Page has been reloaded while waiting for native events to "
- + "be processed. Remaining events? " + hasEvents.value);
+ fxdriver.logging.info('Page has been reloaded while waiting for native events to '
+ + 'be processed. Remaining events? ' + hasEvents.value);
} else {
Utils.removePageUnloadEventListener(element, pageUnloadedData);
}
@@ -1123,7 +1123,7 @@
numExtraEventsProcessed += 1;
}
- fxdriver.logging.info("Done extra event loop, " + numExtraEventsProcessed);
+ fxdriver.logging.info('Done extra event loop, ' + numExtraEventsProcessed);
};
Utils.getPageUnloadedIndicator = function(element) {
@@ -1139,12 +1139,12 @@
var unloadFunction = function() { toReturn.wasUnloaded = true };
toReturn.callback = unloadFunction;
- element.ownerDocument.body.addEventListener("unload",
+ element.ownerDocument.body.addEventListener('unload',
unloadFunction, false);
// This is a Firefox specific event - See:
// https://developer.mozilla.org/En/Using_Firefox_1.5_caching
- element.ownerDocument.defaultView.addEventListener("pagehide",
+ element.ownerDocument.defaultView.addEventListener('pagehide',
unloadFunction, false);
return toReturn;
@@ -1155,11 +1155,11 @@
// Remove event listeners...
if (element.ownerDocument) {
if (element.ownerDocument.body) {
- element.ownerDocument.body.removeEventListener("unload",
+ element.ownerDocument.body.removeEventListener('unload',
pageUnloadData.callback, false);
}
if (element.ownerDocument.defaultView) {
- element.ownerDocument.defaultView.removeEventListener("pagehide",
+ element.ownerDocument.defaultView.removeEventListener('pagehide',
pageUnloadData.callback, false);
}
}
diff --git a/javascript/firefox-driver/js/wdsession.js b/javascript/firefox-driver/js/wdsession.js
index 3416f9d..848f0dd 100644
--- a/javascript/firefox-driver/js/wdsession.js
+++ b/javascript/firefox-driver/js/wdsession.js
@@ -233,7 +233,7 @@
if (win == win.top) {
self.window_ = null;
}
- }
+ };
// Listen in capture mode to force us to be called (can't stop event
// propagation in capture mode)
@@ -354,7 +354,7 @@
wdSession.prototype.setMouseViewportOffset = function(x, y) {
this.mousePosition_.viewPortXOffset = x;
this.mousePosition_.viewPortYOffset = y;
-}
+};
/**
* Close the browser after a given time delay.
@@ -462,8 +462,8 @@
return new wdSessionModule();
};
-wdSession.prototype.classID = wdSession.CLASS_ID
-fxdriver.moz.load("resource://gre/modules/XPCOMUtils.jsm");
+wdSession.prototype.classID = wdSession.CLASS_ID;
+fxdriver.moz.load('resource://gre/modules/XPCOMUtils.jsm');
if (XPCOMUtils.generateNSGetFactory) {
/** @const */ NSGetFactory = XPCOMUtils.generateNSGetFactory([wdSession]);
}
diff --git a/javascript/firefox-driver/js/webLoadingListener.js b/javascript/firefox-driver/js/webLoadingListener.js
index 614d8db..6712b5d 100644
--- a/javascript/firefox-driver/js/webLoadingListener.js
+++ b/javascript/firefox-driver/js/webLoadingListener.js
@@ -36,9 +36,9 @@
}
var ioService =
- fxdriver.moz.getService("@mozilla.org/network/io-service;1", "nsIIOService");
- var currentUri = ioService.newURI(current, "", null);
- var futureUri = ioService.newURI(future, "", currentUri);
+ fxdriver.moz.getService('@mozilla.org/network/io-service;1', 'nsIIOService');
+ var currentUri = ioService.newURI(current, '', null);
+ var futureUri = ioService.newURI(future, '', currentUri);
var loadEventExpected = true;
if (futureUri.scheme == 'javascript') {
@@ -52,7 +52,7 @@
// Looks like we're at the same url with a ref
// Being clever and checking the ref was causing me headaches.
// Brute force for now
- loadEventExpected = futureUri.path.indexOf("#") == -1;
+ loadEventExpected = futureUri.path.indexOf('#') == -1;
}
return loadEventExpected;
@@ -158,7 +158,7 @@
};
-var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces["nsIPrefBranch"]);
+var prefs = Components.classes['@mozilla.org/preferences-service;1'].getService(Components.interfaces['nsIPrefBranch']);
function buildHandler(browser, toCall, opt_window) {
if (prefs.prefHasUserValue('webdriver.load.strategy')) {
diff --git a/javascript/firefox-driver/js/webdriverserver.js b/javascript/firefox-driver/js/webdriverserver.js
index 85919a3..2621794 100644
--- a/javascript/firefox-driver/js/webdriverserver.js
+++ b/javascript/firefox-driver/js/webdriverserver.js
@@ -31,25 +31,25 @@
WebDriverServer = function() {
this.wrappedJSObject = this;
this.serverSocket =
- Components.classes["@mozilla.org/network/server-socket;1"].
+ Components.classes['@mozilla.org/network/server-socket;1'].
createInstance(Components.interfaces.nsIServerSocket);
- this.generator = fxdriver.moz.getService("@mozilla.org/uuid-generator;1", "nsIUUIDGenerator");
+ this.generator = fxdriver.moz.getService('@mozilla.org/uuid-generator;1', 'nsIUUIDGenerator');
this.enableNativeEvents = null;
// Force our cert override service to be loaded - otherwise, it will not be
// loaded and cause a "too deep recursion" error.
- var overrideService = Components.classes["@mozilla.org/security/certoverride;1"]
+ var overrideService = Components.classes['@mozilla.org/security/certoverride;1']
.getService(Components.interfaces.nsICertOverrideService);
var dispatcher_ = new Dispatcher();
try {
- this.server_ = Utils.newInstance("@mozilla.org/server/jshttp;1", "nsIHttpServer");
+ this.server_ = Utils.newInstance('@mozilla.org/server/jshttp;1', 'nsIHttpServer');
} catch (e) {
fxdriver.logging.warning(e);
}
- this.server_.registerGlobHandler(".*/hub/.*", { handle: function(request, response) {
+ this.server_.registerGlobHandler('.*/hub/.*', { handle: function(request, response) {
response.processAsync();
dispatcher_.dispatch(new Request(request), new Response(response));
}});
@@ -74,10 +74,10 @@
WebDriverServer.prototype.startListening = function(port) {
if (!port) {
var prefs =
- fxdriver.moz.getService("@mozilla.org/preferences-service;1", "nsIPrefBranch");
+ fxdriver.moz.getService('@mozilla.org/preferences-service;1', 'nsIPrefBranch');
- port = prefs.prefHasUserValue("webdriver_firefox_port") ?
- prefs.getIntPref("webdriver_firefox_port") : 7055;
+ port = prefs.prefHasUserValue('webdriver_firefox_port') ?
+ prefs.getIntPref('webdriver_firefox_port') : 7055;
}
if (!this.isListening) {
diff --git a/javascript/firefox-driver/js/wrappedElement.js b/javascript/firefox-driver/js/wrappedElement.js
index f0f892f..137d232 100644
--- a/javascript/firefox-driver/js/wrappedElement.js
+++ b/javascript/firefox-driver/js/wrappedElement.js
@@ -24,13 +24,14 @@
goog.require('bot.ErrorCode');
goog.require('bot.action');
goog.require('bot.dom');
-goog.require('fxdriver.logging');
goog.require('fxdriver.io');
+goog.require('fxdriver.logging');
goog.require('fxdriver.moz');
goog.require('fxdriver.preconditions');
goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('goog.dom.selection');
+goog.require('goog.math.Coordinate');
goog.require('webdriver.atoms.element');
@@ -60,27 +61,27 @@
var unwrapped = fxdriver.moz.unwrapFor4(element);
var nativeMouse = Utils.getNativeMouse();
var node = Utils.getNodeForNativeEvents(unwrapped);
- var appInfo = Components.classes["@mozilla.org/xre/app-info;1"].
+ var appInfo = Components.classes['@mozilla.org/xre/app-info;1'].
getService(Components.interfaces.nsIXULAppInfo);
var versionChecker = Components.
- classes["@mozilla.org/xpcom/version-comparator;1"].
+ classes['@mozilla.org/xpcom/version-comparator;1'].
getService(Components.interfaces.nsIVersionComparator);
// I'm having trouble getting clicks to work on Firefox 2 on Windows. Always
// fall back for that
var useNativeClick =
- versionChecker.compare(appInfo.platformVersion, "1.9") >= 0;
- var thmgr_cls = Components.classes["@mozilla.org/thread-manager;1"];
+ versionChecker.compare(appInfo.platformVersion, '1.9') >= 0;
+ var thmgr_cls = Components.classes['@mozilla.org/thread-manager;1'];
// For now, we need to bypass native events for option elements
- var isOption = "option" == unwrapped.tagName.toLowerCase();
+ var isOption = 'option' == unwrapped.tagName.toLowerCase();
- var location = Utils.getLocation(unwrapped, unwrapped.tagName == "A");
+ var location = Utils.getLocation(unwrapped, unwrapped.tagName == 'A');
var elementHalfWidth = (location.width ? location.width / 2 : 0);
var elementHalfHeight = (location.height ? location.height / 2 : 0);
if (!isOption && this.enableNativeEvents && nativeMouse && node && useNativeClick && thmgr_cls) {
- fxdriver.logging.info("Using native events for click");
+ fxdriver.logging.info('Using native events for click');
var inViewAfterScroll = bot.action.scrollIntoView(
unwrapped,
@@ -93,7 +94,7 @@
return;
}
- location = Utils.getLocationRelativeToWindowHandle(unwrapped, unwrapped.tagName == "A");
+ location = Utils.getLocationRelativeToWindowHandle(unwrapped, unwrapped.tagName == 'A');
var x = location.x + elementHalfWidth;
var y = location.y + elementHalfHeight;
@@ -124,9 +125,9 @@
// the error returned from the native call indicates it's not
// implemented.
- fxdriver.logging.info("Detected error when clicking: " + e.name);
+ fxdriver.logging.info('Detected error when clicking: ' + e.name);
- if (e.name != "NS_ERROR_NOT_IMPLEMENTED") {
+ if (e.name != 'NS_ERROR_NOT_IMPLEMENTED') {
throw new WebDriverError(bot.ErrorCode.INVALID_ELEMENT_STATE, e);
}
@@ -134,7 +135,7 @@
}
}
- fxdriver.logging.info("Falling back to synthesized click");
+ fxdriver.logging.info('Falling back to synthesized click');
// TODO(simon): Delete the above and sink most of it into a "nativeMouse"
Utils.installWindowCloseListener(respond);
@@ -153,7 +154,7 @@
respond.value = res.message;
};
WebElement.clickElement.preconditions =
- [ fxdriver.preconditions.visible ];
+ [fxdriver.preconditions.visible];
WebElement.getElementText = function(respond, parameters) {
@@ -183,32 +184,32 @@
var newDocument = goog.dom.getOwnerDocument(currentlyActive);
if (currentlyActive != element || currentDocument != new XPCNativeWrapper(newDocument)) {
- fxdriver.logging.info("Need to switch focus");
+ fxdriver.logging.info('Need to switch focus');
alreadyFocused = false;
currentlyActive.blur();
element.focus();
element.ownerDocument.defaultView.focus();
} else {
- fxdriver.logging.info("No need to switch focus");
+ fxdriver.logging.info('No need to switch focus');
}
var use = element;
var tagName = element.tagName.toLowerCase();
- if (tagName == "body" && element.ownerDocument.defaultView.frameElement) {
+ if (tagName == 'body' && element.ownerDocument.defaultView.frameElement) {
element.ownerDocument.defaultView.focus();
// Turns out, this is what we should be using as the target
// to send events to
- use = element.ownerDocument.getElementsByTagName("html")[0];
+ use = element.ownerDocument.getElementsByTagName('html')[0];
}
// Handle the special case of the file input element here
if (bot.dom.isElement(element, goog.dom.TagName.INPUT)) {
- var inputtype = element.getAttribute("type");
- if (inputtype && inputtype.toLowerCase() == "file") {
+ var inputtype = element.getAttribute('type');
+ if (inputtype && inputtype.toLowerCase() == 'file') {
element.value = parameters.value.join('');
- Utils.fireHtmlEvent(element, "change");
+ Utils.fireHtmlEvent(element, 'change');
respond.send();
return;
}
@@ -220,7 +221,7 @@
this.jsTimer.setTimeout(function() {
// Unless the element already had focus, set the cursor location to the end of the line
// TODO(simon): This seems a little arbitrary.
- if(!alreadyFocused && bot.dom.isEditable(element)) {
+ if (!alreadyFocused && bot.dom.isEditable(element)) {
var length = element.value ? element.value.length : goog.dom.getTextContent(element).length;
goog.dom.selection.setCursorPosition(element, length);
}
@@ -232,7 +233,7 @@
}, 0);
};
WebElement.sendKeysToElement.preconditions =
- [ fxdriver.preconditions.visible, fxdriver.preconditions.enabled ];
+ [fxdriver.preconditions.visible, fxdriver.preconditions.enabled];
WebElement.clearElement = function(respond, parameters) {
@@ -243,7 +244,7 @@
respond.send();
};
WebElement.clearElement.preconditions =
- [ fxdriver.preconditions.visible, fxdriver.preconditions.enabled, fxdriver.preconditions.writable ];
+ [fxdriver.preconditions.visible, fxdriver.preconditions.enabled, fxdriver.preconditions.writable];
WebElement.getElementTagName = function(respond, parameters) {
@@ -259,7 +260,7 @@
var element = Utils.getElementAt(parameters.id,
respond.session.getDocument());
var attributeName = parameters.name;
-
+
respond.value = webdriver.atoms.element.getAttribute(element, attributeName);
respond.send();
};
@@ -278,12 +279,12 @@
respond.session.getDocument());
if (element) {
- while (element.parentNode != null && element.tagName.toLowerCase() != "form") {
+ while (element.parentNode != null && element.tagName.toLowerCase() != 'form') {
element = element.parentNode;
}
- if (element.tagName && element.tagName.toLowerCase() == "form") {
+ if (element.tagName && element.tagName.toLowerCase() == 'form') {
var current = respond.session.getWindow().location;
- if (Utils.fireHtmlEvent(element, "submit") &&
+ if (Utils.fireHtmlEvent(element, 'submit') &&
fxdriver.io.isLoadExpected(current, element.action)) {
new WebLoadingListener(respond.session.getBrowser(), function(timedOut) {
if (timedOut) {
@@ -318,16 +319,16 @@
var option =
element.QueryInterface(Components.interfaces.nsIDOMHTMLOptionElement);
selected = option.selected;
- } catch(e) {
+ } catch (e) {
}
try {
var inputElement =
element.QueryInterface(Components.interfaces.nsIDOMHTMLInputElement);
- if (inputElement.type == "checkbox" || inputElement.type == "radio") {
+ if (inputElement.type == 'checkbox' || inputElement.type == 'radio') {
selected = inputElement.checked;
}
- } catch(e) {
+ } catch (e) {
}
respond.value = selected;
@@ -397,8 +398,8 @@
var elementLocation = Utils.getLocationOnceScrolledIntoView(element);
respond.value = {
- x : Math.round(elementLocation.x),
- y : Math.round(elementLocation.y)
+ x: Math.round(elementLocation.x),
+ y: Math.round(elementLocation.y)
};
respond.send();
diff --git a/javascript/remote/ui/actiondialog.js b/javascript/remote/ui/actiondialog.js
index a9fc554..ff13269 100644
--- a/javascript/remote/ui/actiondialog.js
+++ b/javascript/remote/ui/actiondialog.js
@@ -16,7 +16,7 @@
goog.require('goog.dom');
goog.require('goog.events');
-goog.require('goog.ui.Component.EventType');
+goog.require('goog.ui.Component');
goog.require('goog.ui.Dialog');
diff --git a/javascript/remote/ui/banner.js b/javascript/remote/ui/banner.js
index 6f461ee..c258b55 100644
--- a/javascript/remote/ui/banner.js
+++ b/javascript/remote/ui/banner.js
@@ -15,6 +15,7 @@
goog.provide('remote.ui.Banner');
goog.require('goog.dom');
+goog.require('goog.dom.TagName');
goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('goog.style');
diff --git a/javascript/remote/ui/client.js b/javascript/remote/ui/client.js
index 5725f7a..6ac0f69 100644
--- a/javascript/remote/ui/client.js
+++ b/javascript/remote/ui/client.js
@@ -20,7 +20,6 @@
goog.require('goog.array');
goog.require('goog.debug.Console');
goog.require('goog.debug.Logger');
-goog.require('goog.dom');
goog.require('goog.events');
goog.require('remote.ui.Banner');
goog.require('remote.ui.Event.Type');
diff --git a/javascript/remote/ui/createsessiondialog.js b/javascript/remote/ui/createsessiondialog.js
index 1e35641..ea234f1 100644
--- a/javascript/remote/ui/createsessiondialog.js
+++ b/javascript/remote/ui/createsessiondialog.js
@@ -20,11 +20,11 @@
goog.require('remote.ui.ActionDialog');
-
/**
* Dialog used to configure a new session request.
- * @param {!Array.<string>} browsers List of possible browsers to create
- * sessions for.
+ * @param {!Array.<!(Object|string)>} browsers List of possible browsers to
+ * create sessions for: each browser should be defined either by its name,
+ * or a fully defined capabilities object.
* @constructor
* @extends {remote.ui.ActionDialog}
*/
@@ -32,10 +32,12 @@
goog.base(this, 'Create a New Session');
/**
- * @type {!Array.<string>}
+ * @type {!Array.<!Object>}
* @private
*/
- this.browsers_ = browsers;
+ this.browsers_ = goog.array.map(browsers, function(browser) {
+ return goog.isString(browser) ? {'browserName': browser} : browser;
+ });
goog.events.listen(this, goog.ui.Component.EventType.SHOW,
this.onShow_, false, this);
@@ -71,11 +73,13 @@
return dom.createDom(goog.dom.TagName.LABEL, null,
'Browser:\xa0', this.browserSelect_);
- function createOption(value) {
- return dom.createDom(goog.dom.TagName.OPTION, {'value': value},
- value.toLowerCase().replace(/\b[a-z]/g, function(c) {
- return c.toUpperCase();
- }));
+ function createOption(capabilities) {
+ var displayText = capabilities['browserName'];
+ var version = capabilities['version'];
+ if (version) {
+ displayText += ' ' + version;
+ }
+ return dom.createDom(goog.dom.TagName.OPTION, null, displayText);
}
};
@@ -90,13 +94,7 @@
/** @override */
remote.ui.CreateSessionDialog.prototype.getUserSelection = function() {
- var selected = this.browserSelect_.selectedIndex;
- return {
- 'browserName': this.browserSelect_.options[selected].value,
- 'version': '',
- 'platform': 'ANY',
- 'javascriptEnabled': true
- };
+ return this.browsers_[this.browserSelect_.selectedIndex - 1];
};
diff --git a/javascript/remote/ui/fieldset.js b/javascript/remote/ui/fieldset.js
index a5032c7..82babd4 100644
--- a/javascript/remote/ui/fieldset.js
+++ b/javascript/remote/ui/fieldset.js
@@ -14,6 +14,7 @@
goog.provide('remote.ui.FieldSet');
+goog.require('goog.dom.TagName');
goog.require('goog.ui.Component');
diff --git a/javascript/remote/ui/openscriptdialog.js b/javascript/remote/ui/openscriptdialog.js
index 5dcff40..766ec47 100644
--- a/javascript/remote/ui/openscriptdialog.js
+++ b/javascript/remote/ui/openscriptdialog.js
@@ -14,9 +14,11 @@
goog.provide('remote.ui.OpenScriptDialog');
+goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('goog.dom.classes');
-goog.require('goog.ui.Component.EventType');
+goog.require('goog.events');
+goog.require('goog.ui.Component');
goog.require('goog.ui.LabelInput');
goog.require('remote.ui.ActionDialog');
diff --git a/javascript/remote/ui/screenshotdialog.js b/javascript/remote/ui/screenshotdialog.js
index 6f8041d..fd2316a 100644
--- a/javascript/remote/ui/screenshotdialog.js
+++ b/javascript/remote/ui/screenshotdialog.js
@@ -15,8 +15,8 @@
goog.provide('remote.ui.ScreenshotDialog');
goog.provide('remote.ui.ScreenshotDialog.State');
+goog.require('goog.dom');
goog.require('goog.dom.TagName');
-goog.require('goog.style');
goog.require('goog.ui.Dialog');
diff --git a/javascript/remote/ui/sessioncontainer.js b/javascript/remote/ui/sessioncontainer.js
index a480337..f6f0c69 100644
--- a/javascript/remote/ui/sessioncontainer.js
+++ b/javascript/remote/ui/sessioncontainer.js
@@ -18,11 +18,10 @@
goog.require('goog.array');
goog.require('goog.dom.TagName');
goog.require('goog.events');
-goog.require('goog.events.Event');
goog.require('goog.events.EventType');
goog.require('goog.structs.Map');
goog.require('goog.style');
-goog.require('goog.ui.Component.EventType');
+goog.require('goog.ui.Component');
goog.require('goog.ui.Tab');
goog.require('goog.ui.TabBar');
goog.require('remote.ui.ControlBlock');
diff --git a/javascript/remote/ui/sessionview.js b/javascript/remote/ui/sessionview.js
index c2b930d..d4d8836 100644
--- a/javascript/remote/ui/sessionview.js
+++ b/javascript/remote/ui/sessionview.js
@@ -16,7 +16,7 @@
goog.require('goog.dom');
goog.require('goog.dom.TagName');
-goog.require('goog.events.Event');
+goog.require('goog.events');
goog.require('goog.math.Box');
goog.require('goog.style');
goog.require('goog.ui.Button');
diff --git a/javascript/safari-driver/extension/commands.js b/javascript/safari-driver/extension/commands.js
index 7ada622..c301d87 100644
--- a/javascript/safari-driver/extension/commands.js
+++ b/javascript/safari-driver/extension/commands.js
@@ -22,6 +22,7 @@
goog.require('bot.response');
goog.require('goog.Uri');
+goog.require('goog.array');
goog.require('goog.debug.Logger');
goog.require('goog.string');
goog.require('safaridriver.extension.Tab');
diff --git a/javascript/safari-driver/extension/session.js b/javascript/safari-driver/extension/session.js
index 9992c35..0f809c0 100644
--- a/javascript/safari-driver/extension/session.js
+++ b/javascript/safari-driver/extension/session.js
@@ -18,7 +18,7 @@
goog.require('goog.string');
goog.require('goog.userAgent');
-goog.require('goog.userAgent.product.isVersion');
+goog.require('goog.userAgent.product');
goog.require('webdriver.Session');
diff --git a/javascript/safari-driver/extension/tab.js b/javascript/safari-driver/extension/tab.js
index 177a912..41cc44b 100644
--- a/javascript/safari-driver/extension/tab.js
+++ b/javascript/safari-driver/extension/tab.js
@@ -19,6 +19,7 @@
goog.require('bot.response');
goog.require('goog.Uri');
goog.require('goog.asserts');
+goog.require('goog.debug.Logger');
goog.require('safaridriver.Tab');
goog.require('safaridriver.message.Command');
goog.require('safaridriver.message.Load');
diff --git a/javascript/safari-driver/extension/tabmanager.js b/javascript/safari-driver/extension/tabmanager.js
index 1d1ba0f..bceea32 100644
--- a/javascript/safari-driver/extension/tabmanager.js
+++ b/javascript/safari-driver/extension/tabmanager.js
@@ -18,7 +18,6 @@
goog.require('goog.array');
goog.require('goog.debug.Logger');
-goog.require('goog.string');
goog.require('safaridriver.extension.Tab');
diff --git a/javascript/safari-driver/externs/extension.js b/javascript/safari-driver/externs/extension.js
index ee12464..45f2be4 100644
--- a/javascript/safari-driver/externs/extension.js
+++ b/javascript/safari-driver/externs/extension.js
@@ -20,4 +20,4 @@
/** @type {SafariNamespace} */
-var safari;
\ No newline at end of file
+var safari;
diff --git a/javascript/safari-driver/inject/pagescript.js b/javascript/safari-driver/inject/pagescript.js
index 099488c..0283b1e 100644
--- a/javascript/safari-driver/inject/pagescript.js
+++ b/javascript/safari-driver/inject/pagescript.js
@@ -18,6 +18,7 @@
goog.require('bot.inject');
goog.require('bot.response');
goog.require('goog.debug.Logger');
+goog.require('goog.dom');
goog.require('safaridriver.inject.Encoder');
goog.require('safaridriver.inject.message');
goog.require('safaridriver.message.Command');
diff --git a/javascript/selenium-atoms/events.js b/javascript/selenium-atoms/events.js
index 01ce888..ebb414c 100644
--- a/javascript/selenium-atoms/events.js
+++ b/javascript/selenium-atoms/events.js
@@ -31,6 +31,7 @@
goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('goog.style');
+goog.require('goog.userAgent');
goog.require('goog.userAgent.product');
@@ -39,6 +40,9 @@
core.events.metaKeyDown_ = false;
core.events.shiftKeyDown_ = false;
+/**
+ * @type {function(*): !Object}
+ */
var XPCNativeWrapper = XPCNativeWrapper || function(_) {};
core.events.getEventFactory_ = function(eventName) {
@@ -140,7 +144,7 @@
metaKey: false,
relatedTarget: null
};
- bot.events.fire(element, type, (/** @type{!bot.events.MouseArgs} */args));
+ bot.events.fire(element, type, (/** @type {!bot.events.MouseArgs} */args));
};
@@ -201,8 +205,8 @@
*/
core.events.setValue = function(locator, value) {
if (core.events.controlKeyDown_ || core.events.altKeyDown_ || core.events.metaKeyDown_) {
- throw new core.Error("type not supported immediately after call to " +
- "controlKeyDown() or altKeyDown() or metaKeyDown()");
+ throw new core.Error('type not supported immediately after call to ' +
+ 'controlKeyDown() or altKeyDown() or metaKeyDown()');
}
// TODO(simon): fail if it can't be typed into.
diff --git a/javascript/selenium-atoms/firefox-chrome.js b/javascript/selenium-atoms/firefox-chrome.js
index 034e244..4f6d259 100644
--- a/javascript/selenium-atoms/firefox-chrome.js
+++ b/javascript/selenium-atoms/firefox-chrome.js
@@ -1,7 +1,7 @@
goog.provide('core.firefox');
/**
- * @returns {boolean} Whether the firefox instance needs elements to be
+ * @return {boolean} Whether the firefox instance needs elements to be
* unwrapped.
*/
core.firefox.isUsingUnwrapping_ = function() {
@@ -12,7 +12,7 @@
getService(Components.interfaces.nsIVersionComparator);
return (versionChecker.compare(appInfo.version, '4.0') >= 0);
- } catch(e) {
+ } catch (e) {
// like when its not Firefox
return false;
}
@@ -25,7 +25,7 @@
* Unwraps a something which is wrapped into a XPCNativeWrapper or XrayWrapper.
*
* @param {!Object} thing The "something" to unwrap.
- * @returns {!Object} The object, unwrapped if possible.
+ * @return {!Object} The object, unwrapped if possible.
*/
core.firefox.unwrap = function(thing) {
if (!core.firefox.isUsingUnwrapping_) {
@@ -61,7 +61,7 @@
toReturn.__fxdriver_unwrapped = true;
return toReturn;
}
- } catch(e) {
+ } catch (e) {
// Unwrapping will fail for JS literals - numbers, for example. Catch
// the exception and proceed, it will eventually be returned as-is.
}
diff --git a/javascript/selenium-atoms/test/event_firing_test.html b/javascript/selenium-atoms/test/event_firing_test.html
index 49c305c..47680a1 100644
--- a/javascript/selenium-atoms/test/event_firing_test.html
+++ b/javascript/selenium-atoms/test/event_firing_test.html
@@ -18,7 +18,8 @@
// These should work, they just dosn't.
var CLICKING_WITH_COORDINATES_BROKEN = goog.userAgent.product.SAFARI ||
goog.userAgent.product.ANDROID ||
- goog.userAgent.product.OPERA;
+ (goog.userAgent.product.OPERA &&
+ bot.userAgent.isEngineVersion(12));
function setUp() {
fired = false;
diff --git a/javascript/selenium-atoms/text.js b/javascript/selenium-atoms/text.js
index 9311c7c..88bf79e 100644
--- a/javascript/selenium-atoms/text.js
+++ b/javascript/selenium-atoms/text.js
@@ -150,7 +150,7 @@
var text = '';
var isRecentFirefox =
(goog.userAgent.GECKO && goog.userAgent.VERSION >= '1.8');
-
+
if (isRecentFirefox ||
goog.userAgent.SAFARI || goog.userAgent.OPERA || goog.userAgent.IE) {
text = core.text.getTextContent_(element, false);
@@ -191,9 +191,9 @@
var allText = core.text.getBodyText();
var patternMatcher = core.patternMatcher.against(pattern);
- if (patternMatcher.strategyName == "glob") {
- if (pattern.indexOf("glob:") == 0) {
- pattern = pattern.substring("glob:".length); // strip off "glob:"
+ if (patternMatcher.strategyName == 'glob') {
+ if (pattern.indexOf('glob:') == 0) {
+ pattern = pattern.substring('glob:'.length); // strip off "glob:"
}
patternMatcher = core.patternMatcher.against('globContains:' + pattern);
}
diff --git a/javascript/webdriver/abstractbuilder.js b/javascript/webdriver/abstractbuilder.js
index 7f1d65b..76d2d21 100644
--- a/javascript/webdriver/abstractbuilder.js
+++ b/javascript/webdriver/abstractbuilder.js
@@ -171,4 +171,4 @@
* current configuration.
* @return {!webdriver.WebDriver} A new WebDriver client.
*/
-webdriver.AbstractBuilder.prototype.build = goog.abstractMethod;
\ No newline at end of file
+webdriver.AbstractBuilder.prototype.build = goog.abstractMethod;
diff --git a/javascript/webdriver/actionsequence.js b/javascript/webdriver/actionsequence.js
index e420ef5..6df1d94 100644
--- a/javascript/webdriver/actionsequence.js
+++ b/javascript/webdriver/actionsequence.js
@@ -353,4 +353,4 @@
webdriver.ActionSequence.prototype.sendKeys = function(var_args) {
var keys = goog.array.flatten(goog.array.slice(arguments, 0));
return this.scheduleKeyboardAction_('sendKeys', keys);
-};
\ No newline at end of file
+};
diff --git a/javascript/webdriver/atoms/element.js b/javascript/webdriver/atoms/element.js
index 4a3ea4b..2447bfa 100644
--- a/javascript/webdriver/atoms/element.js
+++ b/javascript/webdriver/atoms/element.js
@@ -47,6 +47,75 @@
/**
+ * Common aliases for properties. This maps names that users use to the correct
+ * property name.
+ *
+ * @const
+ * @private
+ */
+webdriver.atoms.element.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
+ * @private
+ */
+webdriver.atoms.element.BOOLEAN_PROPERTIES_ = [
+ 'async',
+ 'autofocus',
+ 'autoplay',
+ 'checked',
+ 'compact',
+ 'complete',
+ 'controls',
+ 'declare',
+ 'defaultchecked',
+ 'defaultselected',
+ 'defer',
+ 'disabled',
+ 'draggable',
+ 'ended',
+ 'formnovalidate',
+ 'hidden',
+ 'indeterminate',
+ 'iscontenteditable',
+ 'ismap',
+ 'itemscope',
+ 'loop',
+ 'multiple',
+ 'muted',
+ 'nohref',
+ 'noresize',
+ 'noshade',
+ 'novalidate',
+ 'nowrap',
+ 'open',
+ 'paused',
+ 'pubdate',
+ 'readonly',
+ 'required',
+ 'reversed',
+ 'scoped',
+ 'seamless',
+ 'seeking',
+ 'selected',
+ 'spellcheck',
+ 'truespeed',
+ '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
@@ -70,7 +139,7 @@
return (/** @type {?string} */value);
}
- if ('selected' == name || 'checked' == name &&
+ if (('selected' == name || 'checked' == name) &&
bot.dom.isSelectable(element)) {
return bot.dom.isSelected(element) ? 'true' : null;
}
@@ -92,15 +161,17 @@
return (/** @type {?string} */value);
}
- if (bot.dom.isBooleanAttribute(attribute.toLowerCase())) {
+ var propName = webdriver.atoms.element.PROPERTY_ALIASES_[attribute] ||
+ attribute;
+ if (goog.array.contains(webdriver.atoms.element.BOOLEAN_PROPERTIES_, name)) {
value = bot.dom.getAttribute(element, attribute) ||
- bot.dom.getProperty(element, attribute);
+ bot.dom.getProperty(element, propName);
return value ? 'true' : null;
}
var property;
try {
- property = bot.dom.getProperty(element, attribute);
+ property = bot.dom.getProperty(element, propName);
} catch (e) {
// Leaves property undefined or null
}
@@ -170,7 +241,7 @@
if (webdriver.atoms.element.isInHead_(element)) {
var doc = goog.dom.getOwnerDocument(element);
if (element.tagName.toUpperCase() == goog.dom.TagName.TITLE &&
- goog.dom.getWindow(doc) == bot.window_.top) {
+ goog.dom.getWindow(doc) == bot.getWindow().top) {
return goog.string.trim((/** @type {string} */doc.title));
}
return '';
diff --git a/javascript/webdriver/atoms/inject/action.js b/javascript/webdriver/atoms/inject/action.js
index 346d577..fcfb049 100644
--- a/javascript/webdriver/atoms/inject/action.js
+++ b/javascript/webdriver/atoms/inject/action.js
@@ -20,9 +20,9 @@
goog.provide('webdriver.atoms.inject.action');
goog.require('bot.action');
-goog.require('bot.inject');
goog.require('goog.dom.selection');
goog.require('webdriver.atoms.element');
+goog.require('webdriver.atoms.inject');
/**
@@ -33,8 +33,8 @@
* @return {string} A stringified {@link bot.response.ResponseObject}.
*/
webdriver.atoms.inject.action.type = function(element, keys) {
- return bot.inject.executeScript(webdriver.atoms.element.type,
- [element, keys], true);
+ return webdriver.atoms.inject.executeScript(webdriver.atoms.element.type,
+ [element, keys]);
};
@@ -45,7 +45,7 @@
* @return {string} A stringified {@link bot.response.ResponseObject}.
*/
webdriver.atoms.inject.action.submit = function(element) {
- return bot.inject.executeScript(bot.action.submit, [element], true);
+ return webdriver.atoms.inject.executeScript(bot.action.submit, [element]);
};
@@ -57,7 +57,7 @@
* @see bot.action.clear
*/
webdriver.atoms.inject.action.clear = function(element) {
- return bot.inject.executeScript(bot.action.clear, [element], true);
+ return webdriver.atoms.inject.executeScript(bot.action.clear, [element]);
};
@@ -69,6 +69,6 @@
* @see bot.action.click
*/
webdriver.atoms.inject.action.click = function(element) {
- return bot.inject.executeScript(bot.action.click, [element], true);
+ return webdriver.atoms.inject.executeScript(bot.action.click, [element]);
};
diff --git a/javascript/webdriver/atoms/inject/appcache.js b/javascript/webdriver/atoms/inject/appcache.js
index a846dae..610a792 100644
--- a/javascript/webdriver/atoms/inject/appcache.js
+++ b/javascript/webdriver/atoms/inject/appcache.js
@@ -25,11 +25,9 @@
/**
* Gets the status of the application cache.
*
- * @return {number} The status of the application cache.
+ * @return {string} The status of the application cache.
*/
webdriver.atoms.inject.storage.appcache.getStatus = function() {
- return bot.inject.executeScript(webdriver.atoms.storage.appcache.getStatus,
- [], true);
+ return /**@type {string}*/(bot.inject.executeScript(
+ webdriver.atoms.storage.appcache.getStatus, [], true));
};
-
-
diff --git a/javascript/webdriver/atoms/inject/dom.js b/javascript/webdriver/atoms/inject/dom.js
index 026c972..cf5743f 100644
--- a/javascript/webdriver/atoms/inject/dom.js
+++ b/javascript/webdriver/atoms/inject/dom.js
@@ -14,101 +14,104 @@
// limitations under the License.
/**
- *@fileoverview Ready to inject atoms for querying the DOM.
+ * @fileoverview Ready to inject atoms for querying the DOM.
*/
goog.provide('webdriver.atoms.inject.dom');
goog.require('bot.action');
goog.require('bot.dom');
-goog.require('bot.inject');
goog.require('webdriver.atoms.element');
+goog.require('webdriver.atoms.inject');
/**
* Gets the visisble text for the given element.
- * @param {!{bot.inject.ELEMENT_KEY:string}} element The element to query.
+ * @param {{bot.inject.ELEMENT_KEY: string}} element The element to query.
* @return {string} The visible text wrapped in a JSON string as defined by the
* WebDriver wire protocol.
*/
webdriver.atoms.inject.dom.getText = function(element) {
- return bot.inject.executeScript(webdriver.atoms.element.getText, [element], true);
+ return webdriver.atoms.inject.executeScript(bot.dom.getVisibleText,
+ [element]);
};
/**
- * @param {!{bot.inject.ELEMENT_KEY:string}} element The element to query.
+ * @param {{bot.inject.ELEMENT_KEY: string}} element The element to query.
* @return {string} A boolean describing whether the element is
* checked or selected wrapped in a JSON string as defined by
* the wire protocol.
*/
webdriver.atoms.inject.dom.isSelected = function(element) {
- return bot.inject.executeScript(bot.dom.isSelected, [element], true);
+ return webdriver.atoms.inject.executeScript(bot.dom.isSelected, [element]);
};
/**
- * @param {!{bot.inject.ELEMENT_KEY:string}} element The element to query.
+ * @param {{bot.inject.ELEMENT_KEY: string}} element The element to query.
* @return {string} The coordinates of the top left corner in a JSON
* string as defined by the wire protocol.
*/
webdriver.atoms.inject.dom.getTopLeftCoordinates = function(element) {
- return bot.inject.executeScript(bot.dom.getLocationInView, [element], true);
+ return webdriver.atoms.inject.executeScript(bot.dom.getLocationInView,
+ [element]);
};
/**
- * @param {!{bot.inject.ELEMENT_KEY:string}} element The element to query.
+ * @param {{bot.inject.ELEMENT_KEY: string}} element The element to query.
* @param {string} attribute The attribute to look up.
* @return {string} The requested attribute value in a JSON string
* as defined by the wire protocol.
*/
webdriver.atoms.inject.dom.getAttributeValue = function(element, attribute) {
- return bot.inject.executeScript(
- webdriver.atoms.element.getAttribute, [element, attribute], true);
+ return webdriver.atoms.inject.executeScript(
+ webdriver.atoms.element.getAttribute, [element, attribute]);
};
/**
- * @param {!{bot.inject.ELEMENT_KEY:string}} element The element to query.
+ * @param {{bot.inject.ELEMENT_KEY: string}} element The element to query.
* @return {string} The element size in a JSON string as
* defined by the wire protocol.
*/
webdriver.atoms.inject.dom.getSize = function(element) {
- return bot.inject.executeScript(bot.dom.getElementSize, [element], true);
+ return webdriver.atoms.inject.executeScript(bot.dom.getElementSize,
+ [element]);
};
/**
- * @param {!{bot.inject.ELEMENT_KEY:string}} element The element to query.
+ * @param {{bot.inject.ELEMENT_KEY: string}} element The element to query.
* @param {string} property The property to look up.
* @return {string} The value of the requested CSS property in a JSON
* string as defined by the wire protocol.
*/
-webdriver.atoms.inject.dom.getValueOfCssProperty = function(element, property) {
- return bot.inject.executeScript(bot.dom.getEffectiveStyle,
- [element, property], true);
+webdriver.atoms.inject.dom.getValueOfCssProperty =
+ function(element, property) {
+ return webdriver.atoms.inject.executeScript(bot.dom.getEffectiveStyle,
+ [element, property]);
};
/**
- * @param {!{bot.inject.ELEMENT_KEY:string}} element The element to query.
+ * @param {{bot.inject.ELEMENT_KEY: string}} element The element to query.
* @return {string} A boolean describing whether the element is enabled
* in a JSON string as defined by the wire protocol.
*/
webdriver.atoms.inject.dom.isEnabled = function(element) {
- return bot.inject.executeScript(bot.dom.isEnabled, [element], true);
+ return webdriver.atoms.inject.executeScript(bot.dom.isEnabled, [element]);
};
/**
- * @param {!{bot.inject.ELEMENT_KEY:string}} element The element to check.
+ * @param {{bot.inject.ELEMENT_KEY: string}} element The element to check.
* @return {string} true if the element is visisble, false otherwise.
* The result is wrapped in a JSON string as defined by the wire
* protocol.
*/
webdriver.atoms.inject.dom.isDisplayed = function(element) {
- return bot.inject.executeScript(
- bot.dom.isShown, [element, /*ignoreOpacity=*/true], true);
+ return webdriver.atoms.inject.executeScript(bot.dom.isShown,
+ [element, /*ignoreOpacity=*/true]);
};
-
diff --git a/javascript/webdriver/atoms/inject/execute_script.js b/javascript/webdriver/atoms/inject/execute_script.js
index f1bd679..4c6f8f4 100644
--- a/javascript/webdriver/atoms/inject/execute_script.js
+++ b/javascript/webdriver/atoms/inject/execute_script.js
@@ -31,31 +31,30 @@
* @param {Array.<*>} args Array of arguments to pass to fn.
* @param {{bot.inject.WINDOW_KEY:string}=} opt_window The serialized window
* object to be read from the cache.
- * @return {!(string|bot.response.ResponseObject)} The response object. If
- * opt_stringify is true, the result will be serialized and returned in
- * string format.
+ * @return {string} The response object, serialized and returned in string
+ * format.
*/
webdriver.atoms.inject.executeScript = function(fn, args, opt_window) {
- return bot.inject.executeScript(
- fn, args, true, webdriver.atoms.inject.getWindow_(opt_window));
+ return /**@type {string}*/(bot.inject.executeScript(fn, args, true,
+ webdriver.atoms.inject.getWindow_(opt_window)));
};
/**
*
- * @param {!(string|function)} fn The function to execute.
+ * @param {!(string|Function)} fn The function to execute.
* @param {Array.<*>} args Array of arguments to pass to fn.
- * @param {int} timeout The timeout to wait up to in millis.
+ * @param {number} timeout The timeout to wait up to in millis.
* @param {{bot.inject.WINDOW_KEY:string}=} opt_window The serialized window
* object to be read from the cache.
- * @return {!(string|bot.response.ResponseObject)} The response object. If
- * opt_stringify is true, the result will be serialized and returned in
- * string format.
+ * @return {string} The response object, serialized and returned in string
+ * format.
*/
webdriver.atoms.inject.executeAsyncScript =
function(fn, args, timeout, onDone, opt_window) {
- return bot.inject.executeScript(fn, args, timeout, onDone, true,
- webdriver.atoms.inject.getWindow_(opt_window));
+ return /** @type {string} */(bot.inject.executeAsyncScript(
+ fn, args, timeout, onDone, true,
+ webdriver.atoms.inject.getWindow_(opt_window)));
};
@@ -74,5 +73,5 @@
} else {
win = window;
}
- return win;
+ return /**@type {!Window}*/(win);
};
diff --git a/javascript/webdriver/atoms/inject/find_element.js b/javascript/webdriver/atoms/inject/find_element.js
index 8ab2597..441717a 100644
--- a/javascript/webdriver/atoms/inject/find_element.js
+++ b/javascript/webdriver/atoms/inject/find_element.js
@@ -19,9 +19,8 @@
goog.provide('webdriver.atoms.inject.locators');
-goog.require('bot.inject');
goog.require('bot.locators');
-
+goog.require('webdriver.atoms.inject');
/**
* Finds an element by using the given lookup strategy.
@@ -33,12 +32,12 @@
* @return {string} The result wrapped
* in a JSON string as defined by the WebDriver wire protocol.
*/
-webdriver.atoms.inject.locators.findElement = function(strategy, using,
- opt_root) {
+webdriver.atoms.inject.locators.findElement =
+ function(strategy, using, opt_root) {
var locator = {};
locator[strategy] = using;
- return bot.inject.executeScript(bot.locators.findElement,
- [locator, opt_root], true);
+ return webdriver.atoms.inject.executeScript(bot.locators.findElement,
+ [locator, opt_root]);
};
@@ -52,10 +51,10 @@
* @return {string} The result wrapped
* in a JSON string as defined by the WebDriver wire protocol.
*/
-webdriver.atoms.inject.locators.findElements = function(strategy, using,
- opt_root) {
+webdriver.atoms.inject.locators.findElements =
+ function(strategy, using, opt_root) {
var locator = {};
locator[strategy] = using;
- return bot.inject.executeScript(bot.locators.findElements,
- [locator, opt_root], true);
+ return webdriver.atoms.inject.executeScript(bot.locators.findElements,
+ [locator, opt_root]);
};
diff --git a/javascript/webdriver/atoms/inject/frame.js b/javascript/webdriver/atoms/inject/frame.js
index dd3ca47..03ec6df 100644
--- a/javascript/webdriver/atoms/inject/frame.js
+++ b/javascript/webdriver/atoms/inject/frame.js
@@ -20,8 +20,8 @@
goog.provide('webdriver.atoms.inject.frame');
goog.require('bot.frame');
-goog.require('bot.inject');
goog.require('bot.inject.cache');
+goog.require('webdriver.atoms.inject');
/**
@@ -33,10 +33,10 @@
* @return {string} A frame element wrapped in a JSON string as defined by
* the wire protocol.
*/
-webdriver.atoms.inject.frame.findFrameByIdOrName = function(idOrName,
- opt_root) {
- return bot.inject.executeScript(bot.frame.findFrameByNameOrId,
- [idOrName, opt_root], true);
+webdriver.atoms.inject.frame.findFrameByIdOrName =
+ function(idOrName, opt_root) {
+ return webdriver.atoms.inject.executeScript(bot.frame.findFrameByNameOrId,
+ [idOrName, opt_root]);
};
@@ -44,7 +44,7 @@
* @return {string} A string representing the currently active element.
*/
webdriver.atoms.inject.frame.activeElement = function() {
- return bot.inject.executeScript(bot.frame.activeElement, [], true);
+ return webdriver.atoms.inject.executeScript(bot.frame.activeElement, []);
};
@@ -58,8 +58,8 @@
* the wire protocol.
*/
webdriver.atoms.inject.frame.findFrameByIndex = function(index, opt_root) {
- return bot.inject.executeScript(bot.frame.findFrameByIndex,
- [index, opt_root], true);
+ return webdriver.atoms.inject.executeScript(bot.frame.findFrameByIndex,
+ [index, opt_root]);
};
@@ -68,17 +68,16 @@
* which is the top window.
*/
webdriver.atoms.inject.frame.defaultContent = function() {
- return bot.inject.executeScript(bot.frame.defaultContent,
- [], true);
+ return webdriver.atoms.inject.executeScript(bot.frame.defaultContent, []);
};
/**
* @param {!{bot.inject.ELEMENT_KEY:string}} element The element to query.
* @return {string} The window corresponding to the frame element
- * wrapped in a JSON string as defined by the wire protocol.
+ * wrapped in a JSON string as defined by the wire protocol.
*/
webdriver.atoms.inject.frame.getFrameWindow = function(element) {
- return bot.inject.executeScript(bot.frame.getFrameWindow,
- [element], true);
+ return webdriver.atoms.inject.executeScript(bot.frame.getFrameWindow,
+ [element]);
};
diff --git a/javascript/webdriver/atoms/inject/local_storage.js b/javascript/webdriver/atoms/inject/local_storage.js
index 8dd258f..69b7f72 100644
--- a/javascript/webdriver/atoms/inject/local_storage.js
+++ b/javascript/webdriver/atoms/inject/local_storage.js
@@ -19,7 +19,7 @@
goog.provide('webdriver.atoms.inject.storage.local');
-goog.require('bot.inject');
+goog.require('webdriver.atoms.inject');
goog.require('webdriver.atoms.storage.local');
@@ -28,12 +28,12 @@
*
* @param {string} key The key of the item.
* @param {*} value The value of the item.
- * @return {!bot.response.ResponseObject} The result wrapped according
- * to the wire protocol.
+ * @return {string} The stringified result wrapped according to the wire
+ * protocol.
*/
webdriver.atoms.inject.storage.local.setItem = function(key, value) {
- return bot.inject.executeScript(webdriver.atoms.storage.local.setItem,
- [key, value], true);
+ return webdriver.atoms.inject.executeScript(
+ webdriver.atoms.storage.local.setItem, [key, value]);
};
@@ -41,24 +41,24 @@
* Gets an item from the local storage.
*
* @param {string} key The key of the item.
- * @return {!bot.response.ResponseObject} The result wrapped according
- * to the wire protocol.
+ * @return {string} The stringified result wrapped according to the wire
+ * protocol.
*/
webdriver.atoms.inject.storage.local.getItem = function(key) {
- return bot.inject.executeScript(webdriver.atoms.storage.local.getItem,
- [key], true);
+ return webdriver.atoms.inject.executeScript(
+ webdriver.atoms.storage.local.getItem, [key]);
};
/**
* Gets the key set of the entries.
*
- * @return {!bot.response.ResponseObject} The result wrapped according
- * to the wire protocol.
+ * @return {string} The stringified result wrapped according to the wire
+ * protocol.
*/
webdriver.atoms.inject.storage.local.keySet = function() {
- return bot.inject.executeScript(webdriver.atoms.storage.local.keySet,
- [], true);
+ return webdriver.atoms.inject.executeScript(
+ webdriver.atoms.storage.local.keySet, []);
};
@@ -66,34 +66,34 @@
* Removes an item in the local storage.
*
* @param {string} key The key of the item.
- * @return {!bot.response.ResponseObject} The result wrapped according
- * to the wire protocol.
+ * @return {string} The stringified result wrapped according to the wire
+ * protocol.
*/
webdriver.atoms.inject.storage.local.removeItem = function(key) {
- return bot.inject.executeScript(webdriver.atoms.storage.local.removeItem,
- [key], true);
+ return webdriver.atoms.inject.executeScript(
+ webdriver.atoms.storage.local.removeItem, [key]);
};
/**
* Clears the local storage.
*
- * @return {!bot.response.ResponseObject} The result wrapped according
- * to the wire protocol.
+ * @return {string} The stringified result wrapped according to the wire
+ * protocol.
*/
webdriver.atoms.inject.storage.local.clear = function() {
- return bot.inject.executeScript(webdriver.atoms.storage.local.clear,
- [], true);
+ return webdriver.atoms.inject.executeScript(
+ webdriver.atoms.storage.local.clear, []);
};
/**
* Gets the size of the local storage.
*
- * @return {!bot.response.ResponseObject} The result wrapped according
- * to the wire protocol.
+ * @return {string} The stringified result wrapped according to the wire
+ * protocol.
*/
webdriver.atoms.inject.storage.local.size = function() {
- return bot.inject.executeScript(webdriver.atoms.storage.local.size,
- [], true);
+ return webdriver.atoms.inject.executeScript(
+ webdriver.atoms.storage.local.size, []);
};
diff --git a/javascript/webdriver/atoms/inject/session_storage.js b/javascript/webdriver/atoms/inject/session_storage.js
index bc63e63..462041c 100644
--- a/javascript/webdriver/atoms/inject/session_storage.js
+++ b/javascript/webdriver/atoms/inject/session_storage.js
@@ -19,7 +19,7 @@
goog.provide('webdriver.atoms.inject.storage.session');
-goog.require('bot.inject');
+goog.require('webdriver.atoms.inject');
goog.require('webdriver.atoms.storage.session');
@@ -28,12 +28,12 @@
*
* @param {string} key The key of the item.
* @param {*} value The value of the item.
- * @return {!bot.response.ResponseObject} The result wrapped according
- * to the wire protocol.
+ * @return {string} The stringified result wrapped according to the wire
+ * protocol.
*/
webdriver.atoms.inject.storage.session.setItem = function(key, value) {
- return bot.inject.executeScript(webdriver.atoms.storage.session.setItem,
- [key, value], true);
+ return webdriver.atoms.inject.executeScript(
+ webdriver.atoms.storage.session.setItem, [key, value]);
};
@@ -41,24 +41,24 @@
* Gets an item from the session storage.
*
* @param {string} key The key of the item.
- * @return {!bot.response.ResponseObject} The result wrapped according
- * to the wire protocol.
+ * @return {string} The stringified result wrapped according to the wire
+ * protocol.
*/
webdriver.atoms.inject.storage.session.getItem = function(key) {
- return bot.inject.executeScript(webdriver.atoms.storage.session.getItem,
- [key], true);
+ return webdriver.atoms.inject.executeScript(
+ webdriver.atoms.storage.session.getItem, [key]);
};
/**
* Gets the key set of the entries.
*
- * @return {!bot.response.ResponseObject} The result wrapped according
- * to the wire protocol.
+ * @return {string} The stringified result wrapped according to the wire
+ * protocol.
*/
webdriver.atoms.inject.storage.session.keySet = function() {
- return bot.inject.executeScript(webdriver.atoms.storage.session.keySet,
- [], true);
+ return webdriver.atoms.inject.executeScript(
+ webdriver.atoms.storage.session.keySet, []);
};
@@ -66,34 +66,34 @@
* Removes an item in the session storage.
*
* @param {string} key The key of the item.
- * @return {!bot.response.ResponseObject} The result wrapped according
- * to the wire protocol.
+ * @return {string} The stringified result wrapped according to the wire
+ * protocol.
*/
webdriver.atoms.inject.storage.session.removeItem = function(key) {
- return bot.inject.executeScript(webdriver.atoms.storage.session.removeItem,
- [key], true);
+ return webdriver.atoms.inject.executeScript(
+ webdriver.atoms.storage.session.removeItem, [key]);
};
/**
* Clears the session storage.
*
- * @return {!bot.response.ResponseObject} The result wrapped according
- * to the wire protocol.
+ * @return {string} The stringified result wrapped according to the wire
+ * protocol.
*/
webdriver.atoms.inject.storage.session.clear = function() {
- return bot.inject.executeScript(webdriver.atoms.storage.session.clear,
- [], true);
+ return webdriver.atoms.inject.executeScript(
+ webdriver.atoms.storage.session.clear, []);
};
/**
* Gets the size of the session storage.
*
- * @return {!bot.response.ResponseObject} The result wrapped according
- * to the wire protocol.
+ * @return {string} The stringified result wrapped according to the wire
+ * protocol.
*/
webdriver.atoms.inject.storage.session.size = function() {
- return bot.inject.executeScript(webdriver.atoms.storage.session.size,
- [], true);
+ return webdriver.atoms.inject.executeScript(
+ webdriver.atoms.storage.session.size, []);
};
diff --git a/javascript/webdriver/atoms/inject/sql_database.js b/javascript/webdriver/atoms/inject/sql_database.js
index b531eb4..4831720 100644
--- a/javascript/webdriver/atoms/inject/sql_database.js
+++ b/javascript/webdriver/atoms/inject/sql_database.js
@@ -21,8 +21,8 @@
goog.require('bot.Error');
goog.require('bot.ErrorCode');
-goog.require('bot.inject');
goog.require('bot.storage.database');
+goog.require('webdriver.atoms.inject');
/**
@@ -31,23 +31,23 @@
* @param {string} databaseName The name of the database.
* @param {string} query The SQL statement.
* @param {Array.<*>} args Arguments to pass to the query.
- * @param {!function(string)} onDone The callback to invoke when done. The
+ * @param {function(string)} onDone The callback to invoke when done. The
* result, according to the wire protocol, will be passed to this callback.
*/
webdriver.atoms.inject.storage.database.executeSql =
function(databaseName, query, args, onDone) {
var onSuccessCallback = function(tx, result) {
- onDone(bot.inject.executeScript(function(res) {
+ onDone(webdriver.atoms.inject.executeScript(function(res) {
return result;
- }, [result], true));
+ }, [result]));
};
var onErrorCallback = function(error) {
- onDone(bot.inject.executeScript(function() {
+ onDone(webdriver.atoms.inject.executeScript(function() {
throw new bot.Error(bot.ErrorCode.SQL_DATABASE_ERROR,
'SQL Error Code: ' + error.code + '. SQL Error Message: ' +
error.message);
- }, [], true));
+ }, []));
};
bot.storage.database.executeSql(
diff --git a/javascript/webdriver/atoms/inputs.js b/javascript/webdriver/atoms/inputs.js
index bfbb745..75fec8e 100644
--- a/javascript/webdriver/atoms/inputs.js
+++ b/javascript/webdriver/atoms/inputs.js
@@ -1,183 +1,191 @@
-// Copyright 2012 WebDriver committers
-// Copyright 2012 Software Freedom Conservancy
-//
-// 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.
-
-/**
- * @fileoverview Synthetic events for fun and profit.
- */
-
-goog.provide('webdriver.atoms.inputs');
-
-goog.require('bot.Keyboard');
-goog.require('bot.Mouse');
-goog.require('bot.action');
-goog.require('goog.array');
-goog.require('webdriver.atoms.element');
-
-
-/**
- * Send keyboard input to a particular element.
- *
- * @param {!Element} element The element to send the keyboard input to.
- * @param {Array.<!bot.Keyboard.Key>=} opt_state The keyboard to use, or
- * construct one.
- * @param {...(string|!Array.<string>)} var_args What to type.
- * @return {Array.<!bot.Keyboard.Key>} The keyboard state.
- */
-webdriver.atoms.inputs.sendKeys = function(element, opt_state, var_args) {
- var keyboard = new bot.Keyboard(opt_state);
- var to_type = goog.array.slice(arguments, 2);
- var flattened = goog.array.flatten(to_type);
- if (!element) {
- element = bot.dom.getActiveElement(document);
- }
- webdriver.atoms.element.type(element, flattened, keyboard);
-
- return keyboard.getState();
-};
-goog.exportSymbol('webdriver.atoms.inputs.sendKeys',
- webdriver.atoms.inputs.sendKeys);
-
-
-/**
- * Click on an element.
- *
- * @param {!Element} element The element to click.
- * @param {Object=} opt_state The serialized state of the mouse.
- * @return {!bot.Mouse.State} The mouse state.
- * @return {Object} The mouse state.
- */
-webdriver.atoms.inputs.click = function(element, opt_state) {
- var mouse = new bot.Mouse(opt_state);
- if (!element) {
- element = mouse.getElement();
- }
- bot.action.click(element, null, mouse);
- return mouse.getState();
-};
-goog.exportSymbol('webdriver.atoms.inputs.click',
- webdriver.atoms.inputs.click);
-
-
-/**
- * Move the mouse to a specific element and/or coordinate location.
- *
- * @param {!Element} element The element to move the mouse to.
- * @param {number} x_offset The x coordinate to use as an offset.
- * @param {number} y_offset The y coordinate to use as an offset.
- * @param {bot.Mouse.State=} opt_state The serialized state of the mouse.
- * @return {!bot.Mouse.State} The mouse state.
- */
-webdriver.atoms.inputs.mouseMove = function(element, x_offset, y_offset,
- opt_state) {
- var mouse = new bot.Mouse(opt_state);
- var target = element || mouse.getElement();
-
- var offset_specified = (x_offset != null) && (y_offset != null);
- x_offset = x_offset || 0;
- y_offset = y_offset || 0;
-
- // If we have specified an element and no offset, we should
- // move the mouse to the center of the specified element.
- if (element) {
- if (!offset_specified) {
- var source_element_size = bot.action.getInteractableSize_(element);
- x_offset = Math.floor(source_element_size.width / 2);
- y_offset = Math.floor(source_element_size.height / 2);
- }
- } else {
- // Moving to an absolute offset from the current target element,
- // so we have to account for the existing offset of the current
- // mouse position to the element origin (upper-left corner).
- var pos = goog.style.getClientPosition(target);
- x_offset += (mouse.clientXY_.x - pos.x);
- y_offset += (mouse.clientXY_.y - pos.y);
- }
-
- var doc = goog.dom.getOwnerDocument(target);
- var win = goog.dom.getWindow(doc);
- var inViewAfterScroll = bot.action.scrollIntoView(
- target,
- new goog.math.Coordinate(x_offset, y_offset));
-
- var coords = new goog.math.Coordinate(x_offset, y_offset);
- mouse.move(target, coords);
- return mouse.getState();
-};
-goog.exportSymbol('webdriver.atoms.inputs.mouseMove',
- webdriver.atoms.inputs.mouseMove);
-
-
-/**
- * Presses the primary mouse button at the current location.
- *
- * @param {Object=} opt_state The serialized state of the mouse.
- * @return {Object} The mouse state.
- */
-webdriver.atoms.inputs.mouseButtonDown = function(opt_state) {
- var mouse = new bot.Mouse(opt_state);
- mouse.pressButton(bot.Mouse.Button.LEFT);
- return mouse.getState();
-};
-goog.exportSymbol('webdriver.atoms.inputs.mouseButtonDown',
- webdriver.atoms.inputs.mouseButtonDown);
-
-
-/**
- * Releases the primary mouse button at the current location.
- *
- * @param {Object=} opt_state The serialized state of the mouse.
- * @return {Object} The mouse state.
- */
-webdriver.atoms.inputs.mouseButtonUp = function(opt_state) {
- var mouse = new bot.Mouse(opt_state);
- mouse.releaseButton();
- return mouse.getState();
-};
-goog.exportSymbol('webdriver.atoms.inputs.mouseButtonUp',
- webdriver.atoms.inputs.mouseButtonUp);
-
-
-/**
- * Double-clicks primary mouse button at the current location.
- *
- * @param {Object=} opt_state The serialized state of the mouse.
- * @return {Object} The mouse state.
- */
-webdriver.atoms.inputs.doubleClick = function(opt_state) {
- var mouse = new bot.Mouse(opt_state);
- mouse.pressButton(bot.Mouse.Button.LEFT);
- mouse.releaseButton();
- mouse.pressButton(bot.Mouse.Button.LEFT);
- mouse.releaseButton();
- return mouse.getState();
-};
-goog.exportSymbol('webdriver.atoms.inputs.doubleClick',
- webdriver.atoms.inputs.doubleClick);
-
-
-/**
- * Right-clicks mouse button at the current location.
- *
- * @param {Object=} opt_state The serialized state of the mouse.
- * @return {Object} The mouse state.
- */
-webdriver.atoms.inputs.rightClick = function(opt_state) {
- var mouse = new bot.Mouse(opt_state);
- mouse.pressButton(bot.Mouse.Button.RIGHT);
- mouse.releaseButton();
- return mouse.getState();
-};
-goog.exportSymbol('webdriver.atoms.inputs.rightClick',
- webdriver.atoms.inputs.rightClick);
+// Copyright 2012 WebDriver committers
+// Copyright 2012 Software Freedom Conservancy
+//
+// 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.
+
+/**
+ * @fileoverview Synthetic events for fun and profit.
+ */
+
+goog.provide('webdriver.atoms.inputs');
+
+goog.require('bot.Keyboard');
+goog.require('bot.Mouse');
+goog.require('bot.action');
+goog.require('goog.array');
+goog.require('goog.dom');
+goog.require('goog.math.Coordinate');
+goog.require('goog.style');
+goog.require('webdriver.atoms.element');
+
+
+/**
+ * Send keyboard input to a particular element.
+ *
+ * @param {Element} element The element to send the keyboard input to.
+ * @param {Array.<!bot.Keyboard.Key>=} opt_state The keyboard to use, or
+ * construct one.
+ * @param {...(string|!Array.<string>)} var_args What to type.
+ * @return {Array.<!bot.Keyboard.Key>} The keyboard state.
+ */
+webdriver.atoms.inputs.sendKeys = function(element, opt_state, var_args) {
+ var keyboard = new bot.Keyboard(opt_state);
+ var to_type = goog.array.slice(arguments, 2);
+ var flattened = goog.array.flatten(to_type);
+ if (!element) {
+ element = bot.dom.getActiveElement(document);
+ }
+ if (!element) {
+ throw Error('No element to send keys to');
+ }
+ webdriver.atoms.element.type(element, flattened, keyboard);
+
+ return keyboard.getState();
+};
+goog.exportSymbol('webdriver.atoms.inputs.sendKeys',
+ webdriver.atoms.inputs.sendKeys);
+
+
+/**
+ * Click on an element.
+ *
+ * @param {Element} element The element to click.
+ * @param {bot.Mouse.State=} opt_state The serialized state of the mouse.
+ * @return {!bot.Mouse.State} The mouse state.
+ */
+webdriver.atoms.inputs.click = function(element, opt_state) {
+ var mouse = new bot.Mouse(opt_state);
+ if (!element) {
+ element = mouse.getState().element;
+ }
+ if (!element) {
+ throw Error('No element to send keys to');
+ }
+ bot.action.click(element, null, mouse);
+ return mouse.getState();
+};
+goog.exportSymbol('webdriver.atoms.inputs.click',
+ webdriver.atoms.inputs.click);
+
+
+/**
+ * Move the mouse to a specific element and/or coordinate location.
+ *
+ * @param {!Element} element The element to move the mouse to.
+ * @param {number} x_offset The x coordinate to use as an offset.
+ * @param {number} y_offset The y coordinate to use as an offset.
+ * @param {bot.Mouse.State=} opt_state The serialized state of the mouse.
+ * @return {!bot.Mouse.State} The mouse state.
+ */
+webdriver.atoms.inputs.mouseMove = function(element, x_offset, y_offset,
+ opt_state) {
+ var mouse = new bot.Mouse(opt_state);
+ var target = element || mouse.getState().element;
+
+ var offset_specified = (x_offset != null) && (y_offset != null);
+ x_offset = x_offset || 0;
+ y_offset = y_offset || 0;
+
+ // If we have specified an element and no offset, we should
+ // move the mouse to the center of the specified element.
+ if (element) {
+ if (!offset_specified) {
+ var source_element_size = bot.action.getInteractableSize(element);
+ x_offset = Math.floor(source_element_size.width / 2);
+ y_offset = Math.floor(source_element_size.height / 2);
+ }
+ } else {
+ // Moving to an absolute offset from the current target element,
+ // so we have to account for the existing offset of the current
+ // mouse position to the element origin (upper-left corner).
+ var pos = goog.style.getClientPosition(target);
+ x_offset += (mouse.getState().clientXY.x - pos.x);
+ y_offset += (mouse.getState().clientXY.y - pos.y);
+ }
+
+ var doc = goog.dom.getOwnerDocument(target);
+ var win = goog.dom.getWindow(doc);
+ var inViewAfterScroll = bot.action.scrollIntoView(
+ target,
+ new goog.math.Coordinate(x_offset, y_offset));
+
+ var coords = new goog.math.Coordinate(x_offset, y_offset);
+ mouse.move(target, coords);
+ return mouse.getState();
+};
+goog.exportSymbol('webdriver.atoms.inputs.mouseMove',
+ webdriver.atoms.inputs.mouseMove);
+
+
+/**
+ * Presses the primary mouse button at the current location.
+ *
+ * @param {bot.Mouse.State=} opt_state The serialized state of the mouse.
+ * @return {!bot.Mouse.State} The mouse state.
+ */
+webdriver.atoms.inputs.mouseButtonDown = function(opt_state) {
+ var mouse = new bot.Mouse(opt_state);
+ mouse.pressButton(bot.Mouse.Button.LEFT);
+ return mouse.getState();
+};
+goog.exportSymbol('webdriver.atoms.inputs.mouseButtonDown',
+ webdriver.atoms.inputs.mouseButtonDown);
+
+
+/**
+ * Releases the primary mouse button at the current location.
+ *
+ * @param {bot.Mouse.State=} opt_state The serialized state of the mouse.
+ * @return {!bot.Mouse.State} The mouse state.
+ */
+webdriver.atoms.inputs.mouseButtonUp = function(opt_state) {
+ var mouse = new bot.Mouse(opt_state);
+ mouse.releaseButton();
+ return mouse.getState();
+};
+goog.exportSymbol('webdriver.atoms.inputs.mouseButtonUp',
+ webdriver.atoms.inputs.mouseButtonUp);
+
+
+/**
+ * Double-clicks primary mouse button at the current location.
+ *
+ * @param {bot.Mouse.State=} opt_state The serialized state of the mouse.
+ * @return {!bot.Mouse.State} The mouse state.
+ */
+webdriver.atoms.inputs.doubleClick = function(opt_state) {
+ var mouse = new bot.Mouse(opt_state);
+ mouse.pressButton(bot.Mouse.Button.LEFT);
+ mouse.releaseButton();
+ mouse.pressButton(bot.Mouse.Button.LEFT);
+ mouse.releaseButton();
+ return mouse.getState();
+};
+goog.exportSymbol('webdriver.atoms.inputs.doubleClick',
+ webdriver.atoms.inputs.doubleClick);
+
+
+/**
+ * Right-clicks mouse button at the current location.
+ *
+ * @param {bot.Mouse.State=} opt_state The serialized state of the mouse.
+ * @return {!bot.Mouse.State} The mouse state.
+ */
+webdriver.atoms.inputs.rightClick = function(opt_state) {
+ var mouse = new bot.Mouse(opt_state);
+ mouse.pressButton(bot.Mouse.Button.RIGHT);
+ mouse.releaseButton();
+ return mouse.getState();
+};
+goog.exportSymbol('webdriver.atoms.inputs.rightClick',
+ webdriver.atoms.inputs.rightClick);
diff --git a/javascript/webdriver/atoms/storage/appcache.js b/javascript/webdriver/atoms/storage/appcache.js
index 1d7f045..a9b7e62 100644
--- a/javascript/webdriver/atoms/storage/appcache.js
+++ b/javascript/webdriver/atoms/storage/appcache.js
@@ -31,4 +31,3 @@
webdriver.atoms.storage.appcache.getStatus = function() {
return bot.appcache.getStatus();
};
-
diff --git a/javascript/webdriver/events.js b/javascript/webdriver/events.js
index 2957e3a..951cc5c 100644
--- a/javascript/webdriver/events.js
+++ b/javascript/webdriver/events.js
@@ -115,7 +115,7 @@
* @return {!webdriver.EventEmitter} A self reference.
*/
webdriver.EventEmitter.prototype.addListener = function(type, listenerFn,
- opt_scope) {
+ opt_scope) {
return this.addListener_(type, listenerFn, opt_scope);
};
diff --git a/javascript/webdriver/test/atoms/element_test.html b/javascript/webdriver/test/atoms/element_test.html
index 8d37570..c506a1d 100644
--- a/javascript/webdriver/test/atoms/element_test.html
+++ b/javascript/webdriver/test/atoms/element_test.html
@@ -50,6 +50,13 @@
assertEquals('2', webdriver.atoms.element.getAttribute(element, 'rows'));
}
+ function testShouldReturnSameValueForClassAndClassName() {
+ var element = document.body;
+
+ assertEquals(webdriver.atoms.element.getAttribute(element, 'class'),
+ webdriver.atoms.element.getAttribute(element, 'className'));
+ }
+
function testShouldReturnBooleanPropertiesAsStrings() {
var readOnlyElement = bot.locators.findElement({id: 'ro'}),
normalElement = bot.locators.findElement({id: 'normal'});
@@ -191,7 +198,7 @@
bot.userAgent.isEngineVersion(11)) {
return;
}
-
+
var el = document.getElementById('textarea');
el.value = '';
webdriver.atoms.element.type(el, ['hello\n\nworld']);
@@ -199,7 +206,7 @@
}
</script>
</head>
-<body id="body" style="" name="body">
+<body id="body" class="c" style="" name="body">
<p id="has_id" title="cheese">This has an ID</p>
diff --git a/javascript/webdriver/test/promise_test.html b/javascript/webdriver/test/promise_test.html
index ca3c21c..cbde295 100644
--- a/javascript/webdriver/test/promise_test.html
+++ b/javascript/webdriver/test/promise_test.html
@@ -856,7 +856,7 @@
errback = callbackHelper(function(error) {
assertEquals(e, error);
}));
-
+
callback.assertNotCalled();
errback.assertCalled();
}
diff --git a/javascript/webdriver/test/stacktrace_test.html b/javascript/webdriver/test/stacktrace_test.html
index dbe60fb..f6446b4 100644
--- a/javascript/webdriver/test/stacktrace_test.html
+++ b/javascript/webdriver/test/stacktrace_test.html
@@ -18,6 +18,8 @@
<title>stacktrace_test</title>
<script src="test_bootstrap.js"></script>
<script>
+ goog.require('bot.Error');
+ goog.require('bot.ErrorCode');
goog.require('goog.string');
goog.require('goog.testing.ExpectedFailures');
goog.require('goog.testing.JsUnitException');
@@ -395,5 +397,19 @@
' at file:///bar/bar.js:1'
].join('\n'), ret.stack);
}
+
+function testFormattingBotErrors() {
+ var error = new bot.Error(bot.ErrorCode.NO_SUCH_ELEMENT, 'boom');
+ var expectedStack = [
+ 'NoSuchElementError: boom',
+ ' at Color.red (http://x:1234)',
+ ' at Foo.bar (http://y:5678)'
+ ].join('\n');
+ error.stack = expectedStack;
+
+ var ret = webdriver.stacktrace.format(error);
+ assertEquals(ret, error);
+ assertEquals(expectedStack, error.stack);
+}
</script>
diff --git a/javascript/webdriver/test_e2e/actionsequece_test.html b/javascript/webdriver/test_e2e/actionsequence_test.html
similarity index 79%
rename from javascript/webdriver/test_e2e/actionsequece_test.html
rename to javascript/webdriver/test_e2e/actionsequence_test.html
index 784b82c..5a549b5 100644
--- a/javascript/webdriver/test_e2e/actionsequece_test.html
+++ b/javascript/webdriver/test_e2e/actionsequence_test.html
@@ -283,77 +283,6 @@
});
}
- function assertListOrder(parent, var_args) {
- var order = goog.array.slice(arguments, 1);
- var divs = parent.querySelectorAll('div');
- assertEquals('wrong # children', order.length, divs.length);
-
- var expectedOrder = goog.array.map(order, function(item) {
- return item + '';
- });
- var actualOrder = goog.array.map(divs, function(div) {
- return div.innerHTML;
- });
- assertArrayEquals(expectedOrder, actualOrder);
- }
-
- function testDragAndDrop() {
- assertListOrder(dom.draglist, 1, 2, 3, 4, 5, 6);
-
- wd.draglist.findElements(By.tagName('div')).then(function(items) {
- driver.actions().
- dragAndDrop(items[4], items[1]).
- dragAndDrop(items[3], items[4]).
- dragAndDrop(items[5], items[4]).
- perform();
- });
- driver.call(function() {
- // Well, this is unfortunate. Firefox and Chrome compute coordinates
- // differently, so we end up with different list orders.
- if (goog.userAgent.GECKO) {
- assertListOrder(dom.draglist, 4, 6, 1, 2, 5, 3);
- } else {
- assertListOrder(dom.draglist, 1, 4, 6, 5, 2, 3);
- }
- });
- }
-
- function testDragAndDropWithShift() {
- assertListOrder(dom.dragshiftlist, 1, 2, 3, 4, 5, 6);
-
- wd.dragshiftlist.findElements(By.tagName('div')).then(function(items) {
- driver.actions().
- dragAndDrop(items[4], items[1]).
- dragAndDrop(items[3], items[4]).
- dragAndDrop(items[5], items[4]).
- perform();
- driver.call(function() {
- assertListOrder(dom.dragshiftlist, 1, 2, 3, 4, 5, 6);
- });
-
- driver.actions().
- keyDown(webdriver.Key.SHIFT).
- dragAndDrop(items[4], items[1]).
- dragAndDrop(items[3], items[4]).
- dragAndDrop(items[5], items[4]).
- keyUp(webdriver.Key.SHIFT).
- perform();
- driver.call(function() {
- // Well, this is unfortunate. Firefox and Chrome compute coordinates
- // differently, so we end up with different list orders.
- if (goog.userAgent.GECKO) {
- // TODO(jleyba): Firefox does not execute shift+click correctly.
- // Using a dummy assertion here until the bug is fixed.
- // See http://code.google.com/p/selenium/issues/detail?id=3734
- assertListOrder(dom.dragshiftlist, 1, 2, 3, 4, 5, 6);
- // assertListOrder(dom.dragshiftlist, 4, 6, 1, 2, 5, 3);
- } else {
- assertListOrder(dom.dragshiftlist, 1, 4, 6, 5, 2, 3);
- }
- });
- });
- }
-
function testMultiSelect() {
assertEquals(-1, dom.select.selectedIndex);
diff --git a/rake-tasks/crazy_fun/mappings/javascript.rb b/rake-tasks/crazy_fun/mappings/javascript.rb
index 0ca71d5..0384f60 100644
--- a/rake-tasks/crazy_fun/mappings/javascript.rb
+++ b/rake-tasks/crazy_fun/mappings/javascript.rb
@@ -167,7 +167,7 @@
module Javascript
# CrazyFunJava.ant.taskdef :name => "jscomp",
# :classname => "com.google.javascript.jscomp.ant.CompileTask",
- # :classpath => "third_party/closure/bin/compiler-20120710.jar"
+ # :classpath => "third_party/closure/bin/compiler-20120917.jar"
class BaseJs < Tasks
attr_reader :calcdeps
@@ -178,7 +178,7 @@
py = "python"
end
@calcdeps = "#{py} third_party/closure/bin/calcdeps.py " +
- "-c third_party/closure/bin/compiler-20120710.jar "
+ "-c third_party/closure/bin/compiler-20120917.jar "
end
def js_name(dir, name)
@@ -471,7 +471,7 @@
CrazyFunJava.ant.java :classname => "com.google.javascript.jscomp.CommandLineRunner", :failonerror => true do
classpath do
- pathelement :path => "third_party/closure/bin/compiler-20120710.jar"
+ pathelement :path => "third_party/closure/bin/compiler-20120917.jar"
end
arg :line => cmd
end
@@ -563,7 +563,7 @@
CrazyFunJava.ant.java :classname => "com.google.javascript.jscomp.CommandLineRunner", :fork => false, :failonerror => true do
classpath do
- pathelement :path => "third_party/closure/bin/compiler-20120710.jar"
+ pathelement :path => "third_party/closure/bin/compiler-20120917.jar"
end
arg :line => cmd
end
diff --git a/third_party/closure/bin/README b/third_party/closure/bin/README
index e6d12c4..270cfc2 100644
--- a/third_party/closure/bin/README
+++ b/third_party/closure/bin/README
@@ -146,7 +146,7 @@
significantly for use by Google's JavaScript compiler.
Local Modifications: The packages have been renamespaced. All code not
-relavant to parsing has been removed. A JSDoc parser and static typing
+relevant to parsing has been removed. A JsDoc parser and static typing
system have been added.
@@ -171,7 +171,7 @@
Args4j
URL: https://args4j.dev.java.net/
-Version: 2.0.12
+Version: 2.0.16
License: MIT
Description:
@@ -187,7 +187,7 @@
Guava Libraries
URL: http://code.google.com/p/guava-libraries/
-Version: r08
+Version: 13.0.1
License: Apache License 2.0
Description: Google's core Java libraries.
@@ -230,7 +230,7 @@
JUnit
URL: http://sourceforge.net/projects/junit/
-Version: 4.8.2
+Version: 4.10
License: Common Public License 1.0
Description: A framework for writing and running automated tests in Java.
@@ -244,7 +244,7 @@
Protocol Buffers
URL: http://code.google.com/p/protobuf/
-Version: 2.3.0
+Version: 2.4.1
License: New BSD License
Description: Supporting libraries for protocol buffers,
@@ -281,9 +281,9 @@
---
Code in:
-tools/maven-ant-tasks-2.1.1.jar
+tools/maven-ant-tasks-2.1.3.jar
URL: http://maven.apache.org
-Version 2.1.1
+Version 2.1.3
License: Apache License 2.0
Description:
Maven Ant tasks are used to manage dependencies and to install/deploy to
diff --git a/third_party/closure/bin/compiler-20120710.jar b/third_party/closure/bin/compiler-20120710.jar
deleted file mode 100644
index 52a920f..0000000
--- a/third_party/closure/bin/compiler-20120710.jar
+++ /dev/null
Binary files differ
diff --git a/third_party/closure/bin/compiler-20120917.jar b/third_party/closure/bin/compiler-20120917.jar
new file mode 100644
index 0000000..e23352f
--- /dev/null
+++ b/third_party/closure/bin/compiler-20120917.jar
Binary files differ
diff --git a/third_party/closure/goog/base.js b/third_party/closure/goog/base.js
index ebf8870..99e44c5 100644
--- a/third_party/closure/goog/base.js
+++ b/third_party/closure/goog/base.js
@@ -18,6 +18,9 @@
* In uncompiled mode base.js will write out Closure's deps file, unless the
* global <code>CLOSURE_NO_DEPS</code> is set to true. This allows projects to
* include their own deps file(s) from different locations.
+ *
+ *
+ * @provideGoog
*/
@@ -537,6 +540,23 @@
goog.writeScriptTag_ = function(src) {
if (goog.inHtmlDocument_()) {
var doc = goog.global.document;
+
+ // If the user tries to require a new symbol after document load,
+ // something has gone terribly wrong. Doing a document.write would
+ // wipe out the page.
+ if (doc.readyState == 'complete') {
+ // Certain test frameworks load base.js multiple times, which tries
+ // to write deps.js each time. If that happens, just fail silently.
+ // These frameworks wipe the page between each load of base.js, so this
+ // is OK.
+ var isDeps = /\bdeps.js$/.test(src);
+ if (isDeps) {
+ return false;
+ } else {
+ throw Error('Cannot write "' + src + '" after document load');
+ }
+ }
+
doc.write(
'<script type="text/javascript" src="' + src + '"></' + 'script>');
return true;
@@ -1313,7 +1333,17 @@
/**
- * Abstract implementation of goog.getMsg for use with localized messages.
+ * Gets a localized message.
+ *
+ * This function is a compiler primitive. If you give the compiler a localized
+ * message bundle, it will replace the string at compile-time with a localized
+ * version, and expand goog.getMsg call to a concatenated string.
+ *
+ * Messages must be initialized in the form:
+ * <code>
+ * var MSG_NAME = goog.getMsg('Hello {$placeholder}', {'placeholder': 'world'});
+ * </code>
+ *
* @param {string} str Translatable string, places holders in the form {$foo}.
* @param {Object=} opt_values Map of place holder name to value.
* @return {string} message with placeholders filled.
@@ -1329,6 +1359,26 @@
/**
+ * Gets a localized message. If the message does not have a translation, gives a
+ * fallback message.
+ *
+ * This is useful when introducing a new message that has not yet been
+ * translated into all languages.
+ *
+ * This function is a compiler primtive. Must be used in the form:
+ * <code>var x = goog.getMsgWithFallback(MSG_A, MSG_B);</code>
+ * where MSG_A and MSG_B were initialized with goog.getMsg.
+ *
+ * @param {string} a The preferred message.
+ * @param {string} b The fallback message.
+ * @return {string} The best translated message.
+ */
+goog.getMsgWithFallback = function(a, b) {
+ return a;
+};
+
+
+/**
* Exposes an unobfuscated global namespace path for the given object.
* Note that fields of the exported object *will* be obfuscated,
* unless they are exported in turn via this function or
diff --git a/third_party/closure/goog/crypt/aes.js b/third_party/closure/goog/crypt/aes.js
new file mode 100644
index 0000000..9dad4f2
--- /dev/null
+++ b/third_party/closure/goog/crypt/aes.js
@@ -0,0 +1,1027 @@
+// Copyright 2012 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Implementation of AES in JavaScript.
+ * @see http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
+ *
+ */
+
+goog.provide('goog.crypt.Aes');
+
+goog.require('goog.asserts');
+goog.require('goog.crypt.BlockCipher');
+
+
+
+/**
+ * Implementation of AES in JavaScript.
+ * See http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
+ *
+ * WARNING: This is ECB mode only. If you are encrypting something
+ * longer than 16 bytes, or encrypting more than one value with the same key
+ * (so basically, always) you need to use this with a block cipher mode of
+ * operation. See goog.crypt.Cbc.
+ *
+ * See http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation for more
+ * information.
+ *
+ * @constructor
+ * @implements {goog.crypt.BlockCipher}
+ * @param {!Array.<number>} key The key as an array of integers in {0, 255}.
+ * The key must have lengths of 16, 24, or 32 integers for 128-,
+ * 192-, or 256-bit encryption, respectively.
+ */
+goog.crypt.Aes = function(key) {
+ goog.crypt.Aes.assertKeyArray_(key);
+
+ /**
+ * The AES key.
+ * @type {!Array.<number>}
+ * @private
+ */
+ this.key_ = key;
+
+ /**
+ * Key length, in words.
+ * @type {number}
+ * @private
+ */
+ this.keyLength_ = this.key_.length / 4;
+
+ /**
+ * Number of rounds. Based on key length per AES spec.
+ * @type {number}
+ * @private
+ */
+ this.numberOfRounds_ = this.keyLength_ + 6;
+
+ /**
+ * 4x4 byte array containing the current state.
+ * @type {!Array.<Array.<number>>}
+ * @private
+ */
+ this.state_ = [[], [], [], []];
+
+ /**
+ * Scratch temporary array for calculation.
+ * @type {!Array.<Array.<number>>}
+ * @private
+ */
+ this.temp_ = [[], [], [], []];
+
+ this.keyExpansion_();
+};
+
+
+/**
+ * @define {boolean} Whether to call test method stubs. This can be enabled
+ * for unit testing.
+ */
+goog.crypt.Aes.ENABLE_TEST_MODE = false;
+
+
+/**
+ * @override
+ */
+goog.crypt.Aes.prototype.encrypt = function(input) {
+
+ if (goog.crypt.Aes.ENABLE_TEST_MODE) {
+ this.testKeySchedule_(0, this.keySchedule_, 0);
+ }
+
+ this.copyInput_(input);
+ this.addRoundKey_(0);
+
+ for (var round = 1; round < this.numberOfRounds_; ++round) {
+ if (goog.crypt.Aes.ENABLE_TEST_MODE) {
+ this.testKeySchedule_(round, this.keySchedule_, round);
+ this.testStartRound_(round, this.state_);
+ }
+
+ this.subBytes_(goog.crypt.Aes.SBOX_);
+ if (goog.crypt.Aes.ENABLE_TEST_MODE) {
+ this.testAfterSubBytes_(round, this.state_);
+ }
+
+ this.shiftRows_();
+ if (goog.crypt.Aes.ENABLE_TEST_MODE) {
+ this.testAfterShiftRows_(round, this.state_);
+ }
+
+ this.mixColumns_();
+ if (goog.crypt.Aes.ENABLE_TEST_MODE) {
+ this.testAfterMixColumns_(round, this.state_);
+ }
+
+ this.addRoundKey_(round);
+ }
+
+ this.subBytes_(goog.crypt.Aes.SBOX_);
+ if (goog.crypt.Aes.ENABLE_TEST_MODE) {
+ this.testAfterSubBytes_(round, this.state_);
+ }
+
+ this.shiftRows_();
+ if (goog.crypt.Aes.ENABLE_TEST_MODE) {
+ this.testAfterShiftRows_(round, this.state_);
+ }
+
+ this.addRoundKey_(this.numberOfRounds_);
+
+ return this.generateOutput_();
+};
+
+
+/**
+ * @override
+ */
+goog.crypt.Aes.prototype.decrypt = function(input) {
+
+ if (goog.crypt.Aes.ENABLE_TEST_MODE) {
+ this.testKeySchedule_(0, this.keySchedule_, this.numberOfRounds_);
+ }
+
+ this.copyInput_(input);
+ this.addRoundKey_(this.numberOfRounds_);
+
+ for (var round = 1; round < this.numberOfRounds_; ++round) {
+ if (goog.crypt.Aes.ENABLE_TEST_MODE) {
+ this.testKeySchedule_(round, this.keySchedule_,
+ this.numberOfRounds_ - round);
+ this.testStartRound_(round, this.state_);
+ }
+
+ this.invShiftRows_();
+ if (goog.crypt.Aes.ENABLE_TEST_MODE) {
+ this.testAfterShiftRows_(round, this.state_);
+ }
+
+ this.subBytes_(goog.crypt.Aes.INV_SBOX_);
+ if (goog.crypt.Aes.ENABLE_TEST_MODE) {
+ this.testAfterSubBytes_(round, this.state_);
+ }
+
+ this.addRoundKey_(this.numberOfRounds_ - round);
+ if (goog.crypt.Aes.ENABLE_TEST_MODE) {
+ this.testAfterAddRoundKey_(round, this.state_);
+ }
+
+ this.invMixColumns_();
+ }
+
+ this.invShiftRows_();
+ if (goog.crypt.Aes.ENABLE_TEST_MODE) {
+ this.testAfterShiftRows_(round, this.state_);
+ }
+
+ this.subBytes_(goog.crypt.Aes.INV_SBOX_);
+ if (goog.crypt.Aes.ENABLE_TEST_MODE) {
+ this.testAfterSubBytes_(this.numberOfRounds_, this.state_);
+ }
+
+ if (goog.crypt.Aes.ENABLE_TEST_MODE) {
+ this.testKeySchedule_(this.numberOfRounds_, this.keySchedule_, 0);
+ }
+
+ this.addRoundKey_(0);
+
+ return this.generateOutput_();
+};
+
+
+/**
+ * Block size, in words. Fixed at 4 per AES spec.
+ * @type {number}
+ * @private
+ */
+goog.crypt.Aes.BLOCK_SIZE_ = 4;
+
+
+/**
+ * Asserts that the key's array of integers is in the correct format.
+ * @param {!Array.<number>} arr AES key as array of integers.
+ * @private
+ */
+goog.crypt.Aes.assertKeyArray_ = function(arr) {
+ if (goog.asserts.ENABLE_ASSERTS) {
+ goog.asserts.assert(arr.length == 16 || arr.length == 24 ||
+ arr.length == 32,
+ 'Key must have length 16, 24, or 32.');
+ for (var i = 0; i < arr.length; i++) {
+ goog.asserts.assertNumber(arr[i]);
+ goog.asserts.assert(arr[i] >= 0 && arr[i] <= 255);
+ }
+ }
+};
+
+
+/**
+ * Tests can populate this with a callback, and that callback will get called
+ * at the start of each round *in both functions encrypt() and decrypt()*.
+ * @param {number} roundNum Round number.
+ * @param {!Array.<number>} Current state.
+ * @private
+ */
+goog.crypt.Aes.prototype.testStartRound_ = goog.nullFunction;
+
+
+/**
+ * Tests can populate this with a callback, and that callback will get called
+ * each round right after the SubBytes step gets executed *in both functions
+ * encrypt() and decrypt()*.
+ * @param {number} roundNum Round number.
+ * @param {!Array.<number>} Current state.
+ * @private
+ */
+goog.crypt.Aes.prototype.testAfterSubBytes_ = goog.nullFunction;
+
+
+/**
+ * Tests can populate this with a callback, and that callback will get called
+ * each round right after the ShiftRows step gets executed *in both functions
+ * encrypt() and decrypt()*.
+ * @param {number} roundNum Round number.
+ * @param {!Array.<number>} Current state.
+ * @private
+ */
+goog.crypt.Aes.prototype.testAfterShiftRows_ = goog.nullFunction;
+
+
+/**
+ * Tests can populate this with a callback, and that callback will get called
+ * each round right after the MixColumns step gets executed *but only in the
+ * decrypt() function*.
+ * @param {number} roundNum Round number.
+ * @param {!Array.<number>} Current state.
+ * @private
+ */
+goog.crypt.Aes.prototype.testAfterMixColumns_ = goog.nullFunction;
+
+
+/**
+ * Tests can populate this with a callback, and that callback will get called
+ * each round right after the AddRoundKey step gets executed encrypt().
+ * @param {number} roundNum Round number.
+ * @param {!Array.<number>} Current state.
+ * @private
+ */
+goog.crypt.Aes.prototype.testAfterAddRoundKey_ = goog.nullFunction;
+
+
+/**
+ * Tests can populate this with a callback, and that callback will get called
+ * before each round on the round key. *Gets called in both the encrypt() and
+ * decrypt() functions.*
+ * @param {number} roundNum Round number.
+ * @param {!Array.<Array.<number>>} Computed key schedule.
+ * @param {number} index The index into the key schedule to test. This is not
+ * necessarily roundNum because the key schedule is used in reverse
+ * in the case of decryption.
+ * @private
+ */
+goog.crypt.Aes.prototype.testKeySchedule_ = goog.nullFunction;
+
+
+/**
+ * Helper to copy input into the AES state matrix.
+ * @param {!Array.<number>} input Byte array to copy into the state matrix.
+ * @private
+ */
+goog.crypt.Aes.prototype.copyInput_ = function(input) {
+ var v, p;
+
+ goog.asserts.assert(input.length == goog.crypt.Aes.BLOCK_SIZE_ * 4,
+ 'Expecting input of 4 times block size.');
+
+ for (var r = 0; r < goog.crypt.Aes.BLOCK_SIZE_; r++) {
+ for (var c = 0; c < 4; c++) {
+ p = c * 4 + r;
+ v = input[p];
+
+ goog.asserts.assert(
+ v <= 255 && v >= 0,
+ 'Invalid input. Value %s at position %s is not a byte.', v, p);
+
+ this.state_[r][c] = v;
+ }
+ }
+};
+
+
+/**
+ * Helper to copy the state matrix into an output array.
+ * @return {!Array.<number>} Output byte array.
+ * @private
+ */
+goog.crypt.Aes.prototype.generateOutput_ = function() {
+ var output = [];
+ for (var r = 0; r < goog.crypt.Aes.BLOCK_SIZE_; r++) {
+ for (var c = 0; c < 4; c++) {
+ output[c * 4 + r] = this.state_[r][c];
+ }
+ }
+ return output;
+};
+
+
+/**
+ * AES's AddRoundKey procedure. Add the current round key to the state.
+ * @param {number} round The current round.
+ * @private
+ */
+goog.crypt.Aes.prototype.addRoundKey_ = function(round) {
+ for (var r = 0; r < 4; r++) {
+ for (var c = 0; c < 4; c++) {
+ this.state_[r][c] ^= this.keySchedule_[round * 4 + c][r];
+ }
+ }
+};
+
+
+/**
+ * AES's SubBytes procedure. Substitute bytes from the precomputed SBox lookup
+ * into the state.
+ * @param {!Array.<number>} box The SBox or invSBox.
+ * @private
+ */
+goog.crypt.Aes.prototype.subBytes_ = function(box) {
+ for (var r = 0; r < 4; r++) {
+ for (var c = 0; c < 4; c++) {
+ this.state_[r][c] = box[this.state_[r][c]];
+ }
+ }
+};
+
+
+/**
+ * AES's ShiftRows procedure. Shift the values in each row to the right. Each
+ * row is shifted one more slot than the one above it.
+ * @private
+ */
+goog.crypt.Aes.prototype.shiftRows_ = function() {
+ for (var r = 1; r < 4; r++) {
+ for (var c = 0; c < 4; c++) {
+ this.temp_[r][c] = this.state_[r][c];
+ }
+ }
+
+ for (var r = 1; r < 4; r++) {
+ for (var c = 0; c < 4; c++) {
+ this.state_[r][c] = this.temp_[r][(c + r) %
+ goog.crypt.Aes.BLOCK_SIZE_];
+ }
+ }
+};
+
+
+/**
+ * AES's InvShiftRows procedure. Shift the values in each row to the right.
+ * @private
+ */
+goog.crypt.Aes.prototype.invShiftRows_ = function() {
+ for (var r = 1; r < 4; r++) {
+ for (var c = 0; c < 4; c++) {
+ this.temp_[r][(c + r) % goog.crypt.Aes.BLOCK_SIZE_] =
+ this.state_[r][c];
+ }
+ }
+
+ for (var r = 1; r < 4; r++) {
+ for (var c = 0; c < 4; c++) {
+ this.state_[r][c] = this.temp_[r][c];
+ }
+ }
+};
+
+
+/**
+ * AES's MixColumns procedure. Mix the columns of the state using magic.
+ * @private
+ */
+goog.crypt.Aes.prototype.mixColumns_ = function() {
+ var s = this.state_;
+ var t = this.temp_[0];
+
+ for (var c = 0; c < 4; c++) {
+ t[0] = s[0][c];
+ t[1] = s[1][c];
+ t[2] = s[2][c];
+ t[3] = s[3][c];
+
+ s[0][c] = (goog.crypt.Aes.MULT_2_[t[0]] ^
+ goog.crypt.Aes.MULT_3_[t[1]] ^ t[2] ^ t[3]);
+ s[1][c] = (t[0] ^ goog.crypt.Aes.MULT_2_[t[1]] ^
+ goog.crypt.Aes.MULT_3_[t[2]] ^ t[3]);
+ s[2][c] = (t[0] ^ t[1] ^ goog.crypt.Aes.MULT_2_[t[2]] ^
+ goog.crypt.Aes.MULT_3_[t[3]]);
+ s[3][c] = (goog.crypt.Aes.MULT_3_[t[0]] ^ t[1] ^ t[2] ^
+ goog.crypt.Aes.MULT_2_[t[3]]);
+ }
+};
+
+
+/**
+ * AES's InvMixColumns procedure.
+ * @private
+ */
+goog.crypt.Aes.prototype.invMixColumns_ = function() {
+ var s = this.state_;
+ var t = this.temp_[0];
+
+ for (var c = 0; c < 4; c++) {
+ t[0] = s[0][c];
+ t[1] = s[1][c];
+ t[2] = s[2][c];
+ t[3] = s[3][c];
+
+ s[0][c] = (
+ goog.crypt.Aes.MULT_E_[t[0]] ^ goog.crypt.Aes.MULT_B_[t[1]] ^
+ goog.crypt.Aes.MULT_D_[t[2]] ^ goog.crypt.Aes.MULT_9_[t[3]]);
+
+ s[1][c] = (
+ goog.crypt.Aes.MULT_9_[t[0]] ^ goog.crypt.Aes.MULT_E_[t[1]] ^
+ goog.crypt.Aes.MULT_B_[t[2]] ^ goog.crypt.Aes.MULT_D_[t[3]]);
+
+ s[2][c] = (
+ goog.crypt.Aes.MULT_D_[t[0]] ^ goog.crypt.Aes.MULT_9_[t[1]] ^
+ goog.crypt.Aes.MULT_E_[t[2]] ^ goog.crypt.Aes.MULT_B_[t[3]]);
+
+ s[3][c] = (
+ goog.crypt.Aes.MULT_B_[t[0]] ^ goog.crypt.Aes.MULT_D_[t[1]] ^
+ goog.crypt.Aes.MULT_9_[t[2]] ^ goog.crypt.Aes.MULT_E_[t[3]]);
+ }
+};
+
+
+/**
+ * AES's KeyExpansion procedure. Create the key schedule from the initial key.
+ * @private
+ */
+goog.crypt.Aes.prototype.keyExpansion_ = function() {
+ this.keySchedule_ = new Array(goog.crypt.Aes.BLOCK_SIZE_ * (
+ this.numberOfRounds_ + 1));
+
+ for (var rowNum = 0; rowNum < this.keyLength_; rowNum++) {
+ this.keySchedule_[rowNum] = [
+ this.key_[4 * rowNum],
+ this.key_[4 * rowNum + 1],
+ this.key_[4 * rowNum + 2],
+ this.key_[4 * rowNum + 3]
+ ];
+ }
+
+ var temp = new Array(4);
+
+ for (var rowNum = this.keyLength_;
+ rowNum < (goog.crypt.Aes.BLOCK_SIZE_ * (this.numberOfRounds_ + 1));
+ rowNum++) {
+ temp[0] = this.keySchedule_[rowNum - 1][0];
+ temp[1] = this.keySchedule_[rowNum - 1][1];
+ temp[2] = this.keySchedule_[rowNum - 1][2];
+ temp[3] = this.keySchedule_[rowNum - 1][3];
+
+ if (rowNum % this.keyLength_ == 0) {
+ this.rotWord_(temp);
+ this.subWord_(temp);
+
+ temp[0] ^= goog.crypt.Aes.RCON_[rowNum / this.keyLength_][0];
+ temp[1] ^= goog.crypt.Aes.RCON_[rowNum / this.keyLength_][1];
+ temp[2] ^= goog.crypt.Aes.RCON_[rowNum / this.keyLength_][2];
+ temp[3] ^= goog.crypt.Aes.RCON_[rowNum / this.keyLength_][3];
+ } else if (this.keyLength_ > 6 && rowNum % this.keyLength_ == 4) {
+ this.subWord_(temp);
+ }
+
+ this.keySchedule_[rowNum] = new Array(4);
+ this.keySchedule_[rowNum][0] =
+ this.keySchedule_[rowNum - this.keyLength_][0] ^ temp[0];
+ this.keySchedule_[rowNum][1] =
+ this.keySchedule_[rowNum - this.keyLength_][1] ^ temp[1];
+ this.keySchedule_[rowNum][2] =
+ this.keySchedule_[rowNum - this.keyLength_][2] ^ temp[2];
+ this.keySchedule_[rowNum][3] =
+ this.keySchedule_[rowNum - this.keyLength_][3] ^ temp[3];
+ }
+};
+
+
+/**
+ * AES's SubWord procedure.
+ * @param {!Array.<number>} w Bytes to find the SBox substitution for.
+ * @return {!Array.<number>} The substituted bytes.
+ * @private
+ */
+goog.crypt.Aes.prototype.subWord_ = function(w) {
+ w[0] = goog.crypt.Aes.SBOX_[w[0]];
+ w[1] = goog.crypt.Aes.SBOX_[w[1]];
+ w[2] = goog.crypt.Aes.SBOX_[w[2]];
+ w[3] = goog.crypt.Aes.SBOX_[w[3]];
+
+ return w;
+};
+
+
+/**
+ * AES's RotWord procedure.
+ * @param {!Array.<number>} w Array of bytes to rotate.
+ * @return {!Array.<number>} The rotated bytes.
+ * @private
+ */
+goog.crypt.Aes.prototype.rotWord_ = function(w) {
+ var temp = w[0];
+
+ w[0] = w[1];
+ w[1] = w[2];
+ w[2] = w[3];
+ w[3] = temp;
+
+ return w;
+};
+
+
+/**
+ * The key schedule.
+ * @type {!Array.<number>}
+ * @private
+ */
+goog.crypt.Aes.prototype.keySchedule_;
+
+
+/**
+ * Precomputed SBox lookup.
+ * @type {!Array.<number>}
+ * @private
+ */
+goog.crypt.Aes.SBOX_ = [
+ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe,
+ 0xd7, 0xab, 0x76,
+
+ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c,
+ 0xa4, 0x72, 0xc0,
+
+ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71,
+ 0xd8, 0x31, 0x15,
+
+ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb,
+ 0x27, 0xb2, 0x75,
+
+ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29,
+ 0xe3, 0x2f, 0x84,
+
+ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a,
+ 0x4c, 0x58, 0xcf,
+
+ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50,
+ 0x3c, 0x9f, 0xa8,
+
+ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10,
+ 0xff, 0xf3, 0xd2,
+
+ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64,
+ 0x5d, 0x19, 0x73,
+
+ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde,
+ 0x5e, 0x0b, 0xdb,
+
+ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91,
+ 0x95, 0xe4, 0x79,
+
+ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65,
+ 0x7a, 0xae, 0x08,
+
+ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b,
+ 0xbd, 0x8b, 0x8a,
+
+ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86,
+ 0xc1, 0x1d, 0x9e,
+
+ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce,
+ 0x55, 0x28, 0xdf,
+
+ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0,
+ 0x54, 0xbb, 0x16
+];
+
+
+/**
+ * Precomputed InvSBox lookup.
+ * @type {!Array.<number>}
+ * @private
+ */
+goog.crypt.Aes.INV_SBOX_ = [
+ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81,
+ 0xf3, 0xd7, 0xfb,
+
+ 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4,
+ 0xde, 0xe9, 0xcb,
+
+ 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42,
+ 0xfa, 0xc3, 0x4e,
+
+ 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d,
+ 0x8b, 0xd1, 0x25,
+
+ 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d,
+ 0x65, 0xb6, 0x92,
+
+ 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7,
+ 0x8d, 0x9d, 0x84,
+
+ 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8,
+ 0xb3, 0x45, 0x06,
+
+ 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01,
+ 0x13, 0x8a, 0x6b,
+
+ 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0,
+ 0xb4, 0xe6, 0x73,
+
+ 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c,
+ 0x75, 0xdf, 0x6e,
+
+ 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa,
+ 0x18, 0xbe, 0x1b,
+
+ 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78,
+ 0xcd, 0x5a, 0xf4,
+
+ 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27,
+ 0x80, 0xec, 0x5f,
+
+ 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93,
+ 0xc9, 0x9c, 0xef,
+
+ 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83,
+ 0x53, 0x99, 0x61,
+
+ 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55,
+ 0x21, 0x0c, 0x7d
+];
+
+
+/**
+ * Precomputed RCon lookup.
+ * @type {!Array.<number>}
+ * @private
+ */
+goog.crypt.Aes.RCON_ = [
+ [0x00, 0x00, 0x00, 0x00],
+ [0x01, 0x00, 0x00, 0x00],
+ [0x02, 0x00, 0x00, 0x00],
+ [0x04, 0x00, 0x00, 0x00],
+ [0x08, 0x00, 0x00, 0x00],
+ [0x10, 0x00, 0x00, 0x00],
+ [0x20, 0x00, 0x00, 0x00],
+ [0x40, 0x00, 0x00, 0x00],
+ [0x80, 0x00, 0x00, 0x00],
+ [0x1b, 0x00, 0x00, 0x00],
+ [0x36, 0x00, 0x00, 0x00]
+];
+
+
+/**
+ * Precomputed lookup of multiplication by 2 in GF(2^8)
+ * @type {!Array.<number>}
+ * @private
+ */
+goog.crypt.Aes.MULT_2_ = [
+ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10, 0x12, 0x14, 0x16,
+ 0x18, 0x1A, 0x1C, 0x1E,
+
+ 0x20, 0x22, 0x24, 0x26, 0x28, 0x2A, 0x2C, 0x2E, 0x30, 0x32, 0x34, 0x36,
+ 0x38, 0x3A, 0x3C, 0x3E,
+
+ 0x40, 0x42, 0x44, 0x46, 0x48, 0x4A, 0x4C, 0x4E, 0x50, 0x52, 0x54, 0x56,
+ 0x58, 0x5A, 0x5C, 0x5E,
+
+ 0x60, 0x62, 0x64, 0x66, 0x68, 0x6A, 0x6C, 0x6E, 0x70, 0x72, 0x74, 0x76,
+ 0x78, 0x7A, 0x7C, 0x7E,
+
+ 0x80, 0x82, 0x84, 0x86, 0x88, 0x8A, 0x8C, 0x8E, 0x90, 0x92, 0x94, 0x96,
+ 0x98, 0x9A, 0x9C, 0x9E,
+
+ 0xA0, 0xA2, 0xA4, 0xA6, 0xA8, 0xAA, 0xAC, 0xAE, 0xB0, 0xB2, 0xB4, 0xB6,
+ 0xB8, 0xBA, 0xBC, 0xBE,
+
+ 0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE, 0xD0, 0xD2, 0xD4, 0xD6,
+ 0xD8, 0xDA, 0xDC, 0xDE,
+
+ 0xE0, 0xE2, 0xE4, 0xE6, 0xE8, 0xEA, 0xEC, 0xEE, 0xF0, 0xF2, 0xF4, 0xF6,
+ 0xF8, 0xFA, 0xFC, 0xFE,
+
+ 0x1B, 0x19, 0x1F, 0x1D, 0x13, 0x11, 0x17, 0x15, 0x0B, 0x09, 0x0F, 0x0D,
+ 0x03, 0x01, 0x07, 0x05,
+
+ 0x3B, 0x39, 0x3F, 0x3D, 0x33, 0x31, 0x37, 0x35, 0x2B, 0x29, 0x2F, 0x2D,
+ 0x23, 0x21, 0x27, 0x25,
+
+ 0x5B, 0x59, 0x5F, 0x5D, 0x53, 0x51, 0x57, 0x55, 0x4B, 0x49, 0x4F, 0x4D,
+ 0x43, 0x41, 0x47, 0x45,
+
+ 0x7B, 0x79, 0x7F, 0x7D, 0x73, 0x71, 0x77, 0x75, 0x6B, 0x69, 0x6F, 0x6D,
+ 0x63, 0x61, 0x67, 0x65,
+
+ 0x9B, 0x99, 0x9F, 0x9D, 0x93, 0x91, 0x97, 0x95, 0x8B, 0x89, 0x8F, 0x8D,
+ 0x83, 0x81, 0x87, 0x85,
+
+ 0xBB, 0xB9, 0xBF, 0xBD, 0xB3, 0xB1, 0xB7, 0xB5, 0xAB, 0xA9, 0xAF, 0xAD,
+ 0xA3, 0xA1, 0xA7, 0xA5,
+
+ 0xDB, 0xD9, 0xDF, 0xDD, 0xD3, 0xD1, 0xD7, 0xD5, 0xCB, 0xC9, 0xCF, 0xCD,
+ 0xC3, 0xC1, 0xC7, 0xC5,
+
+ 0xFB, 0xF9, 0xFF, 0xFD, 0xF3, 0xF1, 0xF7, 0xF5, 0xEB, 0xE9, 0xEF, 0xED,
+ 0xE3, 0xE1, 0xE7, 0xE5
+];
+
+
+/**
+ * Precomputed lookup of multiplication by 3 in GF(2^8)
+ * @type {!Array.<number>}
+ * @private
+ */
+goog.crypt.Aes.MULT_3_ = [
+ 0x00, 0x03, 0x06, 0x05, 0x0C, 0x0F, 0x0A, 0x09, 0x18, 0x1B, 0x1E, 0x1D,
+ 0x14, 0x17, 0x12, 0x11,
+
+ 0x30, 0x33, 0x36, 0x35, 0x3C, 0x3F, 0x3A, 0x39, 0x28, 0x2B, 0x2E, 0x2D,
+ 0x24, 0x27, 0x22, 0x21,
+
+ 0x60, 0x63, 0x66, 0x65, 0x6C, 0x6F, 0x6A, 0x69, 0x78, 0x7B, 0x7E, 0x7D,
+ 0x74, 0x77, 0x72, 0x71,
+
+ 0x50, 0x53, 0x56, 0x55, 0x5C, 0x5F, 0x5A, 0x59, 0x48, 0x4B, 0x4E, 0x4D,
+ 0x44, 0x47, 0x42, 0x41,
+
+ 0xC0, 0xC3, 0xC6, 0xC5, 0xCC, 0xCF, 0xCA, 0xC9, 0xD8, 0xDB, 0xDE, 0xDD,
+ 0xD4, 0xD7, 0xD2, 0xD1,
+
+ 0xF0, 0xF3, 0xF6, 0xF5, 0xFC, 0xFF, 0xFA, 0xF9, 0xE8, 0xEB, 0xEE, 0xED,
+ 0xE4, 0xE7, 0xE2, 0xE1,
+
+ 0xA0, 0xA3, 0xA6, 0xA5, 0xAC, 0xAF, 0xAA, 0xA9, 0xB8, 0xBB, 0xBE, 0xBD,
+ 0xB4, 0xB7, 0xB2, 0xB1,
+
+ 0x90, 0x93, 0x96, 0x95, 0x9C, 0x9F, 0x9A, 0x99, 0x88, 0x8B, 0x8E, 0x8D,
+ 0x84, 0x87, 0x82, 0x81,
+
+ 0x9B, 0x98, 0x9D, 0x9E, 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86,
+ 0x8F, 0x8C, 0x89, 0x8A,
+
+ 0xAB, 0xA8, 0xAD, 0xAE, 0xA7, 0xA4, 0xA1, 0xA2, 0xB3, 0xB0, 0xB5, 0xB6,
+ 0xBF, 0xBC, 0xB9, 0xBA,
+
+ 0xFB, 0xF8, 0xFD, 0xFE, 0xF7, 0xF4, 0xF1, 0xF2, 0xE3, 0xE0, 0xE5, 0xE6,
+ 0xEF, 0xEC, 0xE9, 0xEA,
+
+ 0xCB, 0xC8, 0xCD, 0xCE, 0xC7, 0xC4, 0xC1, 0xC2, 0xD3, 0xD0, 0xD5, 0xD6,
+ 0xDF, 0xDC, 0xD9, 0xDA,
+
+ 0x5B, 0x58, 0x5D, 0x5E, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46,
+ 0x4F, 0x4C, 0x49, 0x4A,
+
+ 0x6B, 0x68, 0x6D, 0x6E, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76,
+ 0x7F, 0x7C, 0x79, 0x7A,
+
+ 0x3B, 0x38, 0x3D, 0x3E, 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26,
+ 0x2F, 0x2C, 0x29, 0x2A,
+
+ 0x0B, 0x08, 0x0D, 0x0E, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16,
+ 0x1F, 0x1C, 0x19, 0x1A
+];
+
+
+/**
+ * Precomputed lookup of multiplication by 9 in GF(2^8)
+ * @type {!Array.<number>}
+ * @private
+ */
+goog.crypt.Aes.MULT_9_ = [
+ 0x00, 0x09, 0x12, 0x1B, 0x24, 0x2D, 0x36, 0x3F, 0x48, 0x41, 0x5A, 0x53,
+ 0x6C, 0x65, 0x7E, 0x77,
+
+ 0x90, 0x99, 0x82, 0x8B, 0xB4, 0xBD, 0xA6, 0xAF, 0xD8, 0xD1, 0xCA, 0xC3,
+ 0xFC, 0xF5, 0xEE, 0xE7,
+
+ 0x3B, 0x32, 0x29, 0x20, 0x1F, 0x16, 0x0D, 0x04, 0x73, 0x7A, 0x61, 0x68,
+ 0x57, 0x5E, 0x45, 0x4C,
+
+ 0xAB, 0xA2, 0xB9, 0xB0, 0x8F, 0x86, 0x9D, 0x94, 0xE3, 0xEA, 0xF1, 0xF8,
+ 0xC7, 0xCE, 0xD5, 0xDC,
+
+ 0x76, 0x7F, 0x64, 0x6D, 0x52, 0x5B, 0x40, 0x49, 0x3E, 0x37, 0x2C, 0x25,
+ 0x1A, 0x13, 0x08, 0x01,
+
+ 0xE6, 0xEF, 0xF4, 0xFD, 0xC2, 0xCB, 0xD0, 0xD9, 0xAE, 0xA7, 0xBC, 0xB5,
+ 0x8A, 0x83, 0x98, 0x91,
+
+ 0x4D, 0x44, 0x5F, 0x56, 0x69, 0x60, 0x7B, 0x72, 0x05, 0x0C, 0x17, 0x1E,
+ 0x21, 0x28, 0x33, 0x3A,
+
+ 0xDD, 0xD4, 0xCF, 0xC6, 0xF9, 0xF0, 0xEB, 0xE2, 0x95, 0x9C, 0x87, 0x8E,
+ 0xB1, 0xB8, 0xA3, 0xAA,
+
+ 0xEC, 0xE5, 0xFE, 0xF7, 0xC8, 0xC1, 0xDA, 0xD3, 0xA4, 0xAD, 0xB6, 0xBF,
+ 0x80, 0x89, 0x92, 0x9B,
+
+ 0x7C, 0x75, 0x6E, 0x67, 0x58, 0x51, 0x4A, 0x43, 0x34, 0x3D, 0x26, 0x2F,
+ 0x10, 0x19, 0x02, 0x0B,
+
+ 0xD7, 0xDE, 0xC5, 0xCC, 0xF3, 0xFA, 0xE1, 0xE8, 0x9F, 0x96, 0x8D, 0x84,
+ 0xBB, 0xB2, 0xA9, 0xA0,
+
+ 0x47, 0x4E, 0x55, 0x5C, 0x63, 0x6A, 0x71, 0x78, 0x0F, 0x06, 0x1D, 0x14,
+ 0x2B, 0x22, 0x39, 0x30,
+
+ 0x9A, 0x93, 0x88, 0x81, 0xBE, 0xB7, 0xAC, 0xA5, 0xD2, 0xDB, 0xC0, 0xC9,
+ 0xF6, 0xFF, 0xE4, 0xED,
+
+ 0x0A, 0x03, 0x18, 0x11, 0x2E, 0x27, 0x3C, 0x35, 0x42, 0x4B, 0x50, 0x59,
+ 0x66, 0x6F, 0x74, 0x7D,
+
+ 0xA1, 0xA8, 0xB3, 0xBA, 0x85, 0x8C, 0x97, 0x9E, 0xE9, 0xE0, 0xFB, 0xF2,
+ 0xCD, 0xC4, 0xDF, 0xD6,
+
+ 0x31, 0x38, 0x23, 0x2A, 0x15, 0x1C, 0x07, 0x0E, 0x79, 0x70, 0x6B, 0x62,
+ 0x5D, 0x54, 0x4F, 0x46
+];
+
+
+/**
+ * Precomputed lookup of multiplication by 11 in GF(2^8)
+ * @type {!Array.<number>}
+ * @private
+ */
+goog.crypt.Aes.MULT_B_ = [
+ 0x00, 0x0B, 0x16, 0x1D, 0x2C, 0x27, 0x3A, 0x31, 0x58, 0x53, 0x4E, 0x45,
+ 0x74, 0x7F, 0x62, 0x69,
+
+ 0xB0, 0xBB, 0xA6, 0xAD, 0x9C, 0x97, 0x8A, 0x81, 0xE8, 0xE3, 0xFE, 0xF5,
+ 0xC4, 0xCF, 0xD2, 0xD9,
+
+ 0x7B, 0x70, 0x6D, 0x66, 0x57, 0x5C, 0x41, 0x4A, 0x23, 0x28, 0x35, 0x3E,
+ 0x0F, 0x04, 0x19, 0x12,
+
+ 0xCB, 0xC0, 0xDD, 0xD6, 0xE7, 0xEC, 0xF1, 0xFA, 0x93, 0x98, 0x85, 0x8E,
+ 0xBF, 0xB4, 0xA9, 0xA2,
+
+ 0xF6, 0xFD, 0xE0, 0xEB, 0xDA, 0xD1, 0xCC, 0xC7, 0xAE, 0xA5, 0xB8, 0xB3,
+ 0x82, 0x89, 0x94, 0x9F,
+
+ 0x46, 0x4D, 0x50, 0x5B, 0x6A, 0x61, 0x7C, 0x77, 0x1E, 0x15, 0x08, 0x03,
+ 0x32, 0x39, 0x24, 0x2F,
+
+ 0x8D, 0x86, 0x9B, 0x90, 0xA1, 0xAA, 0xB7, 0xBC, 0xD5, 0xDE, 0xC3, 0xC8,
+ 0xF9, 0xF2, 0xEF, 0xE4,
+
+ 0x3D, 0x36, 0x2B, 0x20, 0x11, 0x1A, 0x07, 0x0C, 0x65, 0x6E, 0x73, 0x78,
+ 0x49, 0x42, 0x5F, 0x54,
+
+ 0xF7, 0xFC, 0xE1, 0xEA, 0xDB, 0xD0, 0xCD, 0xC6, 0xAF, 0xA4, 0xB9, 0xB2,
+ 0x83, 0x88, 0x95, 0x9E,
+
+ 0x47, 0x4C, 0x51, 0x5A, 0x6B, 0x60, 0x7D, 0x76, 0x1F, 0x14, 0x09, 0x02,
+ 0x33, 0x38, 0x25, 0x2E,
+
+ 0x8C, 0x87, 0x9A, 0x91, 0xA0, 0xAB, 0xB6, 0xBD, 0xD4, 0xDF, 0xC2, 0xC9,
+ 0xF8, 0xF3, 0xEE, 0xE5,
+
+ 0x3C, 0x37, 0x2A, 0x21, 0x10, 0x1B, 0x06, 0x0D, 0x64, 0x6F, 0x72, 0x79,
+ 0x48, 0x43, 0x5E, 0x55,
+
+ 0x01, 0x0A, 0x17, 0x1C, 0x2D, 0x26, 0x3B, 0x30, 0x59, 0x52, 0x4F, 0x44,
+ 0x75, 0x7E, 0x63, 0x68,
+
+ 0xB1, 0xBA, 0xA7, 0xAC, 0x9D, 0x96, 0x8B, 0x80, 0xE9, 0xE2, 0xFF, 0xF4,
+ 0xC5, 0xCE, 0xD3, 0xD8,
+
+ 0x7A, 0x71, 0x6C, 0x67, 0x56, 0x5D, 0x40, 0x4B, 0x22, 0x29, 0x34, 0x3F,
+ 0x0E, 0x05, 0x18, 0x13,
+
+ 0xCA, 0xC1, 0xDC, 0xD7, 0xE6, 0xED, 0xF0, 0xFB, 0x92, 0x99, 0x84, 0x8F,
+ 0xBE, 0xB5, 0xA8, 0xA3
+];
+
+
+/**
+ * Precomputed lookup of multiplication by 13 in GF(2^8)
+ * @type {!Array.<number>}
+ * @private
+ */
+goog.crypt.Aes.MULT_D_ = [
+ 0x00, 0x0D, 0x1A, 0x17, 0x34, 0x39, 0x2E, 0x23, 0x68, 0x65, 0x72, 0x7F,
+ 0x5C, 0x51, 0x46, 0x4B,
+
+ 0xD0, 0xDD, 0xCA, 0xC7, 0xE4, 0xE9, 0xFE, 0xF3, 0xB8, 0xB5, 0xA2, 0xAF,
+ 0x8C, 0x81, 0x96, 0x9B,
+
+ 0xBB, 0xB6, 0xA1, 0xAC, 0x8F, 0x82, 0x95, 0x98, 0xD3, 0xDE, 0xC9, 0xC4,
+ 0xE7, 0xEA, 0xFD, 0xF0,
+
+ 0x6B, 0x66, 0x71, 0x7C, 0x5F, 0x52, 0x45, 0x48, 0x03, 0x0E, 0x19, 0x14,
+ 0x37, 0x3A, 0x2D, 0x20,
+
+ 0x6D, 0x60, 0x77, 0x7A, 0x59, 0x54, 0x43, 0x4E, 0x05, 0x08, 0x1F, 0x12,
+ 0x31, 0x3C, 0x2B, 0x26,
+
+ 0xBD, 0xB0, 0xA7, 0xAA, 0x89, 0x84, 0x93, 0x9E, 0xD5, 0xD8, 0xCF, 0xC2,
+ 0xE1, 0xEC, 0xFB, 0xF6,
+
+ 0xD6, 0xDB, 0xCC, 0xC1, 0xE2, 0xEF, 0xF8, 0xF5, 0xBE, 0xB3, 0xA4, 0xA9,
+ 0x8A, 0x87, 0x90, 0x9D,
+
+ 0x06, 0x0B, 0x1C, 0x11, 0x32, 0x3F, 0x28, 0x25, 0x6E, 0x63, 0x74, 0x79,
+ 0x5A, 0x57, 0x40, 0x4D,
+
+ 0xDA, 0xD7, 0xC0, 0xCD, 0xEE, 0xE3, 0xF4, 0xF9, 0xB2, 0xBF, 0xA8, 0xA5,
+ 0x86, 0x8B, 0x9C, 0x91,
+
+ 0x0A, 0x07, 0x10, 0x1D, 0x3E, 0x33, 0x24, 0x29, 0x62, 0x6F, 0x78, 0x75,
+ 0x56, 0x5B, 0x4C, 0x41,
+
+ 0x61, 0x6C, 0x7B, 0x76, 0x55, 0x58, 0x4F, 0x42, 0x09, 0x04, 0x13, 0x1E,
+ 0x3D, 0x30, 0x27, 0x2A,
+
+ 0xB1, 0xBC, 0xAB, 0xA6, 0x85, 0x88, 0x9F, 0x92, 0xD9, 0xD4, 0xC3, 0xCE,
+ 0xED, 0xE0, 0xF7, 0xFA,
+
+ 0xB7, 0xBA, 0xAD, 0xA0, 0x83, 0x8E, 0x99, 0x94, 0xDF, 0xD2, 0xC5, 0xC8,
+ 0xEB, 0xE6, 0xF1, 0xFC,
+
+ 0x67, 0x6A, 0x7D, 0x70, 0x53, 0x5E, 0x49, 0x44, 0x0F, 0x02, 0x15, 0x18,
+ 0x3B, 0x36, 0x21, 0x2C,
+
+ 0x0C, 0x01, 0x16, 0x1B, 0x38, 0x35, 0x22, 0x2F, 0x64, 0x69, 0x7E, 0x73,
+ 0x50, 0x5D, 0x4A, 0x47,
+
+ 0xDC, 0xD1, 0xC6, 0xCB, 0xE8, 0xE5, 0xF2, 0xFF, 0xB4, 0xB9, 0xAE, 0xA3,
+ 0x80, 0x8D, 0x9A, 0x97
+];
+
+
+/**
+ * Precomputed lookup of multiplication by 14 in GF(2^8)
+ * @type {!Array.<number>}
+ * @private
+ */
+goog.crypt.Aes.MULT_E_ = [
+ 0x00, 0x0E, 0x1C, 0x12, 0x38, 0x36, 0x24, 0x2A, 0x70, 0x7E, 0x6C, 0x62,
+ 0x48, 0x46, 0x54, 0x5A,
+
+ 0xE0, 0xEE, 0xFC, 0xF2, 0xD8, 0xD6, 0xC4, 0xCA, 0x90, 0x9E, 0x8C, 0x82,
+ 0xA8, 0xA6, 0xB4, 0xBA,
+
+ 0xDB, 0xD5, 0xC7, 0xC9, 0xE3, 0xED, 0xFF, 0xF1, 0xAB, 0xA5, 0xB7, 0xB9,
+ 0x93, 0x9D, 0x8F, 0x81,
+
+ 0x3B, 0x35, 0x27, 0x29, 0x03, 0x0D, 0x1F, 0x11, 0x4B, 0x45, 0x57, 0x59,
+ 0x73, 0x7D, 0x6F, 0x61,
+
+ 0xAD, 0xA3, 0xB1, 0xBF, 0x95, 0x9B, 0x89, 0x87, 0xDD, 0xD3, 0xC1, 0xCF,
+ 0xE5, 0xEB, 0xF9, 0xF7,
+
+ 0x4D, 0x43, 0x51, 0x5F, 0x75, 0x7B, 0x69, 0x67, 0x3D, 0x33, 0x21, 0x2F,
+ 0x05, 0x0B, 0x19, 0x17,
+
+ 0x76, 0x78, 0x6A, 0x64, 0x4E, 0x40, 0x52, 0x5C, 0x06, 0x08, 0x1A, 0x14,
+ 0x3E, 0x30, 0x22, 0x2C,
+
+ 0x96, 0x98, 0x8A, 0x84, 0xAE, 0xA0, 0xB2, 0xBC, 0xE6, 0xE8, 0xFA, 0xF4,
+ 0xDE, 0xD0, 0xC2, 0xCC,
+
+ 0x41, 0x4F, 0x5D, 0x53, 0x79, 0x77, 0x65, 0x6B, 0x31, 0x3F, 0x2D, 0x23,
+ 0x09, 0x07, 0x15, 0x1B,
+
+ 0xA1, 0xAF, 0xBD, 0xB3, 0x99, 0x97, 0x85, 0x8B, 0xD1, 0xDF, 0xCD, 0xC3,
+ 0xE9, 0xE7, 0xF5, 0xFB,
+
+ 0x9A, 0x94, 0x86, 0x88, 0xA2, 0xAC, 0xBE, 0xB0, 0xEA, 0xE4, 0xF6, 0xF8,
+ 0xD2, 0xDC, 0xCE, 0xC0,
+
+ 0x7A, 0x74, 0x66, 0x68, 0x42, 0x4C, 0x5E, 0x50, 0x0A, 0x04, 0x16, 0x18,
+ 0x32, 0x3C, 0x2E, 0x20,
+
+ 0xEC, 0xE2, 0xF0, 0xFE, 0xD4, 0xDA, 0xC8, 0xC6, 0x9C, 0x92, 0x80, 0x8E,
+ 0xA4, 0xAA, 0xB8, 0xB6,
+
+ 0x0C, 0x02, 0x10, 0x1E, 0x34, 0x3A, 0x28, 0x26, 0x7C, 0x72, 0x60, 0x6E,
+ 0x44, 0x4A, 0x58, 0x56,
+
+ 0x37, 0x39, 0x2B, 0x25, 0x0F, 0x01, 0x13, 0x1D, 0x47, 0x49, 0x5B, 0x55,
+ 0x7F, 0x71, 0x63, 0x6D,
+
+ 0xD7, 0xD9, 0xCB, 0xC5, 0xEF, 0xE1, 0xF3, 0xFD, 0xA7, 0xA9, 0xBB, 0xB5,
+ 0x9F, 0x91, 0x83, 0x8D
+];
diff --git a/third_party/closure/goog/crypt/blockcipher.js b/third_party/closure/goog/crypt/blockcipher.js
new file mode 100644
index 0000000..c0abe45
--- /dev/null
+++ b/third_party/closure/goog/crypt/blockcipher.js
@@ -0,0 +1,52 @@
+// Copyright 2012 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Interface definition of a block cipher. A block cipher is a
+ * pair of algorithms that implement encryption and decryption of input bytes.
+ *
+ * @see http://en.wikipedia.org/wiki/Block_cipher
+ *
+ * @author nnaze@google.com (Nathan Naze)
+ */
+
+goog.provide('goog.crypt.BlockCipher');
+
+
+
+/**
+ * Interface definition for a block cipher.
+ * @interface
+ */
+goog.crypt.BlockCipher = function() {};
+
+
+/**
+ * Encrypt a plaintext block. The implementation may expect (and assert)
+ * a particular block length.
+ * @param {!Array.<number>} input Plaintext array of input bytes.
+ * @return {!Array.<number>} Encrypted ciphertext array of bytes. Should be the
+ * same length as input.
+ */
+goog.crypt.BlockCipher.prototype.encrypt;
+
+
+/**
+ * Decrypt a plaintext block. The implementation may expect (and assert)
+ * a particular block length.
+ * @param {!Array.<number>} input Ciphertext. Array of input bytes.
+ * @return {!Array.<number>} Decrypted plaintext array of bytes. Should be the
+ * same length as input.
+ */
+goog.crypt.BlockCipher.prototype.decrypt;
diff --git a/third_party/closure/goog/crypt/cbc.js b/third_party/closure/goog/crypt/cbc.js
new file mode 100644
index 0000000..4c59c00
--- /dev/null
+++ b/third_party/closure/goog/crypt/cbc.js
@@ -0,0 +1,150 @@
+// Copyright 2012 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Implementation of CBC mode for block ciphers. See
+ * http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation
+ * #Cipher-block_chaining_.28CBC.29. for description.
+ *
+ * @author nnaze@google.com (Nathan Naze)
+ */
+
+goog.provide('goog.crypt.Cbc');
+
+goog.require('goog.array');
+goog.require('goog.crypt');
+
+
+
+/**
+ * Implements the CBC mode for block ciphers. See
+ * http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation
+ * #Cipher-block_chaining_.28CBC.29
+ *
+ * @param {!goog.crypt.BlockCipher} cipher The block cipher to use.
+ * @param {number=} opt_blockSize The block size of the cipher in bytes.
+ * Defaults to 16 bytes.
+ * @constructor
+ */
+goog.crypt.Cbc = function(cipher, opt_blockSize) {
+
+ /**
+ * Block cipher.
+ * @type {!goog.crypt.BlockCipher}
+ * @private
+ */
+ this.cipher_ = cipher;
+
+ /**
+ * Block size in bytes.
+ * @type {number}
+ * @private
+ */
+ this.blockSize_ = opt_blockSize || 16;
+};
+
+
+/**
+ * Encrypt a message.
+ *
+ * @param {!Array.<number>} plainText Message to encrypt. An array of bytes.
+ * The length should be a multiple of the block size.
+ * @param {!Array.<number>} initialVector Initial vector for the CBC mode.
+ * An array of bytes with the same length as the block size.
+ * @return {!Array.<number>} Encrypted message.
+ */
+goog.crypt.Cbc.prototype.encrypt = function(plainText, initialVector) {
+
+ goog.asserts.assert(
+ plainText.length % this.blockSize_ == 0,
+ 'Data\'s length must be multiple of block size.');
+
+ goog.asserts.assert(
+ initialVector.length == this.blockSize_,
+ 'Initial vector must be size of one block.');
+
+ // Implementation of
+ // http://en.wikipedia.org/wiki/File:Cbc_encryption.png
+
+ var cipherText = [];
+ var vector = initialVector;
+
+ // Generate each block of the encrypted cypher text.
+ for (var blockStartIndex = 0;
+ blockStartIndex < plainText.length;
+ blockStartIndex += this.blockSize_) {
+
+ // Takes one block from the input message.
+ var plainTextBlock = goog.array.slice(
+ plainText,
+ blockStartIndex,
+ blockStartIndex + this.blockSize_);
+
+ var input = goog.crypt.xorByteArray(plainTextBlock, vector);
+ var resultBlock = this.cipher_.encrypt(input);
+
+ goog.array.extend(cipherText, resultBlock);
+ vector = resultBlock;
+ }
+
+ return cipherText;
+};
+
+
+/**
+ * Decrypt a message.
+ *
+ * @param {!Array.<number>} cipherText Message to decrypt. An array of bytes.
+ * The length should be a multiple of the block size.
+ * @param {!Array.<number>} initialVector Initial vector for the CBC mode.
+ * An array of bytes with the same length as the block size.
+ * @return {!Array.<number>} Decrypted message.
+ */
+goog.crypt.Cbc.prototype.decrypt = function(cipherText, initialVector) {
+
+ goog.asserts.assert(
+ cipherText.length % this.blockSize_ == 0,
+ 'Data\'s length must be multiple of block size.');
+
+ goog.asserts.assert(
+ initialVector.length == this.blockSize_,
+ 'Initial vector must be size of one block.');
+
+ // Implementation of
+ // http://en.wikipedia.org/wiki/File:Cbc_decryption.png
+
+ var plainText = [];
+ var blockStartIndex = 0;
+ var vector = initialVector;
+
+ // Generate each block of the decrypted plain text.
+ while (blockStartIndex < cipherText.length) {
+
+ // Takes one block.
+ var cipherTextBlock = goog.array.slice(
+ cipherText,
+ blockStartIndex,
+ blockStartIndex + this.blockSize_);
+
+ var resultBlock = this.cipher_.decrypt(cipherTextBlock);
+ var plainTextBlock = goog.crypt.xorByteArray(vector, resultBlock);
+
+ goog.array.extend(plainText, plainTextBlock);
+ vector = cipherTextBlock;
+
+ blockStartIndex += this.blockSize_;
+ }
+
+ return plainText;
+};
diff --git a/third_party/closure/goog/crypt/crypt.js b/third_party/closure/goog/crypt/crypt.js
index 67f3b56..20b3069 100644
--- a/third_party/closure/goog/crypt/crypt.js
+++ b/third_party/closure/goog/crypt/crypt.js
@@ -25,7 +25,7 @@
* Turns a string into an array of bytes; a "byte" being a JS number in the
* range 0-255.
* @param {string} str String value to arrify.
- * @return {Array.<number>} Array of numbers corresponding to the
+ * @return {!Array.<number>} Array of numbers corresponding to the
* UCS character codes of each character in str.
*/
goog.crypt.stringToByteArray = function(str) {
@@ -68,6 +68,23 @@
/**
+ * Converts a hex string into an integer array.
+ * @param {string} hexString Hex string of 16-bit integers (two characters
+ * per integer).
+ * @return {!Array.<number>} Array of {0,255} integers for the given string.
+ */
+goog.crypt.hexToByteArray = function(hexString) {
+ goog.asserts.assert(hexString.length % 2 == 0,
+ 'Key string length must be multiple of 2');
+ var arr = [];
+ for (var i = 0; i < hexString.length; i += 2) {
+ arr.push(parseInt(hexString.substring(i, i + 2), 16));
+ }
+ return arr;
+};
+
+
+/**
* Converts a JS string to a UTF-8 "byte" array.
* @param {string} str 16-bit unicode string.
* @return {Array.<number>} UTF-8 byte array.
@@ -117,3 +134,22 @@
}
return out.join('');
};
+
+
+/**
+ * XOR two byte arrays.
+ * @param {!Array.<number>} bytes1 Byte array 1.
+ * @param {!Array.<number>} bytes2 Byte array 2.
+ * @return {!Array.<number>} Resulting XOR of the two byte arrays.
+ */
+goog.crypt.xorByteArray = function(bytes1, bytes2) {
+ goog.asserts.assert(
+ bytes1.length == bytes2.length,
+ 'XOR array lengths must match');
+
+ var result = [];
+ for (var i = 0; i < bytes1.length; i++) {
+ result.push(bytes1[i] ^ bytes2[i]);
+ }
+ return result;
+};
diff --git a/third_party/closure/goog/crypt/hash.js b/third_party/closure/goog/crypt/hash.js
index 713e4a1..941ebfd 100644
--- a/third_party/closure/goog/crypt/hash.js
+++ b/third_party/closure/goog/crypt/hash.js
@@ -48,7 +48,7 @@
/**
- * @return {Array.<number>} The finalized hash computed
+ * @return {!Array.<number>} The finalized hash computed
* from the internal accumulator.
*/
goog.crypt.Hash.prototype.digest = goog.abstractMethod;
diff --git a/third_party/closure/goog/crypt/hash_test.js b/third_party/closure/goog/crypt/hash_test.js
index bbd0fd6..5944277 100644
--- a/third_party/closure/goog/crypt/hash_test.js
+++ b/third_party/closure/goog/crypt/hash_test.js
@@ -19,6 +19,7 @@
goog.provide('goog.crypt.hash_test');
+goog.require('goog.array');
goog.require('goog.testing.asserts');
goog.setTestOnly('hash_test');
@@ -81,3 +82,66 @@
assertArrayEquals('Updating with an empty string did not give an empty hash',
empty, hash.digest());
};
+
+
+// Run performance tests
+goog.crypt.hash_test.runPerfTests = function (hashFactory, hashName) {
+
+ var body = goog.dom.getDocument().body;
+ var perfTable = goog.dom.createElement('div');
+ goog.dom.appendChild(body, perfTable);
+
+ var table = new goog.testing.PerformanceTable(perfTable)
+
+ function runPerfTest(byteLength, updateCount) {
+
+ var label = (hashName + ': ' + updateCount + ' update(s) of ' + byteLength
+ + ' bytes');
+
+ function run(data, dataType) {
+ table.run(function() {
+ var hash = hashFactory();
+ for (var i = 0; i < updateCount; i++) {
+ hash.update(data, byteLength);
+ }
+ var digest = hash.digest();
+ }, label + ' (' + dataType + ')');
+ }
+
+ var byteArray = goog.crypt.hash_test.createRandomByteArray_(length);
+ var byteString = goog.crypt.hash_test.createByteString_(byteArray);
+
+ run(byteArray, 'byte array');
+ run(byteString, 'byte string');
+ }
+
+ var MESSAGE_LENGTH_LONG = 10000002;
+ var MESSAGE_LENGTH_SHORT = 1000002;
+
+ runPerfTest(MESSAGE_LENGTH_SHORT, 1);
+ runPerfTest(MESSAGE_LENGTH_LONG, 1);
+ runPerfTest(MESSAGE_LENGTH_SHORT, 10);
+};
+
+
+goog.crypt.hash_test.createRandomByteArray_ = function(length) {
+ var random = new goog.testing.PseudoRandom(0);
+ var bytes = [];
+
+ for (var i = 0; i < length; ++i) {
+ // Generates an integer from 0 to 255.
+ var byte = Math.floor(random.random() * 0x100);
+ bytes.push(byte);
+ }
+
+ return bytes;
+};
+
+
+goog.crypt.hash_test.createByteString_ = function(bytes) {
+ var str = '';
+ goog.array.forEach(bytes, function(byte) {
+ str += String.fromCharCode(byte);
+ });
+ return str;
+}
diff --git a/third_party/closure/goog/crypt/hmac.js b/third_party/closure/goog/crypt/hmac.js
index 7d4cfb7..9d181dc 100644
--- a/third_party/closure/goog/crypt/hmac.js
+++ b/third_party/closure/goog/crypt/hmac.js
@@ -153,11 +153,10 @@
* Calculates an HMAC for a given message.
*
* @param {Array.<number>} message An array of integers in {0, 255}.
- * @return {Array} the digest of the given message.
+ * @return {!Array.<number>} the digest of the given message.
*/
goog.crypt.Hmac.prototype.getHmac = function(message) {
this.reset();
this.update(message);
return this.digest();
};
-
diff --git a/third_party/closure/goog/crypt/pbkdf2.js b/third_party/closure/goog/crypt/pbkdf2.js
new file mode 100644
index 0000000..b1b8629
--- /dev/null
+++ b/third_party/closure/goog/crypt/pbkdf2.js
@@ -0,0 +1,127 @@
+// Copyright 2012 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Implementation of PBKDF2 in JavaScript.
+ * @see http://en.wikipedia.org/wiki/PBKDF2
+ *
+ * Currently we only support HMAC-SHA1 as the underlying hash function. To add a
+ * new hash function, add a static method similar to deriveKeyFromPasswordSha1()
+ * and implement the specific computeBlockCallback() using the hash function.
+ *
+ * Usage:
+ * var key = pbkdf2.deriveKeySha1(
+ * stringToByteArray('password'), stringToByteArray('salt'), 1000, 128);
+ *
+ */
+
+goog.provide('goog.crypt.pbkdf2');
+
+goog.require('goog.asserts');
+goog.require('goog.crypt');
+goog.require('goog.crypt.Hmac');
+goog.require('goog.crypt.Sha1');
+
+
+/**
+ * Derives key from password using PBKDF2-SHA1
+ * @param {!Array.<number>} password Byte array representation of the password
+ * from which the key is derived.
+ * @param {!Array.<number>} initialSalt Byte array representation of the salt.
+ * @param {number} iterations Number of interations when computing the key.
+ * @param {number} keyLength Length of the output key in bits.
+ * Must be multiple of 8.
+ * @return {!Array.<number>} Byte array representation of the output key.
+ */
+goog.crypt.pbkdf2.deriveKeySha1 = function(
+ password, initialSalt, iterations, keyLength) {
+ // Length of the HMAC-SHA1 output in bits.
+ var HASH_LENGTH = 160;
+
+ /**
+ * Compute each block of the key using HMAC-SHA1.
+ * @param {!Array.<number>} index Byte array representation of the index of
+ * the block to be computed.
+ * @return {!Array.<number>} Byte array representation of the output block.
+ */
+ var computeBlock = function(index) {
+ // Initialize the result to be array of 0 such that its xor with the first
+ // block would be the first block.
+ var result = goog.array.repeat(0, HASH_LENGTH / 8);
+ // Initialize the salt of the first iteration to initialSalt || i.
+ var salt = initialSalt.concat(index);
+ var hmac = new goog.crypt.Hmac(new goog.crypt.Sha1(), password, 64);
+ // Compute and XOR each iteration.
+ for (var i = 0; i < iterations; i++) {
+ // The salt of the next iteration is the result of the current iteration.
+ salt = hmac.getHmac(salt);
+ result = goog.crypt.xorByteArray(result, salt);
+ }
+ return result;
+ };
+
+ return goog.crypt.pbkdf2.deriveKeyFromPassword_(
+ computeBlock, HASH_LENGTH, keyLength);
+};
+
+
+/**
+ * Compute each block of the key using PBKDF2.
+ * @param {Function} computeBlock Function to compute each block of the output
+ * key.
+ * @param {number} hashLength Length of each block in bits. This is determined
+ * by the specific hash function used. Must be multiple of 8.
+ * @param {number} keyLength Length of the output key in bits.
+ * Must be multiple of 8.
+ * @return {!Array.<number>} Byte array representation of the output key.
+ * @private
+ */
+goog.crypt.pbkdf2.deriveKeyFromPassword_ =
+ function(computeBlock, hashLength, keyLength) {
+ goog.asserts.assert(keyLength % 8 == 0, 'invalid output key length');
+
+ // Compute and concactate each block of the output key.
+ var numBlocks = Math.ceil(keyLength / hashLength);
+ goog.asserts.assert(numBlocks >= 1, 'invalid number of blocks');
+ var result = [];
+ for (var i = 1; i <= numBlocks; i++) {
+ var indexBytes = goog.crypt.pbkdf2.integerToByteArray_(i);
+ result = result.concat(computeBlock(indexBytes));
+ }
+
+ // Trim the last block if needed.
+ var lastBlockSize = keyLength % hashLength;
+ if (lastBlockSize != 0) {
+ var desiredBytes = ((numBlocks - 1) * hashLength + lastBlockSize) / 8;
+ result.splice(desiredBytes, (hashLength - lastBlockSize) / 8);
+ }
+ return result;
+};
+
+
+/**
+ * Converts an integer number to a 32-bit big endian byte array.
+ * @param {number} n Integer number to be converted.
+ * @return {!Array.<number>} Byte Array representation of the 32-bit big endian
+ * encoding of n.
+ * @private
+ */
+goog.crypt.pbkdf2.integerToByteArray_ = function(n) {
+ var result = new Array(4);
+ result[0] = n >> 24 & 0xFF;
+ result[1] = n >> 16 & 0xFF;
+ result[2] = n >> 8 & 0xFF;
+ result[3] = n & 0xFF;
+ return result;
+};
diff --git a/third_party/closure/goog/date/relative.js b/third_party/closure/goog/date/relative.js
index be8b642..27ca2cf 100644
--- a/third_party/closure/goog/date/relative.js
+++ b/third_party/closure/goog/date/relative.js
@@ -208,9 +208,11 @@
* "Yesterday" or "Sept 15".
*
* @param {number} dateMs Date in milliseconds.
+ * @param {!function(!Date):string=} opt_formatter Formatter for the date.
+ * Defaults to form 'MMM dd'.
* @return {string} The formatted date.
*/
-goog.date.relative.formatDay = function(dateMs) {
+goog.date.relative.formatDay = function(dateMs, opt_formatter) {
var message;
var today = new Date(goog.now());
@@ -229,7 +231,8 @@
var MSG_YESTERDAY = goog.getMsg('Yesterday');
message = MSG_YESTERDAY;
} else {
- message = goog.date.relative.formatMonth_(new Date(dateMs));
+ var formatFunction = opt_formatter || goog.date.relative.formatMonth_;
+ message = formatFunction(new Date(dateMs));
}
return message;
};
diff --git a/third_party/closure/goog/db/indexeddb.js b/third_party/closure/goog/db/indexeddb.js
index ed27d16..afc4fe1 100644
--- a/third_party/closure/goog/db/indexeddb.js
+++ b/third_party/closure/goog/db/indexeddb.js
@@ -95,7 +95,7 @@
/**
- * @return {Array} List of object stores in this database.
+ * @return {DOMStringList} List of object stores in this database.
*/
goog.db.IndexedDb.prototype.getObjectStoreNames = function() {
return this.db_.objectStoreNames;
diff --git a/third_party/closure/goog/demos/inputhandler.html b/third_party/closure/goog/demos/inputhandler.html
index 380d0d4..8c0e938 100644
--- a/third_party/closure/goog/demos/inputhandler.html
+++ b/third_party/closure/goog/demos/inputhandler.html
@@ -10,6 +10,8 @@
<title>goog.events.InputHandler</title>
<script src="../base.js"></script>
<script>
+goog.require('goog.debug.DivConsole');
+goog.require('goog.debug.Logger');
goog.require('goog.dom');
goog.require('goog.events');
goog.require('goog.events.InputHandler');
@@ -19,19 +21,38 @@
<body>
<h1>goog.events.InputHandler</h1>
<p><button onclick="addSome('text')">Add Some</button>
-<input type=text id=text><span></span>
-
+<input type="text" id="text"><span></span>
<p><button onclick="addSome('password')">Add Some</button>
-<input type=password id=password><span></span>
+<input type="password" id="password"><span></span>
<p><button onclick="addSome('textarea')">Add Some</button>
-<textarea id=textarea></textarea><span></span>
+<textarea id="textarea"></textarea><span></span>
+
+<br><br>
+<fieldset class="goog-debug-panel">
+ <legend>Event Log</legend>
+ <div id="log" style="height: 400px;"></div>
+</fieldset>
<script>
-
var $ = goog.dom.getElement;
+var logger = goog.debug.Logger.getLogger('demo');
+var logconsole = new goog.debug.DivConsole(goog.dom.getElement('log'));
+
+goog.debug.LogManager.getRoot().setLevel(goog.debug.Logger.Level.FINER);
+logconsole.setCapturing(true);
+
+var DOM_EVENTS = ['keydown', 'keyup', 'keypress', 'change', 'cut', 'paste',
+ 'drop', 'input'];
+var INPUTHANDLER_EVENTS =
+ goog.object.getValues(goog.events.InputHandler.EventType);
+
+
+function logEvent(src, e) {
+ logger.info('[Event from ' + src + ']: ' + e.type);
+}
function addSome(id) {
var el = $(id);
@@ -47,26 +68,21 @@
outputEl.appendChild(document.createTextNode(inputEl.value));
}
-var textIh = new goog.events.InputHandler($('text'));
-var passwordIh = new goog.events.InputHandler($('password'));
-var textareaIh = new goog.events.InputHandler($('textarea'));
-goog.events.listen(textIh, goog.events.InputHandler.EventType.INPUT,
- updateText);
-goog.events.listen(passwordIh, goog.events.InputHandler.EventType.INPUT,
- updateText);
-goog.events.listen(textareaIh, goog.events.InputHandler.EventType.INPUT,
- updateText);
+goog.object.forEach(['text', 'password', 'textarea'], function(id) {
+ var el = $(id);
+ var ih = new goog.events.InputHandler(el);
-goog.events.listen(window, 'unload', function() {
- goog.events.unlisten(textIh, goog.events.InputHandler.EventType.INPUT,
- updateText);
- goog.events.unlisten(passwordIh, goog.events.InputHandler.EventType.INPUT,
- updateText);
- goog.events.unlisten(textareaIh, goog.events.InputHandler.EventType.INPUT,
- updateText);
+ goog.events.listen(ih, goog.events.InputHandler.EventType.INPUT, updateText);
+ goog.events.listen(ih, INPUTHANDLER_EVENTS,
+ goog.partial(logEvent, 'InputHandler'));
+ goog.events.listen(el, DOM_EVENTS,
+ goog.partial(logEvent, 'DOM'));
});
+goog.events.listen(window, 'unload', function() {
+ goog.events.removeAll();
+});
</script>
</body>
</html>
diff --git a/third_party/closure/goog/deps.js b/third_party/closure/goog/deps.js
index 2d835dc..7b0b267 100644
--- a/third_party/closure/goog/deps.js
+++ b/third_party/closure/goog/deps.js
@@ -35,16 +35,21 @@
goog.addDependency('color/alpha.js', ['goog.color.alpha'], ['goog.color']);
goog.addDependency('color/color.js', ['goog.color'], ['goog.color.names', 'goog.math']);
goog.addDependency('color/names.js', ['goog.color.names'], []);
+goog.addDependency('crypt/aes.js', ['goog.crypt.Aes'], ['goog.asserts', 'goog.crypt.BlockCipher']);
goog.addDependency('crypt/arc4.js', ['goog.crypt.Arc4'], ['goog.asserts']);
goog.addDependency('crypt/base64.js', ['goog.crypt.base64'], ['goog.crypt', 'goog.userAgent']);
goog.addDependency('crypt/basen.js', ['goog.crypt.baseN'], []);
goog.addDependency('crypt/blobhasher.js', ['goog.crypt.BlobHasher', 'goog.crypt.BlobHasher.EventType'], ['goog.asserts', 'goog.crypt', 'goog.crypt.Hash', 'goog.debug.Logger', 'goog.events.EventTarget', 'goog.fs']);
+goog.addDependency('crypt/blockcipher.js', ['goog.crypt.BlockCipher'], []);
+goog.addDependency('crypt/cbc.js', ['goog.crypt.Cbc'], ['goog.array', 'goog.crypt']);
+goog.addDependency('crypt/cbc_test.js', ['goog.crypt.CbcTest'], ['goog.crypt', 'goog.crypt.Aes', 'goog.crypt.Cbc', 'goog.testing.jsunit']);
goog.addDependency('crypt/crypt.js', ['goog.crypt'], ['goog.array']);
goog.addDependency('crypt/hash.js', ['goog.crypt.Hash'], []);
goog.addDependency('crypt/hash32.js', ['goog.crypt.hash32'], ['goog.crypt']);
-goog.addDependency('crypt/hash_test.js', ['goog.crypt.hash_test'], ['goog.testing.asserts']);
+goog.addDependency('crypt/hash_test.js', ['goog.crypt.hash_test'], ['goog.array', 'goog.testing.asserts']);
goog.addDependency('crypt/hmac.js', ['goog.crypt.Hmac'], ['goog.asserts', 'goog.crypt.Hash']);
goog.addDependency('crypt/md5.js', ['goog.crypt.Md5'], ['goog.crypt.Hash']);
+goog.addDependency('crypt/pbkdf2.js', ['goog.crypt.pbkdf2'], ['goog.asserts', 'goog.crypt', 'goog.crypt.Hmac', 'goog.crypt.Sha1']);
goog.addDependency('crypt/sha1.js', ['goog.crypt.Sha1'], ['goog.crypt.Hash']);
goog.addDependency('cssom/cssom.js', ['goog.cssom', 'goog.cssom.CssRuleType'], ['goog.array', 'goog.dom']);
goog.addDependency('cssom/iframe/style.js', ['goog.cssom.iframe.style'], ['goog.cssom', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.classes', 'goog.string', 'goog.style', 'goog.userAgent']);
@@ -105,6 +110,8 @@
goog.addDependency('dom/browserrange/operarange.js', ['goog.dom.browserrange.OperaRange'], ['goog.dom.browserrange.W3cRange']);
goog.addDependency('dom/browserrange/w3crange.js', ['goog.dom.browserrange.W3cRange'], ['goog.dom', 'goog.dom.NodeType', 'goog.dom.RangeEndpoint', 'goog.dom.browserrange.AbstractRange', 'goog.string']);
goog.addDependency('dom/browserrange/webkitrange.js', ['goog.dom.browserrange.WebKitRange'], ['goog.dom.RangeEndpoint', 'goog.dom.browserrange.W3cRange', 'goog.userAgent']);
+goog.addDependency('dom/bufferedviewportsizemonitor.js', ['goog.dom.BufferedViewportSizeMonitor'], ['goog.asserts', 'goog.async.Delay', 'goog.events', 'goog.events.EventTarget', 'goog.events.EventType']);
+goog.addDependency('dom/bufferedviewportsizemonitor_test.js', ['goog.dom.BufferedViewportSizeMonitorTest'], ['goog.dom.BufferedViewportSizeMonitor', 'goog.dom.ViewportSizeMonitor', 'goog.events', 'goog.events.EventType', 'goog.math.Size', 'goog.testing.MockClock', 'goog.testing.events', 'goog.testing.events.Event', 'goog.testing.jsunit']);
goog.addDependency('dom/classes.js', ['goog.dom.classes'], ['goog.array']);
goog.addDependency('dom/classes_test.js', ['goog.dom.classes_test'], ['goog.dom', 'goog.dom.classes', 'goog.testing.jsunit']);
goog.addDependency('dom/controlrange.js', ['goog.dom.ControlRange', 'goog.dom.ControlRangeIterator'], ['goog.array', 'goog.dom', 'goog.dom.AbstractMultiRange', 'goog.dom.AbstractRange', 'goog.dom.RangeIterator', 'goog.dom.RangeType', 'goog.dom.SavedRange', 'goog.dom.TagWalkType', 'goog.dom.TextRange', 'goog.iter.StopIteration', 'goog.userAgent']);
@@ -151,14 +158,14 @@
goog.addDependency('editor/command.js', ['goog.editor.Command'], []);
goog.addDependency('editor/contenteditablefield.js', ['goog.editor.ContentEditableField'], ['goog.asserts', 'goog.debug.Logger', 'goog.editor.Field']);
goog.addDependency('editor/defines.js', ['goog.editor.defines'], []);
-goog.addDependency('editor/field.js', ['goog.editor.Field', 'goog.editor.Field.EventType'], ['goog.array', 'goog.async.Delay', 'goog.debug.Logger', 'goog.dom', 'goog.dom.Range', 'goog.dom.TagName', 'goog.dom.classes', 'goog.editor.BrowserFeature', 'goog.editor.Command', 'goog.editor.Plugin', 'goog.editor.icontent', 'goog.editor.icontent.FieldFormatInfo', 'goog.editor.icontent.FieldStyleInfo', 'goog.editor.node', 'goog.editor.range', 'goog.events', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.functions', 'goog.string', 'goog.string.Unicode', 'goog.style', 'goog.userAgent', 'goog.userAgent.product']);
+goog.addDependency('editor/field.js', ['goog.editor.Field', 'goog.editor.Field.EventType'], ['goog.array', 'goog.asserts', 'goog.async.Delay', 'goog.debug.Logger', 'goog.dom', 'goog.dom.Range', 'goog.dom.TagName', 'goog.editor.BrowserFeature', 'goog.editor.Command', 'goog.editor.Plugin', 'goog.editor.icontent', 'goog.editor.icontent.FieldFormatInfo', 'goog.editor.icontent.FieldStyleInfo', 'goog.editor.node', 'goog.editor.range', 'goog.events', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.functions', 'goog.string', 'goog.string.Unicode', 'goog.style', 'goog.userAgent', 'goog.userAgent.product']);
goog.addDependency('editor/field_test.js', ['goog.editor.field_test'], ['goog.dom.Range', 'goog.editor.Command', 'goog.editor.Field', 'goog.editor.Plugin', 'goog.events', 'goog.events.KeyCodes', 'goog.functions', 'goog.testing.LooseMock', 'goog.testing.MockClock', 'goog.testing.dom', 'goog.testing.events', 'goog.testing.recordFunction', 'goog.userAgent', 'goog.userAgent.product']);
goog.addDependency('editor/focus.js', ['goog.editor.focus'], ['goog.dom.selection']);
goog.addDependency('editor/icontent.js', ['goog.editor.icontent', 'goog.editor.icontent.FieldFormatInfo', 'goog.editor.icontent.FieldStyleInfo'], ['goog.editor.BrowserFeature', 'goog.style', 'goog.userAgent']);
goog.addDependency('editor/link.js', ['goog.editor.Link'], ['goog.array', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.Range', 'goog.editor.BrowserFeature', 'goog.editor.Command', 'goog.editor.node', 'goog.editor.range', 'goog.string', 'goog.string.Unicode', 'goog.uri.utils', 'goog.uri.utils.ComponentIndex']);
goog.addDependency('editor/node.js', ['goog.editor.node'], ['goog.dom', 'goog.dom.NodeType', 'goog.dom.TagName', 'goog.dom.iter.ChildIterator', 'goog.dom.iter.SiblingIterator', 'goog.iter', 'goog.object', 'goog.string', 'goog.string.Unicode']);
goog.addDependency('editor/plugin.js', ['goog.editor.Plugin'], ['goog.debug.Logger', 'goog.editor.Command', 'goog.events.EventTarget', 'goog.functions', 'goog.object', 'goog.reflect']);
-goog.addDependency('editor/plugins/abstractbubbleplugin.js', ['goog.editor.plugins.AbstractBubblePlugin'], ['goog.dom', 'goog.dom.NodeType', 'goog.dom.Range', 'goog.dom.TagName', 'goog.editor.Plugin', 'goog.editor.style', 'goog.events', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.functions', 'goog.string.Unicode', 'goog.ui.Component.EventType', 'goog.ui.editor.Bubble', 'goog.userAgent']);
+goog.addDependency('editor/plugins/abstractbubbleplugin.js', ['goog.editor.plugins.AbstractBubblePlugin'], ['goog.dom', 'goog.dom.NodeType', 'goog.dom.Range', 'goog.dom.TagName', 'goog.editor.Plugin', 'goog.editor.style', 'goog.events', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.events.actionEventWrapper', 'goog.functions', 'goog.string.Unicode', 'goog.ui.Component.EventType', 'goog.ui.editor.Bubble', 'goog.userAgent']);
goog.addDependency('editor/plugins/abstractdialogplugin.js', ['goog.editor.plugins.AbstractDialogPlugin', 'goog.editor.plugins.AbstractDialogPlugin.EventType'], ['goog.dom', 'goog.dom.Range', 'goog.editor.Field.EventType', 'goog.editor.Plugin', 'goog.editor.range', 'goog.events', 'goog.ui.editor.AbstractDialog.EventType']);
goog.addDependency('editor/plugins/abstracttabhandler.js', ['goog.editor.plugins.AbstractTabHandler'], ['goog.editor.Plugin', 'goog.events.KeyCodes']);
goog.addDependency('editor/plugins/basictextformatter.js', ['goog.editor.plugins.BasicTextFormatter', 'goog.editor.plugins.BasicTextFormatter.COMMAND'], ['goog.array', 'goog.debug.Logger', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.Range', 'goog.dom.TagName', 'goog.editor.BrowserFeature', 'goog.editor.Command', 'goog.editor.Link', 'goog.editor.Plugin', 'goog.editor.node', 'goog.editor.range', 'goog.editor.style', 'goog.iter', 'goog.iter.StopIteration', 'goog.object', 'goog.string', 'goog.string.Unicode', 'goog.style', 'goog.ui.editor.messages', 'goog.userAgent']);
@@ -193,6 +200,7 @@
goog.addDependency('events/eventhandler.js', ['goog.events.EventHandler'], ['goog.Disposable', 'goog.array', 'goog.events', 'goog.events.EventWrapper']);
goog.addDependency('events/events.js', ['goog.events'], ['goog.array', 'goog.debug.entryPointRegistry', 'goog.debug.errorHandlerWeakDep', 'goog.events.BrowserEvent', 'goog.events.BrowserFeature', 'goog.events.Event', 'goog.events.EventWrapper', 'goog.events.Listener', 'goog.object', 'goog.userAgent']);
goog.addDependency('events/eventtarget.js', ['goog.events.EventTarget'], ['goog.Disposable', 'goog.events']);
+goog.addDependency('events/eventtargettester.js', ['goog.events.eventTargetTester', 'goog.events.eventTargetTester.KeyType', 'goog.events.eventTargetTester.UnlistenReturnType'], ['goog.events.Event', 'goog.events.EventTarget', 'goog.testing.asserts', 'goog.testing.recordFunction']);
goog.addDependency('events/eventtype.js', ['goog.events.EventType'], ['goog.userAgent']);
goog.addDependency('events/eventwrapper.js', ['goog.events.EventWrapper'], []);
goog.addDependency('events/filedrophandler.js', ['goog.events.FileDropHandler', 'goog.events.FileDropHandler.EventType'], ['goog.array', 'goog.debug.Logger', 'goog.dom', 'goog.events', 'goog.events.BrowserEvent', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.events.EventType']);
@@ -216,7 +224,7 @@
goog.addDependency('fs/filesaver.js', ['goog.fs.FileSaver', 'goog.fs.FileSaver.EventType', 'goog.fs.FileSaver.ProgressEvent', 'goog.fs.FileSaver.ReadyState'], ['goog.events.Event', 'goog.events.EventTarget', 'goog.fs.Error', 'goog.fs.ProgressEvent']);
goog.addDependency('fs/filesystem.js', ['goog.fs.FileSystem'], ['goog.fs.DirectoryEntry']);
goog.addDependency('fs/filewriter.js', ['goog.fs.FileWriter'], ['goog.fs.Error', 'goog.fs.FileSaver']);
-goog.addDependency('fs/fs.js', ['goog.fs'], ['goog.async.Deferred', 'goog.events', 'goog.fs.Error', 'goog.fs.FileReader', 'goog.fs.FileSystem', 'goog.userAgent']);
+goog.addDependency('fs/fs.js', ['goog.fs'], ['goog.array', 'goog.async.Deferred', 'goog.events', 'goog.fs.Error', 'goog.fs.FileReader', 'goog.fs.FileSystem', 'goog.userAgent']);
goog.addDependency('fs/progressevent.js', ['goog.fs.ProgressEvent'], ['goog.events.Event']);
goog.addDependency('functions/functions.js', ['goog.functions'], []);
goog.addDependency('fx/abstractdragdrop.js', ['goog.fx.AbstractDragDrop', 'goog.fx.AbstractDragDrop.EventType', 'goog.fx.DragDropEvent', 'goog.fx.DragDropItem'], ['goog.dom', 'goog.dom.classes', 'goog.events', 'goog.events.Event', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.fx.Dragger', 'goog.fx.Dragger.EventType', 'goog.math.Box', 'goog.math.Coordinate', 'goog.style']);
@@ -319,9 +327,10 @@
goog.addDependency('json/json.js', ['goog.json', 'goog.json.Serializer'], []);
goog.addDependency('json/nativejsonprocessor.js', ['goog.json.NativeJsonProcessor'], ['goog.asserts', 'goog.json', 'goog.json.Processor']);
goog.addDependency('json/processor.js', ['goog.json.Processor'], ['goog.string.Parser', 'goog.string.Stringifier']);
-goog.addDependency('labs/mock/mock.js', ['goog.labs.mock'], ['goog.array']);
+goog.addDependency('labs/classdef/classdef.js', ['goog.labs.classdef'], []);
+goog.addDependency('labs/mock/mock.js', ['goog.labs.mock'], ['goog.array', 'goog.debug.Error', 'goog.functions', 'goog.testing.recordFunction']);
goog.addDependency('labs/net/image.js', ['goog.labs.net.image'], ['goog.events.EventHandler', 'goog.events.EventType', 'goog.net.EventType', 'goog.result.SimpleResult', 'goog.userAgent']);
-goog.addDependency('labs/net/image_test.js', ['goog.labs.net.imageTest'], ['goog.events', 'goog.labs.net.image', 'goog.net.EventType', 'goog.result', 'goog.result.Result', 'goog.string', 'goog.testing.AsyncTestCase', 'goog.testing.jsunit', 'goog.testing.recordFunction']);
+goog.addDependency('labs/net/image_test.js', ['goog.labs.net.imageTest'], ['goog.events', 'goog.labs.net.image', 'goog.result', 'goog.result.Result', 'goog.string', 'goog.testing.AsyncTestCase', 'goog.testing.jsunit', 'goog.testing.recordFunction']);
goog.addDependency('labs/net/xhr.js', ['goog.labs.net.xhr', 'goog.labs.net.xhr.Error', 'goog.labs.net.xhr.HttpError', 'goog.labs.net.xhr.TimeoutError'], ['goog.debug.Error', 'goog.json', 'goog.net.HttpStatus', 'goog.net.XmlHttp', 'goog.result', 'goog.string', 'goog.uri.utils']);
goog.addDependency('labs/object/object.js', ['goog.labs.object'], []);
goog.addDependency('labs/observe/notice.js', ['goog.labs.observe.Notice'], []);
@@ -340,7 +349,7 @@
goog.addDependency('labs/testing/matcher.js', ['goog.labs.testing.Matcher'], []);
goog.addDependency('labs/testing/numbermatcher.js', ['goog.labs.testing.CloseToMatcher', 'goog.labs.testing.EqualToMatcher', 'goog.labs.testing.GreaterThanEqualToMatcher', 'goog.labs.testing.GreaterThanMatcher', 'goog.labs.testing.LessThanEqualToMatcher', 'goog.labs.testing.LessThanMatcher'], ['goog.asserts', 'goog.labs.testing.Matcher']);
goog.addDependency('labs/testing/objectmatcher.js', ['goog.labs.testing.HasPropertyMatcher', 'goog.labs.testing.InstanceOfMatcher', 'goog.labs.testing.IsNullMatcher', 'goog.labs.testing.IsNullOrUndefinedMatcher', 'goog.labs.testing.IsUndefinedMatcher', 'goog.labs.testing.ObjectEqualsMatcher'], ['goog.labs.testing.Matcher', 'goog.string']);
-goog.addDependency('labs/testing/stringmatcher.js', ['goog.labs.testing.ContainsStringMatcher', 'goog.labs.testing.EndsWithMatcher', 'goog.labs.testing.EqualToIgnoringCaseMatcher', 'goog.labs.testing.EqualToIgnoringWhitespaceMatcher', 'goog.labs.testing.EqualsMatcher', 'goog.labs.testing.StartsWithMatcher', 'goog.labs.testing.StringContainsInOrderMatcher'], ['goog.asserts', 'goog.labs.testing.Matcher', 'goog.string']);
+goog.addDependency('labs/testing/stringmatcher.js', ['goog.labs.testing.ContainsStringMatcher', 'goog.labs.testing.EndsWithMatcher', 'goog.labs.testing.EqualToIgnoringCaseMatcher', 'goog.labs.testing.EqualToIgnoringWhitespaceMatcher', 'goog.labs.testing.EqualsMatcher', 'goog.labs.testing.RegexMatcher', 'goog.labs.testing.StartsWithMatcher', 'goog.labs.testing.StringContainsInOrderMatcher'], ['goog.asserts', 'goog.labs.testing.Matcher', 'goog.string']);
goog.addDependency('locale/countries.js', ['goog.locale.countries'], []);
goog.addDependency('locale/defaultlocalenameconstants.js', ['goog.locale.defaultLocaleNameConstants'], []);
goog.addDependency('locale/genericfontnames.js', ['goog.locale.genericFontNames'], []);
@@ -396,7 +405,7 @@
goog.addDependency('module/moduleinfo.js', ['goog.module.ModuleInfo'], ['goog.Disposable', 'goog.functions', 'goog.module.BaseModule', 'goog.module.ModuleLoadCallback']);
goog.addDependency('module/moduleloadcallback.js', ['goog.module.ModuleLoadCallback'], ['goog.debug.entryPointRegistry', 'goog.debug.errorHandlerWeakDep']);
goog.addDependency('module/moduleloader.js', ['goog.module.ModuleLoader'], ['goog.Timer', 'goog.array', 'goog.debug.Logger', 'goog.events', 'goog.events.Event', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.module.AbstractModuleLoader', 'goog.net.BulkLoader', 'goog.net.EventType', 'goog.net.jsloader']);
-goog.addDependency('module/modulemanager.js', ['goog.module.ModuleManager', 'goog.module.ModuleManager.CallbackType', 'goog.module.ModuleManager.FailureType'], ['goog.Disposable', 'goog.array', 'goog.asserts', 'goog.async.Deferred', 'goog.debug.Logger', 'goog.debug.Trace', 'goog.module.ModuleInfo', 'goog.module.ModuleLoadCallback', 'goog.object']);
+goog.addDependency('module/modulemanager.js', ['goog.module.ModuleManager', 'goog.module.ModuleManager.CallbackType', 'goog.module.ModuleManager.FailureType'], ['goog.Disposable', 'goog.array', 'goog.asserts', 'goog.async.Deferred', 'goog.debug.Logger', 'goog.debug.Trace', 'goog.dispose', 'goog.module.ModuleInfo', 'goog.module.ModuleLoadCallback', 'goog.object']);
goog.addDependency('module/testdata/modA_1.js', ['goog.module.testdata.modA_1'], []);
goog.addDependency('module/testdata/modA_2.js', ['goog.module.testdata.modA_2'], ['goog.module.ModuleManager']);
goog.addDependency('module/testdata/modB_1.js', ['goog.module.testdata.modB_1'], ['goog.module.ModuleManager']);
@@ -405,14 +414,14 @@
goog.addDependency('net/bulkloader.js', ['goog.net.BulkLoader'], ['goog.debug.Logger', 'goog.events.Event', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.net.BulkLoaderHelper', 'goog.net.EventType', 'goog.net.XhrIo']);
goog.addDependency('net/bulkloaderhelper.js', ['goog.net.BulkLoaderHelper'], ['goog.Disposable', 'goog.debug.Logger']);
goog.addDependency('net/channeldebug.js', ['goog.net.ChannelDebug'], ['goog.debug.Logger', 'goog.json']);
-goog.addDependency('net/channelrequest.js', ['goog.net.ChannelRequest', 'goog.net.ChannelRequest.Error'], ['goog.Timer', 'goog.events', 'goog.events.EventHandler', 'goog.net.EventType', 'goog.net.XmlHttp.ReadyState', 'goog.object', 'goog.userAgent']);
-goog.addDependency('net/cookies.js', ['goog.net.Cookies', 'goog.net.cookies'], ['goog.userAgent']);
+goog.addDependency('net/channelrequest.js', ['goog.net.ChannelRequest', 'goog.net.ChannelRequest.Error'], ['goog.Timer', 'goog.async.Throttle', 'goog.events', 'goog.events.EventHandler', 'goog.net.EventType', 'goog.net.XmlHttp.ReadyState', 'goog.object', 'goog.userAgent']);
+goog.addDependency('net/cookies.js', ['goog.net.Cookies', 'goog.net.cookies'], []);
goog.addDependency('net/crossdomainrpc.js', ['goog.net.CrossDomainRpc'], ['goog.Uri.QueryData', 'goog.debug.Logger', 'goog.dom', 'goog.events', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.json', 'goog.net.EventType', 'goog.net.HttpStatus', 'goog.userAgent']);
goog.addDependency('net/errorcode.js', ['goog.net.ErrorCode'], []);
goog.addDependency('net/eventtype.js', ['goog.net.EventType'], []);
goog.addDependency('net/filedownloader.js', ['goog.net.FileDownloader', 'goog.net.FileDownloader.Error'], ['goog.Disposable', 'goog.asserts', 'goog.async.Deferred', 'goog.crypt.hash32', 'goog.debug.Error', 'goog.events.EventHandler', 'goog.fs', 'goog.fs.DirectoryEntry.Behavior', 'goog.fs.Error.ErrorCode', 'goog.fs.FileSaver.EventType', 'goog.net.EventType', 'goog.net.XhrIo.ResponseType', 'goog.net.XhrIoPool']);
goog.addDependency('net/httpstatus.js', ['goog.net.HttpStatus'], []);
-goog.addDependency('net/iframeio.js', ['goog.net.IframeIo', 'goog.net.IframeIo.IncrementalDataEvent'], ['goog.Timer', 'goog.Uri', 'goog.debug', 'goog.debug.Logger', 'goog.dom', 'goog.events', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.json', 'goog.net.ErrorCode', 'goog.net.EventType', 'goog.reflect', 'goog.string', 'goog.structs', 'goog.userAgent']);
+goog.addDependency('net/iframeio.js', ['goog.net.IframeIo', 'goog.net.IframeIo.IncrementalDataEvent'], ['goog.Timer', 'goog.Uri', 'goog.debug', 'goog.debug.Logger', 'goog.dom', 'goog.events', 'goog.events.Event', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.json', 'goog.net.ErrorCode', 'goog.net.EventType', 'goog.reflect', 'goog.string', 'goog.structs', 'goog.userAgent']);
goog.addDependency('net/iframeloadmonitor.js', ['goog.net.IframeLoadMonitor'], ['goog.dom', 'goog.events', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.userAgent']);
goog.addDependency('net/imageloader.js', ['goog.net.ImageLoader'], ['goog.array', 'goog.dom', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.net.EventType', 'goog.object', 'goog.userAgent']);
goog.addDependency('net/ipaddress.js', ['goog.net.IpAddress', 'goog.net.Ipv4Address', 'goog.net.Ipv6Address'], ['goog.array', 'goog.math.Integer', 'goog.object', 'goog.string']);
@@ -583,6 +592,7 @@
goog.addDependency('testing/performancetable.js', ['goog.testing.PerformanceTable'], ['goog.dom', 'goog.testing.PerformanceTimer']);
goog.addDependency('testing/performancetimer.js', ['goog.testing.PerformanceTimer', 'goog.testing.PerformanceTimer.Task'], ['goog.array', 'goog.math']);
goog.addDependency('testing/propertyreplacer.js', ['goog.testing.PropertyReplacer'], ['goog.userAgent']);
+goog.addDependency('testing/proto2/proto2.js', ['goog.testing.proto2'], ['goog.proto2.Message', 'goog.testing.asserts']);
goog.addDependency('testing/pseudorandom.js', ['goog.testing.PseudoRandom'], ['goog.Disposable']);
goog.addDependency('testing/recordfunction.js', ['goog.testing.FunctionCall', 'goog.testing.recordConstructor', 'goog.testing.recordFunction'], []);
goog.addDependency('testing/shardingtestcase.js', ['goog.testing.ShardingTestCase'], ['goog.asserts', 'goog.testing.TestCase']);
@@ -639,7 +649,7 @@
goog.addDependency('ui/colorsplitbehavior.js', ['goog.ui.ColorSplitBehavior'], ['goog.ui.ColorButton', 'goog.ui.ColorMenuButton', 'goog.ui.SplitBehavior']);
goog.addDependency('ui/combobox.js', ['goog.ui.ComboBox', 'goog.ui.ComboBoxItem'], ['goog.Timer', 'goog.debug.Logger', 'goog.dom.classes', 'goog.events', 'goog.events.InputHandler', 'goog.events.KeyCodes', 'goog.events.KeyHandler', 'goog.positioning.Corner', 'goog.positioning.MenuAnchoredPosition', 'goog.string', 'goog.style', 'goog.ui.Component', 'goog.ui.ItemEvent', 'goog.ui.LabelInput', 'goog.ui.Menu', 'goog.ui.MenuItem', 'goog.ui.registry', 'goog.userAgent']);
goog.addDependency('ui/component.js', ['goog.ui.Component', 'goog.ui.Component.Error', 'goog.ui.Component.EventType', 'goog.ui.Component.State'], ['goog.array', 'goog.array.ArrayLike', 'goog.dom', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.object', 'goog.style', 'goog.ui.IdGenerator']);
-goog.addDependency('ui/container.js', ['goog.ui.Container', 'goog.ui.Container.EventType', 'goog.ui.Container.Orientation'], ['goog.dom', 'goog.dom.a11y', 'goog.dom.a11y.State', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.events.KeyHandler', 'goog.events.KeyHandler.EventType', 'goog.style', 'goog.ui.Component', 'goog.ui.Component.Error', 'goog.ui.Component.EventType', 'goog.ui.Component.State', 'goog.ui.ContainerRenderer']);
+goog.addDependency('ui/container.js', ['goog.ui.Container', 'goog.ui.Container.EventType', 'goog.ui.Container.Orientation'], ['goog.dom', 'goog.dom.a11y', 'goog.dom.a11y.State', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.events.KeyHandler', 'goog.events.KeyHandler.EventType', 'goog.style', 'goog.ui.Component', 'goog.ui.Component.Error', 'goog.ui.Component.EventType', 'goog.ui.Component.State', 'goog.ui.ContainerRenderer', 'goog.ui.Control']);
goog.addDependency('ui/containerrenderer.js', ['goog.ui.ContainerRenderer'], ['goog.array', 'goog.dom', 'goog.dom.a11y', 'goog.dom.classes', 'goog.string', 'goog.style', 'goog.ui.Separator', 'goog.ui.registry', 'goog.userAgent']);
goog.addDependency('ui/containerscroller.js', ['goog.ui.ContainerScroller'], ['goog.Timer', 'goog.events.EventHandler', 'goog.style', 'goog.ui.Component', 'goog.ui.Component.EventType', 'goog.ui.Container.EventType']);
goog.addDependency('ui/control.js', ['goog.ui.Control'], ['goog.array', 'goog.dom', 'goog.events.BrowserEvent.MouseButton', 'goog.events.Event', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.events.KeyHandler', 'goog.events.KeyHandler.EventType', 'goog.string', 'goog.ui.Component', 'goog.ui.Component.Error', 'goog.ui.Component.EventType', 'goog.ui.Component.State', 'goog.ui.ControlContent', 'goog.ui.ControlRenderer', 'goog.ui.decorate', 'goog.ui.registry', 'goog.userAgent']);
@@ -743,7 +753,7 @@
goog.addDependency('ui/offlinestatuscard.js', ['goog.ui.OfflineStatusCard', 'goog.ui.OfflineStatusCard.EventType'], ['goog.dom', 'goog.events.EventType', 'goog.gears.StatusType', 'goog.structs.Map', 'goog.style', 'goog.ui.Component', 'goog.ui.Component.EventType', 'goog.ui.ProgressBar']);
goog.addDependency('ui/offlinestatuscomponent.js', ['goog.ui.OfflineStatusComponent', 'goog.ui.OfflineStatusComponent.StatusClassNames'], ['goog.dom.classes', 'goog.events.EventType', 'goog.gears.StatusType', 'goog.positioning', 'goog.positioning.AnchoredPosition', 'goog.positioning.Corner', 'goog.positioning.Overflow', 'goog.ui.Component', 'goog.ui.OfflineStatusCard.EventType', 'goog.ui.Popup']);
goog.addDependency('ui/option.js', ['goog.ui.Option'], ['goog.ui.Component.EventType', 'goog.ui.ControlContent', 'goog.ui.MenuItem', 'goog.ui.registry']);
-goog.addDependency('ui/palette.js', ['goog.ui.Palette'], ['goog.array', 'goog.dom', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.math.Size', 'goog.ui.Component.Error', 'goog.ui.Component.EventType', 'goog.ui.Control', 'goog.ui.PaletteRenderer', 'goog.ui.SelectionModel']);
+goog.addDependency('ui/palette.js', ['goog.ui.Palette'], ['goog.array', 'goog.dom', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.math.Size', 'goog.ui.Component', 'goog.ui.Control', 'goog.ui.PaletteRenderer', 'goog.ui.SelectionModel']);
goog.addDependency('ui/paletterenderer.js', ['goog.ui.PaletteRenderer'], ['goog.array', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.a11y', 'goog.dom.classes', 'goog.style', 'goog.ui.ControlRenderer', 'goog.userAgent']);
goog.addDependency('ui/plaintextspellchecker.js', ['goog.ui.PlainTextSpellChecker'], ['goog.Timer', 'goog.dom', 'goog.dom.a11y', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.events.KeyHandler', 'goog.events.KeyHandler.EventType', 'goog.style', 'goog.ui.AbstractSpellChecker', 'goog.ui.AbstractSpellChecker.AsyncResult', 'goog.ui.Component.EventType', 'goog.userAgent']);
goog.addDependency('ui/popup.js', ['goog.ui.Popup', 'goog.ui.Popup.AbsolutePosition', 'goog.ui.Popup.AnchoredPosition', 'goog.ui.Popup.AnchoredViewPortPosition', 'goog.ui.Popup.ClientPosition', 'goog.ui.Popup.Corner', 'goog.ui.Popup.Overflow', 'goog.ui.Popup.ViewPortClientPosition', 'goog.ui.Popup.ViewPortPosition'], ['goog.math.Box', 'goog.positioning', 'goog.positioning.AbsolutePosition', 'goog.positioning.AnchoredPosition', 'goog.positioning.AnchoredViewportPosition', 'goog.positioning.ClientPosition', 'goog.positioning.Corner', 'goog.positioning.Overflow', 'goog.positioning.OverflowStatus', 'goog.positioning.ViewportClientPosition', 'goog.positioning.ViewportPosition', 'goog.style', 'goog.ui.PopupBase']);
diff --git a/third_party/closure/goog/disposable/disposable.js b/third_party/closure/goog/disposable/disposable.js
index 3f99e16..8e10083 100644
--- a/third_party/closure/goog/disposable/disposable.js
+++ b/third_party/closure/goog/disposable/disposable.js
@@ -203,8 +203,9 @@
/**
* Invokes a callback function when this object is disposed. Callbacks are
* invoked in the order in which they were added.
- * @param {!Function} callback The callback function.
- * @param {Object=} opt_scope An optional scope to call the callback in.
+ * @param {function(this:T):?} callback The callback function.
+ * @param {T=} opt_scope An optional scope to call the callback in.
+ * @template T
*/
goog.Disposable.prototype.addOnDisposeCallback = function(callback, opt_scope) {
if (!this.onDisposeCallbacks_) {
diff --git a/third_party/closure/goog/dom/bufferedviewportsizemonitor.js b/third_party/closure/goog/dom/bufferedviewportsizemonitor.js
new file mode 100644
index 0000000..a4e32a5
--- /dev/null
+++ b/third_party/closure/goog/dom/bufferedviewportsizemonitor.js
@@ -0,0 +1,202 @@
+// Copyright 2012 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview A viewport size monitor that buffers RESIZE events until the
+ * window size has stopped changing, within a specified period of time. For
+ * every RESIZE event dispatched, this will dispatch up to two *additional*
+ * events:
+ * - {@link #EventType.RESIZE_WIDTH} if the viewport's width has changed since
+ * the last buffered dispatch.
+ * - {@link #EventType.RESIZE_HEIGHT} if the viewport's height has changed since
+ * the last buffered dispatch.
+ * You likely only need to listen to one of the three events. But if you need
+ * more, just be cautious of duplicating effort.
+ *
+ */
+
+goog.provide('goog.dom.BufferedViewportSizeMonitor');
+
+goog.require('goog.asserts');
+goog.require('goog.async.Delay');
+goog.require('goog.events');
+goog.require('goog.events.EventTarget');
+goog.require('goog.events.EventType');
+
+
+
+/**
+ * Creates a new BufferedViewportSizeMonitor.
+ * @param {!goog.dom.ViewportSizeMonitor} viewportSizeMonitor The
+ * underlying viewport size monitor.
+ * @param {number=} opt_bufferMs The buffer time, in ms. If not specified, this
+ * value defaults to {@link #RESIZE_EVENT_DELAY_MS_}.
+ * @constructor
+ * @extends {goog.events.EventTarget}
+ */
+goog.dom.BufferedViewportSizeMonitor = function(
+ viewportSizeMonitor, opt_bufferMs) {
+ goog.base(this);
+
+ /**
+ * The underlying viewport size monitor.
+ * @type {goog.dom.ViewportSizeMonitor}
+ * @private
+ */
+ this.viewportSizeMonitor_ = viewportSizeMonitor;
+
+ /**
+ * The current size of the viewport.
+ * @type {goog.math.Size}
+ * @private
+ */
+ this.currentSize_ = this.viewportSizeMonitor_.getSize();
+
+ /**
+ * The resize buffer time in ms.
+ * @type {number}
+ * @private
+ */
+ this.resizeBufferMs_ = opt_bufferMs ||
+ goog.dom.BufferedViewportSizeMonitor.RESIZE_EVENT_DELAY_MS_;
+
+ /**
+ * Listener key for the viewport size monitor.
+ * @type {number}
+ * @private
+ */
+ this.listenerKey_ = /** @type {number} */ goog.events.listen(
+ viewportSizeMonitor,
+ goog.events.EventType.RESIZE,
+ this.handleResize_,
+ false,
+ this);
+};
+goog.inherits(goog.dom.BufferedViewportSizeMonitor, goog.events.EventTarget);
+
+
+/**
+ * Additional events to dispatch.
+ * @enum {string}
+ */
+goog.dom.BufferedViewportSizeMonitor.EventType = {
+ RESIZE_HEIGHT: goog.events.getUniqueId('resizeheight'),
+ RESIZE_WIDTH: goog.events.getUniqueId('resizewidth')
+};
+
+
+/**
+ * Delay for the resize event.
+ * @type {goog.async.Delay}
+ * @private
+ */
+goog.dom.BufferedViewportSizeMonitor.prototype.resizeDelay_;
+
+
+/**
+ * Default number of milliseconds to wait after a resize event to relayout the
+ * page.
+ * @type {number}
+ * @const
+ * @private
+ */
+goog.dom.BufferedViewportSizeMonitor.RESIZE_EVENT_DELAY_MS_ = 100;
+
+
+/** @override */
+goog.dom.BufferedViewportSizeMonitor.prototype.disposeInternal =
+ function() {
+ goog.events.unlistenByKey(this.listenerKey_);
+ goog.base(this, 'disposeInternal');
+};
+
+
+/**
+ * Handles resize events on the underlying ViewportMonitor.
+ * @private
+ */
+goog.dom.BufferedViewportSizeMonitor.prototype.handleResize_ =
+ function() {
+ // Lazily create when needed.
+ if (!this.resizeDelay_) {
+ this.resizeDelay_ = new goog.async.Delay(
+ this.onWindowResize_,
+ this.resizeBufferMs_,
+ this);
+ this.registerDisposable(this.resizeDelay_);
+ }
+ this.resizeDelay_.start();
+};
+
+
+/**
+ * Window resize callback that determines whether to reflow the view contents.
+ * @private
+ */
+goog.dom.BufferedViewportSizeMonitor.prototype.onWindowResize_ =
+ function() {
+ if (this.viewportSizeMonitor_.isDisposed()) {
+ return;
+ }
+
+ var previousSize = this.currentSize_;
+ var currentSize = this.viewportSizeMonitor_.getSize();
+
+ goog.asserts.assert(currentSize,
+ 'Viewport size should be set at this point');
+
+ this.currentSize_ = currentSize;
+
+ if (previousSize) {
+
+ var resized = false;
+
+ // Width has changed
+ if (previousSize.width != currentSize.width) {
+ this.dispatchEvent(
+ goog.dom.BufferedViewportSizeMonitor.EventType.RESIZE_WIDTH);
+ resized = true;
+ }
+
+ // Height has changed
+ if (previousSize.height != currentSize.height) {
+ this.dispatchEvent(
+ goog.dom.BufferedViewportSizeMonitor.EventType.RESIZE_HEIGHT);
+ resized = true;
+ }
+
+ // If either has changed, this is a resize event.
+ if (resized) {
+ this.dispatchEvent(goog.events.EventType.RESIZE);
+ }
+
+ } else {
+ // If we didn't have a previous size, we consider all events to have
+ // changed.
+ this.dispatchEvent(
+ goog.dom.BufferedViewportSizeMonitor.EventType.RESIZE_HEIGHT);
+ this.dispatchEvent(
+ goog.dom.BufferedViewportSizeMonitor.EventType.RESIZE_WIDTH);
+ this.dispatchEvent(goog.events.EventType.RESIZE);
+ }
+};
+
+
+/**
+ * Returns the current size of the viewport.
+ * @return {goog.math.Size?} The current viewport size.
+ */
+goog.dom.BufferedViewportSizeMonitor.prototype.getSize = function() {
+ return this.currentSize_ ? this.currentSize_.clone() : null;
+};
diff --git a/third_party/closure/goog/dom/dom.js b/third_party/closure/goog/dom/dom.js
index 2a179a5..164a93d 100644
--- a/third_party/closure/goog/dom/dom.js
+++ b/third_party/closure/goog/dom/dom.js
@@ -1290,9 +1290,7 @@
}
// Special case for document nodes on IE 7 and 8.
- if ((node1.nodeType == goog.dom.NodeType.DOCUMENT ||
- node2.nodeType == goog.dom.NodeType.DOCUMENT) &&
- goog.userAgent.IE && !goog.userAgent.isVersion(9)) {
+ if (goog.userAgent.IE && !goog.userAgent.isDocumentMode(9)) {
if (node1.nodeType == goog.dom.NodeType.DOCUMENT) {
return -1;
}
diff --git a/third_party/closure/goog/editor/browserfeature.js b/third_party/closure/goog/editor/browserfeature.js
index 678dd04..5e03eca 100644
--- a/third_party/closure/goog/editor/browserfeature.js
+++ b/third_party/closure/goog/editor/browserfeature.js
@@ -147,7 +147,8 @@
goog.userAgent.WEBKIT || goog.userAgent.OPERA,
// Whether clicking on an editable link will take you to that site.
- FOLLOWS_EDITABLE_LINKS: goog.userAgent.WEBKIT,
+ FOLLOWS_EDITABLE_LINKS: goog.userAgent.WEBKIT ||
+ goog.userAgent.IE && goog.userAgent.isVersion('9'),
// Whether this browser has document.activeElement available.
HAS_ACTIVE_ELEMENT:
diff --git a/third_party/closure/goog/editor/field.js b/third_party/closure/goog/editor/field.js
index cd66ea2..0a4f8bc 100644
--- a/third_party/closure/goog/editor/field.js
+++ b/third_party/closure/goog/editor/field.js
@@ -26,12 +26,12 @@
goog.provide('goog.editor.Field.EventType');
goog.require('goog.array');
+goog.require('goog.asserts');
goog.require('goog.async.Delay');
goog.require('goog.debug.Logger');
goog.require('goog.dom');
goog.require('goog.dom.Range');
goog.require('goog.dom.TagName');
-goog.require('goog.dom.classes');
goog.require('goog.editor.BrowserFeature');
goog.require('goog.editor.Command');
goog.require('goog.editor.Plugin');
@@ -334,6 +334,22 @@
/**
+ * Flag controlling wether to capture mouse up events on the window or not.
+ * @type {boolean}
+ * @private
+ */
+goog.editor.Field.prototype.useWindowMouseUp_ = false;
+
+
+/**
+ * FLag indicating the handling of a mouse event sequence.
+ * @type {boolean}
+ * @private
+ */
+goog.editor.Field.prototype.waitingForMouseUp_ = false;
+
+
+/**
* Sets the active field id.
* @param {?string} fieldId The active field id.
*/
@@ -351,6 +367,18 @@
/**
+ * Sets flag to control whether to use window mouse up after seeing
+ * a mouse down operation on the field.
+ * @param {boolean} flag True to track window mouse up.
+ */
+goog.editor.Field.prototype.setUseWindowMouseUp = function(flag) {
+ goog.asserts.assert(!flag || !this.usesIframe(),
+ 'procssing window mouse up should only be enabled when not using iframe');
+ this.useWindowMouseUp_ = flag;
+};
+
+
+/**
* @return {boolean} Whether we're in modal interaction mode. When this
* returns true, another plugin is interacting with the field contents
* in a synchronous way, and expects you not to make changes to
@@ -840,7 +868,13 @@
}
this.addListener(goog.events.EventType.MOUSEDOWN, this.handleMouseDown_);
- this.addListener(goog.events.EventType.MOUSEUP, this.handleMouseUp_);
+ if (this.useWindowMouseUp_) {
+ this.eventRegister.listen(this.editableDomHelper.getDocument(),
+ goog.events.EventType.MOUSEUP, this.handleMouseUp_);
+ this.addListener(goog.events.EventType.DRAGSTART, this.handleDragStart_);
+ } else {
+ this.addListener(goog.events.EventType.MOUSEUP, this.handleMouseUp_);
+ }
};
@@ -1927,11 +1961,7 @@
* @private
*/
goog.editor.Field.prototype.handleMouseDown_ = function(e) {
- // If the user clicks on an object (like an image) in the field
- // and the activeField is not set, set it.
- if (!goog.editor.Field.getActiveFieldId()) {
- goog.editor.Field.setActiveFieldId(this.id);
- }
+ goog.editor.Field.setActiveFieldId(this.id);
// Open links in a new window if the user control + clicks.
if (goog.userAgent.IE) {
@@ -1941,6 +1971,18 @@
this.originalDomHelper.getWindow().open(targetElement.href);
}
}
+ this.waitingForMouseUp_ = true;
+};
+
+
+/**
+ * Handle drag start. Needs to cancel listening for the mouse up event on the
+ * window.
+ * @param {goog.events.BrowserEvent} e The event.
+ * @private
+ */
+goog.editor.Field.prototype.handleDragStart_ = function(e) {
+ this.waitingForMouseUp_ = false;
};
@@ -1950,6 +1992,11 @@
* @private
*/
goog.editor.Field.prototype.handleMouseUp_ = function(e) {
+ if (this.useWindowMouseUp_ && !this.waitingForMouseUp_) {
+ return;
+ }
+ this.waitingForMouseUp_ = false;
+
/*
* We fire a selection change event immediately for listeners that depend on
* the native browser event object (e). On IE, a listener that tries to
diff --git a/third_party/closure/goog/editor/link.js b/third_party/closure/goog/editor/link.js
index 6d89a64..0a50665 100644
--- a/third_party/closure/goog/editor/link.js
+++ b/third_party/closure/goog/editor/link.js
@@ -214,20 +214,33 @@
/**
+ * @return {string?} The modified string for the link if the link
+ * text appears to be a valid link. Returns null if this is not
+ * a valid link address.
+ */
+goog.editor.Link.prototype.getValidLinkFromText = function() {
+ var text = this.getCurrentText();
+ if (goog.editor.Link.isLikelyUrl(text)) {
+ if (text.search(/:/) < 0) {
+ return 'http://' + goog.string.trimLeft(text);
+ }
+ return text;
+ } else if (goog.editor.Link.isLikelyEmailAddress(text)) {
+ return 'mailto:' + text;
+ }
+ return null;
+};
+
+
+/**
* After link creation, finish creating the link depending on the type
* of link being created.
* @param {goog.editor.Field} field The field where this link is being created.
*/
goog.editor.Link.prototype.finishLinkCreation = function(field) {
- var text = this.getCurrentText();
- if (goog.editor.Link.isLikelyUrl(text)) {
- if (text.search(/:/) < 0) {
- text = 'http://' + goog.string.trimLeft(text);
- }
- this.updateLinkDisplay_(field, text);
- } else if (goog.editor.Link.isLikelyEmailAddress(text)) {
- text = 'mailto:' + text;
- this.updateLinkDisplay_(field, text);
+ var linkFromText = this.getValidLinkFromText();
+ if (linkFromText) {
+ this.updateLinkDisplay_(field, linkFromText);
} else {
field.execCommand(goog.editor.Command.MODAL_LINK_EDITOR, this);
}
diff --git a/third_party/closure/goog/editor/plugins/abstractbubbleplugin.js b/third_party/closure/goog/editor/plugins/abstractbubbleplugin.js
index d02556d..cc230ee 100644
--- a/third_party/closure/goog/editor/plugins/abstractbubbleplugin.js
+++ b/third_party/closure/goog/editor/plugins/abstractbubbleplugin.js
@@ -27,6 +27,8 @@
goog.require('goog.events');
goog.require('goog.events.EventHandler');
goog.require('goog.events.EventType');
+goog.require('goog.events.KeyCodes');
+goog.require('goog.events.actionEventWrapper');
goog.require('goog.functions');
goog.require('goog.string.Unicode');
goog.require('goog.ui.Component.EventType');
@@ -153,6 +155,27 @@
/**
+ * Whether this bubble should support tabbing through the link elements. False
+ * by default.
+ * @type {boolean}
+ * @private
+ */
+goog.editor.plugins.AbstractBubblePlugin.prototype.keyboardNavigationEnabled_ =
+ false;
+
+
+/**
+ * Sets whether the bubble should support tabbing through the link elements.
+ * @param {boolean} keyboardNavigationEnabled Whether the bubble should support
+ * tabbing through the link elements.
+ */
+goog.editor.plugins.AbstractBubblePlugin.prototype.enableKeyboardNavigation =
+ function(keyboardNavigationEnabled) {
+ this.keyboardNavigationEnabled_ = keyboardNavigationEnabled;
+};
+
+
+/**
* Sets the bubble parent.
* @param {Element} bubbleParent An element where the bubble will be
* anchored. If null, we will use the application document. This
@@ -338,7 +361,13 @@
this.shouldPreferBubbleAboveElement());
this.eventRegister.listen(bubble, goog.ui.Component.EventType.HIDE,
this.handlePanelClosed_);
+
this.onShow();
+
+ if (this.keyboardNavigationEnabled_) {
+ this.eventRegister.listen(bubble.getContentElement(),
+ goog.events.EventType.KEYDOWN, this.onBubbleKey_);
+ }
}
};
@@ -388,10 +417,25 @@
* @param {Element} target The event source element.
* @param {Function} handler The event handler.
* @protected
+ * @deprecated Use goog.editor.plugins.AbstractBubblePlugin.
+ * registerActionHandler to register click and enter events.
*/
goog.editor.plugins.AbstractBubblePlugin.prototype.registerClickHandler =
function(target, handler) {
- this.eventRegister.listen(target, goog.events.EventType.CLICK, handler);
+ this.registerActionHandler(target, handler);
+};
+
+
+/**
+ * Register the handler for the target's CLICK and ENTER key events.
+ * @param {Element} target The event source element.
+ * @param {Function} handler The event handler.
+ * @protected
+ */
+goog.editor.plugins.AbstractBubblePlugin.prototype.registerActionHandler =
+ function(target, handler) {
+ this.eventRegister.listenWithWrapper(target, goog.events.actionEventWrapper,
+ handler);
};
@@ -428,6 +472,53 @@
/**
+ * In case the keyboard navigation is enabled, this will focus to the first link
+ * element in the bubble when TAB is clicked. The user could still go through
+ * the rest of tabbable UI elements using shift + TAB.
+ * @override
+ */
+goog.editor.plugins.AbstractBubblePlugin.prototype.handleKeyDown = function(e) {
+ if (this.keyboardNavigationEnabled_ &&
+ this.isVisible() &&
+ e.keyCode == goog.events.KeyCodes.TAB && !e.shiftKey) {
+ var bubbleEl = this.getSharedBubble_().getContentElement();
+ var linkEl = goog.dom.getElementByClass(
+ goog.editor.plugins.AbstractBubblePlugin.LINK_CLASSNAME_, bubbleEl);
+ if (linkEl) {
+ linkEl.focus();
+ e.preventDefault();
+ return true;
+ }
+ }
+ return false;
+};
+
+
+/**
+ * Handles a key event on the bubble. This ensures that the focus loops through
+ * the link elements found in the bubble and then the focus is got by the field
+ * element.
+ * @param {goog.events.BrowserEvent} e The event.
+ * @private
+ */
+goog.editor.plugins.AbstractBubblePlugin.prototype.onBubbleKey_ = function(e) {
+ if (this.isVisible() &&
+ e.keyCode == goog.events.KeyCodes.TAB) {
+ var bubbleEl = this.getSharedBubble_().getContentElement();
+ var links = goog.dom.getElementsByClass(
+ goog.editor.plugins.AbstractBubblePlugin.LINK_CLASSNAME_, bubbleEl);
+ var tabbingOutOfBubble = e.shiftKey ?
+ links[0] == e.target :
+ links.length && links[links.length - 1] == e.target;
+ if (tabbingOutOfBubble) {
+ this.getFieldObject().focus();
+ e.preventDefault();
+ }
+ }
+};
+
+
+/**
* @return {boolean} Whether the bubble is visible.
*/
goog.editor.plugins.AbstractBubblePlugin.prototype.isVisible = function() {
@@ -482,7 +573,7 @@
linkId, linkText, opt_onClick, opt_container) {
var link = this.createLinkHelper(linkId, linkText, false, opt_container);
if (opt_onClick) {
- this.registerClickHandler(link, opt_onClick);
+ this.registerActionHandler(link, opt_onClick);
}
return link;
};
@@ -506,6 +597,10 @@
isAnchor ? goog.dom.TagName.A : goog.dom.TagName.SPAN,
{className: goog.editor.plugins.AbstractBubblePlugin.LINK_CLASSNAME_},
linkText);
+ if (this.keyboardNavigationEnabled_) {
+ link.setAttribute('tabindex', 0);
+ }
+ link.setAttribute('role', 'link');
this.setupLink(link, linkId, opt_container);
goog.editor.style.makeUnselectable(link, this.eventRegister);
return link;
diff --git a/third_party/closure/goog/editor/plugins/linkbubble.js b/third_party/closure/goog/editor/plugins/linkbubble.js
index 0707357..f761f6c 100644
--- a/third_party/closure/goog/editor/plugins/linkbubble.js
+++ b/third_party/closure/goog/editor/plugins/linkbubble.js
@@ -439,10 +439,15 @@
/**
- * Shows the link dialog
+ * Shows the link dialog.
+ * @param {goog.events.BrowserEvent} e The event.
* @private
*/
-goog.editor.plugins.LinkBubble.prototype.showLinkDialog_ = function() {
+goog.editor.plugins.LinkBubble.prototype.showLinkDialog_ = function(e) {
+ // Needed when this occurs due to an ENTER key event, else the newly created
+ // dialog manages to have its OK button pressed, causing it to disappear.
+ e.preventDefault();
+
this.getFieldObject().execCommand(goog.editor.Command.MODAL_LINK_EDITOR,
new goog.editor.Link(
/** @type {HTMLAnchorElement} */ (this.getTargetElement()),
@@ -466,6 +471,7 @@
this.closeBubble();
this.getFieldObject().dispatchChange();
+ this.getFieldObject().focus();
};
diff --git a/third_party/closure/goog/editor/plugins/linkdialogplugin.js b/third_party/closure/goog/editor/plugins/linkdialogplugin.js
index 5776493..eb525ad 100644
--- a/third_party/closure/goog/editor/plugins/linkdialogplugin.js
+++ b/third_party/closure/goog/editor/plugins/linkdialogplugin.js
@@ -292,7 +292,7 @@
dialog.setStopReferrerLeaks(this.stopReferrerLeaks_);
this.eventHandler_.
listen(dialog, goog.ui.editor.AbstractDialog.EventType.OK,
- this.handleOk_).
+ this.handleOk).
listen(dialog, goog.ui.editor.AbstractDialog.EventType.CANCEL,
this.handleCancel_).
listen(dialog, goog.ui.editor.LinkDialog.EventType.BEFORE_TEST_LINK,
@@ -311,9 +311,9 @@
/**
* Handles the OK event from the dialog by updating the link in the field.
* @param {goog.ui.editor.LinkDialog.OkEvent} e OK event object.
- * @private
+ * @protected
*/
-goog.editor.plugins.LinkDialogPlugin.prototype.handleOk_ = function(e) {
+goog.editor.plugins.LinkDialogPlugin.prototype.handleOk = function(e) {
// We're not restoring the original selection, so clear it out.
this.disposeOriginalSelection();
diff --git a/third_party/closure/goog/editor/plugins/linkshortcutplugin.js b/third_party/closure/goog/editor/plugins/linkshortcutplugin.js
index 1f1ac69..c7f22a1 100644
--- a/third_party/closure/goog/editor/plugins/linkshortcutplugin.js
+++ b/third_party/closure/goog/editor/plugins/linkshortcutplugin.js
@@ -49,7 +49,7 @@
goog.editor.plugins.LinkShortcutPlugin.prototype.handleKeyboardShortcut =
function(e, key, isModifierPressed) {
var command;
- if (isModifierPressed && key == 'k') {
+ if (isModifierPressed && key == 'k' && !e.shiftKey) {
var link = /** @type {goog.editor.Link?} */ (
this.getFieldObject().execCommand(goog.editor.Command.LINK));
if (link) {
diff --git a/third_party/closure/goog/events/browserfeature.js b/third_party/closure/goog/events/browserfeature.js
index 0cc103f..f5b28fd 100644
--- a/third_party/closure/goog/events/browserfeature.js
+++ b/third_party/closure/goog/events/browserfeature.js
@@ -67,5 +67,17 @@
*/
HTML5_NETWORK_EVENTS_FIRE_ON_BODY:
goog.userAgent.GECKO && !goog.userAgent.isVersion('8') ||
- goog.userAgent.IE && !goog.userAgent.isVersion('9')
+ goog.userAgent.IE && !goog.userAgent.isVersion('9'),
+
+ /**
+ * Whether touch is enabled in the browser.
+ */
+ TOUCH_ENABLED:
+ ('ontouchstart' in goog.global ||
+ !!(goog.global['document'] &&
+ document.documentElement &&
+ 'ontouchstart' in document.documentElement) ||
+ // IE10 uses non-standard touch events, so it has a different check.
+ !!(goog.global['navigator'] &&
+ goog.global['navigator']['msMaxTouchPoints']))
};
diff --git a/third_party/closure/goog/events/eventtype.js b/third_party/closure/goog/events/eventtype.js
index eaac23c..f99a224 100644
--- a/third_party/closure/goog/events/eventtype.js
+++ b/third_party/closure/goog/events/eventtype.js
@@ -69,10 +69,12 @@
// Drag and drop
DRAGSTART: 'dragstart',
+ DRAG: 'drag',
DRAGENTER: 'dragenter',
DRAGOVER: 'dragover',
DRAGLEAVE: 'dragleave',
DROP: 'drop',
+ DRAGEND: 'dragend',
// WebKit touch events.
TOUCHSTART: 'touchstart',
@@ -81,6 +83,7 @@
TOUCHCANCEL: 'touchcancel',
// Misc
+ BEFOREUNLOAD: 'beforeunload',
CONTEXTMENU: 'contextmenu',
ERROR: 'error',
HELP: 'help',
@@ -121,5 +124,20 @@
// CSS transition events. Based on the browser support described at:
// https://developer.mozilla.org/en/css/css_transitions#Browser_compatibility
TRANSITIONEND: goog.userAgent.WEBKIT ? 'webkitTransitionEnd' :
- (goog.userAgent.OPERA ? 'oTransitionEnd' : 'transitionend')
+ (goog.userAgent.OPERA ? 'oTransitionEnd' : 'transitionend'),
+
+ // IE specific events.
+ // See http://msdn.microsoft.com/en-us/library/ie/hh673557(v=vs.85).aspx
+ MSGESTURECHANGE: 'MSGestureChange',
+ MSGESTUREEND: 'MSGestureEnd',
+ MSGESTUREHOLD: 'MSGestureHold',
+ MSGESTURESTART: 'MSGestureStart',
+ MSGESTURETAP: 'MSGestureTap',
+ MSINERTIASTART: 'MSInertiaStart',
+ MSPOINTERCANCEL: 'MSPointerCancel',
+ MSPOINTERDOWN: 'MSPointerDown',
+ MSPOINTERMOVE: 'MSPointerMove',
+ MSPOINTEROVER: 'MSPointerOver',
+ MSPOINTEROUT: 'MSPointerOut',
+ MSPOINTERUP: 'MSPointerUp'
};
diff --git a/third_party/closure/goog/events/inputhandler.js b/third_party/closure/goog/events/inputhandler.js
index 7c3c9ed..4375339 100644
--- a/third_party/closure/goog/events/inputhandler.js
+++ b/third_party/closure/goog/events/inputhandler.js
@@ -58,7 +58,7 @@
* @extends {goog.events.EventTarget}
*/
goog.events.InputHandler = function(element) {
- goog.events.EventTarget.call(this);
+ goog.base(this);
/**
* The element that you want to listen for input events on.
@@ -67,20 +67,16 @@
*/
this.element_ = element;
- /**
- * Whether input event is emulated.
- * IE doesn't support input events. We could use property change events but
- * they are broken in many ways:
- * - Fire even if value was changed programmatically.
- * - Aren't always delivered. For example, if you change value or even width
- * of input programmatically, next value change made by user won't fire an
- * event.
- * WebKit before version 531 did not support input events for textareas.
- * @type {boolean}
- * @private
- */
- this.inputEventEmulation_ =
- goog.userAgent.IE ||
+ // Determine whether input event should be emulated.
+ // IE8 doesn't support input events. We could use property change events but
+ // they are broken in many ways:
+ // - Fire even if value was changed programmatically.
+ // - Aren't always delivered. For example, if you change value or even width
+ // of input programmatically, next value change made by user won't fire an
+ // event.
+ // IE9 supports input events when characters are inserted, but not deleted.
+ // WebKit before version 531 did not support input events for textareas.
+ var emulateInputEvents = goog.userAgent.IE ||
(goog.userAgent.WEBKIT && !goog.userAgent.isVersion('531') &&
element.tagName == 'TEXTAREA');
@@ -89,9 +85,19 @@
* @private
*/
this.eventHandler_ = new goog.events.EventHandler(this);
+
+ // Even if input event emulation is enabled, still listen for input events
+ // since they may be partially supported by the browser (such as IE9).
+ // If the input event does fire, we will be able to dispatch synchronously.
+ // (InputHandler events being asynchronous for IE is a common issue for
+ // cases like auto-grow textareas where they result in a quick flash of
+ // scrollbars between the textarea content growing and it being resized to
+ // fit.)
this.eventHandler_.listen(
this.element_,
- this.inputEventEmulation_ ? ['keydown', 'paste', 'cut', 'drop'] : 'input',
+ emulateInputEvents ?
+ ['keydown', 'paste', 'cut', 'drop', 'input'] :
+ 'input',
this);
};
goog.inherits(goog.events.InputHandler, goog.events.EventTarget);
@@ -119,7 +125,21 @@
* @param {goog.events.BrowserEvent} e The underlying browser event.
*/
goog.events.InputHandler.prototype.handleEvent = function(e) {
- if (this.inputEventEmulation_) {
+ if (e.type == 'input') {
+ // This event happens after all the other events we listen to, so cancel
+ // an asynchronous event dispatch if we have it queued up. Otherwise, we
+ // will end up firing an extra event.
+ this.cancelTimerIfSet_();
+
+ // Unlike other browsers, Opera fires an extra input event when an element
+ // is blurred after the user has input into it. Since Opera doesn't fire
+ // input event on drop, it's enough to check whether element still has focus
+ // to suppress bogus notification.
+ if (!goog.userAgent.OPERA || this.element_ ==
+ goog.dom.getOwnerDocument(this.element_).activeElement) {
+ this.dispatchEvent(this.createInputEvent_(e));
+ }
+ } else {
// Filter out key events that don't modify text.
if (e.type == 'keydown' &&
!goog.events.KeyCodes.isTextModifyingKeyEvent(e)) {
@@ -156,15 +176,6 @@
this.dispatchEvent(inputEvent);
}
}, 0, this);
- } else {
- // Unlike other browsers, Opera fires an extra input event when an element
- // is blurred after the user has input into it. Since Opera doesn't fire
- // input event on drop, it's enough to check whether element still has focus
- // to suppress bogus notification.
- if (!goog.userAgent.OPERA || this.element_ ==
- goog.dom.getOwnerDocument(this.element_).activeElement) {
- this.dispatchEvent(this.createInputEvent_(e));
- }
}
};
@@ -196,7 +207,7 @@
/** @override */
goog.events.InputHandler.prototype.disposeInternal = function() {
- goog.events.InputHandler.superClass_.disposeInternal.call(this);
+ goog.base(this, 'disposeInternal');
this.eventHandler_.dispose();
this.cancelTimerIfSet_();
delete this.element_;
diff --git a/third_party/closure/goog/events/keycodes.js b/third_party/closure/goog/events/keycodes.js
index cfb3da6..9f17c87 100644
--- a/third_party/closure/goog/events/keycodes.js
+++ b/third_party/closure/goog/events/keycodes.js
@@ -258,10 +258,31 @@
// check the user agent.
if (!opt_shiftKey &&
(opt_heldKeyCode == goog.events.KeyCodes.CTRL ||
- opt_heldKeyCode == goog.events.KeyCodes.ALT)) {
+ opt_heldKeyCode == goog.events.KeyCodes.ALT ||
+ goog.userAgent.MAC &&
+ opt_heldKeyCode == goog.events.KeyCodes.META)) {
return false;
}
+ // Some keys with Ctrl/Shift do not issue keypress in WEBKIT.
+ if (goog.userAgent.WEBKIT && opt_ctrlKey && opt_shiftKey) {
+ switch (keyCode) {
+ case goog.events.KeyCodes.BACKSLASH:
+ case goog.events.KeyCodes.OPEN_SQUARE_BRACKET:
+ case goog.events.KeyCodes.CLOSE_SQUARE_BRACKET:
+ case goog.events.KeyCodes.TILDE:
+ case goog.events.KeyCodes.SEMICOLON:
+ case goog.events.KeyCodes.DASH:
+ case goog.events.KeyCodes.EQUALS:
+ case goog.events.KeyCodes.COMMA:
+ case goog.events.KeyCodes.PERIOD:
+ case goog.events.KeyCodes.SLASH:
+ case goog.events.KeyCodes.APOSTROPHE:
+ case goog.events.KeyCodes.SINGLE_QUOTE:
+ return false;
+ }
+ }
+
// When Ctrl+<somekey> is held in IE, it only fires a keypress once, but it
// continues to fire keydown events as the event repeats.
if (goog.userAgent.IE && opt_ctrlKey && opt_heldKeyCode == keyCode) {
diff --git a/third_party/closure/goog/events/keyhandler.js b/third_party/closure/goog/events/keyhandler.js
index 68d65f9..e6f5e58 100644
--- a/third_party/closure/goog/events/keyhandler.js
+++ b/third_party/closure/goog/events/keyhandler.js
@@ -301,15 +301,28 @@
* @private
*/
goog.events.KeyHandler.prototype.handleKeyDown_ = function(e) {
-
// Ctrl-Tab and Alt-Tab can cause the focus to be moved to another window
// before we've caught a key-up event. If the last-key was one of these we
// reset the state.
- if (goog.userAgent.WEBKIT &&
- (this.lastKey_ == goog.events.KeyCodes.CTRL && !e.ctrlKey ||
- this.lastKey_ == goog.events.KeyCodes.ALT && !e.altKey)) {
- this.lastKey_ = -1;
- this.keyCode_ = -1;
+
+ if (goog.userAgent.WEBKIT) {
+ if (this.lastKey_ == goog.events.KeyCodes.CTRL && !e.ctrlKey ||
+ this.lastKey_ == goog.events.KeyCodes.ALT && !e.altKey ||
+ goog.userAgent.MAC &&
+ this.lastKey_ == goog.events.KeyCodes.META && !e.metaKey) {
+ this.lastKey_ = -1;
+ this.keyCode_ = -1;
+ }
+ }
+
+ if (this.lastKey_ == -1) {
+ if (e.ctrlKey && e.keyCode != goog.events.KeyCodes.CTRL) {
+ this.lastKey_ = goog.events.KeyCodes.CTRL;
+ } else if (e.altKey && e.keyCode != goog.events.KeyCodes.ALT) {
+ this.lastKey_ = goog.events.KeyCodes.ALT;
+ } else if (e.metaKey && e.keyCode != goog.events.KeyCodes.META) {
+ this.lastKey_ = goog.events.KeyCodes.META;
+ }
}
if (goog.events.KeyHandler.USES_KEYDOWN_ &&
@@ -328,6 +341,17 @@
/**
+ * Resets the stored previous values. Needed to be called for webkit which will
+ * not generate a key up for meta key operations. This should only be called
+ * when having finished with repeat key possiblities.
+ */
+goog.events.KeyHandler.prototype.resetState = function() {
+ this.lastKey_ = -1;
+ this.keyCode_ = -1;
+};
+
+
+/**
* Clears the stored previous key value, resetting the key repeat status. Uses
* -1 because the Safari 3 Windows beta reports 0 for certain keys (like Home
* and End.)
@@ -335,8 +359,7 @@
* @private
*/
goog.events.KeyHandler.prototype.handleKeyup_ = function(e) {
- this.lastKey_ = -1;
- this.keyCode_ = -1;
+ this.resetState();
this.altKey_ = e.altKey;
};
diff --git a/third_party/closure/goog/fs/fs.js b/third_party/closure/goog/fs/fs.js
index ceb324b..5873fe6 100644
--- a/third_party/closure/goog/fs/fs.js
+++ b/third_party/closure/goog/fs/fs.js
@@ -25,6 +25,7 @@
goog.provide('goog.fs');
+goog.require('goog.array');
goog.require('goog.async.Deferred');
goog.require('goog.events');
goog.require('goog.fs.Error');
@@ -168,11 +169,16 @@
*/
goog.fs.getBlob = function(var_args) {
var BlobBuilder = goog.global.BlobBuilder || goog.global.WebKitBlobBuilder;
- var bb = new BlobBuilder();
- for (var i = 0; i < arguments.length; i++) {
- bb.append(arguments[i]);
+
+ if (goog.isDef(BlobBuilder)) {
+ var bb = new BlobBuilder();
+ for (var i = 0; i < arguments.length; i++) {
+ bb.append(arguments[i]);
+ }
+ return bb.getBlob();
+ } else {
+ return new Blob(goog.array.toArray(arguments));
}
- return bb.getBlob();
};
diff --git a/third_party/closure/goog/functions/functions.js b/third_party/closure/goog/functions/functions.js
index 280e31c..85aee23 100644
--- a/third_party/closure/goog/functions/functions.js
+++ b/third_party/closure/goog/functions/functions.js
@@ -25,8 +25,9 @@
/**
* Creates a function that always returns the same value.
- * @param {*} retValue The value to return.
- * @return {!Function} The new function.
+ * @param {T} retValue The value to return.
+ * @return {function():T} The new function.
+ * @template T
*/
goog.functions.constant = function(retValue) {
return function() {
@@ -59,10 +60,10 @@
/**
* A simple function that returns the first argument of whatever is passed
* into it.
- * @param {*=} opt_returnValue The single value that will be returned.
+ * @param {T=} opt_returnValue The single value that will be returned.
* @param {...*} var_args Optional trailing arguments. These are ignored.
- * @return {?} The first argument passed in, or undefined if nothing was passed.
- * We can't know the type -- just pass it along without type.
+ * @return {T} The first argument passed in, or undefined if nothing was passed.
+ * @template T
*/
goog.functions.identity = function(opt_returnValue, var_args) {
return opt_returnValue;
@@ -101,8 +102,9 @@
* Given a function, create a new function that swallows its return value
* and replaces it with a new one.
* @param {Function} f A function.
- * @param {*} retValue A new return value.
- * @return {!Function} A new function.
+ * @param {T} retValue A new return value.
+ * @return {function(...[?]):T} A new function.
+ * @template T
*/
goog.functions.withReturnValue = function(f, retValue) {
return goog.functions.sequence(f, goog.functions.constant(retValue));
@@ -112,10 +114,12 @@
/**
* Creates the composition of the functions passed in.
* For example, (goog.functions.compose(f, g))(a) is equivalent to f(g(a)).
+ * @param {function(...[?]):T} fn The final function.
* @param {...Function} var_args A list of functions.
- * @return {!Function} The composition of all inputs.
+ * @return {function(...[?]):T} The composition of all inputs.
+ * @template T
*/
-goog.functions.compose = function(var_args) {
+goog.functions.compose = function(fn, var_args) {
var functions = arguments;
var length = functions.length;
return function() {
@@ -158,7 +162,8 @@
* short-circuited as soon as a function returns false.
* For example, (goog.functions.and(f, g))(x) is equivalent to f(x) && g(x).
* @param {...Function} var_args A list of functions.
- * @return {!Function} A function that ANDs its component functions.
+ * @return {function(...[?]):boolean} A function that ANDs its component
+ * functions.
*/
goog.functions.and = function(var_args) {
var functions = arguments;
@@ -180,7 +185,8 @@
* short-circuited as soon as a function returns true.
* For example, (goog.functions.or(f, g))(x) is equivalent to f(x) || g(x).
* @param {...Function} var_args A list of functions.
- * @return {!Function} A function that ORs its component functions.
+ * @return {function(...[?]):boolean} A function that ORs its component
+ * functions.
*/
goog.functions.or = function(var_args) {
var functions = arguments;
@@ -200,7 +206,8 @@
* Creates a function that returns the Boolean opposite of a provided function.
* For example, (goog.functions.not(f))(x) is equivalent to !f(x).
* @param {!Function} f The original function.
- * @return {!Function} A function that delegates to f and returns opposite.
+ * @return {function(...[?]):boolean} A function that delegates to f and returns
+ * opposite.
*/
goog.functions.not = function(f) {
return function() {
diff --git a/third_party/closure/goog/history/history.js b/third_party/closure/goog/history/history.js
index 6b4d650..1540f71 100644
--- a/third_party/closure/goog/history/history.js
+++ b/third_party/closure/goog/history/history.js
@@ -286,6 +286,7 @@
* @private
*/
this.timer_ = new goog.Timer(goog.History.PollingType.NORMAL);
+ this.registerDisposable(this.timer_);
/**
* True if the state tokens are displayed in the address bar, false for hidden
diff --git a/third_party/closure/goog/iter/iter.js b/third_party/closure/goog/iter/iter.js
index f270463..f9a0dcc 100644
--- a/third_party/closure/goog/iter/iter.js
+++ b/third_party/closure/goog/iter/iter.js
@@ -138,19 +138,23 @@
* @param {goog.iter.Iterable} iterable The iterator to iterate
* over. If the iterable is an object {@code toIterator} will be called on
* it.
- * @param {Function} f The function to call for every element. This function
+* @param {function(this:T,?,?,?):?} f The function to call for every
+ * element. This function
* takes 3 arguments (the element, undefined, and the iterator) and the
* return value is irrelevant. The reason for passing undefined as the
* second argument is so that the same function can be used in
* {@see goog.array#forEach} as well as others.
- * @param {Object=} opt_obj The object to be used as the value of 'this' within
+ * @param {T=} opt_obj The object to be used as the value of 'this' within
* {@code f}.
+ * @template T
*/
goog.iter.forEach = function(iterable, f, opt_obj) {
if (goog.isArrayLike(iterable)) {
/** @preserveTry */
try {
- goog.array.forEach((/** @type {goog.array.ArrayLike} */ iterable), f,
+ // NOTES: this passes the index number to the second parameter
+ // of the callback contrary to the documentation above.
+ goog.array.forEach(/** @type {goog.array.ArrayLike} */(iterable), f,
opt_obj);
} catch (ex) {
if (ex !== goog.iter.StopIteration) {
@@ -178,15 +182,17 @@
* returns true adds the element to a new iterator.
*
* @param {goog.iter.Iterable} iterable The iterator to iterate over.
- * @param {Function} f The function to call for every element. This function
+ * @param {function(this:T,?,undefined,?):boolean} f The function to call for
+ * every element. This function
* takes 3 arguments (the element, undefined, and the iterator) and should
* return a boolean. If the return value is true the element will be
* included in the returned iteror. If it is false the element is not
* included.
- * @param {Object=} opt_obj The object to be used as the value of 'this' within
+ * @param {T=} opt_obj The object to be used as the value of 'this' within
* {@code f}.
* @return {!goog.iter.Iterator} A new iterator in which only elements that
* passed the test are present.
+ * @template T
*/
goog.iter.filter = function(iterable, f, opt_obj) {
var iterator = goog.iter.toIterator(iterable);
@@ -262,13 +268,15 @@
* with that value.
*
* @param {goog.iter.Iterable} iterable The iterator to iterate over.
- * @param {Function} f The function to call for every element. This function
+ * @param {function(this:T,?,undefined,?):?} f The function to call for every
+ * element. This function
* takes 3 arguments (the element, undefined, and the iterator) and should
* return a new value.
- * @param {Object=} opt_obj The object to be used as the value of 'this' within
+ * @param {T=} opt_obj The object to be used as the value of 'this' within
* {@code f}.
* @return {!goog.iter.Iterator} A new iterator that returns the results of
* applying the function to each element in the original iterator.
+ * @template T
*/
goog.iter.map = function(iterable, f, opt_obj) {
var iterator = goog.iter.toIterator(iterable);
@@ -288,15 +296,16 @@
* result.
*
* @param {goog.iter.Iterable} iterable The iterator to iterate over.
- * @param {Function} f The function to call for every element. This function
- * takes 2 arguments (the function's previous result or the initial value,
- * and the value of the current element).
+ * @param {function(this:T,V,?):V} f The function to call for every
+ * element. This function takes 2 arguments (the function's previous result
+ * or the initial value, and the value of the current element).
* function(previousValue, currentElement) : newValue.
- * @param {*} val The initial value to pass into the function on the first call.
- * @param {Object=} opt_obj The object to be used as the value of 'this'
+ * @param {V} val The initial value to pass into the function on the first call.
+ * @param {T=} opt_obj The object to be used as the value of 'this'
* within f.
- * @return {*} Result of evaluating f repeatedly across the values of
+ * @return {V} Result of evaluating f repeatedly across the values of
* the iterator.
+ * @template T,V
*/
goog.iter.reduce = function(iterable, f, val, opt_obj) {
var rval = val;
@@ -313,12 +322,14 @@
* return false this will return false.
*
* @param {goog.iter.Iterable} iterable The iterator object.
- * @param {Function} f The function to call for every value. This function
+ * @param {function(this:T,?,undefined,?):boolean} f The function to call for
+ * every value. This function
* takes 3 arguments (the value, undefined, and the iterator) and should
* return a boolean.
- * @param {Object=} opt_obj The object to be used as the value of 'this' within
+ * @param {T=} opt_obj The object to be used as the value of 'this' within
* {@code f}.
* @return {boolean} true if any value passes the test.
+ * @template T
*/
goog.iter.some = function(iterable, f, opt_obj) {
iterable = goog.iter.toIterator(iterable);
@@ -344,12 +355,14 @@
* return true this will return true.
*
* @param {goog.iter.Iterable} iterable The iterator object.
- * @param {Function} f The function to call for every value. This function
+ * @param {function(this:T,?,undefined,?):boolean} f The function to call for
+ * every value. This function
* takes 3 arguments (the value, undefined, and the iterator) and should
* return a boolean.
- * @param {Object=} opt_obj The object to be used as the value of 'this' within
+ * @param {T=} opt_obj The object to be used as the value of 'this' within
* {@code f}.
* @return {boolean} true if every value passes the test.
+ * @template T
*/
goog.iter.every = function(iterable, f, opt_obj) {
iterable = goog.iter.toIterator(iterable);
@@ -413,13 +426,15 @@
* Builds a new iterator that iterates over the original, but skips elements as
* long as a supplied function returns true.
* @param {goog.iter.Iterable} iterable The iterator object.
- * @param {Function} f The function to call for every value. This function
+ * @param {function(this:T,?,undefined,?):boolean} f The function to call for
+ * every value. This function
* takes 3 arguments (the value, undefined, and the iterator) and should
* return a boolean.
- * @param {Object=} opt_obj The object to be used as the value of 'this' within
+ * @param {T=} opt_obj The object to be used as the value of 'this' within
* {@code f}.
* @return {!goog.iter.Iterator} A new iterator that drops elements from the
* original iterator as long as {@code f} is true.
+ * @template T
*/
goog.iter.dropWhile = function(iterable, f, opt_obj) {
var iterator = goog.iter.toIterator(iterable);
@@ -444,12 +459,14 @@
* Builds a new iterator that iterates over the original, but only as long as a
* supplied function returns true.
* @param {goog.iter.Iterable} iterable The iterator object.
- * @param {Function} f The function to call for every value. This function
+ * @param {function(this:T,?,undefined,?):boolean} f The function to call for
+ * every value. This function
* takes 3 arguments (the value, undefined, and the iterator) and should
* return a boolean.
- * @param {Object=} opt_obj This is used as the 'this' object in f when called.
+ * @param {T=} opt_obj This is used as the 'this' object in f when called.
* @return {!goog.iter.Iterator} A new iterator that keeps elements in the
* original iterator as long as the function is true.
+ * @template T
*/
goog.iter.takeWhile = function(iterable, f, opt_obj) {
var iterator = goog.iter.toIterator(iterable);
@@ -481,7 +498,7 @@
goog.iter.toArray = function(iterable) {
// Fast path for array-like.
if (goog.isArrayLike(iterable)) {
- return goog.array.toArray((/** @type {!goog.array.ArrayLike} */ iterable));
+ return goog.array.toArray(/** @type {!goog.array.ArrayLike} */(iterable));
}
iterable = goog.iter.toIterator(iterable);
var array = [];
@@ -656,7 +673,6 @@
// Pull elements off the original iterator if not using cache
if (!useCache) {
-
try {
// Return the element from the iterable
returnElement = baseIterator.next();
diff --git a/third_party/closure/goog/json/json.js b/third_party/closure/goog/json/json.js
index 3fa6400..2a0b74c 100644
--- a/third_party/closure/goog/json/json.js
+++ b/third_party/closure/goog/json/json.js
@@ -61,7 +61,7 @@
// Don't make these static since they have the global flag.
var backslashesRe = /\\["\\\/bfnrtu]/g;
var simpleValuesRe =
- /"[^"\\\n\r\u2028\u2029\x00-\x08\x10-\x1f\x80-\x9f]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
+ /"[^"\\\n\r\u2028\u2029\x00-\x08\x0a-\x1f]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
var openBracketsRe = /(?:^|:|,)(?:[\s\u2028\u2029]*\[)+/g;
var remainderRe = /^[\],:{}\s\u2028\u2029]*$/;
diff --git a/third_party/closure/goog/locale/locale.js b/third_party/closure/goog/locale/locale.js
index c139b86..9506989 100644
--- a/third_party/closure/goog/locale/locale.js
+++ b/third_party/closure/goog/locale/locale.js
@@ -150,8 +150,8 @@
goog.locale.getNativeCountryName = function(countryCode) {
var key = goog.locale.getLanguageSubTag(countryCode) + '_' +
goog.locale.getRegionSubTag(countryCode);
- return key in goog.locale.nativeNameConstants.COUNTRY ?
- goog.locale.nativeNameConstants.COUNTRY[key] : countryCode;
+ return key in goog.locale.nativeNameConstants['COUNTRY'] ?
+ goog.locale.nativeNameConstants['COUNTRY'][key] : countryCode;
};
@@ -193,9 +193,11 @@
* @return {string} Language name for the provided language code.
*/
goog.locale.getNativeLanguageName = function(languageCode) {
+ if (languageCode in goog.locale.nativeNameConstants['LANGUAGE'])
+ return goog.locale.nativeNameConstants['LANGUAGE'][languageCode];
var code = goog.locale.getLanguageSubTag(languageCode);
- return code in goog.locale.nativeNameConstants.LANGUAGE ?
- goog.locale.nativeNameConstants.LANGUAGE[code] : languageCode;
+ return code in goog.locale.nativeNameConstants['LANGUAGE'] ?
+ goog.locale.nativeNameConstants['LANGUAGE'][code] : languageCode;
};
@@ -218,6 +220,8 @@
opt_localeSymbols = goog.locale.getResource('LocaleNameConstants',
goog.locale.getLocale());
}
+ if (languageCode in opt_localeSymbols['LANGUAGE'])
+ return opt_localeSymbols['LANGUAGE'][languageCode];
var code = goog.locale.getLanguageSubTag(languageCode);
return code in opt_localeSymbols['LANGUAGE'] ?
opt_localeSymbols['LANGUAGE'][code] : languageCode;
diff --git a/third_party/closure/goog/messaging/respondingchannel.js b/third_party/closure/goog/messaging/respondingchannel.js
index 50775ef..3ac1912 100644
--- a/third_party/closure/goog/messaging/respondingchannel.js
+++ b/third_party/closure/goog/messaging/respondingchannel.js
@@ -141,6 +141,8 @@
goog.messaging.RespondingChannel.prototype.disposeInternal = function() {
goog.dispose(this.messageChannel_);
delete this.messageChannel_;
+ // Note: this.publicChannel_ and this.privateChannel_ get disposed by
+ // this.messageChannel_
delete this.publicChannel_;
delete this.privateChannel_;
};
@@ -224,8 +226,11 @@
var resultMessage = {};
resultMessage['data'] = callback(message['data']);
resultMessage['signature'] = message['signature'];
-
- this.privateChannel_.send(
- goog.messaging.RespondingChannel.CALLBACK_SERVICE_,
- resultMessage);
+ // The callback invoked above may have disposed the channel so check if it
+ // exists.
+ if (this.privateChannel_) {
+ this.privateChannel_.send(
+ goog.messaging.RespondingChannel.CALLBACK_SERVICE_,
+ resultMessage);
+ }
};
diff --git a/third_party/closure/goog/module/modulemanager.js b/third_party/closure/goog/module/modulemanager.js
index 6b445e1..43503d6 100644
--- a/third_party/closure/goog/module/modulemanager.js
+++ b/third_party/closure/goog/module/modulemanager.js
@@ -27,6 +27,7 @@
goog.require('goog.async.Deferred');
goog.require('goog.debug.Logger');
goog.require('goog.debug.Trace');
+goog.require('goog.dispose');
goog.require('goog.module.ModuleInfo');
goog.require('goog.module.ModuleLoadCallback');
goog.require('goog.object');
@@ -1247,7 +1248,8 @@
goog.module.ModuleManager.superClass_.disposeInternal.call(this);
// Dispose of each ModuleInfo object.
- goog.array.forEach(goog.object.getValues(this.moduleInfoMap_), goog.dispose);
+ goog.disposeAll(
+ goog.object.getValues(this.moduleInfoMap_), this.baseModuleInfo_);
this.moduleInfoMap_ = null;
this.loadingModuleIds_ = null;
this.requestedLoadingModuleIds_ = null;
diff --git a/third_party/closure/goog/net/browserchannel.js b/third_party/closure/goog/net/browserchannel.js
index 6d3bbba..3efe031 100644
--- a/third_party/closure/goog/net/browserchannel.js
+++ b/third_party/closure/goog/net/browserchannel.js
@@ -401,6 +401,33 @@
/**
+ * A throttle time in ms for readystatechange events for the backchannel.
+ * Useful for throttling when ready state is INTERACTIVE (partial data).
+ *
+ * This throttle is useful if the server sends large data chunks down the
+ * backchannel. It prevents examining XHR partial data on every
+ * readystate change event. This is useful because large chunks can
+ * trigger hundreds of readystatechange events, each of which takes ~5ms
+ * or so to handle, in turn making the UI unresponsive for a significant period.
+ *
+ * If set to zero no throttle is used.
+ * @type {number}
+ * @private
+ */
+goog.net.BrowserChannel.prototype.readyStateChangeThrottleMs_ = 0;
+
+
+/**
+ * Whether cross origin requests are supported for the browser channel.
+ *
+ * See {@link goog.net.XhrIo#setWithCredentials}.
+ * @type {boolean}
+ * @private
+ */
+goog.net.BrowserChannel.prototype.supportsCrossDomainXhrs_ = false;
+
+
+/**
* The latest protocol version that this class supports. We request this version
* from the server when opening the connection. Should match
* com.google.net.browserchannel.BrowserChannel.LATEST_CHANNEL_VERSION.
@@ -1028,6 +1055,37 @@
/**
+ * Sets the throttle for handling onreadystatechange events for the request.
+ *
+ * @param {number} throttle The throttle in ms. A value of zero indicates
+ * no throttle.
+ */
+goog.net.BrowserChannel.prototype.setReadyStateChangeThrottle = function(
+ throttle) {
+ this.readyStateChangeThrottleMs_ = throttle;
+};
+
+
+/**
+ * Sets whether cross origin requests are supported for the browser channel.
+ *
+ * Setting this allows the creation of requests to secondary domains and
+ * sends XHRs with the CORS withCredentials bit set to true.
+ *
+ * In order for cross-origin requests to work, the server will also need to set
+ * CORS response headers as per:
+ * https://developer.mozilla.org/en-US/docs/HTTP_access_control
+ *
+ * See {@link goog.net.XhrIo#setWithCredentials}.
+ * @param {boolean} supportCrossDomain Whether cross domain XHRs are supported.
+ */
+goog.net.BrowserChannel.prototype.setSupportsCrossDomainXhrs = function(
+ supportCrossDomain) {
+ this.supportsCrossDomainXhrs_ = supportCrossDomain;
+};
+
+
+/**
* Returns the handler used for channel callback events.
*
* @return {goog.net.BrowserChannel.Handler} The handler.
@@ -1620,6 +1678,8 @@
'rpc',
this.backChannelAttemptId_);
this.backChannelRequest_.setExtraHeaders(this.extraHeaders_);
+ this.backChannelRequest_.setReadyStateChangeThrottle(
+ this.readyStateChangeThrottleMs_);
var uri = this.backChannelUri_.clone();
uri.setParameterValue('RID', 'rpc');
uri.setParameterValue('SID', this.sid_);
@@ -2282,17 +2342,19 @@
/**
* Called when BC needs to create an XhrIo object. Override in a subclass if
* you need to customize the behavior, for example to enable the creation of
- * XHR's capable of calling a secondary domain.
+ * XHR's capable of calling a secondary domain. Will also allow calling
+ * a secondary domain if withCredentials (CORS) is enabled.
* @param {?string} hostPrefix The host prefix, if we need an XhrIo object
* capable of calling a secondary domain.
* @return {!goog.net.XhrIo} A new XhrIo object.
*/
goog.net.BrowserChannel.prototype.createXhrIo = function(hostPrefix) {
- if (hostPrefix) {
- throw new Error('Can\'t create secondary domain capable XhrIo object.');
- } else {
- return new goog.net.XhrIo();
+ if (hostPrefix && !this.supportsCrossDomainXhrs_) {
+ throw Error('Can\'t create secondary domain capable XhrIo object.');
}
+ var xhr = new goog.net.XhrIo();
+ xhr.setWithCredentials(this.supportsCrossDomainXhrs_);
+ return xhr;
};
@@ -2397,22 +2459,27 @@
* a host prefix. This allows us to work around browser per-domain
* connection limits.
*
- * Currently, we only use secondary domains when using Trident's ActiveXObject,
- * because it supports cross-domain requests out of the box. Even if we wanted
- * to use secondary domains on Gecko/Webkit, they wouldn't work due to
- * security restrictions on cross-origin XHRs. Note that in IE10 we no longer
- * use ActiveX since it's not supported in Metro mode and IE10 supports XHR
- * streaming.
+ * Currently, we use secondary domains when using Trident's ActiveXObject,
+ * because it supports cross-domain requests out of the box. Note that in IE10
+ * we no longer use ActiveX since it's not supported in Metro mode and IE10
+ * supports XHR streaming.
*
- * If you need to use secondary domains on other browsers, you'll need
- * to override this method in a subclass, and make sure that those browsers
- * use some messaging mechanism that works cross-domain.
+ * If you need to use secondary domains on other browsers and IE10,
+ * you have two choices:
+ * 1) If you only care about browsers that support CORS
+ * (https://developer.mozilla.org/en-US/docs/HTTP_access_control), you
+ * can use {@link #setSupportsCrossDomainXhrs} and set the appropriate
+ * CORS response headers on the server.
+ * 2) Or, override this method in a subclass, and make sure that those
+ * browsers use some messaging mechanism that works cross-domain (e.g
+ * iframes and window.postMessage).
*
* @return {boolean} Whether to use secondary domains.
* @see http://code.google.com/p/closure-library/issues/detail?id=339
*/
goog.net.BrowserChannel.prototype.shouldUseSecondaryDomains = function() {
- return !goog.net.ChannelRequest.supportsXhrStreaming();
+ return this.supportsCrossDomainXhrs_ ||
+ !goog.net.ChannelRequest.supportsXhrStreaming();
};
diff --git a/third_party/closure/goog/net/channelrequest.js b/third_party/closure/goog/net/channelrequest.js
index b09ba97..78701b8 100644
--- a/third_party/closure/goog/net/channelrequest.js
+++ b/third_party/closure/goog/net/channelrequest.js
@@ -28,6 +28,7 @@
goog.provide('goog.net.ChannelRequest.Error');
goog.require('goog.Timer');
+goog.require('goog.async.Throttle');
goog.require('goog.events');
goog.require('goog.events.EventHandler');
goog.require('goog.net.EventType');
@@ -255,6 +256,28 @@
/**
+ * A throttle time in ms for readystatechange events for the backchannel.
+ * Useful for throttling when ready state is INTERACTIVE (partial data).
+ * If set to zero no throttle is used.
+ *
+ * @see goog.net.BrowserChannel.prototype.readyStateChangeThrottleMs_
+ *
+ * @type {number}
+ * @private
+ */
+goog.net.ChannelRequest.prototype.readyStateChangeThrottleMs_ = 0;
+
+
+/**
+ * The throttle for readystatechange events for the current request, or null
+ * if there is none.
+ * @type {goog.async.Throttle}
+ * @private
+ */
+goog.net.ChannelRequest.prototype.readyStateChangeThrottle_ = null;
+
+
+/**
* Default timeout in MS for a request. The server must return data within this
* time limit for the request to not timeout.
* @type {number}
@@ -422,6 +445,18 @@
/**
+ * Sets the throttle for handling onreadystatechange events for the request.
+ *
+ * @param {number} throttle The throttle in ms. A value of zero indicates
+ * no throttle.
+ */
+goog.net.ChannelRequest.prototype.setReadyStateChangeThrottle = function(
+ throttle) {
+ this.readyStateChangeThrottleMs_ = throttle;
+};
+
+
+/**
* Uses XMLHTTP to send an HTTP POST to the server.
*
* @param {goog.Uri} uri The uri of the request.
@@ -486,9 +521,16 @@
var useSecondaryDomains = this.channel_.shouldUseSecondaryDomains();
this.xmlHttp_ = this.channel_.createXhrIo(useSecondaryDomains ?
hostPrefix : null);
+
+ if (this.readyStateChangeThrottleMs_ > 0) {
+ this.readyStateChangeThrottle_ = new goog.async.Throttle(
+ goog.bind(this.xmlHttpHandler_, this, this.xmlHttp_),
+ this.readyStateChangeThrottleMs_);
+ }
+
this.eventHandler_.listen(this.xmlHttp_,
goog.net.EventType.READY_STATE_CHANGE,
- this.xmlHttpHandler_, false, this);
+ this.readyStateChangeHandler_);
var headers = this.extraHeaders_ ? goog.object.clone(this.extraHeaders_) : {};
if (this.postData_) {
@@ -517,12 +559,31 @@
/**
- * XmlHttp handler
- * @param {goog.events.Event} e Event object, target is a XhrIo object.
+ * Handles a readystatechange event.
+ * @param {goog.events.Event} evt The event.
* @private
*/
-goog.net.ChannelRequest.prototype.xmlHttpHandler_ = function(e) {
- var xmlhttp = e.target;
+goog.net.ChannelRequest.prototype.readyStateChangeHandler_ = function(evt) {
+ var xhr = /** @type {goog.net.XhrIo} */ (evt.target);
+ var throttle = this.readyStateChangeThrottle_;
+ if (throttle &&
+ xhr.getReadyState() == goog.net.XmlHttp.ReadyState.INTERACTIVE) {
+ // Only throttle in the partial data case.
+ this.channelDebug_.debug('Throttling readystatechange.');
+ throttle.fire();
+ } else {
+ // If we haven't throttled, just handle response directly.
+ this.xmlHttpHandler_(xhr);
+ }
+};
+
+
+/**
+ * XmlHttp handler
+ * @param {goog.net.XhrIo} xmlhttp The XhrIo object for the current request.
+ * @private
+ */
+goog.net.ChannelRequest.prototype.xmlHttpHandler_ = function(xmlhttp) {
goog.net.BrowserChannel.onStartExecution();
/** @preserveTry */
try {
@@ -1101,6 +1162,9 @@
goog.net.ChannelRequest.prototype.cleanup_ = function() {
this.cancelWatchDogTimer_();
+ goog.dispose(this.readyStateChangeThrottle_);
+ this.readyStateChangeThrottle_ = null;
+
// Stop the polling timer, if necessary.
this.pollingTimer_.stop();
diff --git a/third_party/closure/goog/net/cookies.js b/third_party/closure/goog/net/cookies.js
index 10cc5b8..4ab6fb6 100644
--- a/third_party/closure/goog/net/cookies.js
+++ b/third_party/closure/goog/net/cookies.js
@@ -22,8 +22,6 @@
goog.provide('goog.net.Cookies');
goog.provide('goog.net.cookies');
-goog.require('goog.userAgent');
-
/**
@@ -190,7 +188,8 @@
var nameEq = name + '=';
var parts = this.getParts_();
for (var i = 0, part; part = parts[i]; i++) {
- if (part.indexOf(nameEq) == 0) {
+ // startsWith
+ if (part.lastIndexOf(nameEq, 0) == 0) {
return part.substr(nameEq.length);
}
if (part == name) {
diff --git a/third_party/closure/goog/net/iframeio.js b/third_party/closure/goog/net/iframeio.js
index deb8b39..ae12bcb 100644
--- a/third_party/closure/goog/net/iframeio.js
+++ b/third_party/closure/goog/net/iframeio.js
@@ -141,6 +141,7 @@
goog.require('goog.debug.Logger');
goog.require('goog.dom');
goog.require('goog.events');
+goog.require('goog.events.Event');
goog.require('goog.events.EventTarget');
goog.require('goog.events.EventType');
goog.require('goog.json');
@@ -344,8 +345,9 @@
* @private
*/
goog.net.IframeIo.addFormInputs_ = function(form, data) {
+ var helper = goog.dom.getDomHelper(form);
goog.structs.forEach(data, function(value, key) {
- var inp = goog.dom.createDom('input',
+ var inp = helper.createDom('input',
{'type': 'hidden', 'name': key, 'value': value});
form.appendChild(inp);
});
@@ -1114,8 +1116,8 @@
iframeAttributes.src = 'javascript:""';
}
- this.iframe_ = /** @type {HTMLIFrameElement} */(goog.dom.createDom(
- 'iframe', iframeAttributes));
+ this.iframe_ = /** @type {HTMLIFrameElement} */(
+ goog.dom.getDomHelper(this.form_).createDom('iframe', iframeAttributes));
var s = this.iframe_.style;
s.visibility = 'hidden';
@@ -1137,7 +1139,8 @@
* @private
*/
goog.net.IframeIo.prototype.appendIframe_ = function() {
- goog.dom.getDocument().body.appendChild(this.iframe_);
+ goog.dom.getDomHelper(this.form_).getDocument().body.appendChild(
+ this.iframe_);
};
diff --git a/third_party/closure/goog/net/xhrio.js b/third_party/closure/goog/net/xhrio.js
index 3be2347..534a9ef 100644
--- a/third_party/closure/goog/net/xhrio.js
+++ b/third_party/closure/goog/net/xhrio.js
@@ -156,9 +156,12 @@
* request.
* @param {number=} opt_timeoutInterval Number of milliseconds after which an
* incomplete request will be aborted; 0 means no timeout is set.
+ * @param {boolean=} opt_withCredentials Whether to send credentials with the
+ * request. Default to false. See {@link goog.net.XhrIo#setWithCredentials}.
*/
goog.net.XhrIo.send = function(url, opt_callback, opt_method, opt_content,
- opt_headers, opt_timeoutInterval) {
+ opt_headers, opt_timeoutInterval,
+ opt_withCredentials) {
var x = new goog.net.XhrIo();
goog.net.XhrIo.sendInstances_.push(x);
if (opt_callback) {
@@ -170,6 +173,9 @@
if (opt_timeoutInterval) {
x.setTimeoutInterval(opt_timeoutInterval);
}
+ if (opt_withCredentials) {
+ x.setWithCredentials(opt_withCredentials);
+ }
x.send(url, opt_method, opt_content, opt_headers);
};
@@ -444,7 +450,8 @@
goog.net.XhrIo.prototype.send = function(url, opt_method, opt_content,
opt_headers) {
if (this.xhr_) {
- throw Error('[goog.net.XhrIo] Object is active with another request');
+ throw Error('[goog.net.XhrIo] Object is active with another request=' +
+ this.lastUri_ + '; newUri=' + url);
}
var method = opt_method ? opt_method.toUpperCase() : 'GET';
diff --git a/third_party/closure/goog/net/xhrmanager.js b/third_party/closure/goog/net/xhrmanager.js
index ee68f65..d7b786b 100644
--- a/third_party/closure/goog/net/xhrmanager.js
+++ b/third_party/closure/goog/net/xhrmanager.js
@@ -178,6 +178,8 @@
* complete. The only param is the event object from the COMPLETE event.
* @param {number=} opt_maxRetries The maximum number of times the request
* should be retried.
+ * @param {goog.net.XhrIo.ResponseType=} opt_responseType The response type of
+ * this request; defaults to goog.net.XhrIo.ResponseType.DEFAULT.
* @return {goog.net.XhrManager.Request} The queued request object.
*/
goog.net.XhrManager.prototype.send = function(
@@ -188,7 +190,8 @@
opt_headers,
opt_priority,
opt_callback,
- opt_maxRetries) {
+ opt_maxRetries,
+ opt_responseType) {
var requests = this.requests_;
// Check if there is already a request with the given id.
if (requests.get(id)) {
@@ -203,7 +206,8 @@
opt_content,
opt_headers,
opt_callback,
- goog.isDef(opt_maxRetries) ? opt_maxRetries : this.maxRetries_);
+ goog.isDef(opt_maxRetries) ? opt_maxRetries : this.maxRetries_,
+ opt_responseType);
this.requests_.set(id, request);
// Setup the callback for the pool.
@@ -263,6 +267,7 @@
// Set properties for the XhrIo.
xhrIo.setTimeoutInterval(this.timeoutInterval_);
+ xhrIo.setResponseType(request.getResponseType());
// Add a reference to the XhrIo object to the request.
request.xhrIo = request.xhrLite = xhrIo;
@@ -546,12 +551,14 @@
* complete. NOTE: Only 1 callback supported across all events.
* @param {number=} opt_maxRetries The maximum number of times the request
* should be retried (Default: 1).
+ * @param {goog.net.XhrIo.ResponseType=} opt_responseType The response type of
+ * this request; defaults to goog.net.XhrIo.ResponseType.DEFAULT.
*
* @constructor
* @extends {goog.Disposable}
*/
goog.net.XhrManager.Request = function(url, xhrEventCallback, opt_method,
- opt_content, opt_headers, opt_callback, opt_maxRetries) {
+ opt_content, opt_headers, opt_callback, opt_maxRetries, opt_responseType) {
goog.Disposable.call(this);
/**
@@ -625,6 +632,13 @@
this.completeCallback_ = opt_callback;
/**
+ * A response type to set on this.xhrIo when it's populated.
+ * @type {!goog.net.XhrIo.ResponseType}
+ * @private
+ */
+ this.responseType_ = opt_responseType || goog.net.XhrIo.ResponseType.DEFAULT;
+
+ /**
* The XhrIo instance handling this request. Set in handleAvailableXhr.
* @type {goog.net.XhrIo}
*/
@@ -761,6 +775,17 @@
};
+/**
+ * Gets the response type that will be set on this request's XhrIo when it's
+ * available.
+ * @return {!goog.net.XhrIo.ResponseType} The response type to be set
+ * when an XhrIo becomes available to this request.
+ */
+goog.net.XhrManager.Request.prototype.getResponseType = function() {
+ return this.responseType_;
+};
+
+
/** @override */
goog.net.XhrManager.Request.prototype.disposeInternal = function() {
goog.net.XhrManager.Request.superClass_.disposeInternal.call(this);
diff --git a/third_party/closure/goog/net/xpc/crosspagechannel.js b/third_party/closure/goog/net/xpc/crosspagechannel.js
index 6c5645e..2efb366 100644
--- a/third_party/closure/goog/net/xpc/crosspagechannel.js
+++ b/third_party/closure/goog/net/xpc/crosspagechannel.js
@@ -109,7 +109,7 @@
goog.uri.utils.getHost(cfg[goog.net.xpc.CfgFields.PEER_URI] || '') +
'/robots.txt';
- goog.net.xpc.channels_[this.name] = this;
+ goog.net.xpc.channels[this.name] = this;
goog.events.listen(window, 'unload',
goog.net.xpc.CrossPageChannel.disposeAll_);
@@ -199,6 +199,28 @@
/**
+ * Returns the configuration object for this channel.
+ * Package private. Do not call from outside goog.net.xpc.
+ *
+ * @return {Object} The configuration object for this channel.
+ */
+goog.net.xpc.CrossPageChannel.prototype.getConfig = function() {
+ return this.cfg_;
+};
+
+
+/**
+ * Returns a reference to the iframe-element.
+ * Package private. Do not call from outside goog.net.xpc.
+ *
+ * @return {Object} A reference to the iframe-element.
+ */
+goog.net.xpc.CrossPageChannel.prototype.getIframeElement = function() {
+ return this.iframeElement_;
+};
+
+
+/**
* Sets the window object the foreign document resides in.
*
* @param {Object} peerWindowObject The window object of the peer.
@@ -211,7 +233,7 @@
/**
* Returns the window object the foreign document resides in.
- * Package private.
+ * Package private. Do not call from outside goog.net.xpc.
*
* @return {Object} The window object of the peer.
*/
@@ -222,7 +244,7 @@
/**
* Determines whether the peer window is available (e.g. not closed).
- * Package private.
+ * Package private. Do not call from outside goog.net.xpc.
*
* @return {boolean} Whether the peer window is available.
*/
@@ -599,9 +621,9 @@
/**
* Called by the transport in case of an unrecoverable failure.
- * @private
+ * Package private. Do not call from outside goog.net.xpc.
*/
-goog.net.xpc.CrossPageChannel.prototype.notifyTransportError_ = function() {
+goog.net.xpc.CrossPageChannel.prototype.notifyTransportError = function() {
goog.net.xpc.logger.info('Transport Error');
this.close();
};
@@ -631,7 +653,11 @@
/**
- * Delivers messages to the appropriate service-handler.
+ * Delivers messages to the appropriate service-handler. Named xpcDeliver to
+ * avoid name conflict with {@code deliver} function in superclass
+ * goog.messaging.AbstractChannel.
+ *
+ * Package private. Do not call from outside goog.net.xpc.
*
* @param {string} serviceName The name of the port.
* @param {string} payload The payload.
@@ -640,34 +666,17 @@
* the PEER_HOSTNAME parameter was provided, they must match or the message
* will be rejected.
*/
-goog.net.xpc.CrossPageChannel.prototype.safeDeliver = function(
- serviceName, payload, opt_origin) {
- this.deliver_(serviceName, payload, opt_origin);
-};
-
-
-/**
- * Delivers messages to the appropriate service-handler.
- *
- * @param {string} serviceName The name of the port.
- * @param {string} payload The payload.
- * @param {string=} opt_origin An optional origin for the message, where the
- * underlying transport makes that available. If this is specified, and
- * the PEER_HOSTNAME parameter was provided, they must match or the message
- * will be rejected.
- * @private
- */
-goog.net.xpc.CrossPageChannel.prototype.deliver_ = function(
+goog.net.xpc.CrossPageChannel.prototype.xpcDeliver = function(
serviceName, payload, opt_origin) {
- // This covers the very rare (but producable) case where the inner frame
+ // This check covers the very rare (but producable) case where the inner frame
// becomes ready and sends its setup message while the outer frame is
- // deferring its connect method waiting for the inner frame to be ready.
- // Without it that message can be passed to deliver_, which is unable to
- // process it because the channel is not yet fully configured.
+ // deferring its connect method waiting for the inner frame to be ready. The
+ // resulting deferral ensures the message will not be processed until the
+ // channel is fully configured.
if (this.peerWindowDeferred_) {
this.deferredDeliveries_.push(
- goog.bind(this.deliver_, this, serviceName, payload, opt_origin));
+ goog.bind(this.xpcDeliver, this, serviceName, payload, opt_origin));
return;
}
@@ -679,7 +688,7 @@
}
if (this.isDisposed()) {
- goog.net.xpc.logger.warning('CrossPageChannel::deliver_(): Disposed.');
+ goog.net.xpc.logger.warning('CrossPageChannel::xpcDeliver(): Disposed.');
} else if (!serviceName ||
serviceName == goog.net.xpc.TRANSPORT_SERVICE_) {
this.transport_.transportServiceHandler(payload);
@@ -688,7 +697,8 @@
if (this.isConnected()) {
this.deliver(this.unescapeServiceName_(serviceName), payload);
} else {
- goog.net.xpc.logger.info('CrossPageChannel::deliver_(): Not connected.');
+ goog.net.xpc.logger.info(
+ 'CrossPageChannel::xpcDeliver(): Not connected.');
}
}
};
@@ -772,7 +782,7 @@
this.peerWindowObject_ = null;
this.iframeElement_ = null;
- delete goog.net.xpc.channels_[this.name];
+ delete goog.net.xpc.channels[this.name];
goog.dispose(this.peerLoadHandler_);
delete this.peerLoadHandler_;
goog.base(this, 'disposeInternal');
@@ -784,7 +794,7 @@
* @private
*/
goog.net.xpc.CrossPageChannel.disposeAll_ = function() {
- for (var name in goog.net.xpc.channels_) {
- goog.dispose(goog.net.xpc.channels_[name]);
+ for (var name in goog.net.xpc.channels) {
+ goog.dispose(goog.net.xpc.channels[name]);
}
};
diff --git a/third_party/closure/goog/net/xpc/frameelementmethodtransport.js b/third_party/closure/goog/net/xpc/frameelementmethodtransport.js
index 650da64..0b601dd 100644
--- a/third_party/closure/goog/net/xpc/frameelementmethodtransport.js
+++ b/third_party/closure/goog/net/xpc/frameelementmethodtransport.js
@@ -81,7 +81,7 @@
* @override
*/
goog.net.xpc.FrameElementMethodTransport.prototype.transportType =
- goog.net.xpc.TransportTypes.FRAME_ELEMENT_METHOD;
+ goog.net.xpc.TransportTypes.FRAME_ELEMENT_METHOD;
/**
@@ -116,7 +116,7 @@
goog.net.xpc.FrameElementMethodTransport.prototype.connect = function() {
if (this.channel_.getRole() == goog.net.xpc.CrossPageChannelRole.OUTER) {
// get shortcut to iframe-element
- this.iframeElm_ = this.channel_.iframeElement_;
+ this.iframeElm_ = this.channel_.getIframeElement();
// add the gateway function to the iframe-element
// (to be called by the peer)
@@ -205,7 +205,7 @@
goog.net.xpc.FrameElementMethodTransport.prototype.incoming_ =
function(serviceName, payload) {
if (!this.recursive_ && this.queue_.length == 0) {
- this.channel_.deliver_(serviceName, payload);
+ this.channel_.xpcDeliver(serviceName, payload);
}
else {
this.queue_.push({serviceName: serviceName, payload: payload});
@@ -224,7 +224,7 @@
function() {
while (this.queue_.length) {
var msg = this.queue_.shift();
- this.channel_.deliver_(msg.serviceName, msg.payload);
+ this.channel_.xpcDeliver(msg.serviceName, msg.payload);
}
};
diff --git a/third_party/closure/goog/net/xpc/iframepollingtransport.js b/third_party/closure/goog/net/xpc/iframepollingtransport.js
index 5944b8e..9f52c44 100644
--- a/third_party/closure/goog/net/xpc/iframepollingtransport.js
+++ b/third_party/closure/goog/net/xpc/iframepollingtransport.js
@@ -60,14 +60,16 @@
* @type {string}
* @private
*/
- this.sendUri_ = this.channel_.cfg_[goog.net.xpc.CfgFields.PEER_POLL_URI];
+ this.sendUri_ =
+ this.channel_.getConfig()[goog.net.xpc.CfgFields.PEER_POLL_URI];
/**
* The URI which is polled for incoming messages.
* @type {string}
* @private
*/
- this.rcvUri_ = this.channel_.cfg_[goog.net.xpc.CfgFields.LOCAL_POLL_URI];
+ this.rcvUri_ =
+ this.channel_.getConfig()[goog.net.xpc.CfgFields.LOCAL_POLL_URI];
/**
* The queue to hold messages which can't be sent immediately.
@@ -174,7 +176,7 @@
/**
* Safely retrieves the frames from the peer window. If an error is thrown
* (e.g. the window is closing) an empty frame object is returned.
- * @return {!Object.<!Window>}
+ * @return {!Object.<!Window>} The frames from the peer window.
* @private
*/
goog.net.xpc.IframePollingTransport.prototype.getPeerFrames_ = function() {
@@ -193,7 +195,8 @@
/**
* Safely retrieves the peer frame with the specified name.
* @param {string} frameName The name of the peer frame to retrieve.
- * @return {Window}
+ * @return {Window} The peer frame with the specified name.
+ * @private
*/
goog.net.xpc.IframePollingTransport.prototype.getPeerFrame_ = function(
frameName) {
@@ -474,7 +477,7 @@
for (var i = 0, m; i < this.deliveryQueue_.length; i++) {
m = this.deliveryQueue_[i];
- this.channel_.deliver_(m.service, m.payload);
+ this.channel_.xpcDeliver(m.service, m.payload);
}
delete this.deliveryQueue_;
}
@@ -619,7 +622,7 @@
push({service: service, payload: payload});
goog.net.xpc.logger.finest('queued delivery');
} else {
- this.channel_.deliver_(service, payload);
+ this.channel_.xpcDeliver(service, payload);
}
};
@@ -722,23 +725,25 @@
* @private
*/
goog.net.xpc.IframePollingTransport.receive_ = function() {
+ var receivers = goog.net.xpc.IframePollingTransport.receivers_;
+ var receiver;
var rcvd = false;
+
/** @preserveTry */
try {
- for (var i = 0, l = goog.net.xpc.IframePollingTransport.receivers_.length;
- i < l; i++) {
- rcvd = rcvd ||
- goog.net.xpc.IframePollingTransport.receivers_[i].receive();
+ for (var i = 0; receiver = receivers[i]; i++) {
+ rcvd = rcvd || receiver.receive();
}
} catch (e) {
goog.net.xpc.logger.info('receive_() failed: ' + e);
+
// Notify the channel that the transport had an error.
- goog.net.xpc.IframePollingTransport.receivers_[i].
- transport_.channel_.notifyTransportError_();
- // notifyTransportError_() closes the channel and dispoases the transport.
+ receiver.transport_.channel_.notifyTransportError();
+
+ // notifyTransportError() closes the channel and disposes the transport.
// If there are no other channels present, this.receivers_ will now be empty
- // and there is not need to keep polling.
- if (!goog.net.xpc.IframePollingTransport.receivers_.length) {
+ // and there is no need to keep polling.
+ if (!receivers.length) {
return;
}
}
@@ -856,7 +861,8 @@
* goog.net.xpc.IframePollingTransport.Receiver
*
* @constructor
- * @param {goog.net.xpc.Transport} transport The transport to receive from.
+ * @param {goog.net.xpc.IframePollingTransport} transport The transport to
+ * receive from.
* @param {Object} windowObj The window-object to poll for location-changes.
* @param {Function} callback The callback-function to be called when
* location has changed.
@@ -864,7 +870,11 @@
goog.net.xpc.IframePollingTransport.Receiver = function(transport,
windowObj,
callback) {
-
+ /**
+ * The transport to receive from.
+ * @type {goog.net.xpc.IframePollingTransport}
+ * @private
+ */
this.transport_ = transport;
this.rcvFrame_ = windowObj;
diff --git a/third_party/closure/goog/net/xpc/iframerelaytransport.js b/third_party/closure/goog/net/xpc/iframerelaytransport.js
index 78892f2..1281c49 100644
--- a/third_party/closure/goog/net/xpc/iframerelaytransport.js
+++ b/third_party/closure/goog/net/xpc/iframerelaytransport.js
@@ -56,14 +56,15 @@
* @private
*/
this.peerRelayUri_ =
- this.channel_.cfg_[goog.net.xpc.CfgFields.PEER_RELAY_URI];
+ this.channel_.getConfig()[goog.net.xpc.CfgFields.PEER_RELAY_URI];
/**
* The id of the iframe the peer page lives in.
* @type {string}
* @private
*/
- this.peerIframeId_ = this.channel_.cfg_[goog.net.xpc.CfgFields.IFRAME_ID];
+ this.peerIframeId_ =
+ this.channel_.getConfig()[goog.net.xpc.CfgFields.IFRAME_ID];
if (goog.userAgent.WEBKIT) {
goog.net.xpc.IframeRelayTransport.startCleanupTimer_();
@@ -186,7 +187,7 @@
* @override
*/
goog.net.xpc.IframeRelayTransport.prototype.transportType =
- goog.net.xpc.TransportTypes.IFRAME_RELAY;
+ goog.net.xpc.TransportTypes.IFRAME_RELAY;
/**
@@ -250,13 +251,13 @@
}
// We've received all outstanding fragments; combine what we've received
- // into payload and fall out to the call to deliver_.
+ // into payload and fall out to the call to xpcDeliver.
payload = fragmentInfo.fragments.join('');
delete goog.net.xpc.IframeRelayTransport.fragmentMap_[messageIdStr];
}
- goog.net.xpc.channels_[channelName].deliver_(service,
- decodeURIComponent(payload));
+ goog.net.xpc.channels[channelName].
+ xpcDeliver(service, decodeURIComponent(payload));
};
diff --git a/third_party/closure/goog/net/xpc/nativemessagingtransport.js b/third_party/closure/goog/net/xpc/nativemessagingtransport.js
index a22e5ed..e0674fa 100644
--- a/third_party/closure/goog/net/xpc/nativemessagingtransport.js
+++ b/third_party/closure/goog/net/xpc/nativemessagingtransport.js
@@ -311,9 +311,9 @@
// - channel was created in a different namespace
// - message was sent to the wrong window
// - channel has become stale (e.g. caching iframes and back clicks)
- var channel = goog.net.xpc.channels_[channelName];
+ var channel = goog.net.xpc.channels[channelName];
if (channel) {
- channel.deliver_(service, payload, msgEvt.getBrowserEvent().origin);
+ channel.xpcDeliver(service, payload, msgEvt.getBrowserEvent().origin);
return true;
}
@@ -321,8 +321,8 @@
goog.net.xpc.NativeMessagingTransport.parseTransportPayload_(payload)[0];
// Check if there are any stale channel names that can be updated.
- for (var staleChannelName in goog.net.xpc.channels_) {
- var staleChannel = goog.net.xpc.channels_[staleChannelName];
+ for (var staleChannelName in goog.net.xpc.channels) {
+ var staleChannel = goog.net.xpc.channels[staleChannelName];
if (staleChannel.getRole() == goog.net.xpc.CrossPageChannelRole.INNER &&
!staleChannel.isConnected() &&
service == goog.net.xpc.TRANSPORT_SERVICE_ &&
@@ -338,10 +338,10 @@
goog.net.xpc.logger.fine('changing channel name to ' + channelName);
staleChannel.name = channelName;
// Remove old stale pointer to channel.
- delete goog.net.xpc.channels_[staleChannelName];
+ delete goog.net.xpc.channels[staleChannelName];
// Create fresh pointer to channel.
- goog.net.xpc.channels_[channelName] = staleChannel;
- staleChannel.deliver_(service, payload);
+ goog.net.xpc.channels[channelName] = staleChannel;
+ staleChannel.xpcDeliver(service, payload);
return true;
}
}
@@ -522,7 +522,7 @@
*/
goog.net.xpc.NativeMessagingTransport.prototype.send = function(service,
payload) {
- var win = this.channel_.peerWindowObject_;
+ var win = this.channel_.getPeerWindowObject();
if (!win) {
goog.net.xpc.logger.fine('send(): window not ready');
return;
diff --git a/third_party/closure/goog/net/xpc/nixtransport.js b/third_party/closure/goog/net/xpc/nixtransport.js
index 62080d1..7e00b7b 100644
--- a/third_party/closure/goog/net/xpc/nixtransport.js
+++ b/third_party/closure/goog/net/xpc/nixtransport.js
@@ -39,6 +39,7 @@
goog.require('goog.reflect');
+
/**
* NIX method transport.
*
@@ -153,7 +154,7 @@
window.opener = /** @type {Window} */ ({});
isSupported = goog.reflect.canAccessProperty(window, 'opener');
window.opener = oldOpener;
- } catch(e) { }
+ } catch (e) { }
return isSupported;
};
@@ -165,6 +166,7 @@
* Note that this method can be called multiple times, as
* it internally checks whether the work is necessary before
* proceeding.
+ * @param {Window} listenWindow The window containing the affected page.
* @private
*/
goog.net.xpc.NixTransport.conductGlobalSetup_ = function(listenWindow) {
@@ -174,79 +176,79 @@
// Inject the VBScript code needed.
var vbscript =
- // We create a class to act as a wrapper for
- // a Javascript call, to prevent a break in of
- // the context.
- 'Class ' + goog.net.xpc.NixTransport.NIX_WRAPPER + '\n ' +
+ // We create a class to act as a wrapper for
+ // a Javascript call, to prevent a break in of
+ // the context.
+ 'Class ' + goog.net.xpc.NixTransport.NIX_WRAPPER + '\n ' +
- // An internal member for keeping track of the
- // transport for which this wrapper exists.
- 'Private m_Transport\n' +
+ // An internal member for keeping track of the
+ // transport for which this wrapper exists.
+ 'Private m_Transport\n' +
- // An internal member for keeping track of the
- // auth token associated with the context that
- // created this wrapper. Used for validation
- // purposes.
- 'Private m_Auth\n' +
+ // An internal member for keeping track of the
+ // auth token associated with the context that
+ // created this wrapper. Used for validation
+ // purposes.
+ 'Private m_Auth\n' +
- // Method for internally setting the value
- // of the m_Transport property. We have the
- // isEmpty check to prevent the transport
- // from being overridden with an illicit
- // object by a malicious party.
- 'Public Sub SetTransport(transport)\n' +
- 'If isEmpty(m_Transport) Then\n' +
- 'Set m_Transport = transport\n' +
- 'End If\n' +
- 'End Sub\n' +
+ // Method for internally setting the value
+ // of the m_Transport property. We have the
+ // isEmpty check to prevent the transport
+ // from being overridden with an illicit
+ // object by a malicious party.
+ 'Public Sub SetTransport(transport)\n' +
+ 'If isEmpty(m_Transport) Then\n' +
+ 'Set m_Transport = transport\n' +
+ 'End If\n' +
+ 'End Sub\n' +
- // Method for internally setting the value
- // of the m_Auth property. We have the
- // isEmpty check to prevent the transport
- // from being overridden with an illicit
- // object by a malicious party.
- 'Public Sub SetAuth(auth)\n' +
- 'If isEmpty(m_Auth) Then\n' +
- 'm_Auth = auth\n' +
- 'End If\n' +
- 'End Sub\n' +
+ // Method for internally setting the value
+ // of the m_Auth property. We have the
+ // isEmpty check to prevent the transport
+ // from being overridden with an illicit
+ // object by a malicious party.
+ 'Public Sub SetAuth(auth)\n' +
+ 'If isEmpty(m_Auth) Then\n' +
+ 'm_Auth = auth\n' +
+ 'End If\n' +
+ 'End Sub\n' +
- // Returns the auth token to the gadget, so it can
- // confirm a match before initiating the connection
- 'Public Function GetAuthToken()\n ' +
- 'GetAuthToken = m_Auth\n' +
- 'End Function\n' +
+ // Returns the auth token to the gadget, so it can
+ // confirm a match before initiating the connection
+ 'Public Function GetAuthToken()\n ' +
+ 'GetAuthToken = m_Auth\n' +
+ 'End Function\n' +
- // A wrapper method which causes a
- // message to be sent to the other context.
- 'Public Sub SendMessage(service, payload)\n ' +
- 'Call m_Transport.' +
- goog.net.xpc.NixTransport.NIX_HANDLE_MESSAGE + '(service, payload)\n' +
- 'End Sub\n' +
+ // A wrapper method which causes a
+ // message to be sent to the other context.
+ 'Public Sub SendMessage(service, payload)\n ' +
+ 'Call m_Transport.' +
+ goog.net.xpc.NixTransport.NIX_HANDLE_MESSAGE + '(service, payload)\n' +
+ 'End Sub\n' +
- // Method for setting up the inner->outer
- // channel.
- 'Public Sub CreateChannel(channel)\n ' +
- 'Call m_Transport.' +
- goog.net.xpc.NixTransport.NIX_CREATE_CHANNEL + '(channel)\n' +
- 'End Sub\n' +
+ // Method for setting up the inner->outer
+ // channel.
+ 'Public Sub CreateChannel(channel)\n ' +
+ 'Call m_Transport.' +
+ goog.net.xpc.NixTransport.NIX_CREATE_CHANNEL + '(channel)\n' +
+ 'End Sub\n' +
- // An empty field with a unique identifier to
- // prevent the code from confusing this wrapper
- // with a run-of-the-mill value found in window.opener.
- 'Public Sub ' + goog.net.xpc.NixTransport.NIX_ID_FIELD + '()\n ' +
- 'End Sub\n' +
- 'End Class\n ' +
+ // An empty field with a unique identifier to
+ // prevent the code from confusing this wrapper
+ // with a run-of-the-mill value found in window.opener.
+ 'Public Sub ' + goog.net.xpc.NixTransport.NIX_ID_FIELD + '()\n ' +
+ 'End Sub\n' +
+ 'End Class\n ' +
- // Function to get a reference to the wrapper.
- 'Function ' +
- goog.net.xpc.NixTransport.NIX_GET_WRAPPER + '(transport, auth)\n' +
- 'Dim wrap\n' +
- 'Set wrap = New ' + goog.net.xpc.NixTransport.NIX_WRAPPER + '\n' +
- 'wrap.SetTransport transport\n' +
- 'wrap.SetAuth auth\n' +
- 'Set ' + goog.net.xpc.NixTransport.NIX_GET_WRAPPER + ' = wrap\n' +
- 'End Function';
+ // Function to get a reference to the wrapper.
+ 'Function ' +
+ goog.net.xpc.NixTransport.NIX_GET_WRAPPER + '(transport, auth)\n' +
+ 'Dim wrap\n' +
+ 'Set wrap = New ' + goog.net.xpc.NixTransport.NIX_WRAPPER + '\n' +
+ 'wrap.SetTransport transport\n' +
+ 'wrap.SetAuth auth\n' +
+ 'Set ' + goog.net.xpc.NixTransport.NIX_GET_WRAPPER + ' = wrap\n' +
+ 'End Function';
try {
listenWindow.execScript(vbscript, 'vbscript');
@@ -266,7 +268,7 @@
* @override
*/
goog.net.xpc.NixTransport.prototype.transportType =
- goog.net.xpc.TransportTypes.NIX;
+ goog.net.xpc.TransportTypes.NIX;
/**
@@ -321,14 +323,14 @@
// Get shortcut to iframe-element that contains the inner
// page.
- var innerFrame = this.channel_.iframeElement_;
+ var innerFrame = this.channel_.getIframeElement();
try {
// Attempt to place the NIX wrapper object into the inner
// frame's opener property.
- innerFrame.contentWindow.opener =
- this.getWindow()[goog.net.xpc.NixTransport.NIX_GET_WRAPPER]
- (this, this.authToken_);
+ var theWindow = this.getWindow();
+ var getWrapper = theWindow[goog.net.xpc.NixTransport.NIX_GET_WRAPPER];
+ innerFrame.contentWindow.opener = getWrapper(this, this.authToken_);
this.localSetupCompleted_ = true;
}
catch (e) {
@@ -378,9 +380,9 @@
// Complete the construction of the channel by sending our own
// wrapper to the container via the channel they gave us.
- this.nixChannel_['CreateChannel'](
- this.getWindow()[goog.net.xpc.NixTransport.NIX_GET_WRAPPER](this,
- this.authToken_));
+ var theWindow = this.getWindow();
+ var getWrapper = theWindow[goog.net.xpc.NixTransport.NIX_GET_WRAPPER];
+ this.nixChannel_['CreateChannel'](getWrapper(this, this.authToken_));
this.localSetupCompleted_ = true;
@@ -410,25 +412,25 @@
* @private
*/
goog.net.xpc.NixTransport.prototype.createChannel_ = function(channel) {
- // Verify that the channel is in fact a NIX wrapper.
- if (typeof channel != 'unknown' ||
- !(goog.net.xpc.NixTransport.NIX_ID_FIELD in channel)) {
- goog.net.xpc.logger.severe('Invalid NIX channel given to createChannel_');
- }
+ // Verify that the channel is in fact a NIX wrapper.
+ if (typeof channel != 'unknown' ||
+ !(goog.net.xpc.NixTransport.NIX_ID_FIELD in channel)) {
+ goog.net.xpc.logger.severe('Invalid NIX channel given to createChannel_');
+ }
- this.nixChannel_ = channel;
+ this.nixChannel_ = channel;
- // Ensure that the NIX channel given to use is valid.
- var remoteAuthToken = this.nixChannel_['GetAuthToken']();
+ // Ensure that the NIX channel given to use is valid.
+ var remoteAuthToken = this.nixChannel_['GetAuthToken']();
- if (remoteAuthToken != this.remoteAuthToken_) {
- goog.net.xpc.logger.severe('Invalid auth token from other party');
- return;
- }
+ if (remoteAuthToken != this.remoteAuthToken_) {
+ goog.net.xpc.logger.severe('Invalid auth token from other party');
+ return;
+ }
- // Indicate to the CrossPageChannel that the channel is setup
- // and ready to use.
- this.channel_.notifyConnected();
+ // Indicate to the CrossPageChannel that the channel is setup
+ // and ready to use.
+ this.channel_.notifyConnected();
};
@@ -444,7 +446,7 @@
function(serviceName, payload) {
/** @this {goog.net.xpc.NixTransport} */
var deliveryHandler = function() {
- this.channel_.safeDeliver(serviceName, payload);
+ this.channel_.xpcDeliver(serviceName, payload);
};
this.getWindow().setTimeout(goog.bind(deliveryHandler, this), 1);
};
diff --git a/third_party/closure/goog/net/xpc/xpc.js b/third_party/closure/goog/net/xpc/xpc.js
index edd2ac0..be1b62e 100644
--- a/third_party/closure/goog/net/xpc/xpc.js
+++ b/third_party/closure/goog/net/xpc/xpc.js
@@ -252,10 +252,11 @@
/**
* Object holding active channels.
- * @type {Object}
- * @private
+ * Package private. Do not call from outside goog.net.xpc.
+ *
+ * @type {Object.<string, goog.net.xpc.CrossPageChannel>}
*/
-goog.net.xpc.channels_ = {};
+goog.net.xpc.channels = {};
/**
diff --git a/third_party/closure/goog/object/object.js b/third_party/closure/goog/object/object.js
index 66d3dad..b20cf86 100644
--- a/third_party/closure/goog/object/object.js
+++ b/third_party/closure/goog/object/object.js
@@ -22,11 +22,12 @@
/**
* Calls a function for each element in an object/map/hash.
*
- * @param {Object} obj The object over which to iterate.
- * @param {Function} f The function to call for every element. This function
- * takes 3 arguments (the element, the index and the object)
- * and the return value is irrelevant.
- * @param {Object=} opt_obj This is used as the 'this' object within f.
+ * @param {Object.<K,V>} obj The object over which to iterate.
+ * @param {function(this:T,V,?,Object.<K,V>):?} f The function to call
+ * for every element. This function takes 3 arguments (the element, the
+ * index and the object) and the return value is ignored.
+ * @param {T=} opt_obj This is used as the 'this' object within f.
+ * @template T,K,V
*/
goog.object.forEach = function(obj, f, opt_obj) {
for (var key in obj) {
@@ -39,15 +40,17 @@
* Calls a function for each element in an object/map/hash. If that call returns
* true, adds the element to a new object.
*
- * @param {Object} obj The object over which to iterate.
- * @param {Function} f The function to call for every element. This
+ * @param {Object.<K,V>} obj The object over which to iterate.
+ * @param {function(this:T,K,?,Object.<K,V>):boolean} f The function to call
+ * for every element. This
* function takes 3 arguments (the element, the index and the object)
* and should return a boolean. If the return value is true the
* element is added to the result object. If it is false the
* element is not included.
- * @param {Object=} opt_obj This is used as the 'this' object within f.
- * @return {!Object} a new object in which only elements that passed the test
- * are present.
+ * @param {T=} opt_obj This is used as the 'this' object within f.
+ * @return {!Object.<K,V>} a new object in which only elements that passed the
+ * test are present.
+ * @template T,K,V
*/
goog.object.filter = function(obj, f, opt_obj) {
var res = {};
@@ -64,13 +67,15 @@
* For every element in an object/map/hash calls a function and inserts the
* result into a new object.
*
- * @param {Object} obj The object over which to iterate.
- * @param {Function} f The function to call for every element. This function
+ * @param {Object.<K,V>} obj The object over which to iterate.
+ * @param {function(this:T,V,?,Object.<K,V>):R} f The function to call
+ * for every element. This function
* takes 3 arguments (the element, the index and the object)
* and should return something. The result will be inserted
* into a new object.
- * @param {Object=} opt_obj This is used as the 'this' object within f.
- * @return {!Object} a new object with the results from f.
+ * @param {T=} opt_obj This is used as the 'this' object within f.
+ * @return {!Object.<T,R>} a new object with the results from f.
+ * @template T,K,V,R
*/
goog.object.map = function(obj, f, opt_obj) {
var res = {};
@@ -86,12 +91,14 @@
* call returns true, returns true (without checking the rest). If
* all calls return false, returns false.
*
- * @param {Object} obj The object to check.
- * @param {Function} f The function to call for every element. This function
+ * @param {Object.<K,V>} obj The object to check.
+ * @param {function(this:T,K,?,Object.<K,V>):boolean} f The function to
+ * call for every element. This function
* takes 3 arguments (the element, the index and the object) and should
* return a boolean.
- * @param {Object=} opt_obj This is used as the 'this' object within f.
+ * @param {T=} opt_obj This is used as the 'this' object within f.
* @return {boolean} true if any element passes the test.
+ * @template T,K,V
*/
goog.object.some = function(obj, f, opt_obj) {
for (var key in obj) {
@@ -108,12 +115,14 @@
* all calls return true, returns true. If any call returns false, returns
* false at this point and does not continue to check the remaining elements.
*
- * @param {Object} obj The object to check.
- * @param {Function} f The function to call for every element. This function
+ * @param {Object.<K,V>} obj The object to check.
+ * @param {?function(this:T,V,?,Object.<K,V>):boolean} f The function to
+ * call for every element. This function
* takes 3 arguments (the element, the index and the object) and should
* return a boolean.
- * @param {Object=} opt_obj This is used as the 'this' object within f.
+ * @param {T=} opt_obj This is used as the 'this' object within f.
* @return {boolean} false if any element fails the test.
+ * @template T,K,V
*/
goog.object.every = function(obj, f, opt_obj) {
for (var key in obj) {
@@ -164,8 +173,9 @@
* For map literals the returned value will be the first one in most of the
* browsers (a know exception is Konqueror).
*
- * @param {Object} obj The object to pick a value from.
- * @return {*} The value or undefined if the object is empty.
+ * @param {Object.<K,V>} obj The object to pick a value from.
+ * @return {V|undefined} The value or undefined if the object is empty.
+ * @template K,V
*/
goog.object.getAnyValue = function(obj) {
for (var key in obj) {
@@ -178,9 +188,10 @@
* Whether the object/hash/map contains the given object as a value.
* An alias for goog.object.containsValue(obj, val).
*
- * @param {Object} obj The object in which to look for val.
- * @param {*} val The object for which to check.
+ * @param {Object.<K,V>} obj The object in which to look for val.
+ * @param {V} val The object for which to check.
* @return {boolean} true if val is present.
+ * @template K,V
*/
goog.object.contains = function(obj, val) {
return goog.object.containsValue(obj, val);
@@ -190,8 +201,9 @@
/**
* Returns the values of the object/map/hash.
*
- * @param {Object} obj The object from which to get the values.
- * @return {!Array} The values in the object/map/hash.
+ * @param {Object.<K,V>} obj The object from which to get the values.
+ * @return {!Array.<V>} The values in the object/map/hash.
+ * @template K,V
*/
goog.object.getValues = function(obj) {
var res = [];
@@ -262,9 +274,10 @@
/**
* Whether the object/map/hash contains the given value. This is O(n).
*
- * @param {Object} obj The object in which to look for val.
- * @param {*} val The value for which to check.
+ * @param {Object.<K,V>} obj The object in which to look for val.
+ * @param {V} val The value for which to check.
* @return {boolean} true If the map contains the value.
+ * @template K,V
*/
goog.object.containsValue = function(obj, val) {
for (var key in obj) {
@@ -279,13 +292,14 @@
/**
* Searches an object for an element that satisfies the given condition and
* returns its key.
- * @param {Object} obj The object to search in.
- * @param {function(*, string, Object): boolean} f The function to call for
- * every element. Takes 3 arguments (the value, the key and the object) and
- * should return a boolean.
- * @param {Object=} opt_this An optional "this" context for the function.
+ * @param {Object.<K,V>} obj The object to search in.
+ * @param {function(this:T,V,string,Object.<K,V>):boolean} f The
+ * function to call for every element. Takes 3 arguments (the value,
+ * the key and the object) and should return a boolean.
+ * @param {T=} opt_this An optional "this" context for the function.
* @return {string|undefined} The key of an element for which the function
* returns true or undefined if no such element is found.
+ * @template T,K,V
*/
goog.object.findKey = function(obj, f, opt_this) {
for (var key in obj) {
@@ -300,13 +314,14 @@
/**
* Searches an object for an element that satisfies the given condition and
* returns its value.
- * @param {Object} obj The object to search in.
- * @param {function(*, string, Object): boolean} f The function to call for
- * every element. Takes 3 arguments (the value, the key and the object) and
- * should return a boolean.
- * @param {Object=} opt_this An optional "this" context for the function.
- * @return {*} The value of an element for which the function returns true or
+ * @param {Object.<K,V>} obj The object to search in.
+ * @param {function(this:T,V,string,Object.<K,V>):boolean} f The function
+ * to call for every element. Takes 3 arguments (the value, the key
+ * and the object) and should return a boolean.
+ * @param {T=} opt_this An optional "this" context for the function.
+ * @return {V} The value of an element for which the function returns true or
* undefined if no such element is found.
+ * @template T,K,V
*/
goog.object.findValue = function(obj, f, opt_this) {
var key = goog.object.findKey(obj, f, opt_this);
@@ -360,9 +375,10 @@
* Adds a key-value pair to the object. Throws an exception if the key is
* already in use. Use set if you want to change an existing pair.
*
- * @param {Object} obj The object to which to add the key-value pair.
+ * @param {Object.<K,V>} obj The object to which to add the key-value pair.
* @param {string} key The key to add.
- * @param {*} val The value to add.
+ * @param {V} val The value to add.
+ * @template K,V
*/
goog.object.add = function(obj, key, val) {
if (key in obj) {
@@ -375,11 +391,12 @@
/**
* Returns the value for the given key.
*
- * @param {Object} obj The object from which to get the value.
+ * @param {Object.<K,V>} obj The object from which to get the value.
* @param {string} key The key for which to get the value.
- * @param {*=} opt_val The value to return if no item is found for the given
+ * @param {R=} opt_val The value to return if no item is found for the given
* key (default is undefined).
- * @return {*} The value for the given key.
+ * @return {V|R|undefined} The value for the given key.
+ * @template K,V,R
*/
goog.object.get = function(obj, key, opt_val) {
if (key in obj) {
@@ -392,9 +409,10 @@
/**
* Adds a key-value pair to the object/map/hash.
*
- * @param {Object} obj The object to which to add the key-value pair.
+ * @param {Object.<K,V>} obj The object to which to add the key-value pair.
* @param {string} key The key to add.
- * @param {*} value The value to add.
+ * @param {K} value The value to add.
+ * @template K,V
*/
goog.object.set = function(obj, key, value) {
obj[key] = value;
@@ -404,10 +422,11 @@
/**
* Adds a key-value pair to the object/map/hash if it doesn't exist yet.
*
- * @param {Object} obj The object to which to add the key-value pair.
+ * @param {Object.<K,V>} obj The object to which to add the key-value pair.
* @param {string} key The key to add.
- * @param {*} value The value to add if the key wasn't present.
- * @return {*} The value of the entry at the end of the function.
+ * @param {V} value The value to add if the key wasn't present.
+ * @return {V} The value of the entry at the end of the function.
+ * @template K,V
*/
goog.object.setIfUndefined = function(obj, key, value) {
return key in obj ? obj[key] : (obj[key] = value);
@@ -417,8 +436,9 @@
/**
* Does a flat clone of the object.
*
- * @param {Object} obj Object to clone.
- * @return {!Object} Clone of the input object.
+ * @param {Object.<K,V>} obj Object to clone.
+ * @return {!Object.<K,V>} Clone of the input object.
+ * @template K,V
*/
goog.object.clone = function(obj) {
// We cannot use the prototype trick because a lot of methods depend on where
@@ -591,9 +611,10 @@
* In default mode, writes to this view will fail silently. In strict mode,
* they will throw an error.
*
- * @param {!Object} obj An object.
- * @return {!Object} An immutable view of that object, or the original object
- * if this browser does not support immutables.
+ * @param {!Object.<K,V>} obj An object.
+ * @return {!Object.<K,V>} An immutable view of that object, or the
+ * original object if this browser does not support immutables.
+ * @template K,V
*/
goog.object.createImmutableView = function(obj) {
var result = obj;
diff --git a/third_party/closure/goog/proto2/fielddescriptor.js b/third_party/closure/goog/proto2/fielddescriptor.js
index 77114f1..84c3abd 100644
--- a/third_party/closure/goog/proto2/fielddescriptor.js
+++ b/third_party/closure/goog/proto2/fielddescriptor.js
@@ -171,7 +171,7 @@
* @return {goog.proto2.Descriptor} The descriptor.
*/
goog.proto2.FieldDescriptor.prototype.getContainingType = function() {
- return this.parent_.descriptor_;
+ return this.parent_.getDescriptor();
};
@@ -250,7 +250,7 @@
goog.proto2.FieldDescriptor.prototype.getFieldMessageType = function() {
goog.proto2.Util.assert(this.isCompositeType(), 'Expected message or group');
- return this.nativeType_.descriptor_;
+ return this.nativeType_.getDescriptor();
};
diff --git a/third_party/closure/goog/proto2/message.js b/third_party/closure/goog/proto2/message.js
index 86532d6..c0def32 100644
--- a/third_party/closure/goog/proto2/message.js
+++ b/third_party/closure/goog/proto2/message.js
@@ -39,22 +39,12 @@
*/
this.values_ = {};
- // The descriptor_ is static to the message function that is being created.
- // Therefore, we retrieve it via the constructor.
-
- /**
- * Stores the information (i.e. metadata) about this message.
- * @type {!goog.proto2.Descriptor}
- * @private
- */
- this.descriptor_ = this.constructor.descriptor_;
-
/**
* Stores the field information (i.e. metadata) about this message.
* @type {Object.<number, !goog.proto2.FieldDescriptor>}
* @private
*/
- this.fields_ = this.descriptor_.getFieldsMap();
+ this.fields_ = this.getDescriptor().getFieldsMap();
/**
* The lazy deserializer for this message instance, if any.
@@ -109,6 +99,34 @@
/**
+ * All instances of goog.proto2.Message should have a static descriptorObj_
+ * property. This is a JSON representation of a Descriptor. The real Descriptor
+ * will be deserialized lazily in the getDescriptor() method.
+ *
+ * This declaration is just here for documentation purposes.
+ * goog.proto2.Message does not have its own descriptor.
+ *
+ * @type {undefined}
+ * @private
+ */
+goog.proto2.Message.descriptorObj_;
+
+
+/**
+ * All instances of goog.proto2.Message should have a static descriptor_
+ * property. The Descriptor will be deserialized lazily in the getDescriptor()
+ * method.
+ *
+ * This declaration is just here for documentation purposes.
+ * goog.proto2.Message does not have its own descriptor.
+ *
+ * @type {undefined}
+ * @private
+ */
+goog.proto2.Message.descriptor_;
+
+
+/**
* Initializes the message with a lazy deserializer and its associated data.
* This method should be called by internal methods ONLY.
*
@@ -167,10 +185,18 @@
/**
* Returns the descriptor which describes the current message.
*
- * @return {goog.proto2.Descriptor} The descriptor.
+ * This only works if we assume people never subclass protobufs.
+ *
+ * @return {!goog.proto2.Descriptor} The descriptor.
*/
goog.proto2.Message.prototype.getDescriptor = function() {
- return this.descriptor_;
+ // NOTE(nicksantos): These sorts of indirect references to descriptor
+ // through this.constructor are fragile. See the comments
+ // in set$Metadata for more info.
+ var Ctor = this.constructor;
+ return Ctor.descriptor_ ||
+ (Ctor.descriptor_ = goog.proto2.Message.create$Descriptor(
+ Ctor, Ctor.descriptorObj_));
};
@@ -185,7 +211,7 @@
*/
goog.proto2.Message.prototype.has = function(field) {
goog.proto2.Util.assert(
- field.getContainingType() == this.descriptor_,
+ field.getContainingType() == this.getDescriptor(),
'The current message does not contain the given field');
return this.has$Value(field.getTag());
@@ -202,7 +228,7 @@
*/
goog.proto2.Message.prototype.arrayOf = function(field) {
goog.proto2.Util.assert(
- field.getContainingType() == this.descriptor_,
+ field.getContainingType() == this.getDescriptor(),
'The current message does not contain the given field');
return this.array$Values(field.getTag());
@@ -219,7 +245,7 @@
*/
goog.proto2.Message.prototype.countOf = function(field) {
goog.proto2.Util.assert(
- field.getContainingType() == this.descriptor_,
+ field.getContainingType() == this.getDescriptor(),
'The current message does not contain the given field');
return this.count$Values(field.getTag());
@@ -239,7 +265,7 @@
*/
goog.proto2.Message.prototype.get = function(field, opt_index) {
goog.proto2.Util.assert(
- field.getContainingType() == this.descriptor_,
+ field.getContainingType() == this.getDescriptor(),
'The current message does not contain the given field');
return this.get$Value(field.getTag(), opt_index);
@@ -259,7 +285,7 @@
*/
goog.proto2.Message.prototype.getOrDefault = function(field, opt_index) {
goog.proto2.Util.assert(
- field.getContainingType() == this.descriptor_,
+ field.getContainingType() == this.getDescriptor(),
'The current message does not contain the given field');
return this.get$ValueOrDefault(field.getTag(), opt_index);
@@ -276,7 +302,7 @@
*/
goog.proto2.Message.prototype.set = function(field, value) {
goog.proto2.Util.assert(
- field.getContainingType() == this.descriptor_,
+ field.getContainingType() == this.getDescriptor(),
'The current message does not contain the given field');
this.set$Value(field.getTag(), value);
@@ -293,7 +319,7 @@
*/
goog.proto2.Message.prototype.add = function(field, value) {
goog.proto2.Util.assert(
- field.getContainingType() == this.descriptor_,
+ field.getContainingType() == this.getDescriptor(),
'The current message does not contain the given field');
this.add$Value(field.getTag(), value);
@@ -307,7 +333,7 @@
*/
goog.proto2.Message.prototype.clear = function(field) {
goog.proto2.Util.assert(
- field.getContainingType() == this.descriptor_,
+ field.getContainingType() == this.getDescriptor(),
'The current message does not contain the given field');
this.clear$Field(field.getTag());
@@ -798,13 +824,14 @@
* @param {Object} metadataObj The object containing the metadata.
*/
goog.proto2.Message.set$Metadata = function(messageType, metadataObj) {
- // TODO(nicksantos): Change the code generator so that it doesn't
- // alias the message constructor. Then it will be easier for the compiler
- // to devirtualize these symbols.
- messageType.descriptor_ = goog.proto2.Message.create$Descriptor(
- /** @type {function(new:goog.proto2.Message)} */ (messageType),
- metadataObj);
+ // NOTE(nicksantos): JSCompiler's type-based optimizations really do not
+ // like indirectly defined methods (both prototype methods and
+ // static methods). This is very fragile in compiled code. I think it only
+ // really works by accident, and is highly likely to break in the future.
+ messageType.descriptorObj_ = metadataObj;
messageType.getDescriptor = function() {
- return messageType.descriptor_;
+ // The descriptor is created lazily when we instantiate a new instance.
+ return messageType.descriptor_ ||
+ (new messageType()).getDescriptor();
};
};
diff --git a/third_party/closure/goog/string/string.js b/third_party/closure/goog/string/string.js
index 7875a2b..ee4aafc 100644
--- a/third_party/closure/goog/string/string.js
+++ b/third_party/closure/goog/string/string.js
@@ -136,9 +136,10 @@
/**
- * Checks if a string is null, empty or contains only whitespaces.
+ * Checks if a string is null, undefined, empty or contains only whitespaces.
* @param {*} str The string to check.
- * @return {boolean} True if{@code str} is null, empty, or whitespace only.
+ * @return {boolean} True if{@code str} is null, undefined, empty, or
+ * whitespace only.
*/
goog.string.isEmptySafe = function(str) {
return goog.string.isEmpty(goog.string.makeSafe(str));
diff --git a/third_party/closure/goog/structs/structs.js b/third_party/closure/goog/structs/structs.js
index ec39582..c5b19c5 100644
--- a/third_party/closure/goog/structs/structs.js
+++ b/third_party/closure/goog/structs/structs.js
@@ -161,12 +161,14 @@
* Calls a function for each value in a collection. The function takes
* three arguments; the value, the key and the collection.
*
- * @param {Object} col The collection-like object.
- * @param {Function} f The function to call for every value. This function takes
+ * @param {S} col The collection-like object.
+ * @param {function(this:T,?,?,S):?} f The function to call for every value.
+ * This function takes
* 3 arguments (the value, the key or undefined if the collection has no
* notion of keys, and the collection) and the return value is irrelevant.
- * @param {Object=} opt_obj The object to be used as the value of 'this'
+ * @param {T=} opt_obj The object to be used as the value of 'this'
* within {@code f}.
+ * @template T,S
*/
goog.structs.forEach = function(col, f, opt_obj) {
if (typeof col.forEach == 'function') {
@@ -188,17 +190,19 @@
* Calls a function for every value in the collection. When a call returns true,
* adds the value to a new collection (Array is returned by default).
*
- * @param {Object} col The collection-like object.
- * @param {Function} f The function to call for every value. This function takes
+ * @param {S} col The collection-like object.
+ * @param {function(this:T,?,?,S):boolean} f The function to call for every
+ * value. This function takes
* 3 arguments (the value, the key or undefined if the collection has no
* notion of keys, and the collection) and should return a Boolean. If the
* return value is true the value is added to the result collection. If it
* is false the value is not included.
- * @param {Object=} opt_obj The object to be used as the value of 'this'
+ * @param {T=} opt_obj The object to be used as the value of 'this'
* within {@code f}.
* @return {!Object|!Array} A new collection where the passed values are
* present. If col is a key-less collection an array is returned. If col
* has keys and values a plain old JS object is returned.
+ * @template T,S
*/
goog.structs.filter = function(col, f, opt_obj) {
if (typeof col.filter == 'function') {
@@ -238,16 +242,17 @@
* Calls a function for every value in the collection and adds the result into a
* new collection (defaults to creating a new Array).
*
- * @param {Object} col The collection-like object.
- * @param {Function} f The function to call for every value. This function
- * takes 3 arguments (the value, the key or undefined if the collection has
- * no notion of keys, and the collection) and should return something. The
- * result will be used as the value in the new collection.
- * @param {Object=} opt_obj The object to be used as the value of 'this'
+ * @param {S} col The collection-like object.
+ * @param {function(this:T,?,?,S):V} f The function to call for every value.
+ * This function takes 3 arguments (the value, the key or undefined if the
+ * collection has no notion of keys, and the collection) and should return
+ * something. The result will be used as the value in the new collection.
+ * @param {T=} opt_obj The object to be used as the value of 'this'
* within {@code f}.
- * @return {!Object|!Array} A new collection with the new values. If col is a
- * key-less collection an array is returned. If col has keys and values a
- * plain old JS object is returned.
+ * @return {!Object.<V>|!Array.<V>} A new collection with the new values. If
+ * col is a key-less collection an array is returned. If col has keys and
+ * values a plain old JS object is returned.
+ * @template T,S,V
*/
goog.structs.map = function(col, f, opt_obj) {
if (typeof col.map == 'function') {
@@ -283,13 +288,15 @@
* Calls f for each value in a collection. If any call returns true this returns
* true (without checking the rest). If all returns false this returns false.
*
- * @param {Object|Array|string} col The collection-like object.
- * @param {Function} f The function to call for every value. This function takes
- * 3 arguments (the value, the key or undefined if the collection has no
- * notion of keys, and the collection) and should return a Boolean.
- * @param {Object=} opt_obj The object to be used as the value of 'this'
+ * @param {S} col The collection-like object.
+ * @param {function(this:T,?,?,S):boolean} f The function to call for every
+ * value. This function takes 3 arguments (the value, the key or undefined
+ * if the collection has no notion of keys, and the collection) and should
+ * return a boolean.
+ * @param {T=} opt_obj The object to be used as the value of 'this'
* within {@code f}.
* @return {boolean} True if any value passes the test.
+ * @template T,S
*/
goog.structs.some = function(col, f, opt_obj) {
if (typeof col.some == 'function') {
@@ -315,13 +322,15 @@
* true this returns true. If any returns false this returns false at this point
* and does not continue to check the remaining values.
*
- * @param {Object} col The collection-like object.
- * @param {Function} f The function to call for every value. This function takes
- * 3 arguments (the value, the key or undefined if the collection has no
- * notion of keys, and the collection) and should return a Boolean.
- * @param {Object=} opt_obj The object to be used as the value of 'this'
+ * @param {S} col The collection-like object.
+ * @param {function(this:T,?,?,S):boolean} f The function to call for every
+ * value. This function takes 3 arguments (the value, the key or
+ * undefined if the collection has no notion of keys, and the collection)
+ * and should return a boolean.
+ * @param {T=} opt_obj The object to be used as the value of 'this'
* within {@code f}.
* @return {boolean} True if all key-value pairs pass the test.
+ * @template T,S
*/
goog.structs.every = function(col, f, opt_obj) {
if (typeof col.every == 'function') {
diff --git a/third_party/closure/goog/style/transition.js b/third_party/closure/goog/style/transition.js
index a0e13d0..fe05ecc 100644
--- a/third_party/closure/goog/style/transition.js
+++ b/third_party/closure/goog/style/transition.js
@@ -62,10 +62,14 @@
if (goog.isString(p)) {
return p;
} else {
- goog.asserts.assert(p && p.property && goog.isNumber(p.duration) &&
- p.timing && goog.isNumber(p.delay));
- return p.property + ' ' + p.duration + 's ' + p.timing + ' ' +
- p.delay + 's';
+ goog.asserts.assertObject(p,
+ 'Expected css3 property to be an object.');
+ var propString = p.property + ' ' + p.duration + 's ' + p.timing +
+ ' ' + p.delay + 's';
+ goog.asserts.assert(p.property && goog.isNumber(p.duration) &&
+ p.timing && goog.isNumber(p.delay),
+ 'Unexpected css3 property value: %s', propString);
+ return propString;
}
});
goog.style.transition.setPropertyValue_(element, values.join(','));
diff --git a/third_party/closure/goog/testing/asserts.js b/third_party/closure/goog/testing/asserts.js
index feff5cc..bdf7b19 100644
--- a/third_party/closure/goog/testing/asserts.js
+++ b/third_party/closure/goog/testing/asserts.js
@@ -141,7 +141,7 @@
};
var fail = function(failureMessage) {
- goog.testing.asserts.raiseException_('Call to fail()', failureMessage);
+ goog.testing.asserts.raiseException('Call to fail()', failureMessage);
};
var argumentsIncludeComments = function(expectedNumberOfNonCommentArgs, args) {
@@ -172,7 +172,7 @@
var _assert = function(comment, booleanValue, failureMessage) {
if (!booleanValue) {
- goog.testing.asserts.raiseException_(comment, failureMessage);
+ goog.testing.asserts.raiseException(comment, failureMessage);
}
};
@@ -267,7 +267,7 @@
}
return e;
}
- goog.testing.asserts.raiseException_(comment,
+ goog.testing.asserts.raiseException(comment,
'No exception thrown from function passed to assertThrows');
};
@@ -296,7 +296,7 @@
// Some browsers don't have a stack trace so at least have the error
// description.
var stackTrace = e['stack'] || e['stacktrace'] || e.toString();
- goog.testing.asserts.raiseException_(comment, stackTrace);
+ goog.testing.asserts.raiseException(comment, stackTrace);
}
};
@@ -1143,9 +1143,8 @@
* Raises a JsUnit exception with the given comment.
* @param {string} comment A summary for the exception.
* @param {string=} opt_message A description of the exception.
- * @private
*/
-goog.testing.asserts.raiseException_ = function(comment, opt_message) {
+goog.testing.asserts.raiseException = function(comment, opt_message) {
if (goog.global['CLOSURE_INSPECTOR___'] &&
goog.global['CLOSURE_INSPECTOR___']['supportsJSUnit']) {
goog.global['CLOSURE_INSPECTOR___']['jsUnitFailure'](comment, opt_message);
diff --git a/third_party/closure/goog/testing/dom.js b/third_party/closure/goog/testing/dom.js
index 1179a29..487fbb4 100644
--- a/third_party/closure/goog/testing/dom.js
+++ b/third_party/closure/goog/testing/dom.js
@@ -157,7 +157,7 @@
/**
* Map function that converts end tags to a specific object.
* @param {Node} node The node to map.
- * @param {Object} ignore Always undefined.
+ * @param {undefined} ignore Always undefined.
* @param {goog.dom.TagIterator} iterator The iterator.
* @return {Node|Object} The resulting iteration item.
* @private
diff --git a/third_party/closure/goog/testing/events/events.js b/third_party/closure/goog/testing/events/events.js
index fc7e578..4e9bf72 100644
--- a/third_party/closure/goog/testing/events/events.js
+++ b/third_party/closure/goog/testing/events/events.js
@@ -575,3 +575,84 @@
return event.returnValue_;
};
+
+
+/**
+ * Simulates a touchstart event on the given target.
+ * @param {EventTarget} target The target for the event.
+ * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
+ * target's position (if available), otherwise (0, 0).
+ * @param {Object=} opt_eventProperties Event properties to be mixed into the
+ * BrowserEvent.
+ * @return {boolean} The returnValue of the event: false if preventDefault() was
+ * called on it, true otherwise.
+ */
+goog.testing.events.fireTouchStartEvent = function(
+ target, opt_coords, opt_eventProperties) {
+ // TODO: Support multi-touch events with array of coordinates.
+ var touchstart =
+ new goog.testing.events.Event(goog.events.EventType.TOUCHSTART, target);
+ goog.testing.events.setEventClientXY_(touchstart, opt_coords);
+ return goog.testing.events.fireBrowserEvent(touchstart);
+};
+
+
+/**
+ * Simulates a touchmove event on the given target.
+ * @param {EventTarget} target The target for the event.
+ * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
+ * target's position (if available), otherwise (0, 0).
+ * @param {Object=} opt_eventProperties Event properties to be mixed into the
+ * BrowserEvent.
+ * @return {boolean} The returnValue of the event: false if preventDefault() was
+ * called on it, true otherwise.
+ */
+goog.testing.events.fireTouchMoveEvent = function(
+ target, opt_coords, opt_eventProperties) {
+ // TODO: Support multi-touch events with array of coordinates.
+ var touchmove =
+ new goog.testing.events.Event(goog.events.EventType.TOUCHMOVE, target);
+ goog.testing.events.setEventClientXY_(touchmove, opt_coords);
+ return goog.testing.events.fireBrowserEvent(touchmove);
+};
+
+
+/**
+ * Simulates a touchend event on the given target.
+ * @param {EventTarget} target The target for the event.
+ * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
+ * target's position (if available), otherwise (0, 0).
+ * @param {Object=} opt_eventProperties Event properties to be mixed into the
+ * BrowserEvent.
+ * @return {boolean} The returnValue of the event: false if preventDefault() was
+ * called on it, true otherwise.
+ */
+goog.testing.events.fireTouchEndEvent = function(
+ target, opt_coords, opt_eventProperties) {
+ // TODO: Support multi-touch events with array of coordinates.
+ var touchend =
+ new goog.testing.events.Event(goog.events.EventType.TOUCHEND, target);
+ goog.testing.events.setEventClientXY_(touchend, opt_coords);
+ return goog.testing.events.fireBrowserEvent(touchend);
+};
+
+
+/**
+ * Simulates a simple touch sequence on the given target.
+ * @param {EventTarget} target The target for the event.
+ * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event
+ * target's position (if available), otherwise (0, 0).
+ * @param {Object=} opt_eventProperties Event properties to be mixed into the
+ * BrowserEvent.
+ * @return {boolean} The returnValue of the sequence: false if preventDefault()
+ * was called on any of the events, true otherwise.
+ */
+goog.testing.events.fireTouchSequence = function(
+ target, opt_coords, opt_eventProperties) {
+ // TODO: Support multi-touch events with array of coordinates.
+ // Fire touchstart, touchmove, touchend then return the bitwise AND of the 3.
+ return !!(goog.testing.events.fireTouchStartEvent(
+ target, opt_coords, opt_eventProperties) &
+ goog.testing.events.fireTouchEndEvent(
+ target, opt_coords, opt_eventProperties));
+};
diff --git a/third_party/closure/goog/testing/net/xhrio.js b/third_party/closure/goog/testing/net/xhrio.js
index 6271c12..b59a1d5 100644
--- a/third_party/closure/goog/testing/net/xhrio.js
+++ b/third_party/closure/goog/testing/net/xhrio.js
@@ -63,6 +63,13 @@
/**
+ * Alias this enum here to make mocking of goog.net.XhrIo easier.
+ * @enum {string}
+ */
+goog.testing.net.XhrIo.ResponseType = goog.net.XhrIo.ResponseType;
+
+
+/**
* All non-disposed instances of goog.testing.net.XhrIo created
* by {@link goog.testing.net.XhrIo.send} are in this Array.
* @see goog.testing.net.XhrIo.cleanup
diff --git a/third_party/closure/goog/ui/ac/autocomplete.js b/third_party/closure/goog/ui/ac/autocomplete.js
index 5ce7570..46bcecf 100644
--- a/third_party/closure/goog/ui/ac/autocomplete.js
+++ b/third_party/closure/goog/ui/ac/autocomplete.js
@@ -509,6 +509,7 @@
this.dismissTimer_ = null;
this.renderer_.dismiss();
this.dispatchEvent(goog.ui.ac.AutoComplete.EventType.SUGGESTIONS_UPDATE);
+ this.dispatchEvent(goog.ui.ac.AutoComplete.EventType.DISMISS);
};
diff --git a/third_party/closure/goog/ui/ac/renderer.js b/third_party/closure/goog/ui/ac/renderer.js
index fd90194..2473b37 100644
--- a/third_party/closure/goog/ui/ac/renderer.js
+++ b/third_party/closure/goog/ui/ac/renderer.js
@@ -282,6 +282,15 @@
/**
+ * @return {boolean} Whether we should be aligning to the top of
+ * the target element.
+ */
+goog.ui.ac.Renderer.prototype.getTopAlign = function() {
+ return this.topAlign_;
+};
+
+
+/**
* Set whether to align autocomplete to the right of the target element.
* @param {boolean} align If true, align to right.
*/
@@ -291,6 +300,14 @@
/**
+ * @return {boolean} Whether the autocomplete menu should be right aligned.
+ */
+goog.ui.ac.Renderer.prototype.getRightAlign = function() {
+ return this.rightAlign_;
+};
+
+
+/**
* Set whether or not standard highlighting should be used when rendering rows.
* @param {boolean} useStandardHighlighting true if standard highlighting used.
*/
@@ -566,18 +583,28 @@
/**
+ * @return {goog.positioning.Corner} The anchor corner to position the popup at.
+ * @protected
+ */
+goog.ui.ac.Renderer.prototype.getAnchorCorner = function() {
+ var anchorCorner = this.rightAlign_ ?
+ goog.positioning.Corner.BOTTOM_RIGHT :
+ goog.positioning.Corner.BOTTOM_LEFT;
+ if (this.topAlign_) {
+ anchorCorner = goog.positioning.flipCornerVertical(anchorCorner);
+ }
+ return anchorCorner;
+};
+
+
+/**
* Repositions the auto complete popup relative to the location node, if it
* exists and the auto position has been set.
*/
goog.ui.ac.Renderer.prototype.reposition = function() {
if (this.target_ && this.reposition_) {
var anchorElement = this.anchorElement_ || this.target_;
- var anchorCorner = this.rightAlign_ ?
- goog.positioning.Corner.BOTTOM_RIGHT :
- goog.positioning.Corner.BOTTOM_LEFT;
- if (this.topAlign_) {
- anchorCorner = goog.positioning.flipCornerVertical(anchorCorner);
- }
+ var anchorCorner = this.getAnchorCorner();
goog.positioning.positionAtAnchor(
anchorElement, anchorCorner,
diff --git a/third_party/closure/goog/ui/activitymonitor.js b/third_party/closure/goog/ui/activitymonitor.js
index 136c999..a1557e7 100644
--- a/third_party/closure/goog/ui/activitymonitor.js
+++ b/third_party/closure/goog/ui/activitymonitor.js
@@ -142,7 +142,8 @@
goog.ui.ActivityMonitor.userEventTypesBody_ =
[goog.events.EventType.CLICK, goog.events.EventType.DBLCLICK,
goog.events.EventType.MOUSEDOWN, goog.events.EventType.MOUSEUP,
- goog.events.EventType.MOUSEMOVE];
+ goog.events.EventType.MOUSEMOVE, goog.events.EventType.TOUCHSTART,
+ goog.events.EventType.TOUCHMOVE, goog.events.EventType.TOUCHEND];
/**
diff --git a/third_party/closure/goog/ui/component.js b/third_party/closure/goog/ui/component.js
index eab98dd..48a262f 100644
--- a/third_party/closure/goog/ui/component.js
+++ b/third_party/closure/goog/ui/component.js
@@ -1015,7 +1015,10 @@
// render_() calls enterDocument() if the parent is already in the document.
child.render_(this.getContentElement(), sibling ? sibling.element_ : null);
} else if (this.inDocument_ && !child.inDocument_ && child.element_ &&
- child.element_.parentNode) {
+ child.element_.parentNode &&
+ // Under some circumstances, IE8 implicitly creates a Document Fragment
+ // for detached nodes, so ensure the parent is an Element as it should be.
+ child.element_.parentNode.nodeType == goog.dom.NodeType.ELEMENT) {
// We don't touch the DOM, but if the parent is in the document, and the
// child element is in the document but not marked as such, then we call
// enterDocument on the child.
@@ -1134,9 +1137,10 @@
* {@code opt_obj} is provided, it will be used as the 'this' object in the
* function when called. The function should take two arguments: the child
* component and its 0-based index. The return value is ignored.
- * @param {Function} f The function to call for every child component; should
- * take 2 arguments (the child and its index).
- * @param {Object=} opt_obj Used as the 'this' object in f when called.
+ * @param {function(this:T,?,number):?} f The function to call for every
+ * child component; should take 2 arguments (the child and its index).
+ * @param {T=} opt_obj Used as the 'this' object in f when called.
+ * @template T
*/
goog.ui.Component.prototype.forEachChild = function(f, opt_obj) {
if (this.children_) {
@@ -1233,7 +1237,7 @@
* @see goog.ui.Component#removeChild
* @param {boolean=} opt_unrender If true, calls {@link #exitDocument} on the
* removed child components, and detaches their DOM from the document.
- * @return {!Array.<goog.ui.Component>|undefined} The removed components if any.
+ * @return {!Array.<goog.ui.Component>} The removed components if any.
*/
goog.ui.Component.prototype.removeChildren = function(opt_unrender) {
var removedChildren = [];
diff --git a/third_party/closure/goog/ui/container.js b/third_party/closure/goog/ui/container.js
index 4c71456..7fed245 100644
--- a/third_party/closure/goog/ui/container.js
+++ b/third_party/closure/goog/ui/container.js
@@ -41,7 +41,7 @@
goog.require('goog.ui.Component.EventType');
goog.require('goog.ui.Component.State');
goog.require('goog.ui.ContainerRenderer');
-
+goog.require('goog.ui.Control');
/**
@@ -833,6 +833,8 @@
* @override
*/
goog.ui.Container.prototype.addChild = function(child, opt_render) {
+ goog.asserts.assertInstanceof(child, goog.ui.Control,
+ 'The child of a container must be a control');
goog.ui.Container.superClass_.addChild.call(this, child, opt_render);
};
diff --git a/third_party/closure/goog/ui/editor/bubble.js b/third_party/closure/goog/ui/editor/bubble.js
index 6e93888..a0a2744 100644
--- a/third_party/closure/goog/ui/editor/bubble.js
+++ b/third_party/closure/goog/ui/editor/bubble.js
@@ -176,7 +176,6 @@
/**
* @return {Element} The element that where the bubble's contents go.
- * @protected
*/
goog.ui.editor.Bubble.prototype.getContentElement = function() {
return this.bubbleContents_;
diff --git a/third_party/closure/goog/ui/menubutton.js b/third_party/closure/goog/ui/menubutton.js
index 5d0f068..2069528 100644
--- a/third_party/closure/goog/ui/menubutton.js
+++ b/third_party/closure/goog/ui/menubutton.js
@@ -328,8 +328,9 @@
if (e.keyCode == goog.events.KeyCodes.DOWN ||
e.keyCode == goog.events.KeyCodes.UP ||
- e.keyCode == goog.events.KeyCodes.SPACE) {
- // Menu is closed, and the user hit the down/up/space key; open menu.
+ e.keyCode == goog.events.KeyCodes.SPACE ||
+ e.keyCode == goog.events.KeyCodes.ENTER) {
+ // Menu is closed, and the user hit the down/up/space/enter key; open menu.
this.setOpen(true);
return true;
}
diff --git a/third_party/closure/goog/ui/palette.js b/third_party/closure/goog/ui/palette.js
index 85f3a02..3cc6a07 100644
--- a/third_party/closure/goog/ui/palette.js
+++ b/third_party/closure/goog/ui/palette.js
@@ -27,8 +27,7 @@
goog.require('goog.events.EventType');
goog.require('goog.events.KeyCodes');
goog.require('goog.math.Size');
-goog.require('goog.ui.Component.Error');
-goog.require('goog.ui.Component.EventType');
+goog.require('goog.ui.Component');
goog.require('goog.ui.Control');
goog.require('goog.ui.PaletteRenderer');
goog.require('goog.ui.SelectionModel');
@@ -55,8 +54,10 @@
* @extends {goog.ui.Control}
*/
goog.ui.Palette = function(items, opt_renderer, opt_domHelper) {
- goog.ui.Control.call(this, items,
+ goog.base(this, items,
opt_renderer || goog.ui.PaletteRenderer.getInstance(), opt_domHelper);
+ this.setAutoStates(goog.ui.Component.State.CHECKED |
+ goog.ui.Component.State.SELECTED | goog.ui.Component.State.OPENED, false);
};
goog.inherits(goog.ui.Palette, goog.ui.Control);
@@ -237,7 +238,7 @@
var item = this.getHighlightedItem();
if (item) {
this.setSelectedItem(item);
- return this.dispatchEvent(goog.ui.Component.EventType.ACTION);
+ return goog.base(this, 'performActionInternal', e);
}
return false;
};
diff --git a/third_party/closure/goog/ui/splitpane.js b/third_party/closure/goog/ui/splitpane.js
index d36b1f3..f4a480d 100644
--- a/third_party/closure/goog/ui/splitpane.js
+++ b/third_party/closure/goog/ui/splitpane.js
@@ -64,7 +64,7 @@
*/
goog.ui.SplitPane = function(firstComponent, secondComponent, orientation,
opt_domHelper) {
- goog.ui.Component.call(this, opt_domHelper);
+ goog.base(this, opt_domHelper);
/**
* The orientation of the containers.
@@ -97,6 +97,12 @@
* @enum {string}
*/
goog.ui.SplitPane.EventType = {
+
+ /**
+ * Dispatched after handle drag.
+ */
+ HANDLE_DRAG: 'handle_drag',
+
/**
* Dispatched after handle drag end.
*/
@@ -296,8 +302,7 @@
*/
goog.ui.SplitPane.prototype.canDecorate = function(element) {
var className = goog.ui.SplitPane.FIRST_CONTAINER_CLASS_NAME_;
- var firstContainer = goog.dom.getElementsByTagNameAndClass(
- null, className, element)[0];
+ var firstContainer = this.getElementToDecorate_(element, className);
if (!firstContainer) {
return false;
}
@@ -306,16 +311,15 @@
this.firstComponentContainer_ = firstContainer;
className = goog.ui.SplitPane.SECOND_CONTAINER_CLASS_NAME_;
- var secondContainer = goog.dom.getElementsByTagNameAndClass(
- null, className, element)[0];
+ var secondContainer = this.getElementToDecorate_(element, className);
+
if (!secondContainer) {
return false;
}
this.secondComponentContainer_ = secondContainer;
className = goog.ui.SplitPane.HANDLE_CLASS_NAME_;
- var splitpaneHandle = goog.dom.getElementsByTagNameAndClass(
- null, className, element)[0];
+ var splitpaneHandle = this.getElementToDecorate_(element, className);
if (!splitpaneHandle) {
return false;
}
@@ -327,6 +331,34 @@
/**
+ * Obtains the element to be decorated by class name. If multiple such elements
+ * are found, preference is given to those directly attached to the specified
+ * root element.
+ * @param {Element} rootElement The root element from which to retrieve the
+ * element to be decorated.
+ * @param {!string} className The target class name.
+ * @return {Element} The element to decorate.
+ * @private
+ */
+goog.ui.SplitPane.prototype.getElementToDecorate_ = function(rootElement,
+ className) {
+
+ // Decorate the root element's children, if available.
+ var childElements = goog.dom.getChildren(rootElement);
+ for (var i = 0; i < childElements.length; i++) {
+ var childElement = childElements[i];
+ if (goog.dom.classes.has(childElement, className)) {
+ return childElement;
+ }
+ }
+
+ // Default to the first descendent element with the correct class.
+ return goog.dom.getElementsByTagNameAndClass(
+ null, className, rootElement)[0];
+};
+
+
+/**
* Decorates the given HTML element as a SplitPane. Overrides {@link
* goog.ui.Component#decorateInternal}. Considered protected.
* @param {Element} element Element (SplitPane div) to decorate.
@@ -334,7 +366,7 @@
* @override
*/
goog.ui.SplitPane.prototype.decorateInternal = function(element) {
- goog.ui.SplitPane.superClass_.decorateInternal.call(this, element);
+ goog.base(this, 'decorateInternal', element);
this.setUpHandle_();
@@ -385,7 +417,7 @@
* @override
*/
goog.ui.SplitPane.prototype.enterDocument = function() {
- goog.ui.SplitPane.superClass_.enterDocument.call(this);
+ goog.base(this, 'enterDocument');
// If position is not set in the inline style of the element, it is not
// possible to get the element's real CSS position until the element is in
@@ -807,6 +839,7 @@
var left = this.getRelativeLeft_(e.left);
this.setFirstComponentSize(left);
}
+ this.dispatchEvent(goog.ui.SplitPane.EventType.HANDLE_DRAG);
}
};
@@ -849,7 +882,7 @@
/** @override */
goog.ui.SplitPane.prototype.disposeInternal = function() {
- goog.ui.SplitPane.superClass_.disposeInternal.call(this);
+ goog.base(this, 'disposeInternal');
this.splitDragger_.dispose();
this.splitDragger_ = null;
diff --git a/third_party/closure/goog/ui/textarea.js b/third_party/closure/goog/ui/textarea.js
index 2371c5a..3554751 100644
--- a/third_party/closure/goog/ui/textarea.js
+++ b/third_party/closure/goog/ui/textarea.js
@@ -42,7 +42,7 @@
* @param {string} content Text to set as the textarea's value.
* @param {goog.ui.TextareaRenderer=} opt_renderer Renderer used to render or
* decorate the textarea. Defaults to {@link goog.ui.TextareaRenderer}.
- * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM hepler, used for
+ * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
* document interaction.
* @constructor
* @extends {goog.ui.Control}
@@ -505,7 +505,7 @@
/**
- * Resizes the texarea to shrink to fit its contents. The way this works is
+ * Resizes the textarea to shrink to fit its contents. The way this works is
* by increasing the padding of the textarea by 1px (it's important here that
* we're in box-sizing: border-box mode). If the size of the textarea grows,
* then the box is filled up to the padding box with text.
@@ -516,12 +516,6 @@
var textarea = this.getElement();
if (!this.isResizing_) {
this.isResizing_ = true;
- var isEmpty = false;
- if (!textarea.value) {
- // Prevents height from becoming 0.
- textarea.value = ' ';
- isEmpty = true;
- }
var scrollHeight = textarea.scrollHeight;
if (!scrollHeight) {
this.setHeightToEstimate_();
@@ -550,9 +544,6 @@
textarea.style.paddingBottom = paddingBox.bottom + 'px';
}
}
- if (isEmpty) {
- textarea.value = '';
- }
this.isResizing_ = false;
}
};
diff --git a/third_party/closure/goog/uri/uri.js b/third_party/closure/goog/uri/uri.js
index f80b3a6..c486935 100644
--- a/third_party/closure/goog/uri/uri.js
+++ b/third_party/closure/goog/uri/uri.js
@@ -1434,6 +1434,7 @@
rv.encodedQuery_ = this.encodedQuery_;
if (this.keyMap_) {
rv.keyMap_ = this.keyMap_.clone();
+ rv.count_ = this.count_;
}
return rv;
};
diff --git a/third_party/closure/goog/useragent/useragent.js b/third_party/closure/goog/useragent/useragent.js
index d77ac5c..c47c00f 100644
--- a/third_party/closure/goog/useragent/useragent.js
+++ b/third_party/closure/goog/useragent/useragent.js
@@ -474,16 +474,6 @@
/**
- * Cache for {@link goog.userAgent.isDocumentMode}.
- * Browsers document mode version number is unlikely to change during a session
- * we cache the results.
- * @type {Object}
- * @private
- */
-goog.userAgent.isDocumentModeCache_ = {};
-
-
-/**
* Whether the IE effective document mode is higher or the same as the given
* document mode version.
* NOTE: Only for IE, return false for another browser.
@@ -493,7 +483,24 @@
* same as the given version.
*/
goog.userAgent.isDocumentMode = function(documentMode) {
- return goog.userAgent.isDocumentModeCache_[documentMode] ||
- (goog.userAgent.isDocumentModeCache_[documentMode] = goog.userAgent.IE &&
- !!document.documentMode && document.documentMode >= documentMode);
+ return goog.userAgent.IE && goog.userAgent.DOCUMENT_MODE >= documentMode;
};
+
+
+/**
+ * For IE version < 7, documentMode is undefined, so attempt to use the
+ * CSS1Compat property to see if we are in standards mode. If we are in
+ * standards mode, treat the browser version as the document mode. Otherwise,
+ * IE is emulating version 5.
+ * @type {number|undefined}
+ * @const
+ */
+goog.userAgent.DOCUMENT_MODE = (function() {
+ var doc = goog.global['document'];
+ if (!doc || !goog.userAgent.IE) {
+ return undefined;
+ }
+ var mode = goog.userAgent.getDocumentMode_();
+ return mode || (doc['compatMode'] == 'CSS1Compat' ?
+ parseInt(goog.userAgent.VERSION, 10) : 5);
+})();
diff --git a/third_party/closure/goog/vec/float64array.js b/third_party/closure/goog/vec/float64array.js
index 5d7908e..d58336f 100644
--- a/third_party/closure/goog/vec/float64array.js
+++ b/third_party/closure/goog/vec/float64array.js
@@ -98,8 +98,15 @@
* goog.vec.Float64Array as Float64Array.
*/
if (typeof Float64Array == 'undefined') {
- goog.exportProperty(goog.vec.Float64Array, 'BYTES_PER_ELEMENT',
- goog.vec.Float64Array.BYTES_PER_ELEMENT);
+ try {
+ goog.exportProperty(goog.vec.Float64Array, 'BYTES_PER_ELEMENT',
+ goog.vec.Float64Array.BYTES_PER_ELEMENT);
+ } catch (float64ArrayError) {
+ // Do nothing. This code is in place to fix b/7225850, in which an error
+ // is incorrectly thrown for Google TV on an old Chrome.
+ // TODO(user): remove after that version is retired.
+ }
+
goog.exportProperty(goog.vec.Float64Array.prototype, 'BYTES_PER_ELEMENT',
goog.vec.Float64Array.prototype.BYTES_PER_ELEMENT);
goog.exportProperty(goog.vec.Float64Array.prototype, 'set',