Bug 1287655: place textarea/input cursor at end of text when initialized; r?smaug draft
authorDecky Coss <coss@cosstropolis.com>
Thu, 21 Jul 2016 14:52:49 -0400
changeset 396383 3c6ed330862482902919319c8aeb0d5de22022e5
parent 396300 451e84cd0ff9cb2f3090b495730413494795374a
child 527185 8678b93ff01688e3f0c52618e93a4f432c09deea
push id24982
push usercoss@cosstropolis.com
push dateWed, 03 Aug 2016 19:35:53 +0000
reviewerssmaug
bugs1287655
milestone51.0a1
Bug 1287655: place textarea/input cursor at end of text when initialized; r?smaug MozReview-Commit-ID: 2srGXFmla07
accessible/tests/mochitest/jsat/test_content_text.html
dom/browser-element/mochitest/browserElement_SetInputMethodActive.js
dom/html/HTMLInputElement.cpp
dom/html/HTMLTextAreaElement.cpp
dom/html/nsTextEditorState.cpp
dom/html/nsTextEditorState.h
dom/html/test/test_bug674558.html
layout/base/tests/bug1082486-1.html
layout/base/tests/bug646382-1-ref.html
layout/base/tests/bug646382-2-ref.html
layout/base/tests/bug664087-1-ref.html
layout/base/tests/bug664087-2-ref.html
layout/forms/nsTextControlFrame.cpp
layout/forms/test/bug287446_subframe.html
layout/forms/test/test_bug353539.html
layout/forms/test/test_bug534785.html
layout/forms/test/test_bug542914.html
layout/reftests/bugs/240933-1.html
testing/web-platform/meta/html/semantics/forms/textfieldselection/selection.html.ini
toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
widget/tests/test_native_key_bindings_mac.html
--- a/accessible/tests/mochitest/jsat/test_content_text.html
+++ b/accessible/tests/mochitest/jsat/test_content_text.html
@@ -63,26 +63,26 @@
              { android_todo: true /* Bug 980512 */})],
 
           // Editable text tests.
           [ContentMessages.focusSelector('textarea'),
            new ExpectedAnnouncement('editing'),
            new ExpectedEditState({
             editing: true,
             multiline: true,
-            atStart: true,
-            atEnd: false
+            atStart: false,
+            atEnd: true
            }),
            new ExpectedCursorChange(
             ['Please refrain from Mayoneggs during this salmonella scare.',
              {string: 'textarea'}]),
-           new ExpectedTextSelectionChanged(0, 0)
+           new ExpectedTextSelectionChanged(59, 59)
           ],
           [ContentMessages.activateCurrent(10),
-           new ExpectedTextCaretChanged(0, 10),
+           new ExpectedTextCaretChanged(10, 59),
            new ExpectedEditState({ editing: true,
              multiline: true,
              atStart: false,
              atEnd: false }),
            new ExpectedTextSelectionChanged(10, 10)],
           [ContentMessages.activateCurrent(20),
            new ExpectedTextCaretChanged(10, 20),
            new ExpectedTextSelectionChanged(20, 20)
@@ -131,28 +131,30 @@
            new ExpectedClickAction(),
            new ExpectedAnnouncement('editing'),
            new ExpectedEditState({
             editing: true,
             multiline: false,
             atStart: true,
             atEnd: true
            }, { focused: 'input[type=text]' }),
+           new ExpectedTextSelectionChanged(0, 0),
            new ExpectedTextSelectionChanged(0, 0)
            ],
           [ContentMessages.simpleMovePrevious,
            new ExpectedCursorChange(
             ['So we don\'t get dessert?', {string: 'label'}]),
            new ExpectedAnnouncement('navigating'),
            new ExpectedEditState({
             editing: false,
             multiline: false,
             atStart: true,
             atEnd: false
-           }, { focused: 'html' })],
+           },{ focused: 'html' })
+         ],
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(
             [{ string : 'entry' }],
             { focused: 'html'})],
           [ContentMessages.activateCurrent(0),
            new ExpectedClickAction(),
            new ExpectedAnnouncement('editing'),
            new ExpectedEditState({
--- a/dom/browser-element/mochitest/browserElement_SetInputMethodActive.js
+++ b/dom/browser-element/mochitest/browserElement_SetInputMethodActive.js
@@ -114,17 +114,17 @@ function setPermissions() {
       sendAsyncMessage('test:InputMethod:oninput', {
         from: 'input',
         value: this.value
       });
     };
 
     input.onblur = function() {
       // "Expected" lost of focus since the test is finished.
-      if (input.value === '#0#1hello') {
+      if (input.value === 'hello#0#1') {
         return;
       }
 
       sendAsyncMessage('test:InputMethod:oninput', {
         from: 'input',
         error: true,
         value: 'Unexpected lost of focus on the input frame!'
       });
@@ -223,17 +223,17 @@ function next(msg) {
 
         case 'im1':
           is(false, 'Shouldn\'t be hearing anything from second frame.');
 
           break;
 
         case 'input':
           if (gFrameMsgCounts.input === 1) {
-            is(value, '#0hello',
+            is(value, 'hello#0',
               'Failed to get correct input from the first iframe.');
           } else {
             ok(false, 'Unexpected multiple messages from input.')
           }
 
           break;
       }
 
@@ -278,17 +278,17 @@ function next(msg) {
           } else {
             ok(false, 'Unexpected multiple messages from im0.')
           }
 
           break;
 
         case 'input':
           if (gFrameMsgCounts.input === 2) {
-            is(value, '#0#1hello',
+            is(value, 'hello#0#1',
                'Failed to get correct input from the second iframe.');
           } else {
             ok(false, 'Unexpected multiple messages from input.')
           }
           break;
       }
 
       if (gFrameMsgCounts.input !== 2 ||
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -5650,18 +5650,18 @@ HTMLInputElement::SetRangeText(const nsA
     return;
   }
 
   int32_t start, end;
   aRv = GetSelectionRange(&start, &end);
   if (aRv.Failed()) {
     nsTextEditorState* state = GetEditorState();
     if (state && state->IsSelectionCached()) {
-      start = state->GetSelectionProperties().mStart;
-      end = state->GetSelectionProperties().mEnd;
+      start = state->GetSelectionProperties().GetStart();
+      end = state->GetSelectionProperties().GetEnd();
       aRv = NS_OK;
     }
   }
 
   SetRangeText(aReplacement, start, end, mozilla::dom::SelectionMode::Preserve,
                aRv, start, end);
 }
 
@@ -5693,18 +5693,18 @@ HTMLInputElement::SetRangeText(const nsA
     aEnd = inputValueLength;
   }
 
   if (aSelectionStart == -1 && aSelectionEnd == -1) {
     aRv = GetSelectionRange(&aSelectionStart, &aSelectionEnd);
     if (aRv.Failed()) {
       nsTextEditorState* state = GetEditorState();
       if (state && state->IsSelectionCached()) {
-        aSelectionStart = state->GetSelectionProperties().mStart;
-        aSelectionEnd = state->GetSelectionProperties().mEnd;
+        aSelectionStart = state->GetSelectionProperties().GetStart();
+        aSelectionEnd = state->GetSelectionProperties().GetEnd();
         aRv = NS_OK;
       }
     }
   }
 
   if (aStart <= aEnd) {
     value.Replace(aStart, aEnd - aStart, aReplacement);
     nsresult rv =
@@ -5763,17 +5763,17 @@ HTMLInputElement::GetSelectionStart(Erro
 {
   int32_t selEnd, selStart;
   aRv = GetSelectionRange(&selStart, &selEnd);
 
   if (aRv.Failed()) {
     nsTextEditorState* state = GetEditorState();
     if (state && state->IsSelectionCached()) {
       aRv = NS_OK;
-      return state->GetSelectionProperties().mStart;
+      return state->GetSelectionProperties().GetStart();
     }
   }
 
   return selStart;
 }
 
 NS_IMETHODIMP
 HTMLInputElement::GetSelectionStart(int32_t* aSelectionStart)
@@ -5785,17 +5785,17 @@ HTMLInputElement::GetSelectionStart(int3
   return rv.StealNSResult();
 }
 
 void
 HTMLInputElement::SetSelectionStart(int32_t aSelectionStart, ErrorResult& aRv)
 {
   nsTextEditorState* state = GetEditorState();
   if (state && state->IsSelectionCached()) {
-    state->GetSelectionProperties().mStart = aSelectionStart;
+    state->GetSelectionProperties().SetStart(aSelectionStart);
     return;
   }
 
   nsAutoString direction;
   aRv = GetSelectionDirection(direction);
   if (aRv.Failed()) {
     return;
   }
@@ -5827,17 +5827,17 @@ HTMLInputElement::GetSelectionEnd(ErrorR
 {
   int32_t selStart, selEnd;
   aRv = GetSelectionRange(&selStart, &selEnd);
 
   if (aRv.Failed()) {
     nsTextEditorState* state = GetEditorState();
     if (state && state->IsSelectionCached()) {
       aRv = NS_OK;
-      return state->GetSelectionProperties().mEnd;
+      return state->GetSelectionProperties().GetEnd();
     }
   }
 
   return selEnd;
 }
 
 NS_IMETHODIMP
 HTMLInputElement::GetSelectionEnd(int32_t* aSelectionEnd)
@@ -5849,17 +5849,17 @@ HTMLInputElement::GetSelectionEnd(int32_
   return rv.StealNSResult();
 }
 
 void
 HTMLInputElement::SetSelectionEnd(int32_t aSelectionEnd, ErrorResult& aRv)
 {
   nsTextEditorState* state = GetEditorState();
   if (state && state->IsSelectionCached()) {
-    state->GetSelectionProperties().mEnd = aSelectionEnd;
+    state->GetSelectionProperties().SetEnd(aSelectionEnd);
     return;
   }
 
   nsAutoString direction;
   aRv = GetSelectionDirection(direction);
   if (aRv.Failed()) {
     return;
   }
@@ -5933,17 +5933,17 @@ HTMLInputElement::GetSelectionDirection(
     if (NS_SUCCEEDED(rv)) {
       DirectionToName(dir, aDirection);
     }
   }
 
   if (NS_FAILED(rv)) {
     nsTextEditorState* state = GetEditorState();
     if (state && state->IsSelectionCached()) {
-      DirectionToName(state->GetSelectionProperties().mDirection, aDirection);
+      DirectionToName(state->GetSelectionProperties().GetDirection(), aDirection);
       return;
     }
   }
 
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
   }
 }
@@ -5962,17 +5962,17 @@ HTMLInputElement::SetSelectionDirection(
   nsTextEditorState* state = GetEditorState();
   if (state && state->IsSelectionCached()) {
     nsITextControlFrame::SelectionDirection dir = nsITextControlFrame::eNone;
     if (aDirection.EqualsLiteral("forward")) {
       dir = nsITextControlFrame::eForward;
     } else if (aDirection.EqualsLiteral("backward")) {
       dir = nsITextControlFrame::eBackward;
     }
-    state->GetSelectionProperties().mDirection = dir;
+    state->GetSelectionProperties().SetDirection(dir);
     return;
   }
 
   int32_t start, end;
   aRv = GetSelectionRange(&start, &end);
   if (!aRv.Failed()) {
     aRv = SetSelectionRange(start, end, aDirection);
   }
--- a/dom/html/HTMLTextAreaElement.cpp
+++ b/dom/html/HTMLTextAreaElement.cpp
@@ -666,17 +666,17 @@ HTMLTextAreaElement::GetSelectionStart(i
 
 uint32_t
 HTMLTextAreaElement::GetSelectionStart(ErrorResult& aError)
 {
   int32_t selStart, selEnd;
   nsresult rv = GetSelectionRange(&selStart, &selEnd);
 
   if (NS_FAILED(rv) && mState.IsSelectionCached()) {
-    return mState.GetSelectionProperties().mStart;
+    return mState.GetSelectionProperties().GetStart();
   }
   if (NS_FAILED(rv)) {
     aError.Throw(rv);
   }
   return selStart;
 }
 
 NS_IMETHODIMP
@@ -686,17 +686,17 @@ HTMLTextAreaElement::SetSelectionStart(i
   SetSelectionStart(aSelectionStart, error);
   return error.StealNSResult();
 }
 
 void
 HTMLTextAreaElement::SetSelectionStart(uint32_t aSelectionStart, ErrorResult& aError)
 {
   if (mState.IsSelectionCached()) {
-    mState.GetSelectionProperties().mStart = aSelectionStart;
+    mState.GetSelectionProperties().SetStart(aSelectionStart);
     return;
   }
 
   nsAutoString direction;
   nsresult rv = GetSelectionDirection(direction);
   if (NS_FAILED(rv)) {
     aError.Throw(rv);
     return;
@@ -729,17 +729,17 @@ HTMLTextAreaElement::GetSelectionEnd(int
 
 uint32_t
 HTMLTextAreaElement::GetSelectionEnd(ErrorResult& aError)
 {
   int32_t selStart, selEnd;
   nsresult rv = GetSelectionRange(&selStart, &selEnd);
 
   if (NS_FAILED(rv) && mState.IsSelectionCached()) {
-    return mState.GetSelectionProperties().mEnd;
+    return mState.GetSelectionProperties().GetEnd();
   }
   if (NS_FAILED(rv)) {
     aError.Throw(rv);
   }
   return selEnd;
 }
 
 NS_IMETHODIMP
@@ -749,17 +749,17 @@ HTMLTextAreaElement::SetSelectionEnd(int
   SetSelectionEnd(aSelectionEnd, error);
   return error.StealNSResult();
 }
 
 void
 HTMLTextAreaElement::SetSelectionEnd(uint32_t aSelectionEnd, ErrorResult& aError)
 {
   if (mState.IsSelectionCached()) {
-    mState.GetSelectionProperties().mEnd = aSelectionEnd;
+    mState.GetSelectionProperties().SetEnd(aSelectionEnd);
     return;
   }
 
   nsAutoString direction;
   nsresult rv = GetSelectionDirection(direction);
   if (NS_FAILED(rv)) {
     aError.Throw(rv);
     return;
@@ -826,17 +826,17 @@ HTMLTextAreaElement::GetSelectionDirecti
     rv = textControlFrame->GetSelectionRange(nullptr, nullptr, &dir);
     if (NS_SUCCEEDED(rv)) {
       DirectionToName(dir, aDirection);
     }
   }
 
   if (NS_FAILED(rv)) {
     if (mState.IsSelectionCached()) {
-      DirectionToName(mState.GetSelectionProperties().mDirection, aDirection);
+      DirectionToName(mState.GetSelectionProperties().GetDirection(), aDirection);
       return;
     }
     aError.Throw(rv);
   }
 }
 
 NS_IMETHODIMP
 HTMLTextAreaElement::SetSelectionDirection(const nsAString& aDirection)
@@ -851,17 +851,17 @@ HTMLTextAreaElement::SetSelectionDirecti
 {
   if (mState.IsSelectionCached()) {
     nsITextControlFrame::SelectionDirection dir = nsITextControlFrame::eNone;
     if (aDirection.EqualsLiteral("forward")) {
       dir = nsITextControlFrame::eForward;
     } else if (aDirection.EqualsLiteral("backward")) {
       dir = nsITextControlFrame::eBackward;
     }
-    mState.GetSelectionProperties().mDirection = dir;
+    mState.GetSelectionProperties().SetDirection(dir);
     return;
   }
 
   int32_t start, end;
   nsresult rv = GetSelectionRange(&start, &end);
   if (NS_SUCCEEDED(rv)) {
     rv = SetSelectionRange(start, end, aDirection);
   }
@@ -918,18 +918,18 @@ HTMLTextAreaElement::SetSelectionRange(u
 void
 HTMLTextAreaElement::SetRangeText(const nsAString& aReplacement,
                                   ErrorResult& aRv)
 {
   int32_t start, end;
   aRv = GetSelectionRange(&start, &end);
   if (aRv.Failed()) {
     if (mState.IsSelectionCached()) {
-      start = mState.GetSelectionProperties().mStart;
-      end = mState.GetSelectionProperties().mEnd;
+      start = mState.GetSelectionProperties().GetStart();
+      end = mState.GetSelectionProperties().GetEnd();
       aRv = NS_OK;
     }
   }
 
   SetRangeText(aReplacement, start, end, mozilla::dom::SelectionMode::Preserve,
                aRv, start, end);
 }
 
@@ -956,18 +956,18 @@ HTMLTextAreaElement::SetRangeText(const 
   if (aEnd > inputValueLength) {
     aEnd = inputValueLength;
   }
 
   if (aSelectionStart == -1 && aSelectionEnd == -1) {
     aRv = GetSelectionRange(&aSelectionStart, &aSelectionEnd);
     if (aRv.Failed()) {
       if (mState.IsSelectionCached()) {
-        aSelectionStart = mState.GetSelectionProperties().mStart;
-        aSelectionEnd = mState.GetSelectionProperties().mEnd;
+        aSelectionStart = mState.GetSelectionProperties().GetStart();
+        aSelectionEnd = mState.GetSelectionProperties().GetEnd();
         aRv = NS_OK;
       }
     }
   }
 
   if (aStart <= aEnd) {
     value.Replace(aStart, aEnd - aStart, aReplacement);
     nsresult rv =
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -98,21 +98,23 @@ public:
 
     AutoHideSelectionChanges hideSelectionChanges
       (mFrame->GetConstFrameSelection());
 
     if (mFrame) {
       // SetSelectionRange leads to Selection::AddRange which flushes Layout -
       // need to block script to avoid nested PrepareEditor calls (bug 642800).
       nsAutoScriptBlocker scriptBlocker;
-       nsTextEditorState::SelectionProperties& properties =
-         mTextEditorState->GetSelectionProperties();
-       mFrame->SetSelectionRange(properties.mStart,
-                                 properties.mEnd,
-                                 properties.mDirection);
+      nsTextEditorState::SelectionProperties& properties =
+        mTextEditorState->GetSelectionProperties();
+      if (properties.IsDirty()) {
+        mFrame->SetSelectionRange(properties.GetStart(),
+                                  properties.GetEnd(),
+                                  properties.GetDirection());
+      }
       if (!mTextEditorState->mSelectionRestoreEagerInit) {
         mTextEditorState->HideSelectionIfBlurred();
       }
       mTextEditorState->mSelectionRestoreEagerInit = false;
     }
 
     if (mTextEditorState) {
       mTextEditorState->FinishedRestoringSelection();
@@ -1604,30 +1606,36 @@ nsTextEditorState::UnbindFromFrame(nsTex
   }
 
   // Save our selection state if needed.
   // Note that nsTextControlFrame::GetSelectionRange attempts to initialize the
   // editor before grabbing the range, and because this is not an acceptable
   // side effect for unbinding from a text control frame, we need to call
   // GetSelectionRange before calling DestroyEditor, and only if
   // mEditorInitialized indicates that we actually have an editor available.
+  int32_t start = 0, end = 0;
+  nsITextControlFrame::SelectionDirection direction =
+    nsITextControlFrame::eForward;
   if (mEditorInitialized) {
     HTMLInputElement* number = GetParentNumberControl(aFrame);
     if (number) {
       // If we are inside a number control, cache the selection on the
       // parent control, because this text editor state will be destroyed
       // together with the native anonymous text control.
       SelectionProperties props;
-      mBoundFrame->GetSelectionRange(&props.mStart, &props.mEnd,
-                                     &props.mDirection);
+      mBoundFrame->GetSelectionRange(&start, &end, &direction);
+      props.SetStart(start);
+      props.SetEnd(end);
+      props.SetDirection(direction);
       number->SetSelectionProperties(props);
     } else {
-      mBoundFrame->GetSelectionRange(&mSelectionProperties.mStart,
-                                     &mSelectionProperties.mEnd,
-                                     &mSelectionProperties.mDirection);
+      mBoundFrame->GetSelectionRange(&start, &end, &direction);
+      mSelectionProperties.SetStart(start);
+      mSelectionProperties.SetEnd(end);
+      mSelectionProperties.SetDirection(direction);
       mSelectionCached = true;
     }
   }
 
   // Destroy our editor
   DestroyEditor();
 
   // Clean up the controller
--- a/dom/html/nsTextEditorState.h
+++ b/dom/html/nsTextEditorState.h
@@ -193,38 +193,74 @@ public:
     return mTextCtrlElement->GetRows();
   }
 
   // placeholder methods
   void UpdatePlaceholderVisibility(bool aNotify);
   bool GetPlaceholderVisibility() {
     return mPlaceholderVisibility;
   }
-  void UpdatePlaceholderText(bool aNotify); 
+  void UpdatePlaceholderText(bool aNotify);
 
   /**
    * Get the maxlength attribute
    * @param aMaxLength the value of the max length attr
    * @returns false if attr not defined
    */
   bool GetMaxLength(int32_t* aMaxLength);
 
   void ClearValueCache() { mCachedValue.Truncate(); }
 
   void HideSelectionIfBlurred();
 
   struct SelectionProperties {
-    SelectionProperties() : mStart(0), mEnd(0),
-      mDirection(nsITextControlFrame::eForward) {}
-    bool IsDefault() const {
-      return mStart == 0 && mEnd == 0 &&
-             mDirection == nsITextControlFrame::eForward;
-    }
-    int32_t mStart, mEnd;
-    nsITextControlFrame::SelectionDirection mDirection;
+    public:
+      SelectionProperties() : mStart(0), mEnd(0),
+        mDirection(nsITextControlFrame::eForward) {}
+      bool IsDefault() const
+      {
+        return mStart == 0 && mEnd == 0 &&
+               mDirection == nsITextControlFrame::eForward;
+      }
+      int32_t GetStart() const
+      {
+        return mStart;
+      }
+      void SetStart(int32_t value)
+      {
+        mIsDirty = true;
+        mStart = value;
+      }
+      int32_t GetEnd() const
+      {
+        return mEnd;
+      }
+      void SetEnd(int32_t value)
+      {
+        mIsDirty = true;
+        mEnd = value;
+      }
+      nsITextControlFrame::SelectionDirection GetDirection() const
+      {
+        return mDirection;
+      }
+      void SetDirection(nsITextControlFrame::SelectionDirection value)
+      {
+        mIsDirty = true;
+        mDirection = value;
+      }
+      // return true only if mStart, mEnd, or mDirection have been modified
+      bool IsDirty() const
+      {
+        return mIsDirty;
+      }
+    private:
+      int32_t mStart, mEnd;
+      bool mIsDirty = false;
+      nsITextControlFrame::SelectionDirection mDirection;
   };
 
   bool IsSelectionCached() const;
   SelectionProperties& GetSelectionProperties();
   void WillInitEagerly() { mSelectionRestoreEagerInit = true; }
   bool HasNeverInitializedBefore() const { return !mEverInited; }
 
   void UpdateEditableState(bool aNotify) {
--- a/dom/html/test/test_bug674558.html
+++ b/dom/html/test/test_bug674558.html
@@ -46,18 +46,18 @@ function startTest() {
 }
 
 function test(ctor) {
   var elem = ctor();
   ok(true, "Testing " + name(elem));
 
   ok("selectionDirection" in elem, "elem should have the selectionDirection property");
 
-  is(elem.selectionStart, 0, "Default value");
-  is(elem.selectionEnd, 0, "Default value");
+  is(elem.selectionStart, elem.value.length, "Default value");
+  is(elem.selectionEnd, elem.value.length, "Default value");
   is(elem.selectionDirection, "forward", "Default value");
 
   var content = document.getElementById("content");
   content.appendChild(elem);
 
   function flush() { document.body.clientWidth; }
   function hide() {
     content.style.display = "none";
@@ -65,18 +65,18 @@ function test(ctor) {
   }
   function show() {
     content.style.display = "";
     flush();
   }
 
   elem.value = "foobar";
 
-  is(elem.selectionStart, 0, "Default value");
-  is(elem.selectionEnd, 0, "Default value");
+  is(elem.selectionStart, elem.value.length, "Default value");
+  is(elem.selectionEnd, elem.value.length, "Default value");
   is(elem.selectionDirection, "forward", "Default value");
 
   elem.setSelectionRange(1, 3);
   is(elem.selectionStart, 1, "Correct value");
   is(elem.selectionEnd, 3, "Correct value");
   is(elem.selectionDirection, "forward", "If not set, should default to forward");
 
   hide();
--- a/layout/base/tests/bug1082486-1.html
+++ b/layout/base/tests/bug1082486-1.html
@@ -6,15 +6,24 @@
      /* Eliminate the blue glow when focusing the element. */
      input {
        background: none;
        border: none;
        outline: none;
      }
      </style>
   </head>
-  <body onload="document.getElementById('i').focus();">
+  <body onload="focusInput();">
+    <script>
+      function focusInput() {
+        var inp = document.getElementById('i');
+        inp.selectionStart = 0;
+        inp.selectionEnd = 0;
+        inp.focus();
+      }
+    </script>
+
     <a target="_blank" href="https://bugzil.la/1082486">Mozilla Bug 1082486</a>
 
     <!-- The caret will not be seen when the input is focused. -->
     <input id='i' value="abcdefghd" style="text-indent: -10px">
   </body>
 </html>
--- a/layout/base/tests/bug646382-1-ref.html
+++ b/layout/base/tests/bug646382-1-ref.html
@@ -2,16 +2,18 @@
   <head>
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
   </head>
   <body onload="start()">
     <textarea onfocus="done()" style="-moz-appearance: none">س</textarea>
     <script>
       var textarea = document.querySelector("textarea");
       function start() {
+        textarea.selectionStart = 0;
+        textarea.selectionEnd = 0;
         textarea.focus();
       }
       function done() {
         document.documentElement.removeAttribute("class");
       }
     </script>
   </body>
 </html>
--- a/layout/base/tests/bug646382-2-ref.html
+++ b/layout/base/tests/bug646382-2-ref.html
@@ -1,14 +1,16 @@
 <html class="reftest-wait">
   <body onload="start()">
     <textarea dir="rtl" onfocus="done()" style="-moz-appearance: none">s</textarea>
     <script>
       var textarea = document.querySelector("textarea");
       function start() {
+        textarea.selectionStart = 0;
+        textarea.selectionEnd = 0;
         textarea.focus();
       }
       function done() {
         document.documentElement.removeAttribute("class");
       }
     </script>
   </body>
 </html>
--- a/layout/base/tests/bug664087-1-ref.html
+++ b/layout/base/tests/bug664087-1-ref.html
@@ -4,16 +4,18 @@
     <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   </head>
   <body onload="start()">
     <textarea rows="3" onfocus="done()" spellcheck="false" style="-moz-appearance: none">אב
 ג</textarea>
     <script>
       var textarea = document.querySelector("textarea");
       function start() {
+        textarea.selectionStart = 0;
+        textarea.selectionEnd = 0;
         textarea.focus();
       }
       function done() {
         synthesizeKey("VK_LEFT", {});
         synthesizeKey("VK_LEFT", {});
         document.documentElement.removeAttribute("class");
       }
     </script>
--- a/layout/base/tests/bug664087-2-ref.html
+++ b/layout/base/tests/bug664087-2-ref.html
@@ -4,16 +4,18 @@
     <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   </head>
   <body onload="start()">
     <textarea dir="rtl" onfocus="done()" spellcheck="false" style="-moz-appearance: none">ab
 c</textarea>
     <script>
       var textarea = document.querySelector("textarea");
       function start() {
+        textarea.selectionStart = 0;
+        textarea.selectionEnd = 0;
         textarea.focus();
       }
       function done() {
         synthesizeKey("VK_RIGHT", {});
         synthesizeKey("VK_RIGHT", {});
         document.documentElement.removeAttribute("class");
       }
     </script>
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -298,19 +298,23 @@ nsTextControlFrame::EnsureEditorInitiali
     nsresult rv = txtCtrl->CreateEditor();
     NS_ENSURE_SUCCESS(rv, rv);
     NS_ENSURE_STATE(weakFrame.IsAlive());
 
     // Set mEditorHasBeenInitialized so that subsequent calls will use the
     // editor.
     mEditorHasBeenInitialized = true;
 
-    // Set the selection to the beginning of the text field.
+    nsAutoString val;
+    txtCtrl->GetTextEditorValue(val, true);
+    int32_t length = val.Length();
+
+    // Set the selection to the end of the text field. (bug 1287655)
     if (weakFrame.IsAlive()) {
-      SetSelectionEndPoints(0, 0);
+      SetSelectionEndPoints(length, length);
     }
   }
   NS_ENSURE_STATE(weakFrame.IsAlive());
   return NS_OK;
 }
 
 nsresult
 nsTextControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
@@ -421,17 +425,17 @@ nsTextControlFrame::AppendAnonymousConte
   nsIContent* root = txtCtrl->GetRootEditorNode();
   if (root) {
     aElements.AppendElement(root);
   }
 
   nsIContent* placeholder = txtCtrl->GetPlaceholderNode();
   if (placeholder && !(aFilter & nsIContent::eSkipPlaceholderContent))
     aElements.AppendElement(placeholder);
-  
+
 }
 
 nscoord
 nsTextControlFrame::GetPrefISize(nsRenderingContext* aRenderingContext)
 {
     DebugOnly<nscoord> result = 0;
     DISPLAY_PREF_WIDTH(this, result);
 
@@ -922,49 +926,49 @@ nsTextControlFrame::SetSelectionRange(in
 
 
 NS_IMETHODIMP
 nsTextControlFrame::SetSelectionStart(int32_t aSelectionStart)
 {
   nsresult rv = EnsureEditorInitialized();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  int32_t selStart = 0, selEnd = 0; 
+  int32_t selStart = 0, selEnd = 0;
 
   rv = GetSelectionRange(&selStart, &selEnd);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aSelectionStart > selEnd) {
     // Collapse to the new start point.
-    selEnd = aSelectionStart; 
+    selEnd = aSelectionStart;
   }
 
   selStart = aSelectionStart;
-  
+
   return SetSelectionEndPoints(selStart, selEnd);
 }
 
 NS_IMETHODIMP
 nsTextControlFrame::SetSelectionEnd(int32_t aSelectionEnd)
 {
   nsresult rv = EnsureEditorInitialized();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  int32_t selStart = 0, selEnd = 0; 
+  int32_t selStart = 0, selEnd = 0;
 
   rv = GetSelectionRange(&selStart, &selEnd);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aSelectionEnd < selStart) {
     // Collapse to the new end point.
-    selStart = aSelectionEnd; 
+    selStart = aSelectionEnd;
   }
 
   selEnd = aSelectionEnd;
-  
+
   return SetSelectionEndPoints(selStart, selEnd);
 }
 
 nsresult
 nsTextControlFrame::OffsetToDOMPoint(int32_t aOffset,
                                      nsIDOMNode** aResult,
                                      int32_t* aPosition)
 {
@@ -1041,17 +1045,17 @@ nsTextControlFrame::GetSelectionRange(in
     *aDirection = eNone;
   }
 
   nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
   NS_ASSERTION(txtCtrl, "Content not a text control element");
   nsISelectionController* selCon = txtCtrl->GetSelectionController();
   NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
   nsCOMPtr<nsISelection> selection;
-  rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));  
+  rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
 
   dom::Selection* sel = selection->AsSelection();
   if (aDirection) {
     nsDirection direction = sel->GetSelectionDirection();
     if (direction == eDirNext) {
       *aDirection = eForward;
@@ -1180,17 +1184,17 @@ nsTextControlFrame::GetText(nsString& aT
   }
   return rv;
 }
 
 
 nsresult
 nsTextControlFrame::GetPhonetic(nsAString& aPhonetic)
 {
-  aPhonetic.Truncate(0); 
+  aPhonetic.Truncate(0);
 
   nsCOMPtr<nsIEditor> editor;
   nsresult rv = GetEditor(getter_AddRefs(editor));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIEditorIMESupport> imeSupport = do_QueryInterface(editor);
   if (imeSupport) {
     nsCOMPtr<nsIPhonetic> phonetic = do_QueryInterface(imeSupport);
--- a/layout/forms/test/bug287446_subframe.html
+++ b/layout/forms/test/bug287446_subframe.html
@@ -13,23 +13,23 @@
 
       window.addEventListener("message",
         function(evt) {
           var t = $("target");
           if (evt.data == "start") {
             doIs(t.value, "Test", "Shouldn't have lost our initial value");
             t.focus();
             sendString("Foo");
-            doIs(t.value, "FooTest", "Typing should work");
+            doIs(t.value, "TestFoo", "Typing should work");
             window.parent.postMessage("c", "*");
           } else {
             doIs(evt.data, "continue", "Unexpected message");
-            doIs(t.value, "FooTest", "Shouldn't have lost our typed value");
+            doIs(t.value, "TestFoo", "Shouldn't have lost our typed value");
             sendString("Bar");
-            doIs(t.value, "BarFooTest", "Typing should still work");
+            doIs(t.value, "TestFooBar", "Typing should still work");
             window.parent.postMessage("f", "*");
           }
         },
         "false");
       
     </script>
   </head>
   <body>
--- a/layout/forms/test/test_bug353539.html
+++ b/layout/forms/test/test_bug353539.html
@@ -33,16 +33,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 <script type="application/javascript">
 
 /** Test for Bug 353539 **/
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function() {
   var area = document.getElementById("area");
 
   is(area.scrollTop, 0, "The textarea should not be scrolled initially");
+  area.selectionStart = 0;
+  area.selectionEnd = 0;
   area.focus();
   setTimeout(function() {
     is(area.scrollTop, 0, "The textarea's insertion point should not be scrolled into view");
 
     SimpleTest.finish();
   }, 0);
 });
 
--- a/layout/forms/test/test_bug534785.html
+++ b/layout/forms/test/test_bug534785.html
@@ -28,27 +28,27 @@ SimpleTest.waitForExplicitFinish();
 
 SimpleTest.waitForFocus(function() {
   var i = document.querySelector("input");
   i.addEventListener("focus", function() {
     is(i.value, "test", "Sanity check");
 
     is(document.activeElement, i, "Should be focused before frame reconstruction");
     synthesizeKey("1", {});
-    is(i.value, "1test", "Can accept keyboard events before frame reconstruction");
+    is(i.value, "test1", "Can accept keyboard events before frame reconstruction");
 
     // force frame reconstruction
     i.style.display = "none";
     document.offsetHeight;
     i.style.display = "";
     document.offsetHeight;
 
     is(document.activeElement, i, "Should be focused after frame reconstruction");
     synthesizeKey("2", {});
-    is(i.value, "12test", "Can accept keyboard events after frame reconstruction");
+    is(i.value, "test12", "Can accept keyboard events after frame reconstruction");
 
     // Make sure reframing happens gracefully
     var reframeDiv = document.getElementById("reframe");
     var textAreaWithoutValue = reframeDiv.querySelectorAll("textarea")[0];
     var textAreaWithValue = reframeDiv.querySelectorAll("textarea")[1];
     var inputWithoutValue = reframeDiv.querySelectorAll("input")[0];
     var inputWithValue = reframeDiv.querySelectorAll("input")[1];
     reframeDiv.style.display = "none";
--- a/layout/forms/test/test_bug542914.html
+++ b/layout/forms/test/test_bug542914.html
@@ -75,19 +75,19 @@ function runTests(callback, type) {
   document.body.offsetHeight;
 
   // Make sure dynamically injected inputs work as expected
   is(d.value, "", "Dynamic control's initial value should be empty");
   d.value = "new";
   d.focus();
   is(d.value, "new", "Dynamic control's value can be set before initialization");
   sendChar("x");
-  is(d.value, "xnew", "Dynamic control accepts keyboard input without explicit initialization");
+  is(d.value, "newx", "Dynamic control accepts keyboard input without explicit initialization");
   $("display").removeChild(d);
-  is(d.value, "xnew", "Dynamic control retains value after being removed from the document");
+  is(d.value, "newx", "Dynamic control retains value after being removed from the document");
 
   callback();
 }
 
 var gPreviousType = "text";
 function setTypes(aType) {
   var content = document.getElementById("display");
   content.innerHTML = content.innerHTML.replace(gPreviousType, aType);
--- a/layout/reftests/bugs/240933-1.html
+++ b/layout/reftests/bugs/240933-1.html
@@ -7,13 +7,13 @@
 
 </textarea>
 <textarea id="tb">
 
 abc
 
 </textarea>
 
-<div id="coords1">0</div>
-<div id="coords2">0</div>
+<div id="coords1">6</div>
+<div id="coords2">6</div>
 
 </body>
 </html>
deleted file mode 100644
--- a/testing/web-platform/meta/html/semantics/forms/textfieldselection/selection.html.ini
+++ /dev/null
@@ -1,18 +0,0 @@
-[selection.html]
-  type: testharness
-  [test SelectionStart offset for input]
-    expected: FAIL
-    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1287655
-
-  [test SelectionStart offset for textarea]
-    expected: FAIL
-    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1287655
-
-  [test SelectionEnd offset for input]
-    expected: FAIL
-    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1287655
-
-  [test SelectionEnd offset for textarea]
-    expected: FAIL
-    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1287655
-
--- a/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
@@ -627,17 +627,17 @@ add_task(function* test_form6_changeUser
   // Test that the password field remains filled in after changing
   // the username.
   uname.focus();
   doKey("right");
   sendChar("X");
   // Trigger the 'blur' event on uname
   pword.focus();
   yield spinEventLoop();
-  checkACForm("sXingleuser5", "singlepass5");
+  checkACForm("singleuser5X", "singlepass5");
 
   setupScript.sendSyncMessage("removeLogin", "login5");
 });
 
 add_task(function* test_form7() {
   uname = $_(7, "uname");
   pword = $_(7, "pword");
   checkACForm("", "");
--- a/widget/tests/test_native_key_bindings_mac.html
+++ b/widget/tests/test_native_key_bindings_mac.html
@@ -93,16 +93,20 @@
       Nullam pellentesque rip the couch iaculis rhoncus nibh, give me fish orci
       turpis purr sleep on your face quis nunc bibendum.">
 
     <script type="text/javascript;version=1.8">
       SimpleTest.waitForExplicitFinish();
 
       let synthesizedKeys = [];
       let expectations = [];
+      
+      let textarea = document.getElementById("textarea");
+      textarea.selectionStart = 0;
+      textarea.selectionEnd = 0;
 
       // Move to beginning of line
       synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_LeftArrow,
                             {ctrlKey: true}, "\uf702", "\uf702"]);
       expectations.push({
         editable: [0, 0],
         textarea: [0, 0],
         input:    [0, 0]
@@ -336,9 +340,8 @@
           is(ret.value, true, "Successfully synthesized key");
         }
       }
 
       SimpleTest.waitForFocus(continueTest);
     </script>
   </body>
 </html>
-