Bug 1221043. Revert to including trailing whitespace for accessibility APIs. r=marcoz,mats
authorRobert O'Callahan <robert@ocallahan.org>
Tue, 01 Dec 2015 02:21:25 +1300
changeset 309297 ef68dc6a289da306e5afa73f5a51d0ba41ec9e2f
parent 309296 8684df98e6495ab2898b4a9da13ecb3777953952
child 309298 0bc40a9a58044c65e5b3630e4f0b296d5df06662
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmarcoz, mats
bugs1221043
milestone45.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 1221043. Revert to including trailing whitespace for accessibility APIs. r=marcoz,mats
accessible/base/NotificationController.cpp
accessible/base/nsAccessibilityService.cpp
accessible/base/nsTextEquivUtils.cpp
accessible/generic/Accessible.cpp
accessible/generic/HyperTextAccessible.cpp
accessible/tests/mochitest/jsat/test_traversal.html
accessible/tests/mochitest/pivot/test_virtualcursor.html
accessible/tests/mochitest/text/test_doc.html
accessible/tests/mochitest/text/test_hypertext.html
accessible/tests/mochitest/text/test_lineboundary.html
accessible/tests/mochitest/textattrs/test_general.html
accessible/tests/mochitest/textcaret/test_general.html
accessible/tests/mochitest/tree/test_txtcntr.html
layout/generic/nsIFrame.h
layout/generic/nsTextFrame.cpp
layout/generic/nsTextFrame.h
--- a/accessible/base/NotificationController.cpp
+++ b/accessible/base/NotificationController.cpp
@@ -225,17 +225,19 @@ NotificationController::WillRefresh(mozi
       NS_ASSERTION(!textAcc,
                    "Text node isn't rendered but accessible is kept alive!");
       continue;
     }
 
     nsIContent* containerElm = containerNode->IsElement() ?
       containerNode->AsElement() : nullptr;
 
-    nsIFrame::RenderedText text = textFrame->GetRenderedText();
+    nsIFrame::RenderedText text = textFrame->GetRenderedText(0,
+        UINT32_MAX, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
+        nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
 
     // Remove text accessible if rendered text is empty.
     if (textAcc) {
       if (text.mString.IsEmpty()) {
   #ifdef A11Y_LOG
         if (logging::IsEnabled(logging::eTree | logging::eText)) {
           logging::MsgBegin("TREE", "text node lost its content");
           logging::Node("container", containerElm);
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -1086,17 +1086,19 @@ nsAccessibilityService::GetOrCreateAcces
                "Image map manages the area accessible creation!");
 #endif
 
   // Attempt to create an accessible based on what we know.
   RefPtr<Accessible> newAcc;
 
   // Create accessible for visible text frames.
   if (content->IsNodeOfType(nsINode::eTEXT)) {
-    nsIFrame::RenderedText text = frame->GetRenderedText();
+    nsIFrame::RenderedText text = frame->GetRenderedText(0,
+        UINT32_MAX, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
+        nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
     // Ignore not rendered text nodes and whitespace text nodes between table
     // cells.
     if (text.mString.IsEmpty() ||
         (aContext->IsTableRow() && nsCoreUtils::IsWhitespaceString(text.mString))) {
       if (aIsSubtreeHidden)
         *aIsSubtreeHidden = true;
 
       return nullptr;
--- a/accessible/base/nsTextEquivUtils.cpp
+++ b/accessible/base/nsTextEquivUtils.cpp
@@ -134,17 +134,19 @@ nsTextEquivUtils::AppendTextEquivFromTex
           }
         }
       }
     }
     
     if (aContent->TextLength() > 0) {
       nsIFrame *frame = aContent->GetPrimaryFrame();
       if (frame) {
-        nsIFrame::RenderedText text = frame->GetRenderedText();
+        nsIFrame::RenderedText text = frame->GetRenderedText(0,
+            UINT32_MAX, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
+            nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
         aString->Append(text.mString);
       } else {
         // If aContent is an object that is display: none, we have no a frame.
         aContent->AppendTextTo(*aString);
       }
       if (isHTMLBlock && !aString->IsEmpty()) {
         aString->Append(char16_t(' '));
       }
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -389,17 +389,19 @@ Accessible::VisibilityState()
   // Zero area rects can occur in the first frame of a multi-frame text flow,
   // in which case the rendered text is not empty and the frame should not be
   // marked invisible.
   // XXX Can we just remove this check? Why do we need to mark empty
   // text invisible?
   if (frame->GetType() == nsGkAtoms::textFrame &&
       !(frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
       frame->GetRect().IsEmpty()) {
-    nsIFrame::RenderedText text = frame->GetRenderedText();
+    nsIFrame::RenderedText text = frame->GetRenderedText(0,
+        UINT32_MAX, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
+        nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
     if (text.mString.IsEmpty()) {
       return states::INVISIBLE;
     }
   }
 
   return 0;
 }
 
--- a/accessible/generic/HyperTextAccessible.cpp
+++ b/accessible/generic/HyperTextAccessible.cpp
@@ -1980,17 +1980,18 @@ HyperTextAccessible::ContentToRenderedOf
   }
 
   NS_ASSERTION(aFrame->GetType() == nsGkAtoms::textFrame,
                "Need text frame for offset conversion");
   NS_ASSERTION(aFrame->GetPrevContinuation() == nullptr,
                "Call on primary frame only");
 
   nsIFrame::RenderedText text = aFrame->GetRenderedText(aContentOffset,
-      aContentOffset + 1);
+      aContentOffset + 1, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
+      nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
   *aRenderedOffset = text.mOffsetWithinNodeRenderedText;
 
   return NS_OK;
 }
 
 nsresult
 HyperTextAccessible::RenderedToContentOffset(nsIFrame* aFrame, uint32_t aRenderedOffset,
                                              int32_t* aContentOffset) const
@@ -2004,17 +2005,18 @@ HyperTextAccessible::RenderedToContentOf
   NS_ENSURE_TRUE(aFrame, NS_ERROR_FAILURE);
 
   NS_ASSERTION(aFrame->GetType() == nsGkAtoms::textFrame,
                "Need text frame for offset conversion");
   NS_ASSERTION(aFrame->GetPrevContinuation() == nullptr,
                "Call on primary frame only");
 
   nsIFrame::RenderedText text = aFrame->GetRenderedText(aRenderedOffset,
-      aRenderedOffset + 1, nsIFrame::TextOffsetType::OFFSETS_IN_RENDERED_TEXT);
+      aRenderedOffset + 1, nsIFrame::TextOffsetType::OFFSETS_IN_RENDERED_TEXT,
+      nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
   *aContentOffset = text.mOffsetWithinNodeText;
 
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HyperTextAccessible public
 
--- a/accessible/tests/mochitest/jsat/test_traversal.html
+++ b/accessible/tests/mochitest/jsat/test_traversal.html
@@ -108,28 +108,28 @@
                               'Or me! ', 'Value 1', 'Value 2', 'Value 3',
                               'Electronic mailing address:', 'input-1-5',
                               'button-1-3', 'heading-2', 'heading-3',
                               'button-2-1', 'button-2-2', 'button-2-3',
                               'button-2-4', 'Programming Language',
                               'A esoteric weapon wielded by only the most ' +
                               'formidable warriors, for its unrelenting strict' +
                               ' power is unfathomable.',
-                              '• Lists of Programming Languages', 'Lisp',
+                              '• Lists of Programming Languages', 'Lisp ',
                               '1. Scheme', '2. Racket', '3. Clojure',
                               '4. Standard Lisp', 'link-0', ' Lisp',
                               'checkbox-1-5', ' LeLisp', '• JavaScript',
                               'heading-5', 'image-2', 'image-3',
                               'Not actually an image', 'link-1', 'anchor-1',
                               'link-2', 'anchor-2', 'link-3', '3', '1', '4',
                               '1', 'Sunday', 'M', 'Week 1', '3', '4', '7', '2',
                               '5 8', 'gridcell4', 'Just an innocuous separator',
                               'Dirty Words', 'Meaning', 'Mud', 'Wet Dirt',
                               'Dirt', 'Messy Stuff', 'statusbar-1', 'statusbar-2',
-                              'switch-1', 'This is a MathML formula', 'math-1',
+                              'switch-1', 'This is a MathML formula ', 'math-1',
                               'with some text after.']);
 
       queueTraversalSequence(gQueue, docAcc, TraversalRules.Landmark, null,
                              ['header-1', 'main-1', 'footer-1']);
 
 
       queueTraversalSequence(gQueue, docAcc, TraversalRules.Control, null,
                              ['input-1-1', 'label-1-2', 'button-1-1',
--- a/accessible/tests/mochitest/pivot/test_virtualcursor.html
+++ b/accessible/tests/mochitest/pivot/test_virtualcursor.html
@@ -47,17 +47,17 @@
 
       queueTraversalSequence(gQueue, docAcc, HeadersTraversalRule, null,
                              ['heading-1-1', 'heading-2-1', 'heading-2-2']);
 
       queueTraversalSequence(
         gQueue, docAcc, ObjectTraversalRule, null,
         ['Main Title', 'Lorem ipsum ',
          'dolor', ' sit amet. Integer vitae urna leo, id ',
-         'semper', ' nulla.', 'Second Section Title',
+         'semper', ' nulla. ', 'Second Section Title',
          'Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.',
          'An ', 'embedded', ' document.', 'Hide me', 'Link 1', 'Link 2',
          'Link 3', 'Hello', 'World']);
 
       // Just a random smoke test to see if our setTextRange works.
       gQueue.push(
         new setVCRangeInvoker(
           docAcc,
@@ -85,17 +85,17 @@
       // Attempting a coordinate outside any header, should move to null
       gQueue.push(new moveVCCoordInvoker(docAcc, x - 1, y - 1, false,
                                          HeadersTraversalRule, null));
 
       queueTraversalSequence(
         gQueue, docAcc, ObjectTraversalRule,
         getAccessible(doc.getElementById('paragraph-1')),
         ['Lorem ipsum ', 'dolor', ' sit amet. Integer vitae urna leo, id ',
-         'semper', ' nulla.']);
+         'semper', ' nulla. ']);
 
       gQueue.push(new setModalRootInvoker(docAcc, docAcc.parent,
                                           NS_ERROR_INVALID_ARG));
 
       // Put cursor in an ignored subtree
       // set isFromUserInput to false, just to test..
       gQueue.push(new setVCPosInvoker(docAcc, null, null,
                                       getAccessible(doc.getElementById("hidden-link")),
--- a/accessible/tests/mochitest/text/test_doc.html
+++ b/accessible/tests/mochitest/text/test_doc.html
@@ -11,18 +11,18 @@
           src="../common.js"></script>
   <script type="application/javascript"
           src="../text.js"></script>
   <script type="application/javascript">
     
     function doTest()
     {
       var iframeDoc = [ getNode("iframe").contentDocument ];
-      testCharacterCount(iframeDoc, 13);
-      testText(iframeDoc, 0, 13, "outbodyinbody");
+      testCharacterCount(iframeDoc, 15);
+      testText(iframeDoc, 0, 15, "outbody inbody ");
 
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
--- a/accessible/tests/mochitest/text/test_hypertext.html
+++ b/accessible/tests/mochitest/text/test_hypertext.html
@@ -53,17 +53,17 @@
       testText(IDs, 0, 1, "h");
       testText(IDs, 5, 7, " " + kEmbedChar);
       testText(IDs, 10, 13, "e " + kEmbedChar);
       testText(IDs, 0, 13, "hello " + kEmbedChar + " see " + kEmbedChar);
 
       ////////////////////////////////////////////////////////////////////////
       // getTextAtOffset line boundary
 
-      testTextAtOffset(0, BOUNDARY_LINE_START, "line", 0, 4,
+      testTextAtOffset(0, BOUNDARY_LINE_START, "line ", 0, 5,
                        "hypertext3", kOk, kOk, kOk);
 
       // XXX: see bug 634202.
       testTextAtOffset(0, BOUNDARY_LINE_START, "line ", 0, 5,
                        "hypertext4", kTodo, kOk, kTodo);
 
       //////////////////////////////////////////////////////////////////////////
       // list
--- a/accessible/tests/mochitest/text/test_lineboundary.html
+++ b/accessible/tests/mochitest/text/test_lineboundary.html
@@ -9,19 +9,19 @@
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../text.js"></script>
   <script type="application/javascript">
     function doTest()
     {
       testTextAtOffset("line_test_1", BOUNDARY_LINE_START,
-                       [[0, 5, "Line 1", 0, 6],
-                        [6, 6, "", 6, 6],
-                        [7, 13, "Line 3", 7, 13]]);
+                       [[0, 6, "Line 1 ", 0, 7],
+                        [7, 7, "", 7, 7],
+                        [8, 15, "Line 3 ", 8, 15]]);
 
       //////////////////////////////////////////////////////////////////////////
       // __h__e__l__l__o__ __m__y__ __f__r__i__e__n__d__
       //  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
 
       var IDs = [ "input", "div", "editable", "textarea",
                   getNode("ta", getNode("ta_cntr").contentDocument) ];
 
@@ -109,20 +109,20 @@
       testTextAtOffset([ getAccessible("ht_2").firstChild.firstChild ],
                        BOUNDARY_LINE_START,
                        [ [ 0, 3, "foo", 0, 3 ] ]);
       testTextAtOffset([ getAccessible("ht_3").firstChild.firstChild ],
                        BOUNDARY_LINE_START,
                        [ [ 0, 3, "foo\n", 0, 4 ], [ 4, 4, "", 4, 4 ] ]);
 
       //////////////////////////////////////////////////////////////////////////
-      // 'Hello world'
+      // 'Hello world ' (\n is rendered as space)
 
       testTextAtOffset([ "ht_4" ], BOUNDARY_LINE_START,
-                       [ [ 0, 11, "Hello world", 0, 11 ] ]);
+                       [ [ 0, 12, "Hello world ", 0, 12 ] ]);
 
       //////////////////////////////////////////////////////////////////////////
       // list items
 
       testTextAtOffset([ "li1" ], BOUNDARY_LINE_START,
                        [ [ 0, 6, kDiscBulletText + "Item", 0, 6 ] ]);
       testTextAtOffset([ "li2" ], BOUNDARY_LINE_START,
                        [ [ 0, 2, kDiscBulletText, 0, 2 ] ]);
--- a/accessible/tests/mochitest/textattrs/test_general.html
+++ b/accessible/tests/mochitest/textattrs/test_general.html
@@ -84,17 +84,17 @@
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"color": gComputedStyle.color};
       testTextAttrs(ID, 26, attrs, defAttrs, 26, 27);
 
       tempElem = tempElem.nextSibling;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"color": gComputedStyle.color,
                "background-color": gComputedStyle.backgroundColor};
-      testTextAttrs(ID, 27, attrs, defAttrs, 27, 49);
+      testTextAttrs(ID, 27, attrs, defAttrs, 27, 50);
 
       //////////////////////////////////////////////////////////////////////////
       // area4
       ID = "area4";
       defAttrs = buildDefaultTextAttrs(ID, "12pt");
       testDefaultTextAttrs(ID, defAttrs);
 
       tempElem = getNode(ID).firstChild.nextSibling;
@@ -105,17 +105,17 @@
       tempElem = tempElem.nextSibling.firstChild.nextSibling;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"color": gComputedStyle.color};
       testTextAttrs(ID, 16, attrs, defAttrs, 16, 33);
 
       tempElem = tempElem.parentNode;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"color": gComputedStyle.color};
-      testTextAttrs(ID, 34, attrs, defAttrs, 33, 45);
+      testTextAttrs(ID, 34, attrs, defAttrs, 33, 46);
 
       //////////////////////////////////////////////////////////////////////////
       // area5: "Green!*!RedNormal"
       ID = "area5";
       defAttrs = buildDefaultTextAttrs(ID, "12pt");
       testDefaultTextAttrs(ID, defAttrs);
 
       // Green
@@ -139,17 +139,17 @@
       // Red
       tempElem = tempElem.nextSibling.nextSibling.nextSibling.nextSibling;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"color": gComputedStyle.color};
       testTextAttrs(ID, 9, attrs, defAttrs, 8, 11);
 
       // Normal
       attrs = {};
-      testTextAttrs(ID, 11, attrs, defAttrs, 11, 17);
+      testTextAttrs(ID, 11, attrs, defAttrs, 11, 18);
 
       //////////////////////////////////////////////////////////////////////////
       // area6 (CSS vertical-align property, refer to bug 445938 for details
       // 	and sup and sub elements, refer to bug 735645 for details)
       ID = "area6";
       defAttrs = buildDefaultTextAttrs(ID, "12pt");
       testDefaultTextAttrs(ID, defAttrs);
 
@@ -318,17 +318,17 @@
 
       attrs = {
         "text-line-through-style": "solid",
         "text-line-through-color": gComputedStyle.color
       };
       testTextAttrs(ID, 152, attrs, defAttrs, 151, 164);
 
       attrs = {};
-      testTextAttrs(ID, 165, attrs, defAttrs, 164, 171);
+      testTextAttrs(ID, 165, attrs, defAttrs, 164, 172);
 
       //////////////////////////////////////////////////////////////////////////
       // area10, different single style spans in non-styled paragraph
       ID = "area10";
       defAttrs = buildDefaultTextAttrs(ID, "12pt");
       testDefaultTextAttrs(ID, defAttrs);
 
       attrs = {};
@@ -378,17 +378,17 @@
 
       attrs = {
         "text-line-through-style": "solid",
         "text-line-through-color": gComputedStyle.color
       };
       testTextAttrs(ID, 111, attrs, defAttrs, 110, 123);
 
       attrs = {};
-      testTextAttrs(ID, 124, attrs, defAttrs, 123, 130);
+      testTextAttrs(ID, 124, attrs, defAttrs, 123, 131);
 
       //////////////////////////////////////////////////////////////////////////
       // area11, "font-weight" tests
       ID = "area11";
       defAttrs = buildDefaultTextAttrs(ID, "12pt", kBoldFontWeight);
       testDefaultTextAttrs(ID, defAttrs);
 
       attrs = { };
@@ -405,17 +405,17 @@
 
       attrs = { };
       testTextAttrs(ID, 33, attrs, defAttrs, 33, 51);
 
       attrs = { "font-weight": kNormalFontWeight };
       testTextAttrs(ID, 51, attrs, defAttrs, 51, 57);
 
       attrs = { };
-      testTextAttrs(ID, 57, attrs, defAttrs, 57, 96);
+      testTextAttrs(ID, 57, attrs, defAttrs, 57, 97);
 
       //////////////////////////////////////////////////////////////////////////
       // test out of range offset
       testTextAttrsWrongOffset("area12", -1);
       testTextAttrsWrongOffset("area12", 500);
 
       //////////////////////////////////////////////////////////////////////////
       // test zero offset on empty hypertext accessibles
@@ -482,17 +482,17 @@
       if (!LINUX) {
         attrs = { };
         testTextAttrs(ID, 22, attrs, defAttrs, 22, 27);
 
         attrs = { "font-family": kCursiveFontFamily };
         testTextAttrs(ID, 27, attrs, defAttrs, 27, 31);
 
         attrs = { };
-        testTextAttrs(ID, 31, attrs, defAttrs, 31, 44);
+        testTextAttrs(ID, 31, attrs, defAttrs, 31, 45);
       }
 
       //////////////////////////////////////////////////////////////////////////
       // area17, "text-decoration" tests
       ID = "area17";
       defAttrs = buildDefaultTextAttrs(ID, "12pt");
       testDefaultTextAttrs(ID, defAttrs);
 
@@ -525,17 +525,17 @@
         "text-line-through-color": "rgb(0, 0, 255)",
       };
       testTextAttrs(ID, 34, attrs, defAttrs, 34, 39);
 
       attrs = {
         "text-line-through-style": "wavy",
         "text-line-through-color": "rgb(0, 0, 0)",
       };
-      testTextAttrs(ID, 39, attrs, defAttrs, 39, 43);
+      testTextAttrs(ID, 39, attrs, defAttrs, 39, 44);
 
       //////////////////////////////////////////////////////////////////////////
       // area18, "auto-generation text" tests
       ID = "area18";
       defAttrs = buildDefaultTextAttrs(ID, "12pt");
       testDefaultTextAttrs(ID, defAttrs);
 
       var attrs = {
@@ -555,17 +555,17 @@
       testTextAttrs(ID, 0, attrs, defAttrs, 0, 10);
       
       tempElem = getNode(ID).firstChild.nextSibling;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = { "background-color": gComputedStyle.backgroundColor };
       testTextAttrs(ID, 11, attrs, defAttrs, 10, 17);
 
       attrs = {};
-      testTextAttrs(ID, 18, attrs, defAttrs, 17, 27);
+      testTextAttrs(ID, 18, attrs, defAttrs, 17, 28);
 
        //////////////////////////////////////////////////////////////////////////
       // area20, "aOffset as -1 (Mozilla Bug 789621)" test
 
       ID = "area20";
       defAttrs = buildDefaultTextAttrs(ID, "15pt");
       testDefaultTextAttrs(ID, defAttrs);
 
--- a/accessible/tests/mochitest/textcaret/test_general.html
+++ b/accessible/tests/mochitest/textcaret/test_general.html
@@ -66,17 +66,17 @@
     //gA11yEventDumpID = "eventdump"; // debug stuff
     //gA11yEventDumpToConsole = true;
 
     function doTests()
     {
       turnCaretBrowsing(true);
 
       // test caret offsets
-      testCaretOffset(document, 15);
+      testCaretOffset(document, 16);
       testCaretOffset("textbox", -1);
       testCaretOffset("textarea", -1);
       testCaretOffset("p", -1);
 
       // test caret move events and caret offsets
       gQueue = new eventQueue();
 
       gQueue.push(new setCaretOffset("textbox", 1, "textbox"));
--- a/accessible/tests/mochitest/tree/test_txtcntr.html
+++ b/accessible/tests/mochitest/tree/test_txtcntr.html
@@ -44,52 +44,52 @@
             role: ROLE_TEXT_LEAF,
             name: "Hello2"
           },
           {
             role: ROLE_SEPARATOR
           },
           {
             role: ROLE_TEXT_LEAF,
-            name: "Hello3"
+            name: "Hello3 "
           },
           {
             role: ROLE_PARAGRAPH,
             children: [
               {
                 role: ROLE_TEXT_LEAF,
-                name: "Hello4"
+                name: "Hello4 "
               }
             ]
           }
         ]
       };
 
       testAccessibleTree("c3", accTree);
 
       // contentEditable div
       accTree = {
         role: ROLE_SECTION,
         children: [
           {
             role: ROLE_TEXT_LEAF,
-            name: "helllo"
+            name: "helllo "
           },
           {
             role: ROLE_PARAGRAPH,
             children: [
               {
                 role: ROLE_TEXT_LEAF,
                 name: "blabla"
               }
             ]
           },
           {
             role: ROLE_TEXT_LEAF,
-            name: "hello"
+            name: "hello "
           }
         ]
       };
 
       testAccessibleTree("c4", accTree);
 
       // blockquote
       accTree = {
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1933,20 +1933,27 @@ public:
         mOffsetWithinNodeText(0) {}
   };
   enum class TextOffsetType {
     // Passed-in start and end offsets are within the content text.
     OFFSETS_IN_CONTENT_TEXT,
     // Passed-in start and end offsets are within the rendered text.
     OFFSETS_IN_RENDERED_TEXT
   };
+  enum class TrailingWhitespace {
+    TRIM_TRAILING_WHITESPACE,
+    // Spaces preceding a caret at the end of a line should not be trimmed
+    DONT_TRIM_TRAILING_WHITESPACE
+  };
   virtual RenderedText GetRenderedText(uint32_t aStartOffset = 0,
                                        uint32_t aEndOffset = UINT32_MAX,
                                        TextOffsetType aOffsetType =
-                                           TextOffsetType::OFFSETS_IN_CONTENT_TEXT)
+                                           TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
+                                       TrailingWhitespace aTrimTrailingWhitespace =
+                                           TrailingWhitespace::TRIM_TRAILING_WHITESPACE)
   { return RenderedText(); }
 
   /**
    * Returns true if the frame contains any non-collapsed characters.
    * This method is only available for text frames, and it will return false
    * for all other frame types.
    */
   virtual bool HasAnyNoncollapsedCharacters()
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -4039,17 +4039,19 @@ nsTextPaintStyle::GetResolvedForeColor(n
 
 //-----------------------------------------------------------------------------
 
 #ifdef ACCESSIBILITY
 a11y::AccType
 nsTextFrame::AccessibleType()
 {
   if (IsEmpty()) {
-    RenderedText text = GetRenderedText();
+    RenderedText text = GetRenderedText(0,
+        UINT32_MAX, TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
+        TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
     if (text.mString.IsEmpty()) {
       return a11y::eNoType;
     }
   }
 
   return a11y::eTextLeafType;
 }
 #endif
@@ -9192,17 +9194,18 @@ LineEndsInHardLineBreak(nsTextFrame* aFr
     return true;
   }
   return !iter.GetLine()->IsLineWrapped();
 }
 
 nsIFrame::RenderedText
 nsTextFrame::GetRenderedText(uint32_t aStartOffset,
                              uint32_t aEndOffset,
-                             TextOffsetType aOffsetType)
+                             TextOffsetType aOffsetType,
+                             TrailingWhitespace aTrimTrailingWhitespace)
 {
   NS_ASSERTION(!GetPrevContinuation(), "Must be called on first-in-flow");
 
   // The handling of offsets could be more efficient...
   RenderedText result;
   nsTextFrame* textFrame;
   const nsTextFragment* textFrag = mContent->GetText();
   uint32_t offsetInRenderedString = 0;
@@ -9220,17 +9223,18 @@ nsTextFrame::GetRenderedText(uint32_t aS
       textFrame->EnsureTextRun(nsTextFrame::eInflated);
     if (!textFrame->mTextRun) {
       break;
     }
     gfxSkipCharsIterator tmpIter = iter;
 
     // Skip to the start of the text run, past ignored chars at start of line
     TrimmedOffsets trimmedOffsets = textFrame->GetTrimmedOffsets(textFrag,
-       textFrame->IsAtEndOfLine() && LineEndsInHardLineBreak(textFrame));
+       textFrame->IsAtEndOfLine() && LineEndsInHardLineBreak(textFrame) &&
+       aTrimTrailingWhitespace == TrailingWhitespace::TRIM_TRAILING_WHITESPACE);
     bool trimmedSignificantNewline =
         trimmedOffsets.GetEnd() < GetContentEnd() &&
         HasSignificantTerminalNewline();
     uint32_t skippedToRenderedStringOffset = offsetInRenderedString -
         tmpIter.ConvertOriginalToSkipped(trimmedOffsets.mStart);
     uint32_t nextOffsetInRenderedString =
         tmpIter.ConvertOriginalToSkipped(trimmedOffsets.GetEnd()) +
         (trimmedSignificantNewline ? 1 : 0) + skippedToRenderedStringOffset;
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -261,17 +261,19 @@ public:
     bool mChanged;
     // an amount to *subtract* from the frame's width (zero if !mChanged)
     nscoord      mDeltaWidth;
   };
   TrimOutput TrimTrailingWhiteSpace(nsRenderingContext* aRC);
   virtual RenderedText GetRenderedText(uint32_t aStartOffset = 0,
                                        uint32_t aEndOffset = UINT32_MAX,
                                        TextOffsetType aOffsetType =
-                                           TextOffsetType::OFFSETS_IN_CONTENT_TEXT) override;
+                                           TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
+                                       TrailingWhitespace aTrimTrailingWhitespace =
+                                           TrailingWhitespace::TRIM_TRAILING_WHITESPACE) override;
 
   nsOverflowAreas RecomputeOverflow(nsIFrame* aBlockFrame);
 
   enum TextRunType {
     // Anything in reflow (but not intrinsic width calculation) or
     // painting should use the inflated text run (i.e., with font size
     // inflation applied).
     eInflated,