Commit 193fde17 authored by Shimi Zhang's avatar Shimi Zhang Committed by Commit Bot

[Android] Add FLAG_AUTO_CORRESION support for spell checking

Add basic support for SuggestionSpan.FLAG_AUTO_CORRESION flag, make it
show underline for this type.

Bug: 869261
Cq-Include-Trybots: luci.chromium.try:linux_layout_tests_layout_ng
Change-Id: I6db973826ebf761902506905d31968ec4b78819a
Reviewed-on: https://chromium-review.googlesource.com/c/1285312
Commit-Queue: Shimi Zhang <ctzsm@chromium.org>
Reviewed-by: default avatarYoshifumi Inoue <yosin@chromium.org>
Reviewed-by: default avatarChangwan Ryu <changwan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#601669}
parent 0865005c
...@@ -1011,23 +1011,26 @@ public class ImeAdapterImpl implements ImeAdapter, WindowEventObserver, UserData ...@@ -1011,23 +1011,26 @@ public class ImeAdapterImpl implements ImeAdapter, WindowEventObserver, UserData
} else if (span instanceof SuggestionSpan) { } else if (span instanceof SuggestionSpan) {
final SuggestionSpan suggestionSpan = (SuggestionSpan) span; final SuggestionSpan suggestionSpan = (SuggestionSpan) span;
// We currently only support FLAG_EASY_CORRECT and FLAG_MISSPELLED SuggestionSpans. // We support all three flags of SuggestionSpans with caveat:
// - FLAG_EASY_CORRECT, full support.
// Other types: // - FLAG_MISSPELLED, full support.
// - FLAG_AUTO_CORRECTION is used e.g. by Samsung's IME to flash a blue background // - FLAG_AUTO_CORRECTION, no animation support for this flag for
// on a word being replaced by an autocorrect suggestion. We don't currently // commitCorrection().
// support this. // Note that FLAG_AUTO_CORRECTION has precedence than the other two flags.
//
// Other cases:
// - Some IMEs (e.g. the AOSP keyboard on Jelly Bean) add SuggestionSpans with no // - Some IMEs (e.g. the AOSP keyboard on Jelly Bean) add SuggestionSpans with no
// flags set and no underline color to add suggestions to words marked as // flags set and no underline color to add suggestions to words marked as
// misspelled (instead of having the spell checker return the suggestions when // misspelled (instead of having the spell checker return the suggestions when
// called). We don't support these either. // called). We don't support these either.
final boolean isEasyCorrectSpan =
(suggestionSpan.getFlags() & SuggestionSpan.FLAG_EASY_CORRECT) != 0;
final boolean isMisspellingSpan = final boolean isMisspellingSpan =
(suggestionSpan.getFlags() & SuggestionSpan.FLAG_MISSPELLED) != 0; (suggestionSpan.getFlags() & SuggestionSpan.FLAG_MISSPELLED) != 0;
if (suggestionSpan.getFlags() != SuggestionSpan.FLAG_EASY_CORRECT final boolean isAutoCorrectionSpan =
&& !isMisspellingSpan) { (suggestionSpan.getFlags() & SuggestionSpan.FLAG_AUTO_CORRECTION) != 0;
continue;
} if (!isEasyCorrectSpan && !isMisspellingSpan && !isAutoCorrectionSpan) continue;
// Copied from Android's Editor.java so we use the same colors // Copied from Android's Editor.java so we use the same colors
// as the native Android text widget. // as the native Android text widget.
...@@ -1037,10 +1040,14 @@ public class ImeAdapterImpl implements ImeAdapter, WindowEventObserver, UserData ...@@ -1037,10 +1040,14 @@ public class ImeAdapterImpl implements ImeAdapter, WindowEventObserver, UserData
final int suggestionHighlightColor = final int suggestionHighlightColor =
(underlineColor & 0x00FFFFFF) + (newAlpha << 24); (underlineColor & 0x00FFFFFF) + (newAlpha << 24);
// In native side, we treat FLAG_AUTO_CORRECTION span as kMisspellingSuggestion
// marker with 0 suggestion.
nativeAppendSuggestionSpan(imeTextSpans, nativeAppendSuggestionSpan(imeTextSpans,
spannableString.getSpanStart(suggestionSpan), spannableString.getSpanStart(suggestionSpan),
spannableString.getSpanEnd(suggestionSpan), isMisspellingSpan, spannableString.getSpanEnd(suggestionSpan),
underlineColor, suggestionHighlightColor, suggestionSpan.getSuggestions()); isMisspellingSpan || isAutoCorrectionSpan, underlineColor,
suggestionHighlightColor,
isAutoCorrectionSpan ? new String[0] : suggestionSpan.getSuggestions());
} }
} }
} }
......
...@@ -290,6 +290,25 @@ public class TextSuggestionMenuTest { ...@@ -290,6 +290,25 @@ public class TextSuggestionMenuTest {
waitForMenuToHide(webContents); waitForMenuToHide(webContents);
} }
@Test
@LargeTest
public void testAutoCorrectionSuggestionSpan() throws InterruptedException, Throwable {
WebContents webContents = mRule.getWebContents();
DOMUtils.focusNode(webContents, "div");
SpannableString textToCommit = new SpannableString("hello");
SuggestionSpan suggestionSpan = new SuggestionSpan(
mRule.getActivity(), new String[0], SuggestionSpan.FLAG_AUTO_CORRECTION);
textToCommit.setSpan(suggestionSpan, 0, 5, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mRule.commitText(textToCommit, 1);
Assert.assertEquals("1",
JavaScriptUtils.executeJavaScriptAndWaitForResult(webContents,
"internals.markerCountForNode("
+ "document.getElementById('div').firstChild, 'suggestion')"));
}
private void waitForMenuToShow(WebContents webContents) { private void waitForMenuToShow(WebContents webContents) {
CriteriaHelper.pollUiThread(new Criteria() { CriteriaHelper.pollUiThread(new Criteria() {
@Override @Override
......
...@@ -232,6 +232,11 @@ void TextSuggestionController::HandlePotentialSuggestionTap( ...@@ -232,6 +232,11 @@ void TextSuggestionController::HandlePotentialSuggestionTap(
if (!node_and_marker.first) if (!node_and_marker.first)
return; return;
const SuggestionMarker* marker =
ToSuggestionMarkerOrNull(node_and_marker.second);
if (marker && marker->Suggestions().IsEmpty())
return;
if (!text_suggestion_host_) { if (!text_suggestion_host_) {
GetFrame().GetInterfaceProvider().GetInterface( GetFrame().GetInterfaceProvider().GetInterface(
mojo::MakeRequest(&text_suggestion_host_)); mojo::MakeRequest(&text_suggestion_host_));
......
...@@ -47,6 +47,7 @@ class CORE_EXPORT TextSuggestionController final ...@@ -47,6 +47,7 @@ class CORE_EXPORT TextSuggestionController final
void Trace(blink::Visitor*) override; void Trace(blink::Visitor*) override;
private: private:
friend class TextSuggestionControllerTest;
Document& GetDocument() const; Document& GetDocument() const;
bool IsAvailable() const; bool IsAvailable() const;
LocalFrame& GetFrame() const; LocalFrame& GetFrame() const;
......
...@@ -17,7 +17,15 @@ using ws::mojom::ImeTextSpanThickness; ...@@ -17,7 +17,15 @@ using ws::mojom::ImeTextSpanThickness;
namespace blink { namespace blink {
class TextSuggestionControllerTest : public EditingTestBase {}; class TextSuggestionControllerTest : public EditingTestBase {
public:
bool IsTextSuggestionHostAvailable() {
return bool(GetDocument()
.GetFrame()
->GetTextSuggestionController()
.text_suggestion_host_);
}
};
TEST_F(TextSuggestionControllerTest, ApplySpellCheckSuggestion) { TEST_F(TextSuggestionControllerTest, ApplySpellCheckSuggestion) {
SetBodyContent( SetBodyContent(
...@@ -439,4 +447,66 @@ TEST_F(TextSuggestionControllerTest, CallbackHappensAfterDocumentDestroyed) { ...@@ -439,4 +447,66 @@ TEST_F(TextSuggestionControllerTest, CallbackHappensAfterDocumentDestroyed) {
frame.GetTextSuggestionController().SuggestionMenuTimeoutCallback(0); frame.GetTextSuggestionController().SuggestionMenuTimeoutCallback(0);
} }
TEST_F(TextSuggestionControllerTest, SuggestionMarkerWithEmptySuggestion) {
SetBodyContent(
"<div contenteditable>"
"hello"
"</div>");
Element* div = GetDocument().QuerySelector("div");
Text* text = ToText(div->firstChild());
// Set suggestion marker with empty suggestion list.
GetDocument().Markers().AddSuggestionMarker(
EphemeralRange(Position(text, 0), Position(text, 5)),
SuggestionMarkerProperties::Builder()
.SetSuggestions(Vector<String>())
.Build());
// Set the caret inside the word.
GetDocument().GetFrame()->Selection().SetSelectionAndEndTyping(
SelectionInDOMTree::Builder()
.SetBaseAndExtent(Position(text, 3), Position(text, 3))
.Build());
// Handle potential suggestion tap on the caret position.
GetDocument()
.GetFrame()
->GetTextSuggestionController()
.HandlePotentialSuggestionTap(PositionInFlatTree(text, 3));
// We don't trigger menu in this case so there shouldn't be any mojom
// connection available.
EXPECT_FALSE(IsTextSuggestionHostAvailable());
}
TEST_F(TextSuggestionControllerTest, SuggestionMarkerWithSuggestion) {
SetBodyContent(
"<div contenteditable>"
"hello"
"</div>");
Element* div = GetDocument().QuerySelector("div");
Text* text = ToText(div->firstChild());
// Set suggestion marker with two suggestions.
GetDocument().Markers().AddSuggestionMarker(
EphemeralRange(Position(text, 0), Position(text, 5)),
SuggestionMarkerProperties::Builder()
.SetSuggestions(Vector<String>({"marker1", "marker2"}))
.Build());
// Set the caret inside the word.
GetDocument().GetFrame()->Selection().SetSelectionAndEndTyping(
SelectionInDOMTree::Builder()
.SetBaseAndExtent(Position(text, 3), Position(text, 3))
.Build());
// Handle potential suggestion tap on the caret position.
GetDocument()
.GetFrame()
->GetTextSuggestionController()
.HandlePotentialSuggestionTap(PositionInFlatTree(text, 3));
EXPECT_TRUE(IsTextSuggestionHostAvailable());
}
} // namespace blink } // namespace blink
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment