Revert incorrectly committed changes ab657569f554 and a396f4262479

This commit is contained in:
Robert O'Callahan 2015-10-24 22:38:22 +13:00
parent b7fa380c8d
commit 37b74104b6
28 changed files with 149 additions and 978 deletions

View File

@ -228,11 +228,12 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
nsIContent* containerElm = containerNode->IsElement() ? nsIContent* containerElm = containerNode->IsElement() ?
containerNode->AsElement() : nullptr; containerNode->AsElement() : nullptr;
nsIFrame::RenderedText text = textFrame->GetRenderedText(); nsAutoString text;
textFrame->GetRenderedText(&text);
// Remove text accessible if rendered text is empty. // Remove text accessible if rendered text is empty.
if (textAcc) { if (textAcc) {
if (text.mString.IsEmpty()) { if (text.IsEmpty()) {
#ifdef A11Y_LOG #ifdef A11Y_LOG
if (logging::IsEnabled(logging::eTree | logging::eText)) { if (logging::IsEnabled(logging::eTree | logging::eText)) {
logging::MsgBegin("TREE", "text node lost its content"); logging::MsgBegin("TREE", "text node lost its content");
@ -255,17 +256,17 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
logging::MsgEntry("old text '%s'", logging::MsgEntry("old text '%s'",
NS_ConvertUTF16toUTF8(textAcc->AsTextLeaf()->Text()).get()); NS_ConvertUTF16toUTF8(textAcc->AsTextLeaf()->Text()).get());
logging::MsgEntry("new text: '%s'", logging::MsgEntry("new text: '%s'",
NS_ConvertUTF16toUTF8(text.mString).get()); NS_ConvertUTF16toUTF8(text).get());
logging::MsgEnd(); logging::MsgEnd();
} }
#endif #endif
TextUpdater::Run(mDocument, textAcc->AsTextLeaf(), text.mString); TextUpdater::Run(mDocument, textAcc->AsTextLeaf(), text);
continue; continue;
} }
// Append an accessible if rendered text is not empty. // Append an accessible if rendered text is not empty.
if (!text.mString.IsEmpty()) { if (!text.IsEmpty()) {
#ifdef A11Y_LOG #ifdef A11Y_LOG
if (logging::IsEnabled(logging::eTree | logging::eText)) { if (logging::IsEnabled(logging::eTree | logging::eText)) {
logging::MsgBegin("TREE", "text node gains new content"); logging::MsgBegin("TREE", "text node gains new content");

View File

@ -1091,11 +1091,12 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
// Create accessible for visible text frames. // Create accessible for visible text frames.
if (content->IsNodeOfType(nsINode::eTEXT)) { 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 // Ignore not rendered text nodes and whitespace text nodes between table
// cells. // cells.
if (text.mString.IsEmpty() || if (text.IsEmpty() ||
(aContext->IsTableRow() && nsCoreUtils::IsWhitespaceString(text.mString))) { (aContext->IsTableRow() && nsCoreUtils::IsWhitespaceString(text))) {
if (aIsSubtreeHidden) if (aIsSubtreeHidden)
*aIsSubtreeHidden = true; *aIsSubtreeHidden = true;
@ -1107,7 +1108,7 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
return nullptr; return nullptr;
document->BindToDocument(newAcc, nullptr); document->BindToDocument(newAcc, nullptr);
newAcc->AsTextLeaf()->SetText(text.mString); newAcc->AsTextLeaf()->SetText(text);
return newAcc; return newAcc;
} }

View File

@ -139,8 +139,8 @@ nsTextEquivUtils::AppendTextEquivFromTextContent(nsIContent *aContent,
if (aContent->TextLength() > 0) { if (aContent->TextLength() > 0) {
nsIFrame *frame = aContent->GetPrimaryFrame(); nsIFrame *frame = aContent->GetPrimaryFrame();
if (frame) { if (frame) {
nsIFrame::RenderedText text = frame->GetRenderedText(); nsresult rv = frame->GetRenderedText(aString);
aString->Append(text.mString); NS_ENSURE_SUCCESS(rv, rv);
} else { } else {
// If aContent is an object that is display: none, we have no a frame. // If aContent is an object that is display: none, we have no a frame.
aContent->AppendTextTo(*aString); aContent->AppendTextTo(*aString);

View File

@ -394,10 +394,10 @@ Accessible::VisibilityState()
if (frame->GetType() == nsGkAtoms::textFrame && if (frame->GetType() == nsGkAtoms::textFrame &&
!(frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && !(frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
frame->GetRect().IsEmpty()) { frame->GetRect().IsEmpty()) {
nsIFrame::RenderedText text = frame->GetRenderedText(); nsAutoString renderedText;
if (text.mString.IsEmpty()) { frame->GetRenderedText(&renderedText, nullptr, nullptr, 0, 1);
if (renderedText.IsEmpty())
return states::INVISIBLE; return states::INVISIBLE;
}
} }
return 0; return 0;

View File

@ -1984,9 +1984,17 @@ HyperTextAccessible::ContentToRenderedOffset(nsIFrame* aFrame, int32_t aContentO
NS_ASSERTION(aFrame->GetPrevContinuation() == nullptr, NS_ASSERTION(aFrame->GetPrevContinuation() == nullptr,
"Call on primary frame only"); "Call on primary frame only");
nsIFrame::RenderedText text = aFrame->GetRenderedText(aContentOffset, gfxSkipChars skipChars;
aContentOffset + 1); gfxSkipCharsIterator iter;
*aRenderedOffset = text.mOffsetWithinNodeRenderedText; // 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; return NS_OK;
} }
@ -2008,9 +2016,16 @@ HyperTextAccessible::RenderedToContentOffset(nsIFrame* aFrame, uint32_t aRendere
NS_ASSERTION(aFrame->GetPrevContinuation() == nullptr, NS_ASSERTION(aFrame->GetPrevContinuation() == nullptr,
"Call on primary frame only"); "Call on primary frame only");
nsIFrame::RenderedText text = aFrame->GetRenderedText(aRenderedOffset, gfxSkipChars skipChars;
aRenderedOffset + 1, nsIFrame::TextOffsetType::OFFSETS_IN_RENDERED_TEXT); gfxSkipCharsIterator iter;
*aContentOffset = text.mOffsetWithinNodeText; // 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; return NS_OK;
} }

View File

@ -113,7 +113,7 @@
'A esoteric weapon wielded by only the most ' + 'A esoteric weapon wielded by only the most ' +
'formidable warriors, for its unrelenting strict' + 'formidable warriors, for its unrelenting strict' +
' power is unfathomable.', ' power is unfathomable.',
'• Lists of Programming Languages', 'Lisp', '• Lists of Programming Languages', 'Lisp ',
'1. Scheme', '2. Racket', '3. Clojure', '1. Scheme', '2. Racket', '3. Clojure',
'4. Standard Lisp', 'link-0', ' Lisp', '4. Standard Lisp', 'link-0', ' Lisp',
'checkbox-1-5', ' LeLisp', '• JavaScript', 'checkbox-1-5', ' LeLisp', '• JavaScript',
@ -124,7 +124,7 @@
'5 8', 'gridcell4', 'Just an innocuous separator', '5 8', 'gridcell4', 'Just an innocuous separator',
'Dirty Words', 'Meaning', 'Mud', 'Wet Dirt', 'Dirty Words', 'Meaning', 'Mud', 'Wet Dirt',
'Dirt', 'Messy Stuff', 'statusbar-1', 'statusbar-2', '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.']); 'with some text after.']);
queueTraversalSequence(gQueue, docAcc, TraversalRules.Landmark, null, queueTraversalSequence(gQueue, docAcc, TraversalRules.Landmark, null,

View File

@ -52,7 +52,7 @@
gQueue, docAcc, ObjectTraversalRule, null, gQueue, docAcc, ObjectTraversalRule, null,
['Main Title', 'Lorem ipsum ', ['Main Title', 'Lorem ipsum ',
'dolor', ' sit amet. Integer vitae urna leo, id ', '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.', 'Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.',
'An ', 'embedded', ' document.', 'Hide me', 'Link 1', 'Link 2', 'An ', 'embedded', ' document.', 'Hide me', 'Link 1', 'Link 2',
'Link 3', 'Hello', 'World']); 'Link 3', 'Hello', 'World']);
@ -90,7 +90,7 @@
gQueue, docAcc, ObjectTraversalRule, gQueue, docAcc, ObjectTraversalRule,
getAccessible(doc.getElementById('paragraph-1')), getAccessible(doc.getElementById('paragraph-1')),
['Lorem ipsum ', 'dolor', ' sit amet. Integer vitae urna leo, id ', ['Lorem ipsum ', 'dolor', ' sit amet. Integer vitae urna leo, id ',
'semper', ' nulla.']); 'semper', ' nulla. ']);
gQueue.push(new setModalRootInvoker(docAcc, docAcc.parent, gQueue.push(new setModalRootInvoker(docAcc, docAcc.parent,
NS_ERROR_INVALID_ARG)); NS_ERROR_INVALID_ARG));

View File

@ -16,8 +16,8 @@
function doTest() function doTest()
{ {
var iframeDoc = [ getNode("iframe").contentDocument ]; var iframeDoc = [ getNode("iframe").contentDocument ];
testCharacterCount(iframeDoc, 13); testCharacterCount(iframeDoc, 15);
testText(iframeDoc, 0, 13, "outbodyinbody"); testText(iframeDoc, 0, 15, "outbody inbody ");
SimpleTest.finish(); SimpleTest.finish();
} }

View File

@ -58,7 +58,7 @@
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// getTextAtOffset line boundary // getTextAtOffset line boundary
testTextAtOffset(0, BOUNDARY_LINE_START, "line", 0, 4, testTextAtOffset(0, BOUNDARY_LINE_START, "line ", 0, 5,
"hypertext3", kOk, kOk, kOk); "hypertext3", kOk, kOk, kOk);
// XXX: see bug 634202. // XXX: see bug 634202.
@ -122,7 +122,7 @@
<div id="nulltext"></div> <div id="nulltext"></div>
<div id="hypertext">hello <a>friend</a> see <img src="about:blank"></div> <div id="hypertext">hello <a>friend</a> see <img></div>
<div id="hypertext2">hello <a>friend</a> see <input></div> <div id="hypertext2">hello <a>friend</a> see <input></div>
<ol id="list"> <ol id="list">
<li id="listitem">foo</li> <li id="listitem">foo</li>

View File

@ -14,9 +14,9 @@
function doTest() function doTest()
{ {
testTextAtOffset("line_test_1", BOUNDARY_LINE_START, testTextAtOffset("line_test_1", BOUNDARY_LINE_START,
[[0, 5, "Line 1", 0, 6], [[0, 6, "Line 1 ", 0, 7],
[6, 6, "", 6, 6], [7, 7, "", 7, 7],
[7, 13, "Line 3", 7, 13]]); [8, 15, "Line 3 ", 8, 15]]);
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// __h__e__l__l__o__ __m__y__ __f__r__i__e__n__d__ // __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 ] ]); [ [ 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, testTextAtOffset([ "ht_4" ], BOUNDARY_LINE_START,
[ [ 0, 11, "Hello world", 0, 11 ] ]); [ [ 0, 12, "Hello world ", 0, 12 ] ]);
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// list items // list items

View File

@ -42,20 +42,27 @@
[ [ 0, 6, "", 0, 0 ] ]); [ [ 0, 6, "", 0, 0 ] ]);
testTextBeforeOffset(ids, BOUNDARY_WORD_END, testTextBeforeOffset(ids, BOUNDARY_WORD_END,
[ [ 0, 5, "", 0, 0 ], [ [ 0, 5, "", 0, 0 ],
[ 6, 6, "hello", 0, 5 ] [ 6, 6, "hello", 0, 5,
[ [6, "e2", kTodo, kOk, kTodo ] ]
]
]); ]);
testTextAtOffset(ids, BOUNDARY_WORD_START, testTextAtOffset(ids, BOUNDARY_WORD_START,
[ [ 0, 6, "hello ", 0, 6 ] ]); [ [ 0, 6, "hello ", 0, 6 ] ]);
testTextAtOffset(ids, BOUNDARY_WORD_END, testTextAtOffset(ids, BOUNDARY_WORD_END,
[ [ 0, 4, "hello", 0, 5 ], [ [ 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, testTextAfterOffset(ids, BOUNDARY_WORD_START,
[ [ 0, 6, "", 6, 6 ] ]); [ [ 0, 6, "", 6, 6 ] ]);
testTextAfterOffset(ids, BOUNDARY_WORD_END, testTextAfterOffset(ids, BOUNDARY_WORD_END,
[ [ 0, 5, " ", 5, 6 ], [ [ 0, 5, " ", 5, 6,
[ [ 5, "e2", kTodo, kTodo, kOk ] ]
],
[ 6, 6, "", 6, 6 ] [ 6, 6, "", 6, 6 ]
]); ]);
@ -249,7 +256,7 @@
<input id="i2" value="hello "/> <input id="i2" value="hello "/>
<pre><div id="d2">hello </div></pre> <pre><div id="d2">hello </div></pre>
<div id="e2" contenteditable="true" style='white-space:pre'>hello </div> <div id="e2" contenteditable="true">hello </div>
<textarea id="t2">hello </textarea> <textarea id="t2">hello </textarea>
<input id="i6" value="hello all"/> <input id="i6" value="hello all"/>

View File

@ -49,14 +49,14 @@
}, },
{ {
role: ROLE_TEXT_LEAF, role: ROLE_TEXT_LEAF,
name: "Hello3" name: "Hello3 "
}, },
{ {
role: ROLE_PARAGRAPH, role: ROLE_PARAGRAPH,
children: [ children: [
{ {
role: ROLE_TEXT_LEAF, role: ROLE_TEXT_LEAF,
name: "Hello4" name: "Hello4 "
} }
] ]
} }
@ -71,7 +71,7 @@
children: [ children: [
{ {
role: ROLE_TEXT_LEAF, role: ROLE_TEXT_LEAF,
name: "helllo" name: "helllo "
}, },
{ {
role: ROLE_PARAGRAPH, role: ROLE_PARAGRAPH,
@ -84,7 +84,7 @@
}, },
{ {
role: ROLE_TEXT_LEAF, role: ROLE_TEXT_LEAF,
name: "hello" name: "hello "
} }
] ]
}; };

View File

@ -1159,7 +1159,7 @@ void
FragmentOrElement::GetTextContentInternal(nsAString& aTextContent, FragmentOrElement::GetTextContentInternal(nsAString& aTextContent,
ErrorResult& aError) ErrorResult& aError)
{ {
if (!nsContentUtils::GetNodeTextContent(this, true, aTextContent, fallible)) { if(!nsContentUtils::GetNodeTextContent(this, true, aTextContent, fallible)) {
aError.Throw(NS_ERROR_OUT_OF_MEMORY); aError.Throw(NS_ERROR_OUT_OF_MEMORY);
} }
} }

View File

@ -35,9 +35,6 @@
#include "mozilla/Telemetry.h" #include "mozilla/Telemetry.h"
#include "mozilla/Likely.h" #include "mozilla/Likely.h"
#include "nsCSSFrameConstructor.h" #include "nsCSSFrameConstructor.h"
#include "nsStyleStruct.h"
#include "nsStyleStructInlines.h"
#include "nsComputedDOMStyle.h"
using namespace mozilla; using namespace mozilla;
using namespace mozilla::dom; using namespace mozilla::dom;
@ -3196,236 +3193,3 @@ nsRange::ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* 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<nsStyleContext> 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<nsGenericDOMDataNode*>(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<nsGenericDOMDataNode*>(mEndParent.get());
AppendTransformedText(result, t, 0, mEndOffset);
}
// Do not flush trailing line breaks! Required breaks at the end of the text
// are suppressed.
}

View File

@ -212,8 +212,6 @@ public:
bool aFlushLayout = true); bool aFlushLayout = true);
already_AddRefed<DOMRectList> GetClientRects(bool aClampToEdge = true, already_AddRefed<DOMRectList> GetClientRects(bool aClampToEdge = true,
bool aFlushLayout = true); bool aFlushLayout = true);
void GetInnerTextNoFlush(mozilla::dom::DOMString& aValue,
mozilla::ErrorResult& aError);
nsINode* GetParentObject() const { return mOwner; } nsINode* GetParentObject() const { return mOwner; }
virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override final; virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override final;

View File

@ -84,8 +84,6 @@
#include "nsITextControlElement.h" #include "nsITextControlElement.h"
#include "mozilla/dom/Element.h" #include "mozilla/dom/Element.h"
#include "HTMLFieldSetElement.h" #include "HTMLFieldSetElement.h"
#include "nsTextNode.h"
#include "HTMLBRElement.h"
#include "HTMLMenuElement.h" #include "HTMLMenuElement.h"
#include "nsDOMMutationObserver.h" #include "nsDOMMutationObserver.h"
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
@ -106,7 +104,6 @@
#include "nsGlobalWindow.h" #include "nsGlobalWindow.h"
#include "mozilla/dom/HTMLBodyElement.h" #include "mozilla/dom/HTMLBodyElement.h"
#include "imgIContainer.h" #include "imgIContainer.h"
#include "nsComputedDOMStyle.h"
using namespace mozilla; using namespace mozilla;
using namespace mozilla::dom; using namespace mozilla::dom;
@ -3297,105 +3294,3 @@ nsGenericHTMLElement::NewURIFromString(const nsAString& aURISpec,
return NS_OK; return NS_OK;
} }
void
nsGenericHTMLElement::GetInnerText(mozilla::dom::DOMString& aValue,
mozilla::ErrorResult& aError)
{
if (!GetPrimaryFrame(Flush_Layout)) {
RefPtr<nsStyleContext> sc =
nsComputedDOMStyle::GetStyleContextForElementNoFlush(this, nullptr, nullptr);
if (!sc || sc->StyleDisplay()->mDisplay == NS_STYLE_DISPLAY_NONE ||
!GetCurrentDoc()) {
GetTextContentInternal(aValue, aError);
return;
}
}
RefPtr<nsRange> range;
nsresult rv = nsRange::CreateRange(static_cast<nsINode*>(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<nsIContent> 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<nsINode> 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 <br>, so just skip the \r
++s;
}
if (s == end || *s == '\r' || *s == '\n') {
if (!str.IsEmpty()) {
RefPtr<nsTextNode> textContent =
new nsTextNode(NodeInfo()->NodeInfoManager());
textContent->SetText(str, true);
AppendChildTo(textContent, true);
}
if (s == end) {
break;
}
str.SetLength(0);
already_AddRefed<mozilla::dom::NodeInfo> ni =
NodeInfo()->NodeInfoManager()->GetNodeInfo(nsGkAtoms::br,
nullptr, kNameSpaceID_XHTML, nsIDOMNode::ELEMENT_NODE);
RefPtr<HTMLBRElement> br = new HTMLBRElement(ni);
AppendChildTo(br, true);
} else {
str.Append(*s);
}
++s;
}
mb.NodesAdded();
}

View File

@ -241,9 +241,6 @@ public:
mScrollgrab = aValue; 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.) * Determine whether an attribute is an event (onclick, etc.)
* @param aName the attribute * @param aName the attribute

View File

@ -22,9 +22,6 @@ interface HTMLElement : Element {
[Constant] [Constant]
readonly attribute DOMStringMap dataset; readonly attribute DOMStringMap dataset;
[GetterThrows, Pure]
attribute DOMString innerText;
// microdata // microdata
[SetterThrows, Pure] [SetterThrows, Pure]
attribute boolean itemScope; attribute boolean itemScope;

View File

@ -82,7 +82,7 @@ TestIterator()
"[4] Check mapping of original to skipped for " << i; "[4] Check mapping of original to skipped for " << i;
} }
int32_t expectOriginal1[] = uint32_t expectOriginal1[] =
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, { 0, 1, 2, 3, 4, 5, 6, 7, 8,
10, 11, 12, 13, 14, 15, 16, 17, 18, 10, 11, 12, 13, 14, 15, 16, 17, 18,
20, 21, 22, 23, 24, 25, 26, 27, 28 }; 20, 21, 22, 23, 24, 25, 26, 27, 28 };
@ -136,7 +136,7 @@ TestIterator()
"[9] Check mapping of original to skipped for " << i; "[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++) { for (uint32_t i = 0; i < mozilla::ArrayLength(expectOriginal2); i++) {
EXPECT_TRUE(iter2.ConvertSkippedToOriginal(i) == expectOriginal2[i]) << EXPECT_TRUE(iter2.ConvertSkippedToOriginal(i) == expectOriginal2[i]) <<

View File

@ -235,7 +235,7 @@ public:
return GetSkippedOffset(); return GetSkippedOffset();
} }
int32_t ConvertSkippedToOriginal(uint32_t aSkippedStringOffset) uint32_t ConvertSkippedToOriginal(int32_t aSkippedStringOffset)
{ {
SetSkippedOffset(aSkippedStringOffset); SetSkippedOffset(aSkippedStringOffset);
return GetOriginalOffset(); return GetOriginalOffset();

View File

@ -1893,36 +1893,26 @@ public:
virtual bool CanContinueTextRun() const = 0; virtual bool CanContinueTextRun() const = 0;
/** /**
* Computes an approximation of the rendered text of the frame and its * Append the rendered text to the passed-in string.
* continuations. Returns nothing for non-text frames.
* The appended text will often not contain all the whitespace from source, * The appended text will often not contain all the whitespace from source,
* depending on CSS white-space processing. * depending on whether the CSS rule "white-space: pre" is active for this frame.
* if aEndOffset goes past end, use the text up to the string's end. * 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. * Call this on the primary frame for a text node.
* aStartOffset and aEndOffset can be content offsets or offsets in the * @param aAppendToString String to append text to, or null if text should not be returned
* rendered text, depending on aOffsetType. * @param aSkipChars if aSkipIter is non-null, this must also be non-null.
* Returns a string, as well as offsets identifying the start of the text * This gets used as backing data for the iterator so it should outlive the iterator.
* within the rendered text for the whole node, and within the text content * @param aSkipIter Where to fill in the gfxSkipCharsIterator info, or null if not needed by caller
* of the node. * @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 { virtual nsresult GetRenderedText(nsAString* aAppendToString = nullptr,
nsString mString; gfxSkipChars* aSkipChars = nullptr,
uint32_t mOffsetWithinNodeRenderedText; gfxSkipCharsIterator* aSkipIter = nullptr,
int32_t mOffsetWithinNodeText; uint32_t aSkippedStartOffset = 0,
RenderedText() : mOffsetWithinNodeRenderedText(0), uint32_t aSkippedMaxLength = UINT32_MAX)
mOffsetWithinNodeText(0) {} { return NS_ERROR_NOT_IMPLEMENTED; }
};
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(); }
/** /**
* Returns true if the frame contains any non-collapsed characters. * Returns true if the frame contains any non-collapsed characters.

View File

@ -3975,8 +3975,9 @@ a11y::AccType
nsTextFrame::AccessibleType() nsTextFrame::AccessibleType()
{ {
if (IsEmpty()) { if (IsEmpty()) {
RenderedText text = GetRenderedText(); nsAutoString renderedWhitespace;
if (text.mString.IsEmpty()) { GetRenderedText(&renderedWhitespace, nullptr, nullptr, 0, 1);
if (renderedWhitespace.IsEmpty()) {
return a11y::eNoType; return a11y::eNoType;
} }
} }
@ -4089,6 +4090,13 @@ public:
virtual void AddInlinePrefISize(nsRenderingContext *aRenderingContext, virtual void AddInlinePrefISize(nsRenderingContext *aRenderingContext,
InlinePrefISizeData *aData) override; 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: protected:
explicit nsContinuingTextFrame(nsStyleContext* aContext) : nsTextFrame(aContext) {} explicit nsContinuingTextFrame(nsStyleContext* aContext) : nsTextFrame(aContext) {}
nsIFrame* mPrevContinuation; nsIFrame* mPrevContinuation;
@ -8874,151 +8882,80 @@ static char16_t TransformChar(nsTextFrame* aFrame, const nsStyleText* aStyle,
return aChar; return aChar;
} }
static bool nsresult nsTextFrame::GetRenderedText(nsAString* aAppendToString,
LineEndsInHardLineBreak(nsTextFrame* aFrame) gfxSkipChars* aSkipChars,
gfxSkipCharsIterator* aSkipIter,
uint32_t aSkippedStartOffset,
uint32_t aSkippedMaxLength)
{ {
nsIFrame* lineContainer = FindLineContainer(aFrame); // The handling of aSkippedStartOffset and aSkippedMaxLength could be more efficient...
nsBlockFrame* block = do_QueryFrame(lineContainer); gfxSkipChars skipChars;
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;
nsTextFrame* textFrame; nsTextFrame* textFrame;
const nsTextFragment* textFrag = mContent->GetText(); const nsTextFragment* textFrag = mContent->GetText();
uint32_t offsetInRenderedString = 0; uint32_t keptCharsLength = 0;
bool haveOffsets = false; uint32_t validCharsLength = 0;
// Build skipChars and copy text, for each text frame in this continuation block
for (textFrame = this; textFrame; for (textFrame = this; textFrame;
textFrame = static_cast<nsTextFrame*>(textFrame->GetNextContinuation())) { textFrame = static_cast<nsTextFrame*>(textFrame->GetNextContinuation())) {
// For each text frame continuation in this block ...
if (textFrame->GetStateBits() & NS_FRAME_IS_DIRTY) { 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; break;
} }
// Ensure the text run and grab the gfxSkipCharsIterator for it // Ensure the text run and grab the gfxSkipCharsIterator for it
gfxSkipCharsIterator iter = gfxSkipCharsIterator iter =
textFrame->EnsureTextRun(nsTextFrame::eInflated); textFrame->EnsureTextRun(nsTextFrame::eInflated);
if (!textFrame->mTextRun) { if (!textFrame->mTextRun)
break; return NS_ERROR_FAILURE;
}
gfxSkipCharsIterator tmpIter = iter;
// Skip to the start of the text run, past ignored chars at start of line // Skip to the start of the text run, past ignored chars at start of line
TrimmedOffsets trimmedOffsets = textFrame->GetTrimmedOffsets(textFrag, // XXX In the future we may decide to trim extra spaces before a hard line
textFrame->IsAtEndOfLine() && LineEndsInHardLineBreak(textFrame)); // break, in which case we need to accurately detect those sitations and
bool trimmedSignificantNewline = // call GetTrimmedOffsets() with true to trim whitespace at the line's end
trimmedOffsets.GetEnd() < GetContentEnd() && TrimmedOffsets trimmedContentOffsets = textFrame->GetTrimmedOffsets(textFrag, false);
HasSignificantTerminalNewline(); int32_t startOfLineSkipChars = trimmedContentOffsets.mStart - textFrame->mContentOffset;
uint32_t skippedToRenderedStringOffset = offsetInRenderedString - if (startOfLineSkipChars > 0) {
tmpIter.ConvertOriginalToSkipped(trimmedOffsets.mStart); skipChars.SkipChars(startOfLineSkipChars);
uint32_t nextOffsetInRenderedString = iter.SetOriginalOffset(trimmedContentOffsets.mStart);
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<int32_t>(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<uint32_t>(INT32_MAX, aEndOffset);
}
trimmedOffsets.mStart = std::max<uint32_t>(trimmedOffsets.mStart,
startOffset);
trimmedOffsets.mLength = std::min<uint32_t>(trimmedOffsets.GetEnd(),
endOffset) - trimmedOffsets.mStart;
if (trimmedOffsets.mLength <= 0) {
offsetInRenderedString = nextOffsetInRenderedString;
continue;
} }
// Keep and copy the appropriate chars withing the caller's requested range
const nsStyleText* textStyle = textFrame->StyleText(); const nsStyleText* textStyle = textFrame->StyleText();
iter.SetOriginalOffset(trimmedOffsets.mStart); while (iter.GetOriginalOffset() < trimmedContentOffsets.GetEnd() &&
while (iter.GetOriginalOffset() < trimmedOffsets.GetEnd()) { keptCharsLength < aSkippedMaxLength) {
char16_t ch = textFrag->CharAt(iter.GetOriginalOffset()); // For each original char from content text
if (iter.IsOriginalCharSkipped()) { if (iter.IsOriginalCharSkipped() || ++validCharsLength <= aSkippedStartOffset) {
if (ch == CH_SHY) { skipChars.SkipChar();
// We should preserve soft hyphens. They can't be transformed.
result.mString.Append(ch);
}
} else { } else {
result.mString.Append( ++keptCharsLength;
skipChars.KeepChar();
if (aAppendToString) {
aAppendToString->Append(
TransformChar(textFrame, textStyle, textFrame->mTextRun, TransformChar(textFrame, textStyle, textFrame->mTextRun,
iter.GetSkippedOffset(), ch)); iter.GetSkippedOffset(),
textFrag->CharAt(iter.GetOriginalOffset())));
}
} }
iter.AdvanceOriginal(1); iter.AdvanceOriginal(1);
} }
if (keptCharsLength >= aSkippedMaxLength) {
if (trimmedSignificantNewline && GetContentEnd() <= endOffset) { break; // Already past the end, don't build string or gfxSkipCharsIter anymore
// A significant newline was trimmed off (we must be }
// white-space:pre-line). Put it back. }
result.mString.Append('\n');
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) { return NS_OK;
result.mOffsetWithinNodeText = textFrag->GetLength();
result.mOffsetWithinNodeRenderedText = offsetInRenderedString;
}
return result;
} }
nsIAtom* nsIAtom*

View File

@ -261,10 +261,11 @@ public:
nscoord mDeltaWidth; nscoord mDeltaWidth;
}; };
TrimOutput TrimTrailingWhiteSpace(nsRenderingContext* aRC); TrimOutput TrimTrailingWhiteSpace(nsRenderingContext* aRC);
virtual RenderedText GetRenderedText(uint32_t aStartOffset = 0, virtual nsresult GetRenderedText(nsAString* aString = nullptr,
uint32_t aEndOffset = UINT32_MAX, gfxSkipChars* aSkipChars = nullptr,
TextOffsetType aOffsetType = gfxSkipCharsIterator* aSkipIter = nullptr,
TextOffsetType::OFFSETS_IN_CONTENT_TEXT) override; uint32_t aSkippedStartOffset = 0,
uint32_t aSkippedMaxLength = UINT32_MAX) override;
nsOverflowAreas RecomputeOverflow(nsIFrame* aBlockFrame); nsOverflowAreas RecomputeOverflow(nsIFrame* aBlockFrame);

View File

@ -18713,14 +18713,6 @@
"path": "infrastructure/failing-test.html", "path": "infrastructure/failing-test.html",
"url": "/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", "path": "js/behaviours/SetPrototypeOf-window.html",
"url": "/js/behaviours/SetPrototypeOf-window.html" "url": "/js/behaviours/SetPrototypeOf-window.html"

View File

@ -1,322 +0,0 @@
testText("<div>abc", "abc", "Simplest possible test");
/**** white-space:normal ****/
testText("<div> abc", "abc", "Leading whitespace removed");
testText("<div>abc ", "abc", "Trailing whitespace removed");
testText("<div>abc def", "abc def", "Internal whitespace compressed");
testText("<div>abc\ndef", "abc def", "\\n converted to space");
testText("<div>abc\rdef", "abc def", "\\r converted to space");
testText("<div>abc\tdef", "abc def", "\\t converted to space");
testText("<div>abc <br>def", "abc\ndef", "Trailing whitespace before hard line break removed");
/**** <pre> ****/
testText("<pre> abc", " abc", "Leading whitespace preserved");
testText("<pre>abc ", "abc ", "Trailing whitespace preserved");
testText("<pre>abc def", "abc def", "Internal whitespace preserved");
testText("<pre>abc\ndef", "abc\ndef", "\\n preserved");
testText("<pre>abc\rdef", "abc\ndef", "\\r converted to newline");
testText("<pre>abc\tdef", "abc\tdef", "\\t preserved");
/**** <div style="white-space:pre"> ****/
testText("<div style='white-space:pre'> abc", " abc", "Leading whitespace preserved");
testText("<div style='white-space:pre'>abc ", "abc ", "Trailing whitespace preserved");
testText("<div style='white-space:pre'>abc def", "abc def", "Internal whitespace preserved");
testText("<div style='white-space:pre'>abc\ndef", "abc\ndef", "\\n preserved");
testText("<div style='white-space:pre'>abc\rdef", "abc\ndef", "\\r converted to newline");
testText("<div style='white-space:pre'>abc\tdef", "abc\tdef", "\\t preserved");
/**** <span style="white-space:pre"> ****/
testText("<span style='white-space:pre'> abc", " abc", "Leading whitespace preserved");
testText("<span style='white-space:pre'>abc ", "abc ", "Trailing whitespace preserved");
testText("<span style='white-space:pre'>abc def", "abc def", "Internal whitespace preserved");
testText("<span style='white-space:pre'>abc\ndef", "abc\ndef", "\\n preserved");
testText("<span style='white-space:pre'>abc\rdef", "abc\ndef", "\\r converted to newline");
testText("<span style='white-space:pre'>abc\tdef", "abc\tdef", "\\t preserved");
/**** <div style="white-space:pre-line"> ****/
testText("<div style='white-space:pre-line'> abc", "abc", "Leading whitespace removed");
testText("<div style='white-space:pre-line'>abc ", "abc", "Trailing whitespace removed");
testText("<div style='white-space:pre-line'>abc def", "abc def", "Internal whitespace collapsed");
testText("<div style='white-space:pre-line'>abc\ndef", "abc\ndef", "\\n preserved");
testText("<div style='white-space:pre-line'>abc\rdef", "abc\ndef", "\\r converted to newline");
testText("<div style='white-space:pre-line'>abc\tdef", "abc def", "\\t converted to space");
/**** Collapsing whitespace across element boundaries ****/
testText("<div><span>abc </span> def", "abc def", "Whitespace collapses across element boundaries");
testText("<div><span>abc </span><span></span> def", "abc def", "Whitespace collapses across element boundaries");
testText("<div><span>abc </span><span style='white-space:pre'></span> def", "abc def", "Whitespace collapses across element boundaries");
/**** Soft line breaks ****/
testText("<div style='width:0'>abc def", "abc def", "Soft line breaks ignored");
/**** first-line/first-letter ****/
testText("<div class='first-line-uppercase' style='width:0'>abc def", "ABC def", "::first-line styles applied");
testText("<div class='first-letter-uppercase' style='width:0'>abc def", "Abc def", "::first-letter styles applied");
testText("<div class='first-letter-float' style='width:0'>abc def", "abc def", "::first-letter float ignored");
/**** &nbsp; ****/
testText("<div>&nbsp;", "\xA0", "&nbsp; preserved");
/**** display:none ****/
testText("<div style='display:none'>abc", "abc", "display:none container");
testText("<div style='display:none'>abc def", "abc def", "No whitespace compression in display:none container");
testText("<div style='display:none'> abc def ", " abc def ", "No removal of leading/trailing whitespace in display:none container");
testText("<div>123<span style='display:none'>abc", "123", "display:none child not rendered");
/**** display:contents ****/
if (CSS.supports("display", "contents")) {
testText("<div style='display:contents'>abc", "abc", "display:contents container");
testText("<div><div style='display:contents'>abc", "abc", "display:contents container");
testText("<div>123<span style='display:contents'>abc", "123abc", "display:contents rendered");
testText("<div>123<span style='display:contents'>abc", "123abc", "display:contents rendered");
testText("<div style='display:contents'> ", "", "display:contents not processed via textContent");
testText("<div><div style='display:contents'> ", "", "display:contents not processed via textContent");
}
/**** visibility:hidden ****/
testText("<div style='visibility:hidden'>abc", "", "visibility:hidden container");
testText("<div>123<span style='visibility:hidden'>abc", "123", "visibility:hidden child not rendered");
testText("<div style='visibility:hidden'>123<span style='visibility:visible'>abc", "abc", "visibility:visible child rendered");
/**** visibility:collapse ****/
testText("<table><tbody style='visibility:collapse'><tr><td>abc", "", "visibility:collapse row-group");
testText("<table><tr style='visibility:collapse'><td>abc", "", "visibility:collapse row");
testText("<table><tr><td style='visibility:collapse'>abc", "", "visibility:collapse cell");
testText("<table><tbody style='visibility:collapse'><tr><td style='visibility:visible'>abc", "abc",
"visibility:collapse row-group with visible cell");
testText("<table><tr style='visibility:collapse'><td style='visibility:visible'>abc", "abc",
"visibility:collapse row with visible cell");
testText("<div style='display:flex'><span style='visibility:collapse'>1</span><span>2</span></div>",
"2", "visibility:collapse honored on flex item");
testText("<div style='display:grid'><span style='visibility:collapse'>1</span><span>2</span></div>",
"2", "visibility:collapse honored on grid item");
/**** opacity:0 ****/
testText("<div style='opacity:0'>abc", "abc", "opacity:0 container");
testText("<div style='opacity:0'>abc def", "abc def", "Whitespace compression in opacity:0 container");
testText("<div style='opacity:0'> abc def ", "abc def", "Remove leading/trailing whitespace in opacity:0 container");
testText("<div>123<span style='opacity:0'>abc", "123abc", "opacity:0 child rendered");
/**** generated content ****/
testText("<div class='before'>", "", "Generated content not included");
testText("<div><div class='before'>", "", "Generated content on child not included");
/**** innerText on replaced elements ****/
testText("<button>abc", "abc", "<button> contents preserved");
testText("<fieldset>abc", "abc", "<fieldset> contents preserved");
testText("<fieldset><legend>abc", "abc", "<fieldset> <legend> contents preserved");
testText("<input type='text' value='abc'>", "", "<input> contents ignored");
testText("<textarea>abc", "", "<textarea> contents ignored");
testText("<select size='1'><option>abc</option><option>def", "", "<select size='1'> contents ignored");
testText("<select size='2'><option>abc</option><option>def", "", "<select size='2'> contents ignored");
testText("<select size='1'><option id='target'>abc</option><option>def", "", "<select size='1'> contents ignored");
testText("<select size='2'><option id='target'>abc</option><option>def", "", "<select size='2'> contents ignored");
testText("<iframe>abc", "", "<iframe> contents ignored");
testText("<iframe><div id='target'>abc", "", "<iframe> contents ignored");
testText("<iframe src='data:text/html,abc'>", "","<iframe> subdocument ignored");
testText("<audio style='display:block'>abc", "", "<audio> contents ignored");
testText("<audio style='display:block'><source id='target' class='poke' style='display:block'>", "", "<audio> contents ignored");
testText("<audio style='display:block'><source id='target' class='poke' style='display:none'>", "abc", "<audio> contents ok if display:none");
testText("<video>abc", "", "<video> contents ignored");
testText("<video style='display:block'><source id='target' class='poke' style='display:block'>", "", "<video> contents ignored");
testText("<video style='display:block'><source id='target' class='poke' style='display:none'>", "abc", "<video> contents ok if display:none");
testText("<canvas>abc", "", "<canvas> contents ignored");
testText("<canvas><div id='target'>abc", "", "<canvas><div id='target'> contents ignored");
testText("<img alt='abc'>", "", "<img> alt text ignored");
testText("<img src='about:blank' class='poke'>", "", "<img> contents ignored");
/**** innerText on replaced element children ****/
testText("<div><button>abc", "abc", "<button> contents preserved");
testText("<div><fieldset>abc", "abc", "<fieldset> contents preserved");
testText("<div><fieldset><legend>abc", "abc", "<fieldset> <legend> contents preserved");
testText("<div><input type='text' value='abc'>", "", "<input> contents ignored");
testText("<div><textarea>abc", "", "<textarea> contents ignored");
testText("<div><select size='1'><option>abc</option><option>def", "", "<select size='1'> contents ignored");
testText("<div><select size='2'><option>abc</option><option>def", "", "<select size='2'> contents ignored");
testText("<div><iframe>abc", "", "<iframe> contents ignored");
testText("<div><iframe src='data:text/html,abc'>", ""," <iframe> subdocument ignored");
testText("<div><audio>abc", "", "<audio> contents ignored");
testText("<div><video>abc", "", "<video> contents ignored");
testText("<div><canvas>abc", "", "<canvas> contents ignored");
testText("<div><img alt='abc'>", "", "<img> alt text ignored");
/**** Lines around blocks ****/
testText("<div>123<div>abc</div>def", "123\nabc\ndef", "Newline at block boundary");
testText("<div>123<span style='display:block'>abc</span>def", "123\nabc\ndef", "Newline at display:block boundary");
testText("<div>abc<div></div>def", "abc\ndef", "Empty block induces single line break");
testText("<div>abc<div></div><div></div>def", "abc\ndef", "Consecutive empty blocks ignored");
testText("<div><p>abc", "abc", "No blank lines around <p> alone");
testText("<div><p>abc</p> ", "abc", "No blank lines around <p> followed by only collapsible whitespace");
testText("<div> <p>abc</p>", "abc", "No blank lines around <p> preceded by only collapsible whitespace");
testText("<div><p>abc<p>def", "abc\n\ndef", "Blank line between consecutive <p>s");
testText("<div><p>abc</p> <p>def", "abc\n\ndef", "Blank line between consecutive <p>s separated only by collapsible whitespace");
testText("<div><p>abc</p><div></div><p>def", "abc\n\ndef", "Blank line between consecutive <p>s separated only by empty block");
testText("<div><p>abc</p><div>123</div><p>def", "abc\n\n123\n\ndef", "Blank lines between <p>s separated by non-empty block");
testText("<div>abc<div><p>123</p></div>def", "abc\n\n123\n\ndef", "Blank lines around a <p> in its own block");
testText("<div>abc<p>def", "abc\n\ndef", "Blank line before <p>");
testText("<div><p>abc</p>def", "abc\n\ndef", "Blank line after <p>");
testText("<div><p>abc<p></p><p></p><p>def", "abc\n\ndef", "One blank line between <p>s, ignoring empty <p>s");
testText("<div style='visibility:hidden'><p><span style='visibility:visible'>abc</span></p>\n<div style='visibility:visible'>def</div>",
"abc\ndef", "Invisible <p> doesn't induce extra line breaks");
testText("<div>abc<div style='margin:2em'>def", "abc\ndef", "No blank lines around <div> with margin");
testText("<div>123<span style='display:inline-block'>abc</span>def", "123abcdef", "No newlines at display:inline-block boundary");
testText("<div>123<span style='display:inline-block'> abc </span>def", "123abcdef", "Leading/trailing space removal at display:inline-block boundary");
/**** Spans ****/
testText("<div>123<span>abc</span>def", "123abcdef", "<span> boundaries are irrelevant");
testText("<div>123<em>abc</em>def", "123abcdef", "<em> gets no special treatment");
testText("<div>123<b>abc</b>def", "123abcdef", "<b> gets no special treatment");
testText("<div>123<i>abc</i>def", "123abcdef", "<i> gets no special treatment");
testText("<div>123<strong>abc</strong>def", "123abcdef", "<strong> gets no special treatment");
testText("<div>123<tt>abc</tt>def", "123abcdef", "<tt> gets no special treatment");
testText("<div>123<code>abc</code>def", "123abcdef", "<code> gets no special treatment");
/**** Soft hyphen ****/
testText("<div>abc&shy;def", "abc\xADdef", "soft hyphen preserved");
testText("<div style='width:0'>abc&shy;def", "abc\xADdef", "soft hyphen preserved");
/**** Tables ****/
testText("<div><table style='white-space:pre'> <td>abc</td> </table>", "abc", "Ignoring non-rendered table whitespace");
testText("<div><table><tr><td>abc<td>def</table>", "abc\tdef", "Tab-separated table cells");
testText("<div><table><tr><td>abc<td><td>def</table>", "abc\t\tdef", "Tab-separated table cells including empty cells");
testText("<div><table><tr><td>abc<td><td></table>", "abc\t\t", "Tab-separated table cells including trailing empty cells");
testText("<div><table><tr><td>abc<tr><td>def</table>", "abc\ndef", "Newline-separated table rows");
testText("<div>abc<table><td>def</table>ghi", "abc\ndef\nghi", "Newlines around table");
testText("<div><table style='border-collapse:collapse'><tr><td>abc<td>def</table>", "abc\tdef",
"Tab-separated table cells in a border-collapse table");
testText("<div><table><tfoot>x</tfoot><tbody>y</tbody></table>", "xy", "tfoot not reordered");
testText("<table><tfoot><tr><td>footer</tfoot><thead><tr><td style='visibility:collapse'>thead</thead><tbody><tr><td>tbody</tbody></table>",
"footer\n\ntbody", "");
/**** Table captions ****/
testText("<div><table><tr><td>abc<caption>def</caption></table>", "abc\ndef", "Newline between cells and caption");
/**** display:table ****/
testText("<div><div class='table'><span class='cell'>abc</span>\n<span class='cell'>def</span></div>",
"abc\tdef", "Tab-separated table cells");
testText("<div><div class='table'><span class='row'><span class='cell'>abc</span></span>\n<span class='row'><span class='cell'>def</span></span></div>",
"abc\ndef", "Newline-separated table rows");
testText("<div>abc<div class='table'><span class='cell'>def</span></div>ghi", "abc\ndef\nghi", "Newlines around table");
/**** display:inline-table ****/
testText("<div><div class='itable'><span class='cell'>abc</span>\n<span class='cell'>def</span></div>", "abc\tdef", "Tab-separated table cells");
testText("<div><div class='itable'><span class='row'><span class='cell'>abc</span></span>\n<span class='row'><span class='cell'>def</span></span></div>",
"abc\ndef", "Newline-separated table rows");
testText("<div>abc<div class='itable'><span class='cell'>def</span></div>ghi", "abcdefghi", "No newlines around inline-table");
testText("<div>abc<div class='itable'><span class='row'><span class='cell'>def</span></span>\n<span class='row'><span class='cell'>123</span></span></div>ghi",
"abcdef\n123ghi", "Single newline in two-row inline-table");
/**** Lists ****/
testText("<div><ol><li>abc", "abc", "<ol> list items get no special treatment");
testText("<div><ul><li>abc", "abc", "<ul> list items get no special treatment");
/**** Misc elements ****/
testText("<div><script style='display:block'>abc", "abc", "display:block <script> is rendered");
testText("<div><style style='display:block'>abc", "abc", "display:block <style> is rendered");
testText("<div><noscript style='display:block'>abc", "", "display:block <noscript> is not rendered (it's not parsed!)");
testText("<div><template style='display:block'>abc", "",
"display:block <template> contents are not rendered (the contents are in a different document)");
testText("<div>abc<br>def", "abc\ndef", "<br> induces line break");
testText("<div>abc<br>", "abc\n", "<br> induces line break even at end of block");
testText("<div><br class='poke'>", "\n", "<br> content ignored");
testText("<div>abc<hr>def", "abc\ndef", "<hr> induces line break");
testText("<div>abc<hr><hr>def", "abc\ndef", "<hr><hr> induces just one line break");
testText("<div>abc<hr><hr><hr>def", "abc\ndef", "<hr><hr><hr> induces just one line break");
testText("<div><hr class='poke'>", "abc", "<hr> content rendered");
testText("<div>abc<!--comment-->def", "abcdef", "comment ignored");
/**** text-transform ****/
testText("<div><div style='text-transform:uppercase'>abc", "ABC", "text-transform is applied");
/**** block-in-inline ****/
testText("<div>abc<span>123<div>456</div>789</span>def", "abc123\n456\n789def", "block-in-inline doesn't add unnecessary newlines");
/**** floats ****/
testText("<div>abc<div style='float:left'>123</div>def", "abc\n123\ndef", "floats induce a block boundary");
testText("<div>abc<span style='float:left'>123</span>def", "abc\n123\ndef", "floats induce a block boundary");
/**** position ****/
testText("<div>abc<div style='position:absolute'>123</div>def", "abc\n123\ndef", "position:absolute induces a block boundary");
testText("<div>abc<span style='position:absolute'>123</span>def", "abc\n123\ndef", "position:absolute induces a block boundary");
testText("<div>abc<div style='position:relative'>123</div>def", "abc\n123\ndef", "position:relative has no effect");
testText("<div>abc<span style='position:relative'>123</span>def", "abc123def", "position:relative has no effect");
/**** text-overflow:ellipsis ****/
testText("<div style='overflow:hidden'>abc", "abc", "overflow:hidden ignored");
// XXX Chrome skips content with width:0 or height:0 and overflow:hidden;
// should we spec that?
testText("<div style='width:0; overflow:hidden'>abc", "abc", "overflow:hidden ignored even with zero width");
testText("<div style='height:0; overflow:hidden'>abc", "abc", "overflow:hidden ignored even with zero height");
testText("<div style='width:0; overflow:hidden; text-overflow:ellipsis'>abc", "abc", "text-overflow:ellipsis ignored");
/**** Support on non-HTML elements ****/
testText("<svg>abc", undefined, "innerText not supported on SVG elements");
testText("<math>abc", undefined, "innerText not supported on MathML elements");
/**** Ruby ****/
testText("<div><ruby>abc<rp>(</rp><rt>def</rt><rp>)</rp></ruby>", "abc(def)", "<rp> rendered");
testText("<div><rp>abc</rp>", "abc", "Lone <rp> rendered");
testText("<div><rp style='visibility:hidden'>abc</rp>", "", "visibility:hidden <rp> not rendered");
testText("<div><rp> abc </rp>", " abc ", "Lone <rp> rendered without whitespace trimming");
testText("<div><rp style='display:block'>abc</rp>def", "abc\ndef", "display:block <rp> induces line breaks");
testText("<div><rp style='display:block'> abc </rp>def", " abc \ndef", "display:block <rp> induces line breaks but doesn't trim whitespace");
// XXX this is not desirable but the spec currently requires it.
testText("<div><select class='poke-rp'></select>", "abc", "<rp> in a replaced element still renders");
/**** Shadow DOM ****/
if ("createShadowRoot" in document.body) {
testText("<div class='shadow'>", "", "Shadow DOM contents ignored");
testText("<div><div class='shadow'>", "", "Shadow DOM contents ignored");
}
/**** Flexbox ****/
if (CSS.supports('display', 'flex')) {
testText("<div style='display:flex'><div style='order:1'>1</div><div>2</div></div>",
"1\n2", "CSS 'order' property ignored");
testText("<div style='display:flex'><span>1</span><span>2</span></div>",
"1\n2", "Flex items blockified");
}
/**** Grid ****/
if (CSS.supports('display', 'grid')) {
testText("<div style='display:grid'><div style='order:1'>1</div><div>2</div></div>",
"1\n2", "CSS 'order' property ignored");
testText("<div style='display:grid'><span>1</span><span>2</span></div>",
"1\n2", "Grid items blockified");
}

View File

@ -1,46 +0,0 @@
<!DOCTYPE html>
<title>innerText getter test</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
.before::before { content:'abc'; }
.table { display:table; }
.itable { display:inline-table; }
.row { display:table-row; }
.cell { display:table-cell; }
.first-line-uppercase::first-line { text-transform:uppercase; }
.first-letter-uppercase::first-letter { text-transform:uppercase; }
.first-letter-float::first-letter { float:left; }
</style>
<div id="container"></div>
<script>
function testText(html, expectedPlain, msg) {
test(function() {
container.innerHTML = html;
var e = document.getElementById('target');
if (!e) {
e = container.firstChild;
}
var pokes = document.getElementsByClassName('poke');
for (var i = 0; i < pokes.length; ++i) {
pokes[i].textContent = 'abc';
}
pokes = document.getElementsByClassName('poke-rp');
for (var i = 0; i < pokes.length; ++i) {
var rp = document.createElement("rp");
rp.textContent = "abc";
pokes[i].appendChild(rp);
}
var shadows = document.getElementsByClassName('shadow');
for (var i = 0; i < shadows.length; ++i) {
var s = shadows[i].createShadowRoot();
s.textContent = 'abc';
}
while (e && e.nodeType != Node.ELEMENT_NODE) {
e = e.nextSibling;
}
assert_equals(e.innerText, expectedPlain);
}, msg);
}
</script>
<script src="getter-tests.js"></script>

View File

@ -1,24 +0,0 @@
testText("<div>", "abc", "abc", "Simplest possible test");
testHTML("<div>", "abc\ndef", "abc<br>def", "Newlines convert to <br> in non-white-space:pre elements");
testHTML("<pre>", "abc\ndef", "abc<br>def", "Newlines convert to <br> in <pre> element");
testHTML("<div style='white-space:pre'>", "abc\ndef", "abc<br>def", "Newlines convert to <br> in white-space:pre element");
testHTML("<div>", "abc\rdef", "abc<br>def", "CRs convert to <br> in non-white-space:pre elements");
testHTML("<pre>", "abc\rdef", "abc<br>def", "CRs convert to <br> in <pre> element");
testHTML("<div>", "abc\r\ndef", "abc<br>def", "Newline/CR pair converts to <br> in non-white-space:pre element");
testHTML("<div>", "abc\n\ndef", "abc<br><br>def", "Newline/newline pair converts to two <br>s in non-white-space:pre element");
testHTML("<div>", "abc\r\rdef", "abc<br><br>def", "CR/CR pair converts to two <br>s in non-white-space:pre element");
testHTML("<div style='white-space:pre'>", "abc\rdef", "abc<br>def", "CRs convert to <br> in white-space:pre element");
testText("<div>", "abc<def", "abc<def", "< preserved");
testText("<div>", "abc>def", "abc>def", "> preserved");
testText("<div>", "abc&", "abc&", "& preserved");
testText("<div>", "abc\"def", "abc\"def", "\" preserved");
testText("<div>", "abc\'def", "abc\'def", "\' preserved");
testHTML("<svg>", "abc", "", "innerText not supported on SVG elements");
testHTML("<math>", "abc", "", "innerText not supported on MathML elements");
testText("<div>", "abc\0def", "abc\0def", "Null characters preserved");
testText("<div>", "abc\tdef", "abc\tdef", "Tabs preserved");
testText("<div>", " abc", " abc", "Leading whitespace preserved");
testText("<div>", "abc ", "abc ", "Trailing whitespace preserved");
testText("<div>", "abc def", "abc def", "Whitespace not compressed");
testHTML("<div>abc\n\n", "abc", "abc", "Existing text deleted");
testHTML("<div><br>", "abc", "abc", "Existing <br> deleted");

View File

@ -1,32 +0,0 @@
<!DOCTYPE html>
<title>innerText setter test</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="container"></div>
<script>
function setupTest(context, plain) {
container.innerHTML = context;
var e = container.firstChild;
while (e && e.nodeType != Node.ELEMENT_NODE) {
e = e.nextSibling;
}
e.innerText = plain;
return e;
}
function testText(context, plain, expectedText, msg) {
test(function(){
var e = setupTest(context, plain);
assert_not_equals(e.firstChild, null, "Should have a child");
assert_equals(e.firstChild.nodeType, Node.TEXT_NODE, "Child should be a text node");
assert_equals(e.firstChild.nextSibling, null, "Should have only one child");
assert_equals(e.firstChild.data, expectedText);
}, msg);
}
function testHTML(context, plain, expectedHTML, msg) {
test(function(){
var e = setupTest(context, plain);
assert_equals(e.innerHTML, expectedHTML);
}, msg);
}
</script>
<script src="setter-tests.js"></script>