Title: [208090] trunk
Revision
208090
Author
wenson_hs...@apple.com
Date
2016-10-28 18:06:13 -0700 (Fri, 28 Oct 2016)

Log Message

iOS autocorrection does not trigger an input event of inputType "insertReplacementText"
https://bugs.webkit.org/show_bug.cgi?id=164077
<rdar://problem/28987810>

Reviewed by Simon Fraser.

Source/WebCore:

Fixes candidate insertion on iOS, so that it fires input events of type "insertReplacementText" and adds two
iOS unit tests covering this change as well as the test infrastructure needed to support these tests. See
comments below for more details.

Tests: fast/events/ios/before-input-events-prevent-candidate-insertion.html
       fast/events/ios/input-events-insert-replacement-text.html

* dom/TextEvent.h:

Adds isAutocompletion() to TextEvent, as well as the TextEventInputAutocompletion text input type. When the
Editor handles this TextEvent, it will use this information when creating or modifying the corresponding typing
command.

* dom/TextEventInputType.h:
* editing/Editor.cpp:
(WebCore::Editor::insertText):
(WebCore::Editor::insertTextWithoutSendingTextEvent):
* editing/Editor.h:
* editing/TypingCommand.cpp:
(WebCore::editActionForTypingCommand):

Now takes whether the command is autocorrection into account. If so, the corresponding edit action should be
EditActionInsertReplacement rather than EditActionTypingInsertText.

(WebCore::TypingCommand::TypingCommand):
(WebCore::TypingCommand::deleteSelection):
(WebCore::TypingCommand::deleteKeyPressed):
(WebCore::TypingCommand::forwardDeleteKeyPressed):
(WebCore::TypingCommand::insertText):
(WebCore::TypingCommand::insertLineBreak):
(WebCore::TypingCommand::insertParagraphSeparatorInQuotedContent):
(WebCore::TypingCommand::insertParagraphSeparator):
(WebCore::TypingCommand::inputEventData):
(WebCore::TypingCommand::willAddTypingToOpenCommand):
* editing/TypingCommand.h:

Adds a new TypingCommand option, IsAutocompletion.

Source/WebKit2:

Small tweak to mark text insertion when autocorrecting as such, as opposed to regular keyboard input.

* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::syncApplyAutocorrection):

Tools:

Adds test support for inserting text candidates on iOS in the form of
UIScriptController.selectTextCandidateAtIndex(index, callback), which selects the text candidate at a given
index (this needs to be a value between 0-2 on iOS) and fires the callback when done.

To implement this, we wait for the text prediction view to have predictions (we determine this by polling at a
given interval) and then tap the center of the text prediction view at the given index.

* DumpRenderTree/ios/UIScriptControllerIOS.mm:
(WTR::UIScriptController::selectTextCandidateAtIndex):
* TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
* TestRunnerShared/UIScriptContext/UIScriptController.cpp:
(WTR::UIScriptController::selectTextCandidateAtIndex):
(WTR::UIScriptController::waitForTextPredictionsViewAndSelectCandidateAtIndex):
* TestRunnerShared/UIScriptContext/UIScriptController.h:
* WebKitTestRunner/ios/UIKitSPI.h:
* WebKitTestRunner/ios/UIScriptControllerIOS.mm:
(WTR::UIScriptController::selectTextCandidateAtIndex):
(WTR::UIScriptController::waitForTextPredictionsViewAndSelectCandidateAtIndex):

LayoutTests:

Adds 2 new unit tests verifying that candidate text insertion can be prevented via beforeinput events, and that
beforeinput and input events of type "insertReplacementText" are fired when inserting candidate text on iOS.

* fast/events/ios/before-input-events-prevent-candidate-insertion-expected.txt: Added.
* fast/events/ios/before-input-events-prevent-candidate-insertion.html: Added.
* fast/events/ios/input-events-insert-replacement-text-expected.txt: Added.
* fast/events/ios/input-events-insert-replacement-text.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (208089 => 208090)


--- trunk/LayoutTests/ChangeLog	2016-10-29 00:31:42 UTC (rev 208089)
+++ trunk/LayoutTests/ChangeLog	2016-10-29 01:06:13 UTC (rev 208090)
@@ -1,3 +1,19 @@
+2016-10-28  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        iOS autocorrection does not trigger an input event of inputType "insertReplacementText"
+        https://bugs.webkit.org/show_bug.cgi?id=164077
+        <rdar://problem/28987810>
+
+        Reviewed by Simon Fraser.
+
+        Adds 2 new unit tests verifying that candidate text insertion can be prevented via beforeinput events, and that
+        beforeinput and input events of type "insertReplacementText" are fired when inserting candidate text on iOS.
+
+        * fast/events/ios/before-input-events-prevent-candidate-insertion-expected.txt: Added.
+        * fast/events/ios/before-input-events-prevent-candidate-insertion.html: Added.
+        * fast/events/ios/input-events-insert-replacement-text-expected.txt: Added.
+        * fast/events/ios/input-events-insert-replacement-text.html: Added.
+
 2016-10-28  Alex Christensen  <achristen...@webkit.org>
 
         Partially revert 207805 after resolution in URL spec issue 87

Added: trunk/LayoutTests/fast/events/ios/before-input-events-prevent-candidate-insertion-expected.txt (0 => 208090)


--- trunk/LayoutTests/fast/events/ios/before-input-events-prevent-candidate-insertion-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/events/ios/before-input-events-prevent-candidate-insertion-expected.txt	2016-10-29 01:06:13 UTC (rev 208090)
@@ -0,0 +1,5 @@
+
+To manually test, type 't' into the contenteditable and try to select a candidate. The replacement text should not be inserted.
+
+PASS: The replacement text was prevented.
+

Added: trunk/LayoutTests/fast/events/ios/before-input-events-prevent-candidate-insertion.html (0 => 208090)


--- trunk/LayoutTests/fast/events/ios/before-input-events-prevent-candidate-insertion.html	                        (rev 0)
+++ trunk/LayoutTests/fast/events/ios/before-input-events-prevent-candidate-insertion.html	2016-10-29 01:06:13 UTC (rev 208090)
@@ -0,0 +1,92 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+
+<html>
+
+<head>
+    <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
+    <script id="ui-script" type="text/plain">
+        (function() {
+            uiController.didShowKeyboardCallback = function() {
+                uiController.typeCharacterUsingHardwareKeyboard("t", function() {
+                    uiController.selectTextCandidateAtIndex(1, function() {
+                        uiController.uiScriptComplete();
+                    });
+                });
+            }
+            uiController.singleTapAtPoint($x, $y, function() {});
+        })();
+    </script>
+
+    <script>
+        var progress = 0;
+        var replacementText = "";
+        let write = (message) => output.innerHTML += (message + "<br>");
+        function getUIScript() {
+            let rect = editable.getBoundingClientRect();
+            let script = document.getElementById("ui-script").text;
+            script = script.replace("$x", rect.left + rect.width / 2);
+            return script.replace("$y", rect.top + rect.height / 2);
+        }
+
+        if (window.testRunner) {
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+            internals.settings.setInputEventsEnabled(true);
+        }
+
+        function incrementProgress()
+        {
+            progress++;
+            if (!window.testRunner || progress !== 2)
+                return;
+
+            setTimeout(function() {
+                if (editable.value.indexOf(replacementText) == -1)
+                    write(`PASS: The replacement text was prevented.`);
+                else
+                    write(`FAIL: The input value ${editable.value} should not contain replacement text ${replacementText}.`);
+                testRunner.notifyDone();
+            }, 0);
+        }
+
+        function runTest()
+        {
+            editable.addEventListener("beforeinput", preventBeforeInput);
+            if (!window.testRunner || !testRunner.runUIScript)
+                return;
+
+            testRunner.runUIScript(getUIScript(), function(result) {
+                incrementProgress();
+            });
+        }
+
+        function preventBeforeInput(event)
+        {
+            if (event.inputType === "insertReplacementText") {
+                replacementText = event.data;
+                if (!replacementText)
+                    write("FAIL: The replacement text input event lacks data.");
+                event.preventDefault();
+                incrementProgress();
+            }
+        }
+
+    </script>
+    <style>
+    #editable {
+        width: 200px;
+        height: 100px;
+        top: 0;
+        left: 0;
+        position: absolute;
+    }
+    </style>
+</head>
+
+<body style="margin: 0;" _onload_=runTest()>
+    <input contenteditable id="editable"></input>
+    <p>To manually test, type 't' into the contenteditable and try to select a candidate. The replacement text should not be inserted.</p>
+    <div id="output"></div>
+</body>
+
+</html>

Added: trunk/LayoutTests/fast/events/ios/input-events-insert-replacement-text-expected.txt (0 => 208090)


--- trunk/LayoutTests/fast/events/ios/input-events-insert-replacement-text-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/events/ios/input-events-insert-replacement-text-expected.txt	2016-10-29 01:06:13 UTC (rev 208090)
@@ -0,0 +1,5 @@
+To manually test, type 't' into the contenteditable and try to select a candidate. The output should indicate that the beforeinput and input events for the text replacement were handled, and that the range of the beforeinput event is from 0 to 1.
+
+PASS: Handled text replacement before input event with range: [0, 1].
+PASS: Handled text replacement input event.
+

Added: trunk/LayoutTests/fast/events/ios/input-events-insert-replacement-text.html (0 => 208090)


--- trunk/LayoutTests/fast/events/ios/input-events-insert-replacement-text.html	                        (rev 0)
+++ trunk/LayoutTests/fast/events/ios/input-events-insert-replacement-text.html	2016-10-29 01:06:13 UTC (rev 208090)
@@ -0,0 +1,90 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+
+<html>
+
+<head>
+    <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
+    <script id="ui-script" type="text/plain">
+        (function() {
+            uiController.didShowKeyboardCallback = function() {
+                uiController.typeCharacterUsingHardwareKeyboard("t", function() {
+                    uiController.selectTextCandidateAtIndex(1, function() {
+                        uiController.uiScriptComplete();
+                    });
+                });
+            }
+            uiController.singleTapAtPoint($x, $y, function() {});
+        })();
+    </script>
+
+    <script>
+        var progress = 0;
+        let write = (message) => output.innerHTML += (message + "<br>");
+        function getUIScript() {
+            let rect = editable.getBoundingClientRect();
+            let script = document.getElementById("ui-script").text;
+            script = script.replace("$x", rect.left + rect.width / 2);
+            return script.replace("$y", rect.top + rect.height / 2);
+        }
+
+        if (window.testRunner) {
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+            internals.settings.setInputEventsEnabled(true);
+        }
+
+        function incrementProgress()
+        {
+            progress++;
+            if (!window.testRunner || progress !== 3)
+                return;
+
+            setTimeout(function() {
+                editable.textContent = "";
+                testRunner.notifyDone();
+            }, 0);
+        }
+
+        function runTest()
+        {
+            editable.addEventListener("input", logInputEvent);
+            editable.addEventListener("beforeinput", logInputEvent);
+            if (!window.testRunner || !testRunner.runUIScript)
+                return;
+
+            testRunner.runUIScript(getUIScript(), function(result) {
+                incrementProgress();
+            });
+        }
+
+        function logInputEvent(event)
+        {
+            if (event.inputType !== "insertReplacementText")
+                return;
+
+            if (event.type === "beforeinput") {
+                let firstRange = event.getTargetRanges()[0];
+                write(`PASS: Handled text replacement before input event with range: [${firstRange.startOffset}, ${firstRange.endOffset}].`);
+            } else
+                write(`PASS: Handled text replacement input event.`);
+            incrementProgress();
+        }
+    </script>
+    <style>
+    #editable {
+        width: 200px;
+        height: 200px;
+        top: 0;
+        left: 0;
+        position: absolute;
+    }
+    </style>
+</head>
+
+<body style="margin: 0;" _onload_=runTest()>
+    <div contenteditable id="editable"></div>
+    <p>To manually test, type 't' into the contenteditable and try to select a candidate. The output should indicate that the beforeinput and input events for the text replacement were handled, and that the range of the beforeinput event is from 0 to 1.</p>
+    <div id="output"></div>
+</body>
+
+</html>

Modified: trunk/Source/WebCore/ChangeLog (208089 => 208090)


--- trunk/Source/WebCore/ChangeLog	2016-10-29 00:31:42 UTC (rev 208089)
+++ trunk/Source/WebCore/ChangeLog	2016-10-29 01:06:13 UTC (rev 208090)
@@ -1,3 +1,49 @@
+2016-10-28  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        iOS autocorrection does not trigger an input event of inputType "insertReplacementText"
+        https://bugs.webkit.org/show_bug.cgi?id=164077
+        <rdar://problem/28987810>
+
+        Reviewed by Simon Fraser.
+
+        Fixes candidate insertion on iOS, so that it fires input events of type "insertReplacementText" and adds two
+        iOS unit tests covering this change as well as the test infrastructure needed to support these tests. See
+        comments below for more details.
+
+        Tests: fast/events/ios/before-input-events-prevent-candidate-insertion.html
+               fast/events/ios/input-events-insert-replacement-text.html
+
+        * dom/TextEvent.h:
+
+        Adds isAutocompletion() to TextEvent, as well as the TextEventInputAutocompletion text input type. When the
+        Editor handles this TextEvent, it will use this information when creating or modifying the corresponding typing
+        command.
+
+        * dom/TextEventInputType.h:
+        * editing/Editor.cpp:
+        (WebCore::Editor::insertText):
+        (WebCore::Editor::insertTextWithoutSendingTextEvent):
+        * editing/Editor.h:
+        * editing/TypingCommand.cpp:
+        (WebCore::editActionForTypingCommand):
+
+        Now takes whether the command is autocorrection into account. If so, the corresponding edit action should be
+        EditActionInsertReplacement rather than EditActionTypingInsertText.
+
+        (WebCore::TypingCommand::TypingCommand):
+        (WebCore::TypingCommand::deleteSelection):
+        (WebCore::TypingCommand::deleteKeyPressed):
+        (WebCore::TypingCommand::forwardDeleteKeyPressed):
+        (WebCore::TypingCommand::insertText):
+        (WebCore::TypingCommand::insertLineBreak):
+        (WebCore::TypingCommand::insertParagraphSeparatorInQuotedContent):
+        (WebCore::TypingCommand::insertParagraphSeparator):
+        (WebCore::TypingCommand::inputEventData):
+        (WebCore::TypingCommand::willAddTypingToOpenCommand):
+        * editing/TypingCommand.h:
+
+        Adds a new TypingCommand option, IsAutocompletion.
+
 2016-10-28  Commit Queue  <commit-qu...@webkit.org>
 
         Unreviewed, rolling out r207700.

Modified: trunk/Source/WebCore/dom/TextEvent.h (208089 => 208090)


--- trunk/Source/WebCore/dom/TextEvent.h	2016-10-29 00:31:42 UTC (rev 208089)
+++ trunk/Source/WebCore/dom/TextEvent.h	2016-10-29 01:06:13 UTC (rev 208090)
@@ -59,6 +59,7 @@
         bool isPaste() const { return m_inputType == TextEventInputPaste; }
         bool isDrop() const { return m_inputType == TextEventInputDrop; }
         bool isDictation() const { return m_inputType == TextEventInputDictation; }
+        bool isAutocompletion() const { return m_inputType == TextEventInputAutocompletion; }
 
         bool shouldSmartReplace() const { return m_shouldSmartReplace; }
         bool shouldMatchStyle() const { return m_shouldMatchStyle; }

Modified: trunk/Source/WebCore/dom/TextEventInputType.h (208089 => 208090)


--- trunk/Source/WebCore/dom/TextEventInputType.h	2016-10-29 00:31:42 UTC (rev 208089)
+++ trunk/Source/WebCore/dom/TextEventInputType.h	2016-10-29 01:06:13 UTC (rev 208090)
@@ -30,6 +30,7 @@
 
 enum TextEventInputType {
     TextEventInputKeyboard, // any newline characters in the text are line breaks only, not paragraph separators.
+    TextEventInputAutocompletion,
     TextEventInputLineBreak, // any tab characters in the text are backtabs.
     TextEventInputComposition,
     TextEventInputBackTab,

Modified: trunk/Source/WebCore/editing/Editor.cpp (208089 => 208090)


--- trunk/Source/WebCore/editing/Editor.cpp	2016-10-29 00:31:42 UTC (rev 208089)
+++ trunk/Source/WebCore/editing/Editor.cpp	2016-10-29 01:06:13 UTC (rev 208090)
@@ -1213,9 +1213,9 @@
     m_defaultParagraphSeparator = EditorParagraphSeparatorIsDiv;
 }
 
-bool Editor::insertText(const String& text, Event* triggeringEvent)
+bool Editor::insertText(const String& text, Event* triggeringEvent, TextEventInputType inputType)
 {
-    return m_frame.eventHandler().handleTextInputEvent(text, triggeringEvent);
+    return m_frame.eventHandler().handleTextInputEvent(text, triggeringEvent, inputType);
 }
 
 bool Editor::insertTextForConfirmedComposition(const String& text)
@@ -1269,6 +1269,8 @@
                     options |= TypingCommand::SelectInsertedText;
                 if (autocorrectionWasApplied)
                     options |= TypingCommand::RetainAutocorrectionIndicator;
+                if (triggeringEvent && triggeringEvent->isAutocompletion())
+                    options |= TypingCommand::IsAutocompletion;
                 TypingCommand::insertText(document, text, selection, options, triggeringEvent && triggeringEvent->isComposition() ? TypingCommand::TextCompositionFinal : TypingCommand::TextCompositionNone);
             }
 

Modified: trunk/Source/WebCore/editing/Editor.h (208089 => 208090)


--- trunk/Source/WebCore/editing/Editor.h	2016-10-29 00:31:42 UTC (rev 208089)
+++ trunk/Source/WebCore/editing/Editor.h	2016-10-29 01:06:13 UTC (rev 208090)
@@ -36,6 +36,7 @@
 #include "FindOptions.h"
 #include "FrameSelection.h"
 #include "TextChecking.h"
+#include "TextEventInputType.h"
 #include "TextIteratorBehavior.h"
 #include "VisibleSelection.h"
 #include "WritingDirection.h"
@@ -239,7 +240,7 @@
     Command command(const String& commandName, EditorCommandSource);
     WEBCORE_EXPORT static bool commandIsSupportedFromMenuOrKeyBinding(const String& commandName); // Works without a frame.
 
-    WEBCORE_EXPORT bool insertText(const String&, Event* triggeringEvent);
+    WEBCORE_EXPORT bool insertText(const String&, Event* triggeringEvent, TextEventInputType = TextEventInputKeyboard);
     bool insertTextForConfirmedComposition(const String& text);
     WEBCORE_EXPORT bool insertDictatedText(const String&, const Vector<DictationAlternative>& dictationAlternatives, Event* triggeringEvent);
     bool insertTextWithoutSendingTextEvent(const String&, bool selectInsertedText, TextEvent* triggeringEvent);

Modified: trunk/Source/WebCore/editing/TypingCommand.cpp (208089 => 208090)


--- trunk/Source/WebCore/editing/TypingCommand.cpp	2016-10-29 00:31:42 UTC (rev 208089)
+++ trunk/Source/WebCore/editing/TypingCommand.cpp	2016-10-29 01:06:13 UTC (rev 208090)
@@ -77,7 +77,7 @@
     const String& m_text;
 };
 
-static inline EditAction editActionForTypingCommand(TypingCommand::ETypingCommand command, TextGranularity granularity, TypingCommand::TextCompositionType compositionType)
+static inline EditAction editActionForTypingCommand(TypingCommand::ETypingCommand command, TextGranularity granularity, TypingCommand::TextCompositionType compositionType, bool isAutocompletion)
 {
     if (compositionType == TypingCommand::TextCompositionPending) {
         if (command == TypingCommand::InsertText)
@@ -112,7 +112,7 @@
             return EditActionTypingDeleteLineForward;
         return EditActionTypingDeleteForward;
     case TypingCommand::InsertText:
-        return EditActionTypingInsertText;
+        return isAutocompletion ? EditActionInsertReplacement : EditActionTypingInsertText;
     case TypingCommand::InsertLineBreak:
         return EditActionTypingInsertLineBreak;
     case TypingCommand::InsertParagraphSeparator:
@@ -140,7 +140,7 @@
 }
 
 TypingCommand::TypingCommand(Document& document, ETypingCommand commandType, const String &textToInsert, Options options, TextGranularity granularity, TextCompositionType compositionType)
-    : TextInsertionBaseCommand(document, editActionForTypingCommand(commandType, granularity, compositionType))
+    : TextInsertionBaseCommand(document, editActionForTypingCommand(commandType, granularity, compositionType, options & IsAutocompletion))
     , m_commandType(commandType)
     , m_textToInsert(textToInsert)
     , m_currentTextToInsert(textToInsert)
@@ -150,6 +150,7 @@
     , m_granularity(granularity)
     , m_compositionType(compositionType)
     , m_shouldAddToKillRing(options & AddsToKillRing)
+    , m_isAutocompletion(options & IsAutocompletion)
     , m_openedByBackwardDelete(false)
     , m_shouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator)
     , m_shouldPreventSpellChecking(options & PreventSpellChecking)
@@ -167,6 +168,7 @@
         return;
 
     if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*frame)) {
+        lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
         lastTypingCommand->setCompositionType(compositionType);
         lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
         lastTypingCommand->deleteSelection(options & SmartDelete);
@@ -181,6 +183,7 @@
     if (granularity == CharacterGranularity) {
         if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*document.frame())) {
             updateSelectionIfDifferentFromCurrentSelection(lastTypingCommand.get(), document.frame());
+            lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
             lastTypingCommand->setCompositionType(TextCompositionNone);
             lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
             lastTypingCommand->deleteKeyPressed(granularity, options & AddsToKillRing);
@@ -198,6 +201,7 @@
     if (granularity == CharacterGranularity) {
         if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*frame)) {
             updateSelectionIfDifferentFromCurrentSelection(lastTypingCommand.get(), frame);
+            lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
             lastTypingCommand->setCompositionType(TextCompositionNone);
             lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
             lastTypingCommand->forwardDeleteKeyPressed(granularity, options & AddsToKillRing);
@@ -251,6 +255,7 @@
             lastTypingCommand->setEndingSelection(selectionForInsertion);
         }
 
+        lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
         lastTypingCommand->setCompositionType(compositionType);
         lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
         lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
@@ -265,6 +270,7 @@
 void TypingCommand::insertLineBreak(Document& document, Options options)
 {
     if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*document.frame())) {
+        lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
         lastTypingCommand->setCompositionType(TextCompositionNone);
         lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
         lastTypingCommand->insertLineBreakAndNotifyAccessibility();
@@ -277,6 +283,7 @@
 void TypingCommand::insertParagraphSeparatorInQuotedContent(Document& document)
 {
     if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*document.frame())) {
+        lastTypingCommand->setIsAutocompletion(false);
         lastTypingCommand->setCompositionType(TextCompositionNone);
         lastTypingCommand->insertParagraphSeparatorInQuotedContentAndNotifyAccessibility();
         return;
@@ -288,6 +295,7 @@
 void TypingCommand::insertParagraphSeparator(Document& document, Options options)
 {
     if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*document.frame())) {
+        lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
         lastTypingCommand->setCompositionType(TextCompositionNone);
         lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
         lastTypingCommand->insertParagraphSeparatorAndNotifyAccessibility();
@@ -398,6 +406,7 @@
 {
     switch (m_currentTypingEditAction) {
     case EditActionTypingInsertText:
+    case EditActionInsertReplacement:
     case EditActionTypingInsertPendingComposition:
     case EditActionTypingInsertFinalComposition:
         return m_currentTextToInsert;
@@ -469,7 +478,7 @@
 bool TypingCommand::willAddTypingToOpenCommand(ETypingCommand commandType, TextGranularity granularity, const String& text, RefPtr<Range>&& range)
 {
     m_currentTextToInsert = text;
-    m_currentTypingEditAction = editActionForTypingCommand(commandType, granularity, m_compositionType);
+    m_currentTypingEditAction = editActionForTypingCommand(commandType, granularity, m_compositionType, m_isAutocompletion);
 
     if (!shouldDeferWillApplyCommandUntilAddingTypingCommand())
         return true;

Modified: trunk/Source/WebCore/editing/TypingCommand.h (208089 => 208090)


--- trunk/Source/WebCore/editing/TypingCommand.h	2016-10-29 00:31:42 UTC (rev 208089)
+++ trunk/Source/WebCore/editing/TypingCommand.h	2016-10-29 01:06:13 UTC (rev 208090)
@@ -53,7 +53,8 @@
         AddsToKillRing = 1 << 1,
         RetainAutocorrectionIndicator = 1 << 2,
         PreventSpellChecking = 1 << 3,
-        SmartDelete = 1 << 4
+        SmartDelete = 1 << 4,
+        IsAutocompletion = 1 << 5,
     };
     typedef unsigned Options;
 
@@ -79,6 +80,7 @@
     void forwardDeleteKeyPressed(TextGranularity, bool shouldAddToKillRing);
     void deleteSelection(bool smartDelete);
     void setCompositionType(TextCompositionType type) { m_compositionType = type; }
+    void setIsAutocompletion(bool isAutocompletion) { m_isAutocompletion = isAutocompletion; }
 
 #if PLATFORM(IOS)
     void setEndingSelectionOnLastInsertCommand(const VisibleSelection& selection);
@@ -151,6 +153,7 @@
     TextCompositionType m_compositionType;
     bool m_shouldAddToKillRing;
     bool m_preservesTypingStyle;
+    bool m_isAutocompletion;
     
     // Undoing a series of backward deletes will restore a selection around all of the
     // characters that were deleted, but only if the typing command being undone

Modified: trunk/Source/WebKit2/ChangeLog (208089 => 208090)


--- trunk/Source/WebKit2/ChangeLog	2016-10-29 00:31:42 UTC (rev 208089)
+++ trunk/Source/WebKit2/ChangeLog	2016-10-29 01:06:13 UTC (rev 208090)
@@ -1,3 +1,16 @@
+2016-10-28  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        iOS autocorrection does not trigger an input event of inputType "insertReplacementText"
+        https://bugs.webkit.org/show_bug.cgi?id=164077
+        <rdar://problem/28987810>
+
+        Reviewed by Simon Fraser.
+
+        Small tweak to mark text insertion when autocorrecting as such, as opposed to regular keyboard input.
+
+        * WebProcess/WebPage/ios/WebPageIOS.mm:
+        (WebKit::WebPage::syncApplyAutocorrection):
+
 2016-10-28  Megan Gardner  <megan_gard...@apple.com>
 
         Rename SharedMemoryMac to SharedMemoryCocoa

Modified: trunk/Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm (208089 => 208090)


--- trunk/Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm	2016-10-29 00:31:42 UTC (rev 208089)
+++ trunk/Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm	2016-10-29 01:06:13 UTC (rev 208090)
@@ -2171,7 +2171,7 @@
     
     frame.selection().setSelectedRange(range.get(), UPSTREAM, true);
     if (correction.length())
-        frame.editor().insertText(correction, 0);
+        frame.editor().insertText(correction, 0, originalText.isEmpty() ? TextEventInputKeyboard : TextEventInputAutocompletion);
     else
         frame.editor().deleteWithDirection(DirectionBackward, CharacterGranularity, false, true);
     correctionApplied = true;

Modified: trunk/Tools/ChangeLog (208089 => 208090)


--- trunk/Tools/ChangeLog	2016-10-29 00:31:42 UTC (rev 208089)
+++ trunk/Tools/ChangeLog	2016-10-29 01:06:13 UTC (rev 208090)
@@ -1,3 +1,30 @@
+2016-10-28  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        iOS autocorrection does not trigger an input event of inputType "insertReplacementText"
+        https://bugs.webkit.org/show_bug.cgi?id=164077
+        <rdar://problem/28987810>
+
+        Reviewed by Simon Fraser.
+
+        Adds test support for inserting text candidates on iOS in the form of
+        UIScriptController.selectTextCandidateAtIndex(index, callback), which selects the text candidate at a given
+        index (this needs to be a value between 0-2 on iOS) and fires the callback when done.
+
+        To implement this, we wait for the text prediction view to have predictions (we determine this by polling at a
+        given interval) and then tap the center of the text prediction view at the given index.
+
+        * DumpRenderTree/ios/UIScriptControllerIOS.mm:
+        (WTR::UIScriptController::selectTextCandidateAtIndex):
+        * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
+        * TestRunnerShared/UIScriptContext/UIScriptController.cpp:
+        (WTR::UIScriptController::selectTextCandidateAtIndex):
+        (WTR::UIScriptController::waitForTextPredictionsViewAndSelectCandidateAtIndex):
+        * TestRunnerShared/UIScriptContext/UIScriptController.h:
+        * WebKitTestRunner/ios/UIKitSPI.h:
+        * WebKitTestRunner/ios/UIScriptControllerIOS.mm:
+        (WTR::UIScriptController::selectTextCandidateAtIndex):
+        (WTR::UIScriptController::waitForTextPredictionsViewAndSelectCandidateAtIndex):
+
 2016-10-28  Commit Queue  <commit-qu...@webkit.org>
 
         Unreviewed, rolling out r207700.

Modified: trunk/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm (208089 => 208090)


--- trunk/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm	2016-10-29 00:31:42 UTC (rev 208089)
+++ trunk/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm	2016-10-29 01:06:13 UTC (rev 208090)
@@ -116,6 +116,10 @@
 {
 }
 
+void UIScriptController::selectTextCandidateAtIndex(long, JSValueRef)
+{
+}
+
 void UIScriptController::keyDownUsingHardwareKeyboard(JSStringRef character, JSValueRef callback)
 {
 }

Modified: trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl (208089 => 208090)


--- trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl	2016-10-29 00:31:42 UTC (rev 208089)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl	2016-10-29 01:06:13 UTC (rev 208090)
@@ -49,6 +49,8 @@
     void keyDownUsingHardwareKeyboard(DOMString character, object callback);
     void keyUpUsingHardwareKeyboard(DOMString character, object callback);
 
+    void selectTextCandidateAtIndex(long index, object callback);
+
     // eventsJSON describes a series of user events in JSON form. For the keys, see HIDEventGenerator.mm.
     // For example, this JSON describes a touch down followed by a touch up (i.e. a single tap).
     //  {

Modified: trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp (208089 => 208090)


--- trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp	2016-10-29 00:31:42 UTC (rev 208089)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp	2016-10-29 01:06:13 UTC (rev 208090)
@@ -214,6 +214,14 @@
 {
 }
 
+void UIScriptController::selectTextCandidateAtIndex(long, JSValueRef)
+{
+}
+
+void UIScriptController::waitForTextPredictionsViewAndSelectCandidateAtIndex(long, unsigned, float)
+{
+}
+
 void UIScriptController::keyDownUsingHardwareKeyboard(JSStringRef, JSValueRef)
 {
 }

Modified: trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h (208089 => 208090)


--- trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h	2016-10-29 00:31:42 UTC (rev 208089)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h	2016-10-29 01:06:13 UTC (rev 208090)
@@ -70,6 +70,8 @@
     void keyDownUsingHardwareKeyboard(JSStringRef character, JSValueRef callback);
     void keyUpUsingHardwareKeyboard(JSStringRef character, JSValueRef callback);
 
+    void selectTextCandidateAtIndex(long index, JSValueRef callback);
+
     void keyboardAccessoryBarNext();
     void keyboardAccessoryBarPrevious();
     
@@ -136,6 +138,7 @@
     JSClassRef wrapperClass() final;
 
     JSObjectRef objectFromRect(const WebCore::FloatRect&) const;
+    void waitForTextPredictionsViewAndSelectCandidateAtIndex(long index, unsigned callbackID, float interval);
 
     UIScriptContext* m_context;
 };

Modified: trunk/Tools/WebKitTestRunner/ios/UIKitSPI.h (208089 => 208090)


--- trunk/Tools/WebKitTestRunner/ios/UIKitSPI.h	2016-10-29 00:31:42 UTC (rev 208089)
+++ trunk/Tools/WebKitTestRunner/ios/UIKitSPI.h	2016-10-29 01:06:13 UTC (rev 208090)
@@ -37,6 +37,11 @@
 #import <UIKit/UIApplication_Private.h>
 #import <UIKit/UIWindow_Private.h>
 
+@interface UIKeyboardPredictionView : UIView
++ (UIKeyboardPredictionView *)activeInstance;
+- (BOOL)hasPredictions;
+@end
+
 #else
 
 #import "IOKitSPI.h"

Modified: trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm (208089 => 208090)


--- trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm	2016-10-29 00:31:42 UTC (rev 208089)
+++ trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm	2016-10-29 01:06:13 UTC (rev 208090)
@@ -255,6 +255,46 @@
     }];
 }
 
+void UIScriptController::selectTextCandidateAtIndex(long index, JSValueRef callback)
+{
+#if USE(APPLE_INTERNAL_SDK)
+    static const float textPredictionsPollingInterval = 0.1;
+    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
+    waitForTextPredictionsViewAndSelectCandidateAtIndex(index, callbackID, textPredictionsPollingInterval);
+#else
+    // FIXME: This is a no-op on non-internal builds due to UIKeyboardPredictionView being unavailable. Ideally, there should be a better way to
+    // retrieve information and interact with the predictive text view that will be compatible with OpenSource.
+    UNUSED_PARAM(index);
+    UNUSED_PARAM(callback);
+#endif
+}
+
+void UIScriptController::waitForTextPredictionsViewAndSelectCandidateAtIndex(long index, unsigned callbackID, float interval)
+{
+#if USE(APPLE_INTERNAL_SDK)
+    if (![UIKeyboardPredictionView activeInstance].hasPredictions) {
+        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, interval * NSEC_PER_SEC), dispatch_get_main_queue(), ^() {
+            waitForTextPredictionsViewAndSelectCandidateAtIndex(index, callbackID, interval);
+        });
+        return;
+    }
+
+    PlatformWKView webView = TestController::singleton().mainWebView()->platformView();
+    CGRect predictionViewFrame = [UIKeyboardPredictionView activeInstance].frame;
+    // This assumes there are 3 predicted text cells of equal width, which is the case on iOS.
+    float offsetX = (index * 2 + 1) * CGRectGetWidth(predictionViewFrame) / 6;
+    float offsetY = CGRectGetHeight(webView.window.frame) - CGRectGetHeight([UIKeyboardPredictionView activeInstance].superview.frame) + CGRectGetHeight(predictionViewFrame) / 2;
+    [[HIDEventGenerator sharedHIDEventGenerator] tap:CGPointMake(offsetX, offsetY) completionBlock:^{
+        if (m_context)
+            m_context->asyncTaskComplete(callbackID);
+    }];
+#else
+    UNUSED_PARAM(index);
+    UNUSED_PARAM(callbackID);
+    UNUSED_PARAM(interval);
+#endif
+}
+
 void UIScriptController::dismissFormAccessoryView()
 {
     TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to