setSelectionRange should set selection without validation
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 18 Feb 2014 22:23:17 +0000 (22:23 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 18 Feb 2014 22:23:17 +0000 (22:23 +0000)
https://bugs.webkit.org/show_bug.cgi?id=128949

Reviewed by Enrica Casucci.

Source/WebCore:

Since positionForIndex in HTMLTextFormControlElement always returns a candidate Position, we don't have to
validate selection in setSelectionRange.

Also fixed various bugs uncovered by this change.

This patch also fixes fast/forms/input-select-webkit-user-select-none.html, which used to assert wrong outcome
so that WebKit's behavior matches that of Chrome and Firefox.

Test: fast/forms/input-select-webkit-user-select-none.html

* dom/Position.h:
(WebCore::positionInParentBeforeNode): This function had a bug that when node is a child of the shadow root
it would return a null Position. Allow a position anchored inside a shadow root.
(WebCore::positionInParentAfterNode): Ditto.

* editing/FrameSelection.cpp:
(WebCore::FrameSelection::moveWithoutValidationTo): Renamed from moveTo and avoided selection validation.
* editing/FrameSelection.h:

* editing/htmlediting.cpp:
(WebCore::updatePositionForNodeRemoval): Fixed the bug that this function doesn't update positions before
or after children even if the shadow host of the anchor node is getting removed. Move the position before
the shadow host to be removed in both situations.

* html/HTMLTextFormControlElement.cpp:
(WebCore::HTMLTextFormControlElement::setSelectionRange): moveTo is renamed to moveWithoutValidationTo.
(WebCore::HTMLTextFormControlElement::selectionChanged): Check if the cached selection offsets are different
in lieu of FrameSelection::isRange() since they're equivalent here.
(WebCore::positionForIndex): Return the position inside or after the last br when there is one to match
the canonicalization algorithm we have. It's probably harmless to return the last position in the inner text
element anyways since most of our codebase supports that but this would avoid having to rebaseline dozens
of tests and reduces the risk of this patch.

LayoutTests:

Fixed input-select-webkit-user-select-none.html which was erroneously asserting selectionStart and selectionEnd
of a text field to be zero when it has -webkit-user-select: none. This doesn't not match behaviors of Chrome
and Firefox. They both retain the programatically set selection offsets since such style should not bleed into
the shadow DOM of the text field in the first place.

New behavior matches the latest Firefox and Chrome although we still have the bug that user cannot select text
inside such a text field.

Also modernized LayoutTests/editing/selection/5497643.html to make the expected results more readable and made
the test more robust against changes in the node index of textarea element.

* editing/selection/5497643-expected.txt: See above.
* editing/selection/5497643.html:
* fast/forms/input-select-webkit-user-select-none-expected.txt: See above.
* fast/forms/input-select-webkit-user-select-none.html:
* editing/deleting/delete-ligature-001-expected.txt: Progression in the editing delegate callbacks dumps.
Now we set selection directly into the text node inside the inner text element.
* platform/mac/platform/mac/editing/spelling/autocorrection-at-beginning-of-word-1-expected.txt: Ditto.
* platform/mac/platform/mac/editing/spelling/autocorrection-at-beginning-of-word-2-expected.txt: Ditto.
* platform/mac-mountainlion/platform/mac/editing/spelling/autocorrection-at-beginning-of-word-1-expected.txt:
Removed.
* platform/mac-mountainlion/platform/mac/editing/spelling/autocorrection-at-beginning-of-word-2-expected.txt:
Removed.

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

16 files changed:
LayoutTests/ChangeLog
LayoutTests/editing/deleting/delete-ligature-001-expected.txt
LayoutTests/editing/selection/5497643-expected.txt
LayoutTests/editing/selection/5497643.html
LayoutTests/fast/forms/input-select-webkit-user-select-none-expected.txt
LayoutTests/fast/forms/input-select-webkit-user-select-none.html
LayoutTests/platform/mac-mountainlion/platform/mac/editing/spelling/autocorrection-at-beginning-of-word-1-expected.txt
LayoutTests/platform/mac-mountainlion/platform/mac/editing/spelling/autocorrection-at-beginning-of-word-2-expected.txt
LayoutTests/platform/mac/platform/mac/editing/spelling/autocorrection-at-beginning-of-word-1-expected.txt
LayoutTests/platform/mac/platform/mac/editing/spelling/autocorrection-at-beginning-of-word-2-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/dom/Position.h
Source/WebCore/editing/FrameSelection.cpp
Source/WebCore/editing/FrameSelection.h
Source/WebCore/editing/htmlediting.cpp
Source/WebCore/html/HTMLTextFormControlElement.cpp

index ec8d114d19c5ddcfbc6541b1b052ee7fdf956a13..a4c3cc0210bf28a6a1f4dbe04d43d5a9e2a25964 100644 (file)
@@ -1,3 +1,34 @@
+2014-02-18  Ryosuke Niwa  <rniwa@webkit.org>
+
+        setSelectionRange should set selection without validation
+        https://bugs.webkit.org/show_bug.cgi?id=128949
+
+        Reviewed by Enrica Casucci.
+
+        Fixed input-select-webkit-user-select-none.html which was erroneously asserting selectionStart and selectionEnd
+        of a text field to be zero when it has -webkit-user-select: none. This doesn't not match behaviors of Chrome
+        and Firefox. They both retain the programatically set selection offsets since such style should not bleed into
+        the shadow DOM of the text field in the first place.
+
+        New behavior matches the latest Firefox and Chrome although we still have the bug that user cannot select text
+        inside such a text field.
+
+        Also modernized LayoutTests/editing/selection/5497643.html to make the expected results more readable and made
+        the test more robust against changes in the node index of textarea element.
+
+        * editing/selection/5497643-expected.txt: See above.
+        * editing/selection/5497643.html:
+        * fast/forms/input-select-webkit-user-select-none-expected.txt: See above.
+        * fast/forms/input-select-webkit-user-select-none.html:
+        * editing/deleting/delete-ligature-001-expected.txt: Progression in the editing delegate callbacks dumps.
+        Now we set selection directly into the text node inside the inner text element.
+        * platform/mac/platform/mac/editing/spelling/autocorrection-at-beginning-of-word-1-expected.txt: Ditto.
+        * platform/mac/platform/mac/editing/spelling/autocorrection-at-beginning-of-word-2-expected.txt: Ditto.
+        * platform/mac-mountainlion/platform/mac/editing/spelling/autocorrection-at-beginning-of-word-1-expected.txt:
+        Removed.
+        * platform/mac-mountainlion/platform/mac/editing/spelling/autocorrection-at-beginning-of-word-2-expected.txt:
+        Removed.
+
 2014-02-18  Myles C. Maxfield  <mmaxfield@apple.com>
 
         Convert position:sticky to position:static upon copy and paste
index 80fa0e6aa9a195037f4df87bca7777ab3a7e103b..098cca26e775d32eb4f5429947c38554fc005ebc 100644 (file)
@@ -1,6 +1,6 @@
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 0 of DIV > #document-fragment to 0 of DIV > #document-fragment toDOMRange:range from 1 of #text > DIV > #document-fragment to 1 of #text > DIV > #document-fragment affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 1 of #text > DIV > #document-fragment to 1 of #text > DIV > #document-fragment toDOMRange:range from 1 of #text > DIV > #document-fragment to 1 of #text > DIV > #document-fragment affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
 EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 1 of #text > DIV > #document-fragment to 1 of #text > DIV > #document-fragment toDOMRange:range from 2 of #text > DIV > #document-fragment to 2 of #text > DIV > #document-fragment affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
index a4f6f411d954c54cbfc7ce9220e6707ee48a28ba..86c8e02503e50d704615bc28ad314b10d92e8562 100644 (file)
@@ -1,4 +1,10 @@
-ALERT: SUCCESS: Selection is set to position 2 of BODY.
+textareaOffset = nodeIndex(textarea); textarea.setSelectionRange(0, 0); textarea.parentNode.removeChild(textarea);
+PASS getSelection().type is 'Caret'
+PASS getSelection().getRangeAt(0).startContainer is document.body
+PASS getSelection().getRangeAt(0).startOffset is textareaOffset
+PASS successfullyParsed is true
+
+TEST COMPLETE
 This tests to make sure that a selection inside a textarea is updated when the textarea is removed from the document.
 
 
index 90de6df1f7aeb341bb06c8877a977ed0f9a4c398..b1428df97b16a66bad9eb9fc5e583f5f68158173 100644 (file)
@@ -1,15 +1,22 @@
+<!DOCTYPE html>
+<html>
+<body>
 <p>This tests to make sure that a selection inside a textarea is updated when the textarea is removed from the document.</p>
 <textarea id="textarea"></textarea>
+<script src="../../resources/js-test-pre.js"></script>
 <script>
 if (window.testRunner)
     window.testRunner.dumpAsText();
+function nodeIndex(node) {
+    return Array.prototype.slice.call(node.parentNode.childNodes).indexOf(node);
+}
 textarea = document.getElementById("textarea");
-textarea.setSelectionRange(0, 0);
-textarea.parentNode.removeChild(textarea);
-if (window.getSelection().type == 'Caret' &&
-    window.getSelection().getRangeAt(0).startContainer == document.body && 
-    window.getSelection().getRangeAt(0).startOffset == 2)
-    alert("SUCCESS: Selection is set to position 2 of BODY.")
-else
-    alert("FAILURE: The selection is not set correctly after textarea was deleted.")
+evalAndLog("textareaOffset = nodeIndex(textarea); textarea.setSelectionRange(0, 0); textarea.parentNode.removeChild(textarea);");
+shouldBe("getSelection().type", "'Caret'");
+shouldBe("getSelection().getRangeAt(0).startContainer", "document.body");
+shouldBe("getSelection().getRangeAt(0).startOffset", "textareaOffset");
+var successfullyParsed = true;
 </script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
index 9259eccd690a4ec8ef2efb20ca1268099226696b..2902783c11201f7a1533ce41cb9566174a3eca93 100644 (file)
@@ -1,5 +1,12 @@
 Tests behavior of select() in case "-webkit-user-select: none" attribute is specified to the input element. The field should not be selected.
 
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS
+PASS input.select(); input.selectionStart is 0
+PASS input.selectionEnd is input.value.length
+PASS input.value.length is not 0
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
index 4859ec9df10e0be4d22161ab21c8747d955b0166..c1226c2d0c174337bd68498788350e5b2fe74590 100644 (file)
@@ -1,26 +1,25 @@
+<!DOCTYPE html>
 <html>
-<head>
+<body onload="test()">
+<script src="../../resources/js-test-pre.js"></script>
 <script>
+description("Tests behavior of select() in case &quot;-webkit-user-select: none&quot;"
+    + " attribute is specified to the input element. The field should not be selected.");
+
 function test() {
     if (window.testRunner)
         testRunner.dumpAsText();
-    var tf = document.getElementById('tf');
-    tf.select();
-    if (tf.selectionStart == 0 && tf.selectionEnd == 0) { // ;
-        document.getElementById("result").innerHTML = "PASS";
-    } else {
-        document.getElementById("result").innerHTML = "FAIL: selection start is "
-            + tf.selectionStart + " and end is " + tf.selectionEnd + ".";
-    }
+    window.input = document.querySelector('input');
+    shouldBe("input.select(); input.selectionStart", "0");
+    shouldBe("input.selectionEnd", "input.value.length");
+    shouldNotBe("input.value.length", "0");
+    finishJSTest();
 }
+
+var successfullyParsed = true;
+var jsTestIsAsync = true;
 </script>
-</head>
-<body onload="test()">
-<p>
-Tests behavior of select() in case &quot;-webkit-user-select: none&quot;
-attribute is specified to the input element. The field should not be selected.
-</p>
-<p><input type="text" id="tf" value="input text" style="-webkit-user-select: none"></input></p>
-<p id="result">TEST NOT RUN YET</p>
+<script src="../../resources/js-test-post.js"></script>
+<p><input type="text" value="input text" style="-webkit-user-select: none; -moz-user-select: none;"></input></p>
 </body>
 </html>
index af33f7d27ff37870beb23dcda7461e454546c37f..426ebb93136484829c1b8228cb6299db84e92284 100644 (file)
@@ -1,6 +1,6 @@
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 0 of DIV > #document-fragment to 0 of DIV > #document-fragment toDOMRange:range from 1 of #text > DIV > #document-fragment to 1 of #text > DIV > #document-fragment affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 1 of #text > DIV > #document-fragment to 1 of #text > DIV > #document-fragment toDOMRange:range from 1 of #text > DIV > #document-fragment to 1 of #text > DIV > #document-fragment affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
index fe4fecf2abbac5caad8b70aa41e00c8c71900225..29a28857bd76e7297069974924b021b601eaed4c 100644 (file)
@@ -1,6 +1,6 @@
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 0 of DIV > #document-fragment to 0 of DIV > #document-fragment toDOMRange:range from 1 of #text > DIV > #document-fragment to 1 of #text > DIV > #document-fragment affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 1 of #text > DIV > #document-fragment to 1 of #text > DIV > #document-fragment toDOMRange:range from 1 of #text > DIV > #document-fragment to 1 of #text > DIV > #document-fragment affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
index 9bae08087caf90824341ef8bbdbd6b8b0bd7a197..7b5acbdb9182e586cee8e54508def81d6b7d4609 100644 (file)
@@ -1,6 +1,6 @@
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 0 of DIV > #document-fragment to 0 of DIV > #document-fragment toDOMRange:range from 1 of #text > DIV > #document-fragment to 1 of #text > DIV > #document-fragment affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 1 of #text > DIV > #document-fragment to 1 of #text > DIV > #document-fragment toDOMRange:range from 1 of #text > DIV > #document-fragment to 1 of #text > DIV > #document-fragment affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
index 0e79d7e2c932573f43617202f346ab498658d7a6..597eec62c13abd5974ad524bc543bab72712d8d5 100644 (file)
@@ -1,6 +1,6 @@
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 0 of DIV > #document-fragment to 0 of DIV > #document-fragment toDOMRange:range from 1 of #text > DIV > #document-fragment to 1 of #text > DIV > #document-fragment affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 1 of #text > DIV > #document-fragment to 1 of #text > DIV > #document-fragment toDOMRange:range from 1 of #text > DIV > #document-fragment to 1 of #text > DIV > #document-fragment affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
index 2c2639fd9a6cc775843134394471479c76d30287..b3b2e0a59820878e9fa3a3bc868b6e97b997b964 100644 (file)
@@ -1,3 +1,43 @@
+2014-02-18  Ryosuke Niwa  <rniwa@webkit.org>
+
+        setSelectionRange should set selection without validation
+        https://bugs.webkit.org/show_bug.cgi?id=128949
+
+        Reviewed by Enrica Casucci.
+
+        Since positionForIndex in HTMLTextFormControlElement always returns a candidate Position, we don't have to
+        validate selection in setSelectionRange.
+
+        Also fixed various bugs uncovered by this change.
+
+        This patch also fixes fast/forms/input-select-webkit-user-select-none.html, which used to assert wrong outcome
+        so that WebKit's behavior matches that of Chrome and Firefox.
+
+        Test: fast/forms/input-select-webkit-user-select-none.html
+
+        * dom/Position.h:
+        (WebCore::positionInParentBeforeNode): This function had a bug that when node is a child of the shadow root
+        it would return a null Position. Allow a position anchored inside a shadow root.
+        (WebCore::positionInParentAfterNode): Ditto.
+
+        * editing/FrameSelection.cpp:
+        (WebCore::FrameSelection::moveWithoutValidationTo): Renamed from moveTo and avoided selection validation.
+        * editing/FrameSelection.h:
+
+        * editing/htmlediting.cpp:
+        (WebCore::updatePositionForNodeRemoval): Fixed the bug that this function doesn't update positions before
+        or after children even if the shadow host of the anchor node is getting removed. Move the position before
+        the shadow host to be removed in both situations.
+
+        * html/HTMLTextFormControlElement.cpp:
+        (WebCore::HTMLTextFormControlElement::setSelectionRange): moveTo is renamed to moveWithoutValidationTo.
+        (WebCore::HTMLTextFormControlElement::selectionChanged): Check if the cached selection offsets are different
+        in lieu of FrameSelection::isRange() since they're equivalent here.
+        (WebCore::positionForIndex): Return the position inside or after the last br when there is one to match
+        the canonicalization algorithm we have. It's probably harmless to return the last position in the inner text
+        element anyways since most of our codebase supports that but this would avoid having to rebaseline dozens
+        of tests and reduces the risk of this patch.
+
 2014-02-18  Zan Dobersek  <zdobersek@igalia.com>
 
         Move IndexedDB module, LevelDB code to std::unique_ptr
index e49031e89e3284472e70654e17f176a4323f03df..bfdc6f71b8384bdad03ad63bc880d026a2998248 100644 (file)
@@ -272,17 +272,14 @@ inline bool operator<=(const Position& a, const Position& b)
 
 inline Position positionInParentBeforeNode(const Node* node)
 {
-    // FIXME: This should ASSERT(node->parentNode())
-    // At least one caller currently hits this ASSERT though, which indicates
-    // that the caller is trying to make a position relative to a disconnected node (which is likely an error)
-    // Specifically, editing/deleting/delete-ligature-001.html crashes with ASSERT(node->parentNode())
-    return Position(Position::findParent(node), node->nodeIndex(), Position::PositionIsOffsetInAnchor);
+    ASSERT(node->parentNode());
+    return Position(node->parentNode(), node->nodeIndex(), Position::PositionIsOffsetInAnchor);
 }
 
 inline Position positionInParentAfterNode(const Node* node)
 {
-    ASSERT(Position::findParent(node));
-    return Position(Position::findParent(node), node->nodeIndex() + 1, Position::PositionIsOffsetInAnchor);
+    ASSERT(node->parentNode());
+    return Position(node->parentNode(), node->nodeIndex() + 1, Position::PositionIsOffsetInAnchor);
 }
 
 // positionBeforeNode and positionAfterNode return neighbor-anchored positions, construction is O(1)
index 3f0a97787e2be9c9ea68fbd245fb83eaa9e58af7..adc543ff8914036add6387da74f365d04232532b 100644 (file)
@@ -164,10 +164,12 @@ void FrameSelection::moveTo(const Position &base, const Position &extent, EAffin
     setSelection(VisibleSelection(base, extent, affinity, selectionHasDirection), defaultSetSelectionOptions(userTriggered));
 }
 
-void FrameSelection::moveTo(const Position& base, const Position& extent, bool selectionHasDirection, bool shouldSetFocus)
+void FrameSelection::moveWithoutValidationTo(const Position& base, const Position& extent, bool selectionHasDirection, bool shouldSetFocus)
 {
-    setSelection(VisibleSelection(base, extent, DOWNSTREAM, selectionHasDirection),
-        defaultSetSelectionOptions() | (shouldSetFocus ? 0 : DoNotSetFocus));
+    VisibleSelection newSelection;
+    newSelection.setWithoutValidation(base, extent);
+    newSelection.setIsDirectional(selectionHasDirection);
+    setSelection(newSelection, defaultSetSelectionOptions() | (shouldSetFocus ? 0 : DoNotSetFocus));
 }
 
 void DragCaretController::setCaretPosition(const VisiblePosition& position)
index a315edb32d5f8fa10facf661b4d231fcd7883bb8..695e0052ad4985301c55486231ef3df95768b45b 100644 (file)
@@ -140,7 +140,7 @@ public:
     void moveTo(const VisiblePosition&, const VisiblePosition&, EUserTriggered = NotUserTriggered);
     void moveTo(const Position&, EAffinity, EUserTriggered = NotUserTriggered);
     void moveTo(const Position&, const Position&, EAffinity, EUserTriggered = NotUserTriggered);
-    void moveTo(const Position&, const Position&, bool selectionHasDirection, bool shouldSetFocus);
+    void moveWithoutValidationTo(const Position&, const Position&, bool selectionHasDirection, bool shouldSetFocus);
 
     const VisibleSelection& selection() const { return m_selection; }
     void setSelection(const VisibleSelection&, SetSelectionOptions = defaultSetSelectionOptions(), CursorAlignOnScroll = AlignCursorOnScrollIfNeeded, TextGranularity = CharacterGranularity);
index a89029d275cf4a116efbea89a3511df223fac753..d7a9293e254f5d74b7f7a1b630a63040589aa287 100644 (file)
@@ -988,12 +988,12 @@ void updatePositionForNodeRemoval(Position& position, Node* node)
         return;
     switch (position.anchorType()) {
     case Position::PositionIsBeforeChildren:
-        if (position.containerNode() == node)
+        if (node->containsIncludingShadowDOM(position.containerNode()))
             position = positionInParentBeforeNode(node);
         break;
     case Position::PositionIsAfterChildren:
-        if (position.containerNode() == node)
-            position = positionInParentAfterNode(node);
+        if (node->containsIncludingShadowDOM(position.containerNode()))
+            position = positionInParentBeforeNode(node);
         break;
     case Position::PositionIsOffsetInAnchor:
         if (position.containerNode() == node->parentNode() && static_cast<unsigned>(position.offsetInContainerNode()) > node->nodeIndex())
index fe53c3ae087363a9ba5305623498cd995e7f9354..6c325e6e63426fea4f1b5a465c4d8ad82c859fcd 100644 (file)
@@ -315,7 +315,7 @@ void HTMLTextFormControlElement::setSelectionRange(int start, int end, TextField
     }
 
     if (Frame* frame = document().frame())
-        frame->selection().moveTo(startPosition, endPosition, direction != SelectionHasNoDirection, !hasFocus);
+        frame->selection().moveWithoutValidationTo(startPosition, endPosition, direction != SelectionHasNoDirection, !hasFocus);
 }
 
 int HTMLTextFormControlElement::indexForVisiblePosition(const VisiblePosition& position) const
@@ -480,13 +480,8 @@ void HTMLTextFormControlElement::selectionChanged(bool shouldFireSelectEvent)
     // selectionStart() or selectionEnd() will return cached selection when this node doesn't have focus
     cacheSelection(computeSelectionStart(), computeSelectionEnd(), computeSelectionDirection());
     
-    if (!shouldFireSelectEvent)
-        return;
-
-    if (Frame* frame = document().frame()) {
-        if (frame->selection().isRange())
-            dispatchEvent(Event::create(eventNames().selectEvent, true, false));
-    }
+    if (shouldFireSelectEvent && m_cachedSelectionStart != m_cachedSelectionEnd)
+        dispatchEvent(Event::create(eventNames().selectEvent, true, false));
 }
 
 void HTMLTextFormControlElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
@@ -556,19 +551,22 @@ String HTMLTextFormControlElement::innerTextValue() const
 static Position positionForIndex(TextControlInnerTextElement* innerText, unsigned index)
 {
     unsigned remainingCharactersToMoveForward = index;
+    Node* lastBrOrText = innerText;
     for (Node* node = innerText; node; node = NodeTraversal::next(node, innerText)) {
         if (node->hasTagName(brTag)) {
             if (!remainingCharactersToMoveForward)
                 return positionBeforeNode(node);
             remainingCharactersToMoveForward--;
+            lastBrOrText = node;
         } else if (node->isTextNode()) {
             Text& text = toText(*node);
             if (remainingCharactersToMoveForward < text.length())
                 return Position(&text, remainingCharactersToMoveForward);
             remainingCharactersToMoveForward -= text.length();
+            lastBrOrText = node;
         }
     }
-    return lastPositionInNode(innerText);
+    return lastPositionInOrAfterNode(lastBrOrText);
 }
 
 unsigned HTMLTextFormControlElement::indexForPosition(const Position& passedPosition) const