Skip to content

Commit 6d22f48

Browse files
committed
Starting with Chrome 33, certain types of input elements do not
support the selection API (in fact, they throw when you try to access the property). This prevents us from fully simulating typing in the atoms. Try to case these cases and throw a more descriptive error, pointing the user at https://code.google.com/p/chromium/issues/detail?id=330456 so they have the proper context for why typing no longer works as expected. ------------- Created by MOE: http://code.google.com/p/moe-java MOE_MIGRATED_REVID=60184506
1 parent 948fbed commit 6d22f48

File tree

2 files changed

+87
-6
lines changed

2 files changed

+87
-6
lines changed

javascript/atoms/keyboard.js

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -603,8 +603,12 @@ bot.Keyboard.prototype.updateOnCharacter_ = function(key) {
603603

604604
var character = this.getChar_(key);
605605
var newPos = goog.dom.selection.getStart(this.getElement()) + 1;
606-
goog.dom.selection.setText(this.getElement(), character);
607-
goog.dom.selection.setStart(this.getElement(), newPos);
606+
if (bot.Keyboard.supportsSelection(this.getElement())) {
607+
goog.dom.selection.setText(this.getElement(), character);
608+
goog.dom.selection.setStart(this.getElement(), newPos);
609+
} else {
610+
this.getElement().value += character;
611+
}
608612
if (goog.userAgent.WEBKIT) {
609613
this.fireHtmlEvent(bot.events.EventType.TEXTINPUT);
610614
}
@@ -629,8 +633,12 @@ bot.Keyboard.prototype.updateOnEnter_ = function() {
629633
if (bot.dom.isElement(this.getElement(), goog.dom.TagName.TEXTAREA)) {
630634
var newPos = goog.dom.selection.getStart(this.getElement()) +
631635
bot.Keyboard.NEW_LINE_.length;
632-
goog.dom.selection.setText(this.getElement(), bot.Keyboard.NEW_LINE_);
633-
goog.dom.selection.setStart(this.getElement(), newPos);
636+
if (bot.Keyboard.supportsSelection(this.getElement())) {
637+
goog.dom.selection.setText(this.getElement(), bot.Keyboard.NEW_LINE_);
638+
goog.dom.selection.setStart(this.getElement(), newPos);
639+
} else {
640+
this.getElement().value += bot.Keyboard.NEW_LINE_;
641+
}
634642
if (!goog.userAgent.IE) {
635643
this.fireHtmlEvent(bot.events.EventType.INPUT);
636644
}
@@ -650,6 +658,7 @@ bot.Keyboard.prototype.updateOnBackspaceOrDelete_ = function(key) {
650658

651659
// Determine what should be deleted. If text is already selected, that
652660
// text is deleted, else we move left/right from the current cursor.
661+
bot.Keyboard.checkCanUpdateSelection_(this.getElement());
653662
var endpoints = goog.dom.selection.getEndPoints(this.getElement());
654663
if (endpoints[0] == endpoints[1]) {
655664
if (key == bot.Keyboard.Keys.BACKSPACE) {
@@ -692,6 +701,7 @@ bot.Keyboard.prototype.updateOnBackspaceOrDelete_ = function(key) {
692701
* @private
693702
*/
694703
bot.Keyboard.prototype.updateOnLeftOrRight_ = function(key) {
704+
bot.Keyboard.checkCanUpdateSelection_(this.getElement());
695705
var element = this.getElement();
696706
var start = goog.dom.selection.getStart(element);
697707
var end = goog.dom.selection.getEnd(element);
@@ -758,6 +768,7 @@ bot.Keyboard.prototype.updateOnLeftOrRight_ = function(key) {
758768
* @private
759769
*/
760770
bot.Keyboard.prototype.updateOnHomeOrEnd_ = function(key) {
771+
bot.Keyboard.checkCanUpdateSelection_(this.getElement());
761772
var element = this.getElement();
762773
var start = goog.dom.selection.getStart(element);
763774
var end = goog.dom.selection.getEnd(element);
@@ -792,6 +803,47 @@ bot.Keyboard.prototype.updateOnHomeOrEnd_ = function(key) {
792803
};
793804

794805

806+
/**
807+
* Checks that the cursor position can be updated for the given element.
808+
* @param {!Element} element The element to test.
809+
* @throws {Error} If the cursor position cannot be updated for the given
810+
* element.
811+
* @see https://code.google.com/p/chromium/issues/detail?id=330456
812+
* @private
813+
*/
814+
bot.Keyboard.checkCanUpdateSelection_ = function(element) {
815+
try {
816+
/** @suppress {suspiciousCode} */
817+
element.selectionStart;
818+
} catch (ex) {
819+
// The native error message is actually pretty informative, just add a
820+
// reference to the relevant Chrome bug to provide more context.
821+
if (ex.message.indexOf('does not support selection.') != -1) {
822+
// message is a readonly property, so need to rethrow.
823+
throw Error(ex.message + ' (For more information, see ' +
824+
'https://code.google.com/p/chromium/issues/detail?id=330456)');
825+
}
826+
throw ex;
827+
}
828+
};
829+
830+
831+
/**
832+
* @param {!Element} element The element to test.
833+
* @return {boolean} Whether the given element supports the input element
834+
* selection API.
835+
* @see https://code.google.com/p/chromium/issues/detail?id=330456
836+
*/
837+
bot.Keyboard.supportsSelection = function(element) {
838+
try {
839+
bot.Keyboard.checkCanUpdateSelection_(element);
840+
} catch (ex) {
841+
return false;
842+
}
843+
return true;
844+
};
845+
846+
795847
/**
796848
* @param {number} pos New position of the cursor.
797849
* @private

javascript/atoms/test/type_test.html

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<title>type_test</title>
55
<script src="test_bootstrap.js"></script>
66
<script type="text/javascript">
7-
goog.require('bot.Keyboard.Keys');
7+
goog.require('bot.Keyboard');
88
goog.require('bot.action');
99
goog.require('bot.events');
1010
goog.require('bot.userAgent');
@@ -28,6 +28,20 @@
2828

2929
var INPUT_IDS = ['textbox', 'password', 'email', 'search', 'textarea'];
3030

31+
// Keys that impact the cursor's position and require the selection API.
32+
var SELECTION_KEYS = [
33+
bot.Keyboard.Keys.BACKSPACE,
34+
bot.Keyboard.Keys.DELETE,
35+
bot.Keyboard.Keys.PAGE_UP,
36+
bot.Keyboard.Keys.PAGE_DOWN,
37+
bot.Keyboard.Keys.END,
38+
bot.Keyboard.Keys.HOME,
39+
bot.Keyboard.Keys.LEFT,
40+
bot.Keyboard.Keys.UP,
41+
bot.Keyboard.Keys.RIGHT,
42+
bot.Keyboard.Keys.DOWN,
43+
];
44+
3145
var ALT = bot.Keyboard.Keys.ALT;
3246
var BACKSPACE = bot.Keyboard.Keys.BACKSPACE;
3347
var CONTROL = bot.Keyboard.Keys.CONTROL;
@@ -83,7 +97,7 @@
8397
* have the correct values afterwards. textbox_count should display the
8498
* number of times textbox's oninput (non IE) or onpropertychange (IE) event
8599
* fired.
86-
* @param {string|bot.Keyboard.Key|Array.<string, bot.Keyboard.Key>}
100+
* @param {string|bot.Keyboard.Key|Array.<(string|bot.Keyboard.Key)>}
87101
* input Value to type into textbox.
88102
* @param {string=} opt_finalValue The final value that should appear in
89103
* the input. Defaults to input.
@@ -111,8 +125,23 @@
111125
goog.isDef(opt_textInputCount) ? opt_textInputCount : inputCount;
112126
var initValue = opt_initValue || '';
113127

128+
// Determine if our test requires updating the cursor position, which is
129+
// not supported in all scenarios.
130+
var needsSelection = function(input) {
131+
if (goog.isArray(input)) {
132+
return goog.array.some(input, needsSelection);
133+
}
134+
return goog.array.contains(SELECTION_KEYS, input);
135+
}
136+
var requiresSelection = goog.array.some(input, needsSelection);
137+
114138
for (var i = 0; i < inputIds.length; i++) {
115139
var inputBox = document.getElementById(inputIds[i]);
140+
if (requiresSelection && !bot.Keyboard.supportsSelection(inputBox)) {
141+
// Test requires selection, but the current element will throw when
142+
// accessing those properties, so skip this case.
143+
continue;
144+
}
116145
inputBox.value = initValue;
117146
inputCountBox.value = '0';
118147
textInputCountBox.value = '0';

0 commit comments

Comments
 (0)