[Cherry-Pick] Selection in input event handler is different from opensource
authoreustas@chromium.org <eustas@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 31 May 2013 05:14:45 +0000 (05:14 +0000)
committerSangYong Park <sy302.park@samsung.com>
Tue, 17 Sep 2013 04:09:18 +0000 (13:09 +0900)
[Title] Selection in input event handler is different from opensource
[Issue#] DCM-2420
[Solution] Cherry picked.
[Cherry-Picker] Sangyong Park <sy302.park>

selectionStart/selectionEnd return "obsolete" values when requested during "input" event
https://bugs.webkit.org/show_bug.cgi?id=110742

Reviewed by Ryosuke Niwa.

Source/WebCore:

This patch defers firing "webkitEditableContentChanged" until new
selection is applied to control. This makes selection during "input"
more consistent and reliable.

Background: "input" event is fired by "webkitEditableContentChanged"
dispatcher. But "input" is scoped event, so under some conditions its
dispatching may be deferred. When "input" dispatching is deferred,
dispatcher observes updated selectionStart and selectionEnd.
Otherwise values repersent state before applying editing command.

So, to make selectionStart/End to be more predictable and useful, we
need either always dispatch "input" before selection is updated, or
always dispatch "input" after selection is updated.

As it was mentioned, dispatching could be deferred by scoping. So
dispatching before updating selection couldn't be guaranteed.
Moreover, it will be hard to calculate updated selection in user
code. On the other side - old selection could be easily tracked.

So, it looks logically that we should guarantee dispatching "input"
after updating selection. There are no execution paths in
"webkitEditableContentChanged" dispatched that depends on current
selection. So it is safe to fire this event after selection is updated.

Test: editing/selection/caret-after-keypress.html

* editing/Editor.cpp:
Dispatch "input" event after new selection in applied.

LayoutTests:

Test that cursor is up-to-date during "input" event.

* editing/selection/caret-after-keypress-expected.txt: Added.
* editing/selection/caret-after-keypress.html: Added.
* platform/mac-wk2/TestExpectations: Exclude new test.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@151009 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Conflicts:

LayoutTests/ChangeLog
LayoutTests/platform/mac-wk2/TestExpectations
Source/WebCore/ChangeLog

Change-Id: Id5350596aa6633505c817b24b9e68a5cddd16ac1

LayoutTests/editing/selection/caret-after-keypress-expected.txt [new file with mode: 0644]
LayoutTests/editing/selection/caret-after-keypress.html [new file with mode: 0644]
Source/WebCore/editing/Editor.cpp

diff --git a/LayoutTests/editing/selection/caret-after-keypress-expected.txt b/LayoutTests/editing/selection/caret-after-keypress-expected.txt
new file mode 100644 (file)
index 0000000..171750a
--- /dev/null
@@ -0,0 +1,37 @@
+This tests that 'input' event listener gets correct caret position after keypress.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS test.value is ""
+PASS test.selectionStart is 0
+PASS test.selectionEnd is 0
+
+PASS test.value is "a"
+PASS test.selectionStart is 1
+PASS test.selectionEnd is 1
+
+PASS test.value is "ab"
+PASS test.selectionStart is 2
+PASS test.selectionEnd is 2
+
+PASS test.value is "abc"
+PASS test.selectionStart is 3
+PASS test.selectionEnd is 3
+
+PASS test.value is "ab"
+PASS test.selectionStart is 2
+PASS test.selectionEnd is 2
+
+PASS test.value is "a"
+PASS test.selectionStart is 1
+PASS test.selectionEnd is 1
+
+PASS test.value is ""
+PASS test.selectionStart is 0
+PASS test.selectionEnd is 0
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/editing/selection/caret-after-keypress.html b/LayoutTests/editing/selection/caret-after-keypress.html
new file mode 100644 (file)
index 0000000..a6f2c68
--- /dev/null
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+<script>
+description("This tests that 'input' event listener gets correct caret position after keypress.");
+
+var test = document.createElement("input");
+document.body.appendChild(test);
+test.focus();
+
+const backSpace = String.fromCharCode(8);
+var input = ["a", "b", "c", backSpace, backSpace, backSpace];
+var output = ["", "a", "ab", "abc", "ab", "a", ""];
+
+function step() {
+    var expectedValue = output.shift();
+    shouldBeEqualToString("test.value", expectedValue);
+    shouldBe("test.selectionStart", "" + expectedValue.length);
+    shouldBe("test.selectionEnd", "" + expectedValue.length);
+    debug("");
+    eventSender.keyDown(input.shift());
+}
+
+test.addEventListener("input", step);
+step();
+</script>
+<script src="../../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
\ No newline at end of file
index 4b97e25..d69769b 100755 (executable)
@@ -790,7 +790,6 @@ void Editor::appliedEditing(PassRefPtr<CompositeEditCommand> cmd)
 
     EditCommandComposition* composition = cmd->composition();
     ASSERT(composition);
-    dispatchEditableContentChangedEvents(composition->startingRootEditableElement(), composition->endingRootEditableElement());
     VisibleSelection newSelection(cmd->endingSelection());
 
     m_alternativeTextController->respondToAppliedEditing(cmd.get());
@@ -798,6 +797,7 @@ void Editor::appliedEditing(PassRefPtr<CompositeEditCommand> cmd)
     // Don't clear the typing style with this selection change.  We do those things elsewhere if necessary.
     FrameSelection::SetSelectionOptions options = cmd->isDictationCommand() ? FrameSelection::DictationTriggered : 0;
     changeSelectionAfterCommand(newSelection, options);
+    dispatchEditableContentChangedEvents(composition->startingRootEditableElement(), composition->endingRootEditableElement());
 
     if (!cmd->preservesTypingStyle())
         m_frame->selection()->clearTypingStyle();
@@ -819,13 +819,13 @@ void Editor::appliedEditing(PassRefPtr<CompositeEditCommand> cmd)
 void Editor::unappliedEditing(PassRefPtr<EditCommandComposition> cmd)
 {
     m_frame->document()->updateLayout();
-    
-    dispatchEditableContentChangedEvents(cmd->startingRootEditableElement(), cmd->endingRootEditableElement());
-    
+
     VisibleSelection newSelection(cmd->startingSelection());
     changeSelectionAfterCommand(newSelection, FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle);
+    dispatchEditableContentChangedEvents(cmd->startingRootEditableElement(), cmd->endingRootEditableElement());
+
     m_alternativeTextController->respondToUnappliedEditing(cmd.get());
-    
+
     m_lastEditCommand = 0;
     if (client())
         client()->registerRedoStep(cmd);
@@ -835,12 +835,11 @@ void Editor::unappliedEditing(PassRefPtr<EditCommandComposition> cmd)
 void Editor::reappliedEditing(PassRefPtr<EditCommandComposition> cmd)
 {
     m_frame->document()->updateLayout();
-    
-    dispatchEditableContentChangedEvents(cmd->startingRootEditableElement(), cmd->endingRootEditableElement());
-    
+
     VisibleSelection newSelection(cmd->endingSelection());
     changeSelectionAfterCommand(newSelection, FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle);
-    
+    dispatchEditableContentChangedEvents(cmd->startingRootEditableElement(), cmd->endingRootEditableElement());
+
     m_lastEditCommand = 0;
     if (client())
         client()->registerUndoStep(cmd);