mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 978918 - Filter hidden <br> when get content editable text length. r=yxl
This commit is contained in:
parent
2705a9a693
commit
22eb57c00c
@ -85,7 +85,9 @@ this.Keyboard = {
|
||||
mm.addMessageListener('Forms:GetText:Result:OK', this);
|
||||
mm.addMessageListener('Forms:GetText:Result:Error', this);
|
||||
mm.addMessageListener('Forms:SetSelectionRange:Result:OK', this);
|
||||
mm.addMessageListener('Forms:SetSelectionRange:Result:Error', this);
|
||||
mm.addMessageListener('Forms:ReplaceSurroundingText:Result:OK', this);
|
||||
mm.addMessageListener('Forms:ReplaceSurroundingText:Result:Error', this);
|
||||
mm.addMessageListener('Forms:SendKey:Result:OK', this);
|
||||
mm.addMessageListener('Forms:SendKey:Result:Error', this);
|
||||
mm.addMessageListener('Forms:SequenceError', this);
|
||||
@ -146,6 +148,8 @@ this.Keyboard = {
|
||||
case 'Forms:GetContext:Result:OK':
|
||||
case 'Forms:SetComposition:Result:OK':
|
||||
case 'Forms:EndComposition:Result:OK':
|
||||
case 'Forms:SetSelectionRange:Result:Error':
|
||||
case 'Forms:ReplaceSurroundingText:Result:Error':
|
||||
let name = msg.name.replace(/^Forms/, 'Keyboard');
|
||||
this.forwardEvent(name, msg);
|
||||
break;
|
||||
|
@ -24,6 +24,12 @@ XPCOMUtils.defineLazyGetter(this, "domWindowUtils", function () {
|
||||
});
|
||||
|
||||
const RESIZE_SCROLL_DELAY = 20;
|
||||
// In content editable node, when there are hidden elements such as <br>, it
|
||||
// may need more than one (usually less than 3 times) move/extend operations
|
||||
// to change the selection range. If we cannot change the selection range
|
||||
// with more than 20 opertations, we are likely being blocked and cannot change
|
||||
// the selection range any more.
|
||||
const MAX_BLOCKED_COUNT = 20;
|
||||
|
||||
let HTMLDocument = Ci.nsIDOMHTMLDocument;
|
||||
let HTMLHtmlElement = Ci.nsIDOMHTMLHtmlElement;
|
||||
@ -570,7 +576,17 @@ let FormAssistant = {
|
||||
|
||||
let start = json.selectionStart;
|
||||
let end = json.selectionEnd;
|
||||
setSelectionRange(target, start, end);
|
||||
|
||||
if (!setSelectionRange(target, start, end)) {
|
||||
if (json.requestId) {
|
||||
sendAsyncMessage("Forms:SetSelectionRange:Result:Error", {
|
||||
requestId: json.requestId,
|
||||
error: "failed"
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
this.updateSelection();
|
||||
|
||||
if (json.requestId) {
|
||||
@ -586,12 +602,20 @@ let FormAssistant = {
|
||||
CompositionManager.endComposition('');
|
||||
|
||||
let selectionRange = getSelectionRange(target);
|
||||
replaceSurroundingText(target,
|
||||
json.text,
|
||||
selectionRange[0],
|
||||
selectionRange[1],
|
||||
json.offset,
|
||||
json.length);
|
||||
if (!replaceSurroundingText(target,
|
||||
json.text,
|
||||
selectionRange[0],
|
||||
selectionRange[1],
|
||||
json.offset,
|
||||
json.length)) {
|
||||
if (json.requestId) {
|
||||
sendAsyncMessage("Forms:ReplaceSurroundingText:Result:Error", {
|
||||
requestId: json.requestId,
|
||||
error: "failed"
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (json.requestId) {
|
||||
sendAsyncMessage("Forms:ReplaceSurroundingText:Result:OK", {
|
||||
@ -911,6 +935,7 @@ function getDocumentEncoder(element) {
|
||||
.createInstance(Ci.nsIDocumentEncoder);
|
||||
let flags = Ci.nsIDocumentEncoder.SkipInvisibleContent |
|
||||
Ci.nsIDocumentEncoder.OutputRaw |
|
||||
Ci.nsIDocumentEncoder.OutputDropInvisibleBreak |
|
||||
// Bug 902847. Don't trim trailing spaces of a line.
|
||||
Ci.nsIDocumentEncoder.OutputDontRemoveLineEndingSpaces |
|
||||
Ci.nsIDocumentEncoder.OutputLFLineBreak |
|
||||
@ -978,7 +1003,7 @@ function setSelectionRange(element, start, end) {
|
||||
if (!isTextField && !isContentEditable(element)) {
|
||||
// Skip HTMLOptionElement and HTMLSelectElement elements, as they don't
|
||||
// support the operation of setSelectionRange
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
let text = isTextField ? element.value : getContentEditableText(element);
|
||||
@ -996,6 +1021,7 @@ function setSelectionRange(element, start, end) {
|
||||
if (isTextField) {
|
||||
// Set the selection range of <input> and <textarea> elements
|
||||
element.setSelectionRange(start, end, "forward");
|
||||
return true;
|
||||
} else {
|
||||
// set the selection range of contenteditable elements
|
||||
let win = element.ownerDocument.defaultView;
|
||||
@ -1007,8 +1033,22 @@ function setSelectionRange(element, start, end) {
|
||||
sel.modify("move", "forward", "character");
|
||||
}
|
||||
|
||||
while (getContentEditableSelectionStart(element, sel) < start) {
|
||||
// Avoid entering infinite loop in case we cannot change the selection
|
||||
// range. See bug https://bugzilla.mozilla.org/show_bug.cgi?id=978918
|
||||
let oldStart = getContentEditableSelectionStart(element, sel);
|
||||
let counter = 0;
|
||||
while (oldStart < start) {
|
||||
sel.modify("move", "forward", "character");
|
||||
let newStart = getContentEditableSelectionStart(element, sel);
|
||||
if (oldStart == newStart) {
|
||||
counter++;
|
||||
if (counter > MAX_BLOCKED_COUNT) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
counter = 0;
|
||||
oldStart = newStart;
|
||||
}
|
||||
}
|
||||
|
||||
// Extend the selection to the end position
|
||||
@ -1016,10 +1056,25 @@ function setSelectionRange(element, start, end) {
|
||||
sel.modify("extend", "forward", "character");
|
||||
}
|
||||
|
||||
// Avoid entering infinite loop in case we cannot change the selection
|
||||
// range. See bug https://bugzilla.mozilla.org/show_bug.cgi?id=978918
|
||||
counter = 0;
|
||||
let selectionLength = end - start;
|
||||
while (getContentEditableSelectionLength(element, sel) < selectionLength) {
|
||||
let oldSelectionLength = getContentEditableSelectionLength(element, sel);
|
||||
while (oldSelectionLength < selectionLength) {
|
||||
sel.modify("extend", "forward", "character");
|
||||
let newSelectionLength = getContentEditableSelectionLength(element, sel);
|
||||
if (oldSelectionLength == newSelectionLength ) {
|
||||
counter++;
|
||||
if (counter > MAX_BLOCKED_COUNT) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
counter = 0;
|
||||
oldSelectionLength = newSelectionLength;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1068,7 +1123,7 @@ function replaceSurroundingText(element, text, selectionStart, selectionEnd,
|
||||
offset, length) {
|
||||
let editor = FormAssistant.editor;
|
||||
if (!editor) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the parameters.
|
||||
@ -1083,7 +1138,9 @@ function replaceSurroundingText(element, text, selectionStart, selectionEnd,
|
||||
|
||||
if (selectionStart != start || selectionEnd != end) {
|
||||
// Change selection range before replacing.
|
||||
setSelectionRange(element, start, end);
|
||||
if (!setSelectionRange(element, start, end)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (start != end) {
|
||||
@ -1098,6 +1155,7 @@ function replaceSurroundingText(element, text, selectionStart, selectionEnd,
|
||||
// Insert the text to be replaced with.
|
||||
editor.insertText(text);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
let CompositionManager = {
|
||||
|
14
dom/inputmethod/mochitest/file_test_sms_app.html
Normal file
14
dom/inputmethod/mochitest/file_test_sms_app.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<body>
|
||||
<div id="messages-input" x-inputmode="-moz-sms" contenteditable="true"
|
||||
autofocus="autofocus">Httvb<br></div>
|
||||
<script type="application/javascript;version=1.7">
|
||||
let input = document.getElementById('messages-input');
|
||||
input.focus();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -5,11 +5,13 @@ support-files =
|
||||
file_inputmethod.html
|
||||
file_test_app.html
|
||||
file_test_sendkey_cancel.html
|
||||
file_test_sms_app.html
|
||||
|
||||
[test_basic.html]
|
||||
[test_bug944397.html]
|
||||
skip-if = toolkit == 'gonk'
|
||||
[test_bug949059.html]
|
||||
[test_bug978918.html]
|
||||
[test_delete_focused_element.html]
|
||||
skip-if = toolkit == 'gonk'
|
||||
[test_sendkey_cancel.html]
|
||||
|
77
dom/inputmethod/mochitest/test_bug978918.html
Normal file
77
dom/inputmethod/mochitest/test_bug978918.html
Normal file
@ -0,0 +1,77 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=978918
|
||||
-->
|
||||
<head>
|
||||
<title>Basic test for InputMethod API.</title>
|
||||
<script type="application/javascript;version=1.7" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript;version=1.7" src="inputmethod_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=978918">Mozilla Bug 978918</a>
|
||||
<p id="display"></p>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="application/javascript;version=1.7">
|
||||
|
||||
// The input context.
|
||||
var gContext = null;
|
||||
|
||||
inputmethod_setup(function() {
|
||||
runTest();
|
||||
});
|
||||
|
||||
function runTest() {
|
||||
let im = navigator.mozInputMethod;
|
||||
|
||||
im.oninputcontextchange = function() {
|
||||
ok(true, 'inputcontextchange event was fired.');
|
||||
im.oninputcontextchange = null;
|
||||
|
||||
gContext = im.inputcontext;
|
||||
if (!gContext) {
|
||||
ok(false, 'Should have a non-null inputcontext.');
|
||||
inputmethod_cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
test_setSelectionRange();
|
||||
};
|
||||
|
||||
// Set current page as an input method.
|
||||
SpecialPowers.wrap(im).setActive(true);
|
||||
|
||||
let iframe = document.createElement('iframe');
|
||||
iframe.src = 'file_test_sms_app.html';
|
||||
iframe.setAttribute('mozbrowser', true);
|
||||
document.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
function test_setSelectionRange() {
|
||||
gContext.setSelectionRange(0, 100).then(function() {
|
||||
is(gContext.selectionStart, 0, 'selectionStart was set successfully.');
|
||||
is(gContext.selectionEnd, 5, 'selectionEnd was set successfully.');
|
||||
test_replaceSurroundingText();
|
||||
}, function(e) {
|
||||
ok(false, 'setSelectionRange failed:' + e.name);
|
||||
inputmethod_cleanup();
|
||||
});
|
||||
}
|
||||
|
||||
function test_replaceSurroundingText() {
|
||||
// Replace 'Httvb' with 'Hito'.
|
||||
gContext.replaceSurroundingText('Hito', 0, 100).then(function() {
|
||||
ok(true, 'replaceSurroundingText finished');
|
||||
inputmethod_cleanup();
|
||||
}, function(e) {
|
||||
ok(false, 'replaceSurroundingText failed: ' + e.name);
|
||||
inputmethod_cleanup();
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
Reference in New Issue
Block a user