mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 13:51:41 +00:00
Bug 1726532 - part 1: Make OffsetEntryArray::WillSetSelection()
use offset in text node when it sets a DOM node point r=m_kato
This is a mistake at replacing the argument value `aOffset` to `aOffsetInTextInBlock`. https://searchfox.org/mozilla-central/diff/886c96059ef6408618040356017028621bc574b9/editor/spellchecker/TextServicesDocument.cpp#1662 And also make `mozSpellChecker::Replace()` stop replacing if `TextServicesDocument` fails to set selection or insert text because if `TextServicesDocument` succeeded to insert text, but failed to delete text, the loop becomes an infinite loop. Differential Revision: https://phabricator.services.mozilla.com/D123282
This commit is contained in:
parent
1958e4b9fe
commit
922488424d
@ -1652,7 +1652,7 @@ TextServicesDocument::OffsetEntryArray::WillSetSelection(
|
||||
// inserted text offset entry, if the offsets
|
||||
// match exactly!
|
||||
if (entry->mOffsetInTextInBlock == aOffsetInTextInBlock) {
|
||||
newStart.Set(entry->mTextNode, entry->EndOffsetInTextInBlock());
|
||||
newStart.Set(entry->mTextNode, entry->EndOffsetInTextNode());
|
||||
}
|
||||
} else if (aOffsetInTextInBlock >= entry->mOffsetInTextInBlock) {
|
||||
bool foundEntry = false;
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
MOCHITEST_MANIFESTS += ["tests/mochitest.ini"]
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += ["tests/chrome.ini"]
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
"nsIInlineSpellChecker.idl",
|
||||
]
|
||||
|
10
editor/spellchecker/tests/chrome.ini
Normal file
10
editor/spellchecker/tests/chrome.ini
Normal file
@ -0,0 +1,10 @@
|
||||
[DEFAULT]
|
||||
prefs =
|
||||
gfx.font_loader.delay=0
|
||||
gfx.font_loader.interval=0
|
||||
|
||||
skip-if = os == 'android'
|
||||
support-files =
|
||||
spellcheck.js
|
||||
|
||||
[test_nsIEditorSpellCheck_ReplaceWord.html]
|
@ -0,0 +1,62 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for nsIEditorSpellCheck.ReplaceWord()</title>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<div contenteditable spellcheck="true" lang="en-US"></div>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.waitForFocus(async () => {
|
||||
const { onSpellCheck } = SpecialPowers.Cu.import("resource://testing-common/AsyncSpellCheckTestHelper.jsm");
|
||||
const editor = document.querySelector("div[contenteditable]");
|
||||
async function replaceWord(aMisspelledWord, aCorrectWord, aReplaceAll) {
|
||||
const editorObj = SpecialPowers.wrap(window).docShell.editingSession.getEditorForWindow(window);
|
||||
const inlineSpellChecker = editorObj.getInlineSpellChecker(true);
|
||||
await new Promise(resolve => onSpellCheck(editor, resolve));
|
||||
const editorSpellCheck = inlineSpellChecker.spellChecker;
|
||||
editorObj.beginTransaction();
|
||||
try {
|
||||
editorSpellCheck.ReplaceWord(aMisspelledWord, aCorrectWord, aReplaceAll);
|
||||
} catch (e) {
|
||||
ok(false, `Unexpected exception: ${e.message}`);
|
||||
}
|
||||
editorObj.endTransaction();
|
||||
editorSpellCheck.GetNextMisspelledWord();
|
||||
}
|
||||
|
||||
async function testReplaceAllMisspelledWords(aCorrectWord) {
|
||||
editor.innerHTML = "<p>def abc def<br>abc def abc</p><p>abc def abc<br>def abc def</p>";
|
||||
editor.focus();
|
||||
editor.getBoundingClientRect();
|
||||
await replaceWord("abc", aCorrectWord, true);
|
||||
is(
|
||||
editor.innerHTML,
|
||||
`<p>def ${aCorrectWord} def<br>${aCorrectWord} def ${aCorrectWord}</p><p>${aCorrectWord} def ${aCorrectWord}<br>def ${aCorrectWord} def</p>`,
|
||||
`nsIEditorSpellCheck.ReplaceWord(..., true) should replace all misspelled words with ${
|
||||
(() => {
|
||||
if (aCorrectWord.length > "abc".length) {
|
||||
return "longer";
|
||||
}
|
||||
return aCorrectWord.length < "abc".length ? "shorter" : "same length"
|
||||
})()
|
||||
} correct word`
|
||||
);
|
||||
editor.blur();
|
||||
editor.getBoundingClientRect();
|
||||
}
|
||||
await testReplaceAllMisspelledWords("ABC");
|
||||
await testReplaceAllMisspelledWords("ABC!");
|
||||
await testReplaceAllMisspelledWords("AB");
|
||||
|
||||
// TODO: Add tests for not all replacing cases.
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -243,6 +243,9 @@ nsresult mozSpellChecker::Replace(const nsAString& aOldWord,
|
||||
}
|
||||
int32_t currOffset = 0;
|
||||
int32_t currentBlock = 0;
|
||||
int32_t wordLengthDifference =
|
||||
AssertedCast<int32_t>(static_cast<int64_t>(aNewWord.Length()) -
|
||||
static_cast<int64_t>(aOldWord.Length()));
|
||||
while (NS_SUCCEEDED(mTextServicesDocument->IsDone(&done)) && !done) {
|
||||
nsAutoString str;
|
||||
mTextServicesDocument->GetCurrentTextBlock(str);
|
||||
@ -251,18 +254,25 @@ nsresult mozSpellChecker::Replace(const nsAString& aOldWord,
|
||||
// if we are before the current selection point but in the same
|
||||
// block move the selection point forwards
|
||||
if (currentBlock == startBlock && begin < selOffset) {
|
||||
selOffset += int32_t(aNewWord.Length()) - int32_t(aOldWord.Length());
|
||||
selOffset += wordLengthDifference;
|
||||
if (selOffset < begin) {
|
||||
selOffset = begin;
|
||||
}
|
||||
}
|
||||
MOZ_KnownLive(mTextServicesDocument)
|
||||
->SetSelection(AssertedCast<uint32_t>(begin),
|
||||
AssertedCast<uint32_t>(end - begin));
|
||||
MOZ_KnownLive(mTextServicesDocument)->InsertText(aNewWord);
|
||||
// Don't keep running if selecting or inserting text fails because
|
||||
// it may cause infinite loop.
|
||||
if (NS_WARN_IF(NS_FAILED(
|
||||
MOZ_KnownLive(mTextServicesDocument)
|
||||
->SetSelection(AssertedCast<uint32_t>(begin),
|
||||
AssertedCast<uint32_t>(end - begin))))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (NS_WARN_IF(NS_FAILED(
|
||||
MOZ_KnownLive(mTextServicesDocument)->InsertText(aNewWord)))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mTextServicesDocument->GetCurrentTextBlock(str);
|
||||
end += (aNewWord.Length() -
|
||||
aOldWord.Length()); // recursion was cute in GEB, not here.
|
||||
end += wordLengthDifference; // recursion was cute in GEB, not here.
|
||||
}
|
||||
currOffset = end;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user