[FIX] assertion errors in search view when selecting all of a search input's content

Force normalization of the input's root node, Firefox tends to spawn
multiple text node children then create selection ranges over all of
them and worse.

lp bug: https://launchpad.net/bugs/1169945 fixed

bzr revid: xmo@openerp.com-20130604150551-0m0b48aifg9favz8
This commit is contained in:
Xavier Morel 2013-06-04 17:05:51 +02:00
parent 7a5b5e89f9
commit 431e0761c0
1 changed files with 17 additions and 2 deletions

View File

@ -131,6 +131,7 @@ my.InputView = instance.web.Widget.extend({
paste: 'onPaste', paste: 'onPaste',
}, },
getSelection: function () { getSelection: function () {
this.el.normalize();
// get Text node // get Text node
var root = this.el.childNodes[0]; var root = this.el.childNodes[0];
if (!root || !root.textContent) { if (!root || !root.textContent) {
@ -139,6 +140,16 @@ my.InputView = instance.web.Widget.extend({
return {start: 0, end: 0}; return {start: 0, end: 0};
} }
var range = window.getSelection().getRangeAt(0); var range = window.getSelection().getRangeAt(0);
// In Firefox, depending on the way text is selected (drag, double- or
// triple-click) the range may start or end on the parent of the
// selected text node‽ Check for this condition and fixup the range
// note: apparently with C-a this can go even higher?
if (range.startContainer === this.el && range.startOffset === 0) {
range.setStart(root, 0);
}
if (range.endContainer === this.el && range.endOffset === 1) {
range.setEnd(root, root.length)
}
assert(range.startContainer === root, assert(range.startContainer === root,
"selection should be in the input view"); "selection should be in the input view");
assert(range.endContainer === root, assert(range.endContainer === root,
@ -149,6 +160,7 @@ my.InputView = instance.web.Widget.extend({
} }
}, },
onKeydown: function (e) { onKeydown: function (e) {
this.el.normalize();
var sel; var sel;
switch (e.which) { switch (e.which) {
// Do not insert newline, but let it bubble so searchview can use it // Do not insert newline, but let it bubble so searchview can use it
@ -186,6 +198,7 @@ my.InputView = instance.web.Widget.extend({
} }
}, },
setCursorAtEnd: function () { setCursorAtEnd: function () {
this.el.normalize();
var sel = window.getSelection(); var sel = window.getSelection();
sel.removeAllRanges(); sel.removeAllRanges();
var range = document.createRange(); var range = document.createRange();
@ -196,13 +209,14 @@ my.InputView = instance.web.Widget.extend({
// from about half the link to half the text, paste in search box then // from about half the link to half the text, paste in search box then
// hit the left arrow key, getSelection would blow up). // hit the left arrow key, getSelection would blow up).
// //
// Explicitly selecting only the inner text node (only child node at // Explicitly selecting only the inner text node (only child node
// this point, though maybe we should assert that) avoiids the issue // since we've normalized the parent) avoids the issue
range.selectNode(this.el.childNodes[0]); range.selectNode(this.el.childNodes[0]);
range.collapse(false); range.collapse(false);
sel.addRange(range); sel.addRange(range);
}, },
onPaste: function () { onPaste: function () {
this.el.normalize();
// In MSIE and Webkit, it is possible to get various representations of // In MSIE and Webkit, it is possible to get various representations of
// the clipboard data at this point e.g. // the clipboard data at this point e.g.
// window.clipboardData.getData('Text') and // window.clipboardData.getData('Text') and
@ -224,6 +238,7 @@ my.InputView = instance.web.Widget.extend({
var data = this.$el.text(); var data = this.$el.text();
// paste raw text back in // paste raw text back in
this.$el.empty().text(data); this.$el.empty().text(data);
this.el.normalize();
// Set the cursor at the end of the text, so the cursor is not lost // Set the cursor at the end of the text, so the cursor is not lost
// in some kind of error-spawning limbo. // in some kind of error-spawning limbo.
this.setCursorAtEnd(); this.setCursorAtEnd();