Bug 1287655 - place textarea/input cursor at end of text when initialized; r=smaug
authorDecky Coss <coss@cosstropolis.com>
Thu, 21 Jul 2016 14:52:49 -0400
changeset 308355 52a0d2d7639717858ce6868c19a37b95e7039736
parent 308354 00afd3c93ccfa9877c36ae7fda355d02658f1e87
child 308356 a6017ae52872ec220ec31ca6965f931a984469d6
push id30534
push userkwierso@gmail.com
push dateFri, 05 Aug 2016 21:02:01 +0000
treeherdermozilla-central@7c1199a6e38e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1287655
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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
@@ -5652,18 +5652,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);
 }
 
@@ -5695,18 +5695,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 =
@@ -5765,17 +5765,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)
@@ -5787,17 +5787,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;
   }
@@ -5829,17 +5829,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)
@@ -5851,17 +5851,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;
   }
@@ -5935,17 +5935,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);
   }
 }
@@ -5964,17 +5964,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>
-