mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
Add support for element.innerText (getter, setter)
This commit is contained in:
@@ -33,6 +33,9 @@
|
||||
#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;
|
||||
@@ -3131,3 +3134,244 @@ nsRange::ExcludeNonSelectableNodes(nsTArray<nsRefPtr<nsRange>>* aOutRanges)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct InnerTextAccumulator
|
||||
{
|
||||
explicit 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 aCh)
|
||||
{
|
||||
Append(nsAutoString(aCh));
|
||||
}
|
||||
void Append(const nsAString& aString)
|
||||
{
|
||||
if (aString.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
FlushLineBreaks();
|
||||
mString.Append(aString);
|
||||
}
|
||||
void AddRequiredLineBreakCount(int8_t aCount)
|
||||
{
|
||||
mRequiredLineBreakCount = std::max(mRequiredLineBreakCount, aCount);
|
||||
}
|
||||
|
||||
nsAString& mString;
|
||||
int8_t mRequiredLineBreakCount;
|
||||
};
|
||||
|
||||
static bool
|
||||
IsVisibleAndNotInReplacedElement(nsIFrame* aFrame)
|
||||
{
|
||||
if (!aFrame || !aFrame->StyleVisibility()->IsVisible()) {
|
||||
return false;
|
||||
}
|
||||
for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) {
|
||||
if (f->IsFrameOfType(nsIFrame::eReplaced) &&
|
||||
!f->GetContent()->IsHTML(nsGkAtoms::button)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ElementIsVisible(Element* aElement)
|
||||
{
|
||||
if (!aElement) {
|
||||
return false;
|
||||
}
|
||||
nsRefPtr<nsStyleContext> sc = nsComputedDOMStyle::GetStyleContextForElement(
|
||||
aElement, 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* States for tree traversal. AT_NODE means that we are about to enter
|
||||
* the current DOM node. AFTER_NODE means that we have just finished traversing
|
||||
* the children of the current DOM node and are about to apply any
|
||||
* "after processing the node's children" steps before we finish visiting
|
||||
* the node.
|
||||
*/
|
||||
enum TreeTraversalState {
|
||||
AT_NODE,
|
||||
AFTER_NODE
|
||||
};
|
||||
|
||||
static int8_t
|
||||
GetRequiredInnerTextLineBreakCount(nsIFrame* aFrame)
|
||||
{
|
||||
if (aFrame->GetContent()->IsHTML(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(DOMString& aValue, ErrorResult& aError,
|
||||
nsIContent* aStartParent, uint32_t aStartOffset,
|
||||
nsIContent* aEndParent, uint32_t aEndOffset)
|
||||
{
|
||||
InnerTextAccumulator result(aValue);
|
||||
nsIContent* currentNode = aStartParent;
|
||||
TreeTraversalState currentState = AFTER_NODE;
|
||||
if (aStartParent->IsNodeOfType(nsINode::eTEXT)) {
|
||||
auto t = static_cast<nsGenericDOMDataNode*>(aStartParent);
|
||||
if (aStartParent == aEndParent) {
|
||||
AppendTransformedText(result, t, aStartOffset, aEndOffset);
|
||||
return;
|
||||
}
|
||||
AppendTransformedText(result, t, aStartOffset, t->TextLength());
|
||||
} else {
|
||||
if (uint32_t(aStartOffset) < aStartParent->GetChildCount()) {
|
||||
currentNode = aStartParent->GetChildAt(aStartOffset);
|
||||
currentState = AT_NODE;
|
||||
}
|
||||
}
|
||||
|
||||
nsIContent* endNode = aEndParent;
|
||||
TreeTraversalState endState = AFTER_NODE;
|
||||
if (aEndParent->IsNodeOfType(nsINode::eTEXT)) {
|
||||
endState = AT_NODE;
|
||||
} else {
|
||||
if (uint32_t(aEndOffset) < aEndParent->GetChildCount()) {
|
||||
endNode = aEndParent->GetChildAt(aEndOffset);
|
||||
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()->IsHTML(nsGkAtoms::rp) &&
|
||||
ElementIsVisible(currentNode->GetParent()->AsElement())) {
|
||||
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->IsHTML(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 (aEndParent->IsNodeOfType(nsINode::eTEXT)) {
|
||||
nsGenericDOMDataNode* t = static_cast<nsGenericDOMDataNode*>(aEndParent);
|
||||
AppendTransformedText(result, t, 0, aEndOffset);
|
||||
}
|
||||
// Do not flush trailing line breaks! Required breaks at the end of the text
|
||||
// are suppressed.
|
||||
}
|
||||
@@ -242,6 +242,12 @@ public:
|
||||
bool aFlushLayout = true);
|
||||
already_AddRefed<DOMRectList> GetClientRects(bool aClampToEdge = true,
|
||||
bool aFlushLayout = true);
|
||||
static void GetInnerTextNoFlush(mozilla::dom::DOMString& aValue,
|
||||
mozilla::ErrorResult& aError,
|
||||
nsIContent* aStartParent,
|
||||
uint32_t aStartOffset,
|
||||
nsIContent* aEndParent,
|
||||
uint32_t aEndOffset);
|
||||
|
||||
nsINode* GetParentObject() const { return mOwner; }
|
||||
virtual JSObject* WrapObject(JSContext* cx) override final;
|
||||
|
||||
@@ -84,6 +84,8 @@
|
||||
#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"
|
||||
@@ -104,6 +106,7 @@
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "mozilla/dom/HTMLBodyElement.h"
|
||||
#include "imgIContainer.h"
|
||||
#include "nsComputedDOMStyle.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
@@ -3220,3 +3223,77 @@ nsGenericHTMLElement::NewURIFromString(const nsAutoString& aURISpec,
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsGenericHTMLElement::GetInnerText(mozilla::dom::DOMString& aValue,
|
||||
mozilla::ErrorResult& aError)
|
||||
{
|
||||
if (!GetPrimaryFrame(Flush_Layout)) {
|
||||
nsRefPtr<nsStyleContext> sc =
|
||||
nsComputedDOMStyle::GetStyleContextForElementNoFlush(this, nullptr, nullptr);
|
||||
if (!sc || sc->StyleDisplay()->mDisplay == NS_STYLE_DISPLAY_NONE ||
|
||||
!IsInComposedDoc()) {
|
||||
GetTextContentInternal(aValue, aError);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nsRange::GetInnerTextNoFlush(aValue, aError, this, 0, this, GetChildCount());
|
||||
}
|
||||
|
||||
void
|
||||
nsGenericHTMLElement::SetInnerText(const nsAString& aValue)
|
||||
{
|
||||
// Fire DOMNodeRemoved mutation events before we do anything else.
|
||||
nsCOMPtr<nsIContent> kungFuDeathGrip;
|
||||
|
||||
// Batch possible DOMSubtreeModified events.
|
||||
mozAutoSubtreeModified subtree(OwnerDoc(), nullptr);
|
||||
FireNodeRemovedForChildren();
|
||||
|
||||
// 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()) {
|
||||
nsRefPtr<nsTextNode> textContent =
|
||||
new nsTextNode(NodeInfo()->NodeInfoManager());
|
||||
textContent->SetText(str, true);
|
||||
AppendChildTo(textContent, true);
|
||||
}
|
||||
if (s == end) {
|
||||
break;
|
||||
}
|
||||
str.Truncate();
|
||||
already_AddRefed<mozilla::dom::NodeInfo> ni =
|
||||
NodeInfo()->NodeInfoManager()->GetNodeInfo(nsGkAtoms::br,
|
||||
nullptr, kNameSpaceID_XHTML, nsIDOMNode::ELEMENT_NODE);
|
||||
nsRefPtr<HTMLBRElement> br = new HTMLBRElement(ni);
|
||||
AppendChildTo(br, true);
|
||||
} else {
|
||||
str.Append(*s);
|
||||
}
|
||||
++s;
|
||||
}
|
||||
|
||||
mb.NodesAdded();
|
||||
}
|
||||
|
||||
@@ -233,6 +233,9 @@ 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
|
||||
|
||||
@@ -22,6 +22,9 @@ interface HTMLElement : Element {
|
||||
[Constant]
|
||||
readonly attribute DOMStringMap dataset;
|
||||
|
||||
[GetterThrows, Pure]
|
||||
attribute DOMString innerText;
|
||||
|
||||
// microdata
|
||||
[SetterThrows, Pure]
|
||||
attribute boolean itemScope;
|
||||
|
||||
Reference in New Issue
Block a user