diff --git a/accessible/base/NotificationController.cpp b/accessible/base/NotificationController.cpp index 83d3062ef07e..854b364fb58b 100644 --- a/accessible/base/NotificationController.cpp +++ b/accessible/base/NotificationController.cpp @@ -228,11 +228,12 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime) nsIContent* containerElm = containerNode->IsElement() ? containerNode->AsElement() : nullptr; - nsIFrame::RenderedText text = textFrame->GetRenderedText(); + nsAutoString text; + textFrame->GetRenderedText(&text); // Remove text accessible if rendered text is empty. if (textAcc) { - if (text.mString.IsEmpty()) { + if (text.IsEmpty()) { #ifdef A11Y_LOG if (logging::IsEnabled(logging::eTree | logging::eText)) { logging::MsgBegin("TREE", "text node lost its content"); @@ -255,17 +256,17 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime) logging::MsgEntry("old text '%s'", NS_ConvertUTF16toUTF8(textAcc->AsTextLeaf()->Text()).get()); logging::MsgEntry("new text: '%s'", - NS_ConvertUTF16toUTF8(text.mString).get()); + NS_ConvertUTF16toUTF8(text).get()); logging::MsgEnd(); } #endif - TextUpdater::Run(mDocument, textAcc->AsTextLeaf(), text.mString); + TextUpdater::Run(mDocument, textAcc->AsTextLeaf(), text); continue; } // Append an accessible if rendered text is not empty. - if (!text.mString.IsEmpty()) { + if (!text.IsEmpty()) { #ifdef A11Y_LOG if (logging::IsEnabled(logging::eTree | logging::eText)) { logging::MsgBegin("TREE", "text node gains new content"); diff --git a/accessible/base/nsAccessibilityService.cpp b/accessible/base/nsAccessibilityService.cpp index 95cf3f6fb720..ff822c93172d 100644 --- a/accessible/base/nsAccessibilityService.cpp +++ b/accessible/base/nsAccessibilityService.cpp @@ -1091,11 +1091,12 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode, // Create accessible for visible text frames. if (content->IsNodeOfType(nsINode::eTEXT)) { - nsIFrame::RenderedText text = frame->GetRenderedText(); + nsAutoString text; + frame->GetRenderedText(&text, nullptr, nullptr, 0, UINT32_MAX); // Ignore not rendered text nodes and whitespace text nodes between table // cells. - if (text.mString.IsEmpty() || - (aContext->IsTableRow() && nsCoreUtils::IsWhitespaceString(text.mString))) { + if (text.IsEmpty() || + (aContext->IsTableRow() && nsCoreUtils::IsWhitespaceString(text))) { if (aIsSubtreeHidden) *aIsSubtreeHidden = true; @@ -1107,7 +1108,7 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode, return nullptr; document->BindToDocument(newAcc, nullptr); - newAcc->AsTextLeaf()->SetText(text.mString); + newAcc->AsTextLeaf()->SetText(text); return newAcc; } diff --git a/accessible/base/nsTextEquivUtils.cpp b/accessible/base/nsTextEquivUtils.cpp index bb7b3d918de4..d8dcd2ae0727 100644 --- a/accessible/base/nsTextEquivUtils.cpp +++ b/accessible/base/nsTextEquivUtils.cpp @@ -139,8 +139,8 @@ nsTextEquivUtils::AppendTextEquivFromTextContent(nsIContent *aContent, if (aContent->TextLength() > 0) { nsIFrame *frame = aContent->GetPrimaryFrame(); if (frame) { - nsIFrame::RenderedText text = frame->GetRenderedText(); - aString->Append(text.mString); + nsresult rv = frame->GetRenderedText(aString); + NS_ENSURE_SUCCESS(rv, rv); } else { // If aContent is an object that is display: none, we have no a frame. aContent->AppendTextTo(*aString); diff --git a/accessible/generic/Accessible.cpp b/accessible/generic/Accessible.cpp index 6f97835bb9b3..89fc66e63545 100644 --- a/accessible/generic/Accessible.cpp +++ b/accessible/generic/Accessible.cpp @@ -394,10 +394,10 @@ Accessible::VisibilityState() if (frame->GetType() == nsGkAtoms::textFrame && !(frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && frame->GetRect().IsEmpty()) { - nsIFrame::RenderedText text = frame->GetRenderedText(); - if (text.mString.IsEmpty()) { + nsAutoString renderedText; + frame->GetRenderedText(&renderedText, nullptr, nullptr, 0, 1); + if (renderedText.IsEmpty()) return states::INVISIBLE; - } } return 0; diff --git a/accessible/generic/HyperTextAccessible.cpp b/accessible/generic/HyperTextAccessible.cpp index 2fcce652ce21..38c22afa8e3a 100644 --- a/accessible/generic/HyperTextAccessible.cpp +++ b/accessible/generic/HyperTextAccessible.cpp @@ -1984,9 +1984,17 @@ HyperTextAccessible::ContentToRenderedOffset(nsIFrame* aFrame, int32_t aContentO NS_ASSERTION(aFrame->GetPrevContinuation() == nullptr, "Call on primary frame only"); - nsIFrame::RenderedText text = aFrame->GetRenderedText(aContentOffset, - aContentOffset + 1); - *aRenderedOffset = text.mOffsetWithinNodeRenderedText; + gfxSkipChars skipChars; + gfxSkipCharsIterator iter; + // Only get info up to original offset, we know that will be larger than skipped offset + nsresult rv = aFrame->GetRenderedText(nullptr, &skipChars, &iter, 0, aContentOffset); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t ourRenderedStart = iter.GetSkippedOffset(); + int32_t ourContentStart = iter.GetOriginalOffset(); + + *aRenderedOffset = iter.ConvertOriginalToSkipped(aContentOffset + ourContentStart) - + ourRenderedStart; return NS_OK; } @@ -2008,9 +2016,16 @@ HyperTextAccessible::RenderedToContentOffset(nsIFrame* aFrame, uint32_t aRendere NS_ASSERTION(aFrame->GetPrevContinuation() == nullptr, "Call on primary frame only"); - nsIFrame::RenderedText text = aFrame->GetRenderedText(aRenderedOffset, - aRenderedOffset + 1, nsIFrame::TextOffsetType::OFFSETS_IN_RENDERED_TEXT); - *aContentOffset = text.mOffsetWithinNodeText; + gfxSkipChars skipChars; + gfxSkipCharsIterator iter; + // We only need info up to skipped offset -- that is what we're converting to original offset + nsresult rv = aFrame->GetRenderedText(nullptr, &skipChars, &iter, 0, aRenderedOffset); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t ourRenderedStart = iter.GetSkippedOffset(); + int32_t ourContentStart = iter.GetOriginalOffset(); + + *aContentOffset = iter.ConvertSkippedToOriginal(aRenderedOffset + ourRenderedStart) - ourContentStart; return NS_OK; } diff --git a/accessible/tests/mochitest/jsat/test_traversal.html b/accessible/tests/mochitest/jsat/test_traversal.html index 12316034aea6..533e9d89ff90 100644 --- a/accessible/tests/mochitest/jsat/test_traversal.html +++ b/accessible/tests/mochitest/jsat/test_traversal.html @@ -113,7 +113,7 @@ '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', @@ -124,7 +124,7 @@ '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, diff --git a/accessible/tests/mochitest/pivot/test_virtualcursor.html b/accessible/tests/mochitest/pivot/test_virtualcursor.html index 9bb17348434c..2fb339964afb 100644 --- a/accessible/tests/mochitest/pivot/test_virtualcursor.html +++ b/accessible/tests/mochitest/pivot/test_virtualcursor.html @@ -52,7 +52,7 @@ 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']); @@ -90,7 +90,7 @@ 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)); diff --git a/accessible/tests/mochitest/text/test_doc.html b/accessible/tests/mochitest/text/test_doc.html index 76b801c277f0..9c6788275a0d 100644 --- a/accessible/tests/mochitest/text/test_doc.html +++ b/accessible/tests/mochitest/text/test_doc.html @@ -16,8 +16,8 @@ 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(); } diff --git a/accessible/tests/mochitest/text/test_hypertext.html b/accessible/tests/mochitest/text/test_hypertext.html index 6df2419aa6fc..5640dc186509 100644 --- a/accessible/tests/mochitest/text/test_hypertext.html +++ b/accessible/tests/mochitest/text/test_hypertext.html @@ -58,7 +58,7 @@ //////////////////////////////////////////////////////////////////////// // 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. @@ -122,7 +122,7 @@
-
hello friend see
+
hello friend see
hello friend see
  1. foo
  2. diff --git a/accessible/tests/mochitest/text/test_lineboundary.html b/accessible/tests/mochitest/text/test_lineboundary.html index ff1364b9ba6d..e2e170e2f44b 100644 --- a/accessible/tests/mochitest/text/test_lineboundary.html +++ b/accessible/tests/mochitest/text/test_lineboundary.html @@ -14,9 +14,9 @@ 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__ @@ -114,10 +114,10 @@ [ [ 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 diff --git a/accessible/tests/mochitest/text/test_wordboundary.html b/accessible/tests/mochitest/text/test_wordboundary.html index 6d3c09e8c761..e36d5599943c 100644 --- a/accessible/tests/mochitest/text/test_wordboundary.html +++ b/accessible/tests/mochitest/text/test_wordboundary.html @@ -42,20 +42,27 @@ [ [ 0, 6, "", 0, 0 ] ]); testTextBeforeOffset(ids, BOUNDARY_WORD_END, [ [ 0, 5, "", 0, 0 ], - [ 6, 6, "hello", 0, 5 ] + [ 6, 6, "hello", 0, 5, + [ [6, "e2", kTodo, kOk, kTodo ] ] + ] ]); testTextAtOffset(ids, BOUNDARY_WORD_START, [ [ 0, 6, "hello ", 0, 6 ] ]); testTextAtOffset(ids, BOUNDARY_WORD_END, [ [ 0, 4, "hello", 0, 5 ], - [ 5, 6, " ", 5, 6 ] + [ 5, 6, " ", 5, 6, + [ [ 5, "e2", kTodo, kTodo, kOk ], + [ 6, "e2", kTodo, kTodo, kOk ] ] + ] ]); testTextAfterOffset(ids, BOUNDARY_WORD_START, [ [ 0, 6, "", 6, 6 ] ]); testTextAfterOffset(ids, BOUNDARY_WORD_END, - [ [ 0, 5, " ", 5, 6 ], + [ [ 0, 5, " ", 5, 6, + [ [ 5, "e2", kTodo, kTodo, kOk ] ] + ], [ 6, 6, "", 6, 6 ] ]); @@ -249,7 +256,7 @@
    hello
    -
    hello
    +
    hello
    diff --git a/accessible/tests/mochitest/tree/test_txtcntr.html b/accessible/tests/mochitest/tree/test_txtcntr.html index 54e42b7f3758..80a225ce7859 100644 --- a/accessible/tests/mochitest/tree/test_txtcntr.html +++ b/accessible/tests/mochitest/tree/test_txtcntr.html @@ -49,14 +49,14 @@ }, { role: ROLE_TEXT_LEAF, - name: "Hello3" + name: "Hello3 " }, { role: ROLE_PARAGRAPH, children: [ { role: ROLE_TEXT_LEAF, - name: "Hello4" + name: "Hello4 " } ] } @@ -71,7 +71,7 @@ children: [ { role: ROLE_TEXT_LEAF, - name: "helllo" + name: "helllo " }, { role: ROLE_PARAGRAPH, @@ -84,7 +84,7 @@ }, { role: ROLE_TEXT_LEAF, - name: "hello" + name: "hello " } ] }; diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index 017db73d0f2d..c0c0fca951eb 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -1159,7 +1159,7 @@ void FragmentOrElement::GetTextContentInternal(nsAString& aTextContent, ErrorResult& aError) { - if (!nsContentUtils::GetNodeTextContent(this, true, aTextContent, fallible)) { + if(!nsContentUtils::GetNodeTextContent(this, true, aTextContent, fallible)) { aError.Throw(NS_ERROR_OUT_OF_MEMORY); } } diff --git a/dom/base/nsRange.cpp b/dom/base/nsRange.cpp index 537c02a7306f..7709cda1e9ba 100644 --- a/dom/base/nsRange.cpp +++ b/dom/base/nsRange.cpp @@ -35,9 +35,6 @@ #include "mozilla/Telemetry.h" #include "mozilla/Likely.h" #include "nsCSSFrameConstructor.h" -#include "nsStyleStruct.h" -#include "nsStyleStructInlines.h" -#include "nsComputedDOMStyle.h" using namespace mozilla; using namespace mozilla::dom; @@ -3196,236 +3193,3 @@ nsRange::ExcludeNonSelectableNodes(nsTArray>* aOutRanges) } } } - -struct InnerTextAccumulator { - nsString& mString; - int8_t mRequiredLineBreakCount; - InnerTextAccumulator(mozilla::dom::DOMString& aValue) - : mString(aValue.AsAString()), mRequiredLineBreakCount(0) {} - void FlushLineBreaks() - { - while (mRequiredLineBreakCount > 0) { - // Required line breaks at the start of the text are suppressed. - if (!mString.IsEmpty()) { - mString.Append('\n'); - } - --mRequiredLineBreakCount; - } - } - void Append(char ch) - { - Append(nsAutoString(ch)); - } - void Append(const nsAString& aString) - { - if (aString.IsEmpty()) { - return; - } - FlushLineBreaks(); - mString.Append(aString); - } - void AddRequiredLineBreakCount(int8_t aCount) - { - mRequiredLineBreakCount = std::max(mRequiredLineBreakCount, aCount); - } -}; - -static bool -IsVisibleAndNotInReplacedElement(nsIFrame* aFrame) -{ - if (!aFrame || !aFrame->StyleVisibility()->IsVisible()) { - return false; - } - for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) { - if (f->GetContent() && f->GetContent()->IsHTMLElement(nsGkAtoms::button)) { - continue; - } - if (f->IsFrameOfType(nsIFrame::eReplaced)) { - return false; - } - } - return true; -} - -static bool -ElementIsVisible(nsIContent* aContent) -{ - Element* elem = aContent->AsElement(); - if (!elem) { - return false; - } - RefPtr sc = nsComputedDOMStyle::GetStyleContextForElement( - elem, nullptr, nullptr); - return sc && sc->StyleVisibility()->IsVisible(); -} - -static void -AppendTransformedText(InnerTextAccumulator& aResult, - nsGenericDOMDataNode* aTextNode, - int32_t aStart, int32_t aEnd) -{ - nsIFrame* frame = aTextNode->GetPrimaryFrame(); - if (!IsVisibleAndNotInReplacedElement(frame)) { - return; - } - nsIFrame::RenderedText text = frame->GetRenderedText(aStart, aEnd); - aResult.Append(text.mString); -} - -enum TreeTraversalState { - AT_NODE, - AFTER_NODE -}; - -static int8_t -GetRequiredInnerTextLineBreakCount(nsIFrame* aFrame) -{ - if (aFrame->GetContent()->IsHTMLElement(nsGkAtoms::p)) { - return 2; - } - const nsStyleDisplay* styleDisplay = aFrame->StyleDisplay(); - if (styleDisplay->IsBlockOutside(aFrame) || - styleDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_CAPTION) { - return 1; - } - return 0; -} - -static bool -IsLastCellOfRow(nsIFrame* aFrame) -{ - nsIAtom* type = aFrame->GetType(); - if (type != nsGkAtoms::tableCellFrame && - type != nsGkAtoms::bcTableCellFrame) { - return true; - } - for (nsIFrame* c = aFrame; c; c = c->GetNextContinuation()) { - if (c->GetNextSibling()) { - return false; - } - } - return true; -} - -static bool -IsLastRowOfRowGroup(nsIFrame* aFrame) -{ - if (aFrame->GetType() != nsGkAtoms::tableRowFrame) { - return true; - } - for (nsIFrame* c = aFrame; c; c = c->GetNextContinuation()) { - if (c->GetNextSibling()) { - return false; - } - } - return true; -} - -static bool -IsLastNonemptyRowGroupOfTable(nsIFrame* aFrame) -{ - if (aFrame->GetType() != nsGkAtoms::tableRowGroupFrame) { - return true; - } - for (nsIFrame* c = aFrame; c; c = c->GetNextContinuation()) { - for (nsIFrame* next = c->GetNextSibling(); next; next = next->GetNextSibling()) { - if (next->GetFirstPrincipalChild()) { - return false; - } - } - } - return true; -} - -void -nsRange::GetInnerTextNoFlush(mozilla::dom::DOMString& aValue, - mozilla::ErrorResult& aError) -{ - InnerTextAccumulator result(aValue); - nsIContent* currentNode = mStartParent->AsContent(); - TreeTraversalState currentState = AFTER_NODE; - if (mStartParent->IsNodeOfType(nsINode::eTEXT)) { - auto t = static_cast(mStartParent.get()); - if (mStartParent == mEndParent) { - AppendTransformedText(result, t, mStartOffset, mEndOffset); - return; - } - AppendTransformedText(result, t, mStartOffset, t->TextLength()); - } else { - if (uint32_t(mStartOffset) < mStartParent->GetChildCount()) { - currentNode = mStartParent->GetChildAt(mStartOffset); - currentState = AT_NODE; - } - } - - nsIContent* endNode = mEndParent->AsContent(); - TreeTraversalState endState = AFTER_NODE; - if (mEndParent->IsNodeOfType(nsINode::eTEXT)) { - endState = AT_NODE; - } else { - if (uint32_t(mEndOffset) < mEndParent->GetChildCount()) { - endNode = mEndParent->GetChildAt(mEndOffset); - endState = AT_NODE; - } - } - - while (currentNode != endNode || currentState != endState) { - nsIFrame* f = currentNode->GetPrimaryFrame(); - bool isVisibleAndNotReplaced = IsVisibleAndNotInReplacedElement(f); - if (currentState == AT_NODE) { - bool isText = currentNode->IsNodeOfType(nsINode::eTEXT); - if (isText && currentNode->GetParent()->IsHTMLElement(nsGkAtoms::rp) && - ElementIsVisible(currentNode->GetParent())) { - nsAutoString str; - currentNode->GetTextContent(str, aError); - result.Append(str); - } else if (isVisibleAndNotReplaced) { - result.AddRequiredLineBreakCount(GetRequiredInnerTextLineBreakCount(f)); - if (isText) { - nsIFrame::RenderedText text = f->GetRenderedText(); - result.Append(text.mString); - } - } - nsIContent* child = currentNode->GetFirstChild(); - if (child) { - currentNode = child; - } else { - currentState = AFTER_NODE; - } - } else { - if (isVisibleAndNotReplaced) { - if (currentNode->IsHTMLElement(nsGkAtoms::br)) { - result.Append('\n'); - } - switch (f->StyleDisplay()->mDisplay) { - case NS_STYLE_DISPLAY_TABLE_CELL: - if (!IsLastCellOfRow(f)) { - result.Append('\t'); - } - break; - case NS_STYLE_DISPLAY_TABLE_ROW: - if (!IsLastRowOfRowGroup(f) || - !IsLastNonemptyRowGroupOfTable(f->GetParent())) { - result.Append('\n'); - } - break; - } - result.AddRequiredLineBreakCount(GetRequiredInnerTextLineBreakCount(f)); - } - nsIContent* next = currentNode->GetNextSibling(); - if (next) { - currentNode = next; - currentState = AT_NODE; - } else { - currentNode = currentNode->GetParent(); - } - } - } - - if (mEndParent->IsNodeOfType(nsINode::eTEXT)) { - nsGenericDOMDataNode* t = static_cast(mEndParent.get()); - AppendTransformedText(result, t, 0, mEndOffset); - } - // Do not flush trailing line breaks! Required breaks at the end of the text - // are suppressed. -} diff --git a/dom/base/nsRange.h b/dom/base/nsRange.h index 9c254f9f2825..6d8b340eed13 100644 --- a/dom/base/nsRange.h +++ b/dom/base/nsRange.h @@ -212,8 +212,6 @@ public: bool aFlushLayout = true); already_AddRefed GetClientRects(bool aClampToEdge = true, bool aFlushLayout = true); - void GetInnerTextNoFlush(mozilla::dom::DOMString& aValue, - mozilla::ErrorResult& aError); nsINode* GetParentObject() const { return mOwner; } virtual JSObject* WrapObject(JSContext* cx, JS::Handle aGivenProto) override final; diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index f6b33c931e02..1d9aaab05a02 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -84,8 +84,6 @@ #include "nsITextControlElement.h" #include "mozilla/dom/Element.h" #include "HTMLFieldSetElement.h" -#include "nsTextNode.h" -#include "HTMLBRElement.h" #include "HTMLMenuElement.h" #include "nsDOMMutationObserver.h" #include "mozilla/Preferences.h" @@ -106,7 +104,6 @@ #include "nsGlobalWindow.h" #include "mozilla/dom/HTMLBodyElement.h" #include "imgIContainer.h" -#include "nsComputedDOMStyle.h" using namespace mozilla; using namespace mozilla::dom; @@ -3297,105 +3294,3 @@ nsGenericHTMLElement::NewURIFromString(const nsAString& aURISpec, return NS_OK; } - -void -nsGenericHTMLElement::GetInnerText(mozilla::dom::DOMString& aValue, - mozilla::ErrorResult& aError) -{ - if (!GetPrimaryFrame(Flush_Layout)) { - RefPtr sc = - nsComputedDOMStyle::GetStyleContextForElementNoFlush(this, nullptr, nullptr); - if (!sc || sc->StyleDisplay()->mDisplay == NS_STYLE_DISPLAY_NONE || - !GetCurrentDoc()) { - GetTextContentInternal(aValue, aError); - return; - } - } - - RefPtr range; - nsresult rv = nsRange::CreateRange(static_cast(this), 0, this, - GetChildCount(), getter_AddRefs(range)); - if (NS_FAILED(rv)) { - aError.Throw(rv); - return; - } - - range->GetInnerTextNoFlush(aValue, aError); -} - -void -nsGenericHTMLElement::SetInnerText(const nsAString& aValue) -{ - // Fire DOMNodeRemoved mutation events before we do anything else. - nsCOMPtr kungFuDeathGrip; - - // Batch possible DOMSubtreeModified events. - mozAutoSubtreeModified subtree(nullptr, nullptr); - - // Scope firing mutation events so that we don't carry any state that - // might be stale - { - // We're relying on mozAutoSubtreeModified to keep a strong reference if - // needed. - nsIDocument* doc = OwnerDoc(); - - // Optimize the common case of there being no observers - if (nsContentUtils::HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) { - subtree.UpdateTarget(doc, nullptr); - // Keep "this" alive during mutation listener firing - kungFuDeathGrip = this; - nsCOMPtr child; - for (child = nsINode::GetFirstChild(); - child && child->GetParentNode() == this; - child = child->GetNextSibling()) { - nsContentUtils::MaybeFireNodeRemoved(child, this, doc); - } - } - } - - // Might as well stick a batch around this since we're performing several - // mutations. - mozAutoDocUpdate updateBatch(GetComposedDoc(), - UPDATE_CONTENT_MODEL, true); - nsAutoMutationBatch mb; - - uint32_t childCount = GetChildCount(); - - mb.Init(this, true, false); - for (uint32_t i = 0; i < childCount; ++i) { - RemoveChildAt(0, true); - } - mb.RemovalDone(); - - nsString str; - const char16_t* s = aValue.BeginReading(); - const char16_t* end = aValue.EndReading(); - while (true) { - if (s != end && *s == '\r' && s + 1 != end && s[1] == '\n') { - // a \r\n pair should only generate one
    , so just skip the \r - ++s; - } - if (s == end || *s == '\r' || *s == '\n') { - if (!str.IsEmpty()) { - RefPtr textContent = - new nsTextNode(NodeInfo()->NodeInfoManager()); - textContent->SetText(str, true); - AppendChildTo(textContent, true); - } - if (s == end) { - break; - } - str.SetLength(0); - already_AddRefed ni = - NodeInfo()->NodeInfoManager()->GetNodeInfo(nsGkAtoms::br, - nullptr, kNameSpaceID_XHTML, nsIDOMNode::ELEMENT_NODE); - RefPtr br = new HTMLBRElement(ni); - AppendChildTo(br, true); - } else { - str.Append(*s); - } - ++s; - } - - mb.NodesAdded(); -} diff --git a/dom/html/nsGenericHTMLElement.h b/dom/html/nsGenericHTMLElement.h index 71b505d12bfe..a85211be48cd 100644 --- a/dom/html/nsGenericHTMLElement.h +++ b/dom/html/nsGenericHTMLElement.h @@ -241,9 +241,6 @@ public: mScrollgrab = aValue; } - void GetInnerText(mozilla::dom::DOMString& aValue, mozilla::ErrorResult& aError); - void SetInnerText(const nsAString& aValue); - /** * Determine whether an attribute is an event (onclick, etc.) * @param aName the attribute diff --git a/dom/webidl/HTMLElement.webidl b/dom/webidl/HTMLElement.webidl index 7caf68785496..8399c1e7cbac 100644 --- a/dom/webidl/HTMLElement.webidl +++ b/dom/webidl/HTMLElement.webidl @@ -22,9 +22,6 @@ interface HTMLElement : Element { [Constant] readonly attribute DOMStringMap dataset; - [GetterThrows, Pure] - attribute DOMString innerText; - // microdata [SetterThrows, Pure] attribute boolean itemScope; diff --git a/gfx/tests/gtest/TestSkipChars.cpp b/gfx/tests/gtest/TestSkipChars.cpp index 65c96f4172ff..de9f5ead4674 100644 --- a/gfx/tests/gtest/TestSkipChars.cpp +++ b/gfx/tests/gtest/TestSkipChars.cpp @@ -82,7 +82,7 @@ TestIterator() "[4] Check mapping of original to skipped for " << i; } - int32_t expectOriginal1[] = + uint32_t expectOriginal1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, 26, 27, 28 }; @@ -136,7 +136,7 @@ TestIterator() "[9] Check mapping of original to skipped for " << i; } - int32_t expectOriginal2[] = { 9, 19, 29 }; + uint32_t expectOriginal2[] = { 9, 19, 29 }; for (uint32_t i = 0; i < mozilla::ArrayLength(expectOriginal2); i++) { EXPECT_TRUE(iter2.ConvertSkippedToOriginal(i) == expectOriginal2[i]) << diff --git a/gfx/thebes/gfxSkipChars.h b/gfx/thebes/gfxSkipChars.h index 352a16090de5..5663dd328584 100644 --- a/gfx/thebes/gfxSkipChars.h +++ b/gfx/thebes/gfxSkipChars.h @@ -235,7 +235,7 @@ public: return GetSkippedOffset(); } - int32_t ConvertSkippedToOriginal(uint32_t aSkippedStringOffset) + uint32_t ConvertSkippedToOriginal(int32_t aSkippedStringOffset) { SetSkippedOffset(aSkippedStringOffset); return GetOriginalOffset(); diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 99d44e561c3c..ab64ffbd7fc8 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -1893,36 +1893,26 @@ public: virtual bool CanContinueTextRun() const = 0; /** - * Computes an approximation of the rendered text of the frame and its - * continuations. Returns nothing for non-text frames. + * Append the rendered text to the passed-in string. * The appended text will often not contain all the whitespace from source, - * depending on CSS white-space processing. - * if aEndOffset goes past end, use the text up to the string's end. + * depending on whether the CSS rule "white-space: pre" is active for this frame. + * if aStartOffset + aLength goes past end, or if aLength is not specified + * then use the text up to the string's end. * Call this on the primary frame for a text node. - * aStartOffset and aEndOffset can be content offsets or offsets in the - * rendered text, depending on aOffsetType. - * Returns a string, as well as offsets identifying the start of the text - * within the rendered text for the whole node, and within the text content - * of the node. + * @param aAppendToString String to append text to, or null if text should not be returned + * @param aSkipChars if aSkipIter is non-null, this must also be non-null. + * This gets used as backing data for the iterator so it should outlive the iterator. + * @param aSkipIter Where to fill in the gfxSkipCharsIterator info, or null if not needed by caller + * @param aStartOffset Skipped (rendered text) start offset + * @param aSkippedMaxLength Maximum number of characters to return + * The iterator can be used to map content offsets to offsets in the returned string, or vice versa. */ - struct RenderedText { - nsString mString; - uint32_t mOffsetWithinNodeRenderedText; - int32_t mOffsetWithinNodeText; - RenderedText() : mOffsetWithinNodeRenderedText(0), - 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 - }; - virtual RenderedText GetRenderedText(uint32_t aStartOffset = 0, - uint32_t aEndOffset = UINT32_MAX, - TextOffsetType aOffsetType = - TextOffsetType::OFFSETS_IN_CONTENT_TEXT) - { return RenderedText(); } + virtual nsresult GetRenderedText(nsAString* aAppendToString = nullptr, + gfxSkipChars* aSkipChars = nullptr, + gfxSkipCharsIterator* aSkipIter = nullptr, + uint32_t aSkippedStartOffset = 0, + uint32_t aSkippedMaxLength = UINT32_MAX) + { return NS_ERROR_NOT_IMPLEMENTED; } /** * Returns true if the frame contains any non-collapsed characters. diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index 39e9b87587a5..00706c3957ae 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -3975,8 +3975,9 @@ a11y::AccType nsTextFrame::AccessibleType() { if (IsEmpty()) { - RenderedText text = GetRenderedText(); - if (text.mString.IsEmpty()) { + nsAutoString renderedWhitespace; + GetRenderedText(&renderedWhitespace, nullptr, nullptr, 0, 1); + if (renderedWhitespace.IsEmpty()) { return a11y::eNoType; } } @@ -4089,6 +4090,13 @@ public: virtual void AddInlinePrefISize(nsRenderingContext *aRenderingContext, InlinePrefISizeData *aData) override; + virtual nsresult GetRenderedText(nsAString* aString = nullptr, + gfxSkipChars* aSkipChars = nullptr, + gfxSkipCharsIterator* aSkipIter = nullptr, + uint32_t aSkippedStartOffset = 0, + uint32_t aSkippedMaxLength = UINT32_MAX) override + { return NS_ERROR_NOT_IMPLEMENTED; } // Call on a primary text frame only + protected: explicit nsContinuingTextFrame(nsStyleContext* aContext) : nsTextFrame(aContext) {} nsIFrame* mPrevContinuation; @@ -8874,151 +8882,80 @@ static char16_t TransformChar(nsTextFrame* aFrame, const nsStyleText* aStyle, return aChar; } -static bool -LineEndsInHardLineBreak(nsTextFrame* aFrame) +nsresult nsTextFrame::GetRenderedText(nsAString* aAppendToString, + gfxSkipChars* aSkipChars, + gfxSkipCharsIterator* aSkipIter, + uint32_t aSkippedStartOffset, + uint32_t aSkippedMaxLength) { - nsIFrame* lineContainer = FindLineContainer(aFrame); - nsBlockFrame* block = do_QueryFrame(lineContainer); - if (!block) { - // Weird situation where we have a line layout without a block. - // No soft breaks occur in this situation. - return true; - } - bool foundValidLine; - nsBlockInFlowLineIterator iter(block, aFrame, &foundValidLine); - if (!foundValidLine) { - NS_ERROR("Invalid line!"); - return true; - } - return !iter.GetLine()->IsLineWrapped(); -} - -nsIFrame::RenderedText -nsTextFrame::GetRenderedText(uint32_t aStartOffset, - uint32_t aEndOffset, - TextOffsetType aOffsetType) -{ - NS_ASSERTION(!GetPrevContinuation(), "Must be called on first-in-flow"); - - // The handling of offsets could be more efficient... - RenderedText result; + // The handling of aSkippedStartOffset and aSkippedMaxLength could be more efficient... + gfxSkipChars skipChars; nsTextFrame* textFrame; const nsTextFragment* textFrag = mContent->GetText(); - uint32_t offsetInRenderedString = 0; - bool haveOffsets = false; + uint32_t keptCharsLength = 0; + uint32_t validCharsLength = 0; + // Build skipChars and copy text, for each text frame in this continuation block for (textFrame = this; textFrame; textFrame = static_cast(textFrame->GetNextContinuation())) { + // For each text frame continuation in this block ... + if (textFrame->GetStateBits() & NS_FRAME_IS_DIRTY) { - // We don't trust dirty frames, especially when computing rendered text. + // We don't trust dirty frames, expecially when computing rendered text. break; } // Ensure the text run and grab the gfxSkipCharsIterator for it gfxSkipCharsIterator iter = textFrame->EnsureTextRun(nsTextFrame::eInflated); - if (!textFrame->mTextRun) { - break; - } - gfxSkipCharsIterator tmpIter = iter; + if (!textFrame->mTextRun) + return NS_ERROR_FAILURE; // Skip to the start of the text run, past ignored chars at start of line - TrimmedOffsets trimmedOffsets = textFrame->GetTrimmedOffsets(textFrag, - textFrame->IsAtEndOfLine() && LineEndsInHardLineBreak(textFrame)); - 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; - - if (aOffsetType == TextOffsetType::OFFSETS_IN_RENDERED_TEXT) { - if (nextOffsetInRenderedString <= aStartOffset) { - offsetInRenderedString = nextOffsetInRenderedString; - continue; - } - if (!haveOffsets) { - result.mOffsetWithinNodeText = - tmpIter.ConvertSkippedToOriginal(aStartOffset - skippedToRenderedStringOffset); - result.mOffsetWithinNodeRenderedText = aStartOffset; - haveOffsets = true; - } - if (offsetInRenderedString >= aEndOffset) { - break; - } - } else { - if (uint32_t(textFrame->GetContentEnd()) <= aStartOffset) { - offsetInRenderedString = nextOffsetInRenderedString; - continue; - } - if (!haveOffsets) { - result.mOffsetWithinNodeText = aStartOffset; - // Skip trimmed space when computed the rendered text offset. - int32_t clamped = std::max(aStartOffset, trimmedOffsets.mStart); - result.mOffsetWithinNodeRenderedText = - tmpIter.ConvertOriginalToSkipped(clamped) + skippedToRenderedStringOffset; - NS_ASSERTION(result.mOffsetWithinNodeRenderedText >= offsetInRenderedString && - result.mOffsetWithinNodeRenderedText <= INT32_MAX, - "Bad offset within rendered text"); - haveOffsets = true; - } - if (uint32_t(textFrame->mContentOffset) >= aEndOffset) { - break; - } - } - - int32_t startOffset; - int32_t endOffset; - if (aOffsetType == TextOffsetType::OFFSETS_IN_RENDERED_TEXT) { - startOffset = - tmpIter.ConvertSkippedToOriginal(aStartOffset - skippedToRenderedStringOffset); - endOffset = - tmpIter.ConvertSkippedToOriginal(aEndOffset - skippedToRenderedStringOffset); - } else { - startOffset = aStartOffset; - endOffset = std::min(INT32_MAX, aEndOffset); - } - trimmedOffsets.mStart = std::max(trimmedOffsets.mStart, - startOffset); - trimmedOffsets.mLength = std::min(trimmedOffsets.GetEnd(), - endOffset) - trimmedOffsets.mStart; - if (trimmedOffsets.mLength <= 0) { - offsetInRenderedString = nextOffsetInRenderedString; - continue; + // XXX In the future we may decide to trim extra spaces before a hard line + // break, in which case we need to accurately detect those sitations and + // call GetTrimmedOffsets() with true to trim whitespace at the line's end + TrimmedOffsets trimmedContentOffsets = textFrame->GetTrimmedOffsets(textFrag, false); + int32_t startOfLineSkipChars = trimmedContentOffsets.mStart - textFrame->mContentOffset; + if (startOfLineSkipChars > 0) { + skipChars.SkipChars(startOfLineSkipChars); + iter.SetOriginalOffset(trimmedContentOffsets.mStart); } + // Keep and copy the appropriate chars withing the caller's requested range const nsStyleText* textStyle = textFrame->StyleText(); - iter.SetOriginalOffset(trimmedOffsets.mStart); - while (iter.GetOriginalOffset() < trimmedOffsets.GetEnd()) { - char16_t ch = textFrag->CharAt(iter.GetOriginalOffset()); - if (iter.IsOriginalCharSkipped()) { - if (ch == CH_SHY) { - // We should preserve soft hyphens. They can't be transformed. - result.mString.Append(ch); - } + while (iter.GetOriginalOffset() < trimmedContentOffsets.GetEnd() && + keptCharsLength < aSkippedMaxLength) { + // For each original char from content text + if (iter.IsOriginalCharSkipped() || ++validCharsLength <= aSkippedStartOffset) { + skipChars.SkipChar(); } else { - result.mString.Append( + ++keptCharsLength; + skipChars.KeepChar(); + if (aAppendToString) { + aAppendToString->Append( TransformChar(textFrame, textStyle, textFrame->mTextRun, - iter.GetSkippedOffset(), ch)); + iter.GetSkippedOffset(), + textFrag->CharAt(iter.GetOriginalOffset()))); + } } iter.AdvanceOriginal(1); } - - if (trimmedSignificantNewline && GetContentEnd() <= endOffset) { - // A significant newline was trimmed off (we must be - // white-space:pre-line). Put it back. - result.mString.Append('\n'); + if (keptCharsLength >= aSkippedMaxLength) { + break; // Already past the end, don't build string or gfxSkipCharsIter anymore + } + } + + if (aSkipChars) { + aSkipChars->TakeFrom(&skipChars); // Copy skipChars into aSkipChars + if (aSkipIter) { + // Caller must provide both pointers in order to retrieve a gfxSkipCharsIterator, + // because the gfxSkipCharsIterator holds a weak pointer to the gfxSkipChars. + *aSkipIter = gfxSkipCharsIterator(*aSkipChars, GetContentLength()); } - offsetInRenderedString = nextOffsetInRenderedString; } - if (!haveOffsets) { - result.mOffsetWithinNodeText = textFrag->GetLength(); - result.mOffsetWithinNodeRenderedText = offsetInRenderedString; - } - return result; + return NS_OK; } nsIAtom* diff --git a/layout/generic/nsTextFrame.h b/layout/generic/nsTextFrame.h index ff5553d7477e..202e1cdd1296 100644 --- a/layout/generic/nsTextFrame.h +++ b/layout/generic/nsTextFrame.h @@ -261,10 +261,11 @@ public: 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; + virtual nsresult GetRenderedText(nsAString* aString = nullptr, + gfxSkipChars* aSkipChars = nullptr, + gfxSkipCharsIterator* aSkipIter = nullptr, + uint32_t aSkippedStartOffset = 0, + uint32_t aSkippedMaxLength = UINT32_MAX) override; nsOverflowAreas RecomputeOverflow(nsIFrame* aBlockFrame); diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index 56beef69b30c..6558b4d9e406 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -18713,14 +18713,6 @@ "path": "infrastructure/failing-test.html", "url": "/infrastructure/failing-test.html" }, - { - "path": "innerText/getter.html", - "url": "/innerText/getter.html" - }, - { - "path": "innerText/setter.html", - "url": "/innerText/setter.html" - }, { "path": "js/behaviours/SetPrototypeOf-window.html", "url": "/js/behaviours/SetPrototypeOf-window.html" diff --git a/testing/web-platform/tests/innerText/getter-tests.js b/testing/web-platform/tests/innerText/getter-tests.js deleted file mode 100644 index bdb112c5647d..000000000000 --- a/testing/web-platform/tests/innerText/getter-tests.js +++ /dev/null @@ -1,322 +0,0 @@ -testText("
    abc", "abc", "Simplest possible test"); - -/**** white-space:normal ****/ - -testText("
    abc", "abc", "Leading whitespace removed"); -testText("
    abc ", "abc", "Trailing whitespace removed"); -testText("
    abc def", "abc def", "Internal whitespace compressed"); -testText("
    abc\ndef", "abc def", "\\n converted to space"); -testText("
    abc\rdef", "abc def", "\\r converted to space"); -testText("
    abc\tdef", "abc def", "\\t converted to space"); -testText("
    abc
    def", "abc\ndef", "Trailing whitespace before hard line break removed"); - -/****
     ****/
    -
    -testText("
     abc", " abc", "Leading whitespace preserved");
    -testText("
    abc ", "abc ", "Trailing whitespace preserved");
    -testText("
    abc  def", "abc  def", "Internal whitespace preserved");
    -testText("
    abc\ndef", "abc\ndef", "\\n preserved");
    -testText("
    abc\rdef", "abc\ndef", "\\r converted to newline");
    -testText("
    abc\tdef", "abc\tdef", "\\t preserved");
    -
    -/**** 
    ****/ - -testText("
    abc", " abc", "Leading whitespace preserved"); -testText("
    abc ", "abc ", "Trailing whitespace preserved"); -testText("
    abc def", "abc def", "Internal whitespace preserved"); -testText("
    abc\ndef", "abc\ndef", "\\n preserved"); -testText("
    abc\rdef", "abc\ndef", "\\r converted to newline"); -testText("
    abc\tdef", "abc\tdef", "\\t preserved"); - -/**** ****/ - -testText(" abc", " abc", "Leading whitespace preserved"); -testText("abc ", "abc ", "Trailing whitespace preserved"); -testText("abc def", "abc def", "Internal whitespace preserved"); -testText("abc\ndef", "abc\ndef", "\\n preserved"); -testText("abc\rdef", "abc\ndef", "\\r converted to newline"); -testText("abc\tdef", "abc\tdef", "\\t preserved"); - -/****
    ****/ - -testText("
    abc", "abc", "Leading whitespace removed"); -testText("
    abc ", "abc", "Trailing whitespace removed"); -testText("
    abc def", "abc def", "Internal whitespace collapsed"); -testText("
    abc\ndef", "abc\ndef", "\\n preserved"); -testText("
    abc\rdef", "abc\ndef", "\\r converted to newline"); -testText("
    abc\tdef", "abc def", "\\t converted to space"); - -/**** Collapsing whitespace across element boundaries ****/ - -testText("
    abc def", "abc def", "Whitespace collapses across element boundaries"); -testText("
    abc def", "abc def", "Whitespace collapses across element boundaries"); -testText("
    abc def", "abc def", "Whitespace collapses across element boundaries"); - -/**** Soft line breaks ****/ - -testText("
    abc def", "abc def", "Soft line breaks ignored"); - -/**** first-line/first-letter ****/ - -testText("
    abc def", "ABC def", "::first-line styles applied"); -testText("
    abc def", "Abc def", "::first-letter styles applied"); -testText("
    abc def", "abc def", "::first-letter float ignored"); - -/****   ****/ - -testText("
     ", "\xA0", "  preserved"); - -/**** display:none ****/ - -testText("
    abc", "abc", "display:none container"); -testText("
    abc def", "abc def", "No whitespace compression in display:none container"); -testText("
    abc def ", " abc def ", "No removal of leading/trailing whitespace in display:none container"); -testText("
    123abc", "123", "display:none child not rendered"); - -/**** display:contents ****/ - -if (CSS.supports("display", "contents")) { - testText("
    abc", "abc", "display:contents container"); - testText("
    abc", "abc", "display:contents container"); - testText("
    123abc", "123abc", "display:contents rendered"); - testText("
    123abc", "123abc", "display:contents rendered"); - testText("
    ", "", "display:contents not processed via textContent"); - testText("
    ", "", "display:contents not processed via textContent"); -} - -/**** visibility:hidden ****/ - -testText("
    abc", "", "visibility:hidden container"); -testText("
    123abc", "123", "visibility:hidden child not rendered"); -testText("
    123abc", "abc", "visibility:visible child rendered"); - -/**** visibility:collapse ****/ - -testText("
    abc", "", "visibility:collapse row-group"); -testText("
    abc", "", "visibility:collapse row"); -testText("
    abc", "", "visibility:collapse cell"); -testText("
    abc", "abc", - "visibility:collapse row-group with visible cell"); -testText("
    abc", "abc", - "visibility:collapse row with visible cell"); -testText("
    12
    ", - "2", "visibility:collapse honored on flex item"); -testText("
    12
    ", - "2", "visibility:collapse honored on grid item"); - -/**** opacity:0 ****/ - -testText("
    abc", "abc", "opacity:0 container"); -testText("
    abc def", "abc def", "Whitespace compression in opacity:0 container"); -testText("
    abc def ", "abc def", "Remove leading/trailing whitespace in opacity:0 container"); -testText("
    123abc", "123abc", "opacity:0 child rendered"); - -/**** generated content ****/ - -testText("
    ", "", "Generated content not included"); -testText("
    ", "", "Generated content on child not included"); - -/**** innerText on replaced elements ****/ - -testText("