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() ?
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");

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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,

View File

@ -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));

View File

@ -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();
}

View File

@ -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 @@
<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>
<ol id="list">
<li id="listitem">foo</li>

View File

@ -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

View File

@ -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 @@
<input id="i2" value="hello "/>
<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>
<input id="i6" value="hello all"/>

View File

@ -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 "
}
]
};

View File

@ -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);
}
}

View File

@ -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<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);
already_AddRefed<DOMRectList> 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<JSObject*> aGivenProto) override final;

View File

@ -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<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;
}
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

View File

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

View File

@ -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]) <<

View File

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

View File

@ -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.

View File

@ -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<nsTextFrame*>(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<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;
// 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*

View File

@ -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);

View File

@ -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"

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>