Bug 806506 - Part 2: Implement innerHTML on ShadowRoot. r=mrbkap
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
#include "nsIAtom.h"
|
||||
#include "nsINodeInfo.h"
|
||||
#include "nsIDocumentInlines.h"
|
||||
#include "nsIDocumentEncoder.h"
|
||||
#include "nsIDOMNodeList.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIContentIterator.h"
|
||||
@@ -73,6 +74,8 @@
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsTextFragment.h"
|
||||
#include "nsContentCID.h"
|
||||
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsIWebNavigation.h"
|
||||
@@ -121,6 +124,7 @@
|
||||
#include "mozilla/CORSMode.h"
|
||||
|
||||
#include "mozilla/dom/ShadowRoot.h"
|
||||
#include "mozilla/dom/HTMLTemplateElement.h"
|
||||
|
||||
#include "nsStyledElement.h"
|
||||
|
||||
@@ -1902,6 +1906,771 @@ FragmentOrElement::IndexOf(const nsINode* aPossibleChild) const
|
||||
return mAttrsAndChildren.IndexOfChild(aPossibleChild);
|
||||
}
|
||||
|
||||
// Try to keep the size of StringBuilder close to a jemalloc bucket size.
|
||||
#define STRING_BUFFER_UNITS 1020
|
||||
|
||||
namespace {
|
||||
|
||||
// We put StringBuilder in the anonymous namespace to prevent anything outside
|
||||
// this file from accidentally being linked against it.
|
||||
|
||||
class StringBuilder
|
||||
{
|
||||
private:
|
||||
class Unit
|
||||
{
|
||||
public:
|
||||
Unit() : mAtom(nullptr), mType(eUnknown), mLength(0)
|
||||
{
|
||||
MOZ_COUNT_CTOR(StringBuilder::Unit);
|
||||
}
|
||||
~Unit()
|
||||
{
|
||||
if (mType == eString || mType == eStringWithEncode) {
|
||||
delete mString;
|
||||
}
|
||||
MOZ_COUNT_DTOR(StringBuilder::Unit);
|
||||
}
|
||||
|
||||
enum Type
|
||||
{
|
||||
eUnknown,
|
||||
eAtom,
|
||||
eString,
|
||||
eStringWithEncode,
|
||||
eLiteral,
|
||||
eTextFragment,
|
||||
eTextFragmentWithEncode,
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
nsIAtom* mAtom;
|
||||
const char* mLiteral;
|
||||
nsAutoString* mString;
|
||||
const nsTextFragment* mTextFragment;
|
||||
};
|
||||
Type mType;
|
||||
uint32_t mLength;
|
||||
};
|
||||
public:
|
||||
StringBuilder() : mLast(MOZ_THIS_IN_INITIALIZER_LIST()), mLength(0)
|
||||
{
|
||||
MOZ_COUNT_CTOR(StringBuilder);
|
||||
}
|
||||
|
||||
~StringBuilder()
|
||||
{
|
||||
MOZ_COUNT_DTOR(StringBuilder);
|
||||
}
|
||||
|
||||
void Append(nsIAtom* aAtom)
|
||||
{
|
||||
Unit* u = AddUnit();
|
||||
u->mAtom = aAtom;
|
||||
u->mType = Unit::eAtom;
|
||||
uint32_t len = aAtom->GetLength();
|
||||
u->mLength = len;
|
||||
mLength += len;
|
||||
}
|
||||
|
||||
template<int N>
|
||||
void Append(const char (&aLiteral)[N])
|
||||
{
|
||||
Unit* u = AddUnit();
|
||||
u->mLiteral = aLiteral;
|
||||
u->mType = Unit::eLiteral;
|
||||
uint32_t len = N - 1;
|
||||
u->mLength = len;
|
||||
mLength += len;
|
||||
}
|
||||
|
||||
template<int N>
|
||||
void Append(char (&aLiteral)[N])
|
||||
{
|
||||
Unit* u = AddUnit();
|
||||
u->mLiteral = aLiteral;
|
||||
u->mType = Unit::eLiteral;
|
||||
uint32_t len = N - 1;
|
||||
u->mLength = len;
|
||||
mLength += len;
|
||||
}
|
||||
|
||||
void Append(const nsAString& aString)
|
||||
{
|
||||
Unit* u = AddUnit();
|
||||
u->mString = new nsAutoString(aString);
|
||||
u->mType = Unit::eString;
|
||||
uint32_t len = aString.Length();
|
||||
u->mLength = len;
|
||||
mLength += len;
|
||||
}
|
||||
|
||||
void Append(nsAutoString* aString)
|
||||
{
|
||||
Unit* u = AddUnit();
|
||||
u->mString = aString;
|
||||
u->mType = Unit::eString;
|
||||
uint32_t len = aString->Length();
|
||||
u->mLength = len;
|
||||
mLength += len;
|
||||
}
|
||||
|
||||
void AppendWithAttrEncode(nsAutoString* aString, uint32_t aLen)
|
||||
{
|
||||
Unit* u = AddUnit();
|
||||
u->mString = aString;
|
||||
u->mType = Unit::eStringWithEncode;
|
||||
u->mLength = aLen;
|
||||
mLength += aLen;
|
||||
}
|
||||
|
||||
void Append(const nsTextFragment* aTextFragment)
|
||||
{
|
||||
Unit* u = AddUnit();
|
||||
u->mTextFragment = aTextFragment;
|
||||
u->mType = Unit::eTextFragment;
|
||||
uint32_t len = aTextFragment->GetLength();
|
||||
u->mLength = len;
|
||||
mLength += len;
|
||||
}
|
||||
|
||||
void AppendWithEncode(const nsTextFragment* aTextFragment, uint32_t aLen)
|
||||
{
|
||||
Unit* u = AddUnit();
|
||||
u->mTextFragment = aTextFragment;
|
||||
u->mType = Unit::eTextFragmentWithEncode;
|
||||
u->mLength = aLen;
|
||||
mLength += aLen;
|
||||
}
|
||||
|
||||
bool ToString(nsAString& aOut)
|
||||
{
|
||||
if (!aOut.SetCapacity(mLength, fallible_t())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (StringBuilder* current = this; current; current = current->mNext) {
|
||||
uint32_t len = current->mUnits.Length();
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
Unit& u = current->mUnits[i];
|
||||
switch (u.mType) {
|
||||
case Unit::eAtom:
|
||||
aOut.Append(nsDependentAtomString(u.mAtom));
|
||||
break;
|
||||
case Unit::eString:
|
||||
aOut.Append(*(u.mString));
|
||||
break;
|
||||
case Unit::eStringWithEncode:
|
||||
EncodeAttrString(*(u.mString), aOut);
|
||||
break;
|
||||
case Unit::eLiteral:
|
||||
aOut.AppendASCII(u.mLiteral, u.mLength);
|
||||
break;
|
||||
case Unit::eTextFragment:
|
||||
u.mTextFragment->AppendTo(aOut);
|
||||
break;
|
||||
case Unit::eTextFragmentWithEncode:
|
||||
EncodeTextFragment(u.mTextFragment, aOut);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unknown unit type?");
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
Unit* AddUnit()
|
||||
{
|
||||
if (mLast->mUnits.Length() == STRING_BUFFER_UNITS) {
|
||||
new StringBuilder(this);
|
||||
}
|
||||
return mLast->mUnits.AppendElement();
|
||||
}
|
||||
|
||||
StringBuilder(StringBuilder* aFirst)
|
||||
: mLast(nullptr), mLength(0)
|
||||
{
|
||||
MOZ_COUNT_CTOR(StringBuilder);
|
||||
aFirst->mLast->mNext = this;
|
||||
aFirst->mLast = this;
|
||||
}
|
||||
|
||||
void EncodeAttrString(const nsAutoString& aValue, nsAString& aOut)
|
||||
{
|
||||
const PRUnichar* c = aValue.BeginReading();
|
||||
const PRUnichar* end = aValue.EndReading();
|
||||
while (c < end) {
|
||||
switch (*c) {
|
||||
case '"':
|
||||
aOut.AppendLiteral(""");
|
||||
break;
|
||||
case '&':
|
||||
aOut.AppendLiteral("&");
|
||||
break;
|
||||
case 0x00A0:
|
||||
aOut.AppendLiteral(" ");
|
||||
break;
|
||||
default:
|
||||
aOut.Append(*c);
|
||||
break;
|
||||
}
|
||||
++c;
|
||||
}
|
||||
}
|
||||
|
||||
void EncodeTextFragment(const nsTextFragment* aValue, nsAString& aOut)
|
||||
{
|
||||
uint32_t len = aValue->GetLength();
|
||||
if (aValue->Is2b()) {
|
||||
const PRUnichar* data = aValue->Get2b();
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
const PRUnichar c = data[i];
|
||||
switch (c) {
|
||||
case '<':
|
||||
aOut.AppendLiteral("<");
|
||||
break;
|
||||
case '>':
|
||||
aOut.AppendLiteral(">");
|
||||
break;
|
||||
case '&':
|
||||
aOut.AppendLiteral("&");
|
||||
break;
|
||||
case 0x00A0:
|
||||
aOut.AppendLiteral(" ");
|
||||
break;
|
||||
default:
|
||||
aOut.Append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const char* data = aValue->Get1b();
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
const unsigned char c = data[i];
|
||||
switch (c) {
|
||||
case '<':
|
||||
aOut.AppendLiteral("<");
|
||||
break;
|
||||
case '>':
|
||||
aOut.AppendLiteral(">");
|
||||
break;
|
||||
case '&':
|
||||
aOut.AppendLiteral("&");
|
||||
break;
|
||||
case 0x00A0:
|
||||
aOut.AppendLiteral(" ");
|
||||
break;
|
||||
default:
|
||||
aOut.Append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoTArray<Unit, STRING_BUFFER_UNITS> mUnits;
|
||||
nsAutoPtr<StringBuilder> mNext;
|
||||
StringBuilder* mLast;
|
||||
// mLength is used only in the first StringBuilder object in the linked list.
|
||||
uint32_t mLength;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
static void
|
||||
AppendEncodedCharacters(const nsTextFragment* aText, StringBuilder& aBuilder)
|
||||
{
|
||||
uint32_t extraSpaceNeeded = 0;
|
||||
uint32_t len = aText->GetLength();
|
||||
if (aText->Is2b()) {
|
||||
const PRUnichar* data = aText->Get2b();
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
const PRUnichar c = data[i];
|
||||
switch (c) {
|
||||
case '<':
|
||||
extraSpaceNeeded += ArrayLength("<") - 2;
|
||||
break;
|
||||
case '>':
|
||||
extraSpaceNeeded += ArrayLength(">") - 2;
|
||||
break;
|
||||
case '&':
|
||||
extraSpaceNeeded += ArrayLength("&") - 2;
|
||||
break;
|
||||
case 0x00A0:
|
||||
extraSpaceNeeded += ArrayLength(" ") - 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const char* data = aText->Get1b();
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
const unsigned char c = data[i];
|
||||
switch (c) {
|
||||
case '<':
|
||||
extraSpaceNeeded += ArrayLength("<") - 2;
|
||||
break;
|
||||
case '>':
|
||||
extraSpaceNeeded += ArrayLength(">") - 2;
|
||||
break;
|
||||
case '&':
|
||||
extraSpaceNeeded += ArrayLength("&") - 2;
|
||||
break;
|
||||
case 0x00A0:
|
||||
extraSpaceNeeded += ArrayLength(" ") - 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (extraSpaceNeeded) {
|
||||
aBuilder.AppendWithEncode(aText, len + extraSpaceNeeded);
|
||||
} else {
|
||||
aBuilder.Append(aText);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
AppendEncodedAttributeValue(nsAutoString* aValue, StringBuilder& aBuilder)
|
||||
{
|
||||
const PRUnichar* c = aValue->BeginReading();
|
||||
const PRUnichar* end = aValue->EndReading();
|
||||
|
||||
uint32_t extraSpaceNeeded = 0;
|
||||
while (c < end) {
|
||||
switch (*c) {
|
||||
case '"':
|
||||
extraSpaceNeeded += ArrayLength(""") - 2;
|
||||
break;
|
||||
case '&':
|
||||
extraSpaceNeeded += ArrayLength("&") - 2;
|
||||
break;
|
||||
case 0x00A0:
|
||||
extraSpaceNeeded += ArrayLength(" ") - 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
++c;
|
||||
}
|
||||
|
||||
if (extraSpaceNeeded) {
|
||||
aBuilder.AppendWithAttrEncode(aValue, aValue->Length() + extraSpaceNeeded);
|
||||
} else {
|
||||
aBuilder.Append(aValue);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
StartElement(Element* aContent, StringBuilder& aBuilder)
|
||||
{
|
||||
nsIAtom* localName = aContent->Tag();
|
||||
int32_t tagNS = aContent->GetNameSpaceID();
|
||||
|
||||
aBuilder.Append("<");
|
||||
if (aContent->IsHTML() || aContent->IsSVG() || aContent->IsMathML()) {
|
||||
aBuilder.Append(localName);
|
||||
} else {
|
||||
aBuilder.Append(aContent->NodeName());
|
||||
}
|
||||
|
||||
int32_t count = aContent->GetAttrCount();
|
||||
for (int32_t i = count; i > 0;) {
|
||||
--i;
|
||||
const nsAttrName* name = aContent->GetAttrNameAt(i);
|
||||
int32_t attNs = name->NamespaceID();
|
||||
nsIAtom* attName = name->LocalName();
|
||||
|
||||
// Filter out any attribute starting with [-|_]moz
|
||||
nsDependentAtomString attrNameStr(attName);
|
||||
if (StringBeginsWith(attrNameStr, NS_LITERAL_STRING("_moz")) ||
|
||||
StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsAutoString* attValue = new nsAutoString();
|
||||
aContent->GetAttr(attNs, attName, *attValue);
|
||||
|
||||
// Filter out special case of <br type="_moz*"> used by the editor.
|
||||
// Bug 16988. Yuck.
|
||||
if (localName == nsGkAtoms::br && tagNS == kNameSpaceID_XHTML &&
|
||||
attName == nsGkAtoms::type && attNs == kNameSpaceID_None &&
|
||||
StringBeginsWith(*attValue, NS_LITERAL_STRING("_moz"))) {
|
||||
delete attValue;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (MOZ_LIKELY(attNs == kNameSpaceID_None) ||
|
||||
(attNs == kNameSpaceID_XMLNS &&
|
||||
attName == nsGkAtoms::xmlns)) {
|
||||
aBuilder.Append(" ");
|
||||
} else if (attNs == kNameSpaceID_XML) {
|
||||
aBuilder.Append(" xml:");
|
||||
} else if (attNs == kNameSpaceID_XMLNS) {
|
||||
aBuilder.Append(" xmlns:");
|
||||
} else if (attNs == kNameSpaceID_XLink) {
|
||||
aBuilder.Append(" xlink:");
|
||||
} else {
|
||||
nsIAtom* prefix = name->GetPrefix();
|
||||
if (prefix) {
|
||||
aBuilder.Append(" ");
|
||||
aBuilder.Append(prefix);
|
||||
aBuilder.Append(":");
|
||||
}
|
||||
}
|
||||
|
||||
aBuilder.Append(attName);
|
||||
aBuilder.Append("=\"");
|
||||
AppendEncodedAttributeValue(attValue, aBuilder);
|
||||
aBuilder.Append("\"");
|
||||
}
|
||||
|
||||
aBuilder.Append(">");
|
||||
|
||||
/*
|
||||
// Per HTML spec we should append one \n if the first child of
|
||||
// pre/textarea/listing is a textnode and starts with a \n.
|
||||
// But because browsers haven't traditionally had that behavior,
|
||||
// we're not changing our behavior either - yet.
|
||||
if (aContent->IsHTML()) {
|
||||
if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
|
||||
localName == nsGkAtoms::listing) {
|
||||
nsIContent* fc = aContent->GetFirstChild();
|
||||
if (fc &&
|
||||
(fc->NodeType() == nsIDOMNode::TEXT_NODE ||
|
||||
fc->NodeType() == nsIDOMNode::CDATA_SECTION_NODE)) {
|
||||
const nsTextFragment* text = fc->GetText();
|
||||
if (text && text->GetLength() && text->CharAt(0) == PRUnichar('\n')) {
|
||||
aBuilder.Append("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ShouldEscape(nsIContent* aParent)
|
||||
{
|
||||
if (!aParent || !aParent->IsHTML()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static const nsIAtom* nonEscapingElements[] = {
|
||||
nsGkAtoms::style, nsGkAtoms::script, nsGkAtoms::xmp,
|
||||
nsGkAtoms::iframe, nsGkAtoms::noembed, nsGkAtoms::noframes,
|
||||
nsGkAtoms::plaintext,
|
||||
// Per the current spec noscript should be escaped in case
|
||||
// scripts are disabled or if document doesn't have
|
||||
// browsing context. However the latter seems to be a spec bug
|
||||
// and Gecko hasn't traditionally done the former.
|
||||
nsGkAtoms::noscript
|
||||
};
|
||||
static mozilla::BloomFilter<12, nsIAtom> sFilter;
|
||||
static bool sInitialized = false;
|
||||
if (!sInitialized) {
|
||||
sInitialized = true;
|
||||
for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
|
||||
sFilter.add(nonEscapingElements[i]);
|
||||
}
|
||||
}
|
||||
|
||||
nsIAtom* tag = aParent->Tag();
|
||||
if (sFilter.mightContain(tag)) {
|
||||
for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
|
||||
if (tag == nonEscapingElements[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
IsVoidTag(Element* aElement)
|
||||
{
|
||||
if (!aElement->IsHTML()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const nsIAtom* voidElements[] = {
|
||||
nsGkAtoms::area, nsGkAtoms::base, nsGkAtoms::basefont,
|
||||
nsGkAtoms::bgsound, nsGkAtoms::br, nsGkAtoms::col,
|
||||
nsGkAtoms::command, nsGkAtoms::embed, nsGkAtoms::frame,
|
||||
nsGkAtoms::hr, nsGkAtoms::img, nsGkAtoms::input,
|
||||
nsGkAtoms::keygen, nsGkAtoms::link, nsGkAtoms::meta,
|
||||
nsGkAtoms::param, nsGkAtoms::source, nsGkAtoms::track,
|
||||
nsGkAtoms::wbr
|
||||
};
|
||||
|
||||
static mozilla::BloomFilter<12, nsIAtom> sFilter;
|
||||
static bool sInitialized = false;
|
||||
if (!sInitialized) {
|
||||
sInitialized = true;
|
||||
for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
|
||||
sFilter.add(voidElements[i]);
|
||||
}
|
||||
}
|
||||
|
||||
nsIAtom* tag = aElement->Tag();
|
||||
if (sFilter.mightContain(tag)) {
|
||||
for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
|
||||
if (tag == voidElements[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
Serialize(FragmentOrElement* aRoot, bool aDescendentsOnly, nsAString& aOut)
|
||||
{
|
||||
nsINode* current = aDescendentsOnly ?
|
||||
nsNodeUtils::GetFirstChildOfTemplateOrNode(aRoot) : aRoot;
|
||||
|
||||
if (!current) {
|
||||
return true;
|
||||
}
|
||||
|
||||
StringBuilder builder;
|
||||
nsIContent* next;
|
||||
while (true) {
|
||||
bool isVoid = false;
|
||||
switch (current->NodeType()) {
|
||||
case nsIDOMNode::ELEMENT_NODE: {
|
||||
Element* elem = current->AsElement();
|
||||
StartElement(elem, builder);
|
||||
isVoid = IsVoidTag(elem);
|
||||
if (!isVoid &&
|
||||
(next = nsNodeUtils::GetFirstChildOfTemplateOrNode(current))) {
|
||||
current = next;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case nsIDOMNode::TEXT_NODE:
|
||||
case nsIDOMNode::CDATA_SECTION_NODE: {
|
||||
const nsTextFragment* text = static_cast<nsIContent*>(current)->GetText();
|
||||
nsIContent* parent = current->GetParent();
|
||||
if (ShouldEscape(parent)) {
|
||||
AppendEncodedCharacters(text, builder);
|
||||
} else {
|
||||
builder.Append(text);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case nsIDOMNode::COMMENT_NODE: {
|
||||
builder.Append("<!--");
|
||||
builder.Append(static_cast<nsIContent*>(current)->GetText());
|
||||
builder.Append("-->");
|
||||
break;
|
||||
}
|
||||
|
||||
case nsIDOMNode::DOCUMENT_TYPE_NODE: {
|
||||
builder.Append("<!DOCTYPE ");
|
||||
builder.Append(current->NodeName());
|
||||
builder.Append(">");
|
||||
break;
|
||||
}
|
||||
|
||||
case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: {
|
||||
builder.Append("<?");
|
||||
builder.Append(current->NodeName());
|
||||
builder.Append(" ");
|
||||
builder.Append(static_cast<nsIContent*>(current)->GetText());
|
||||
builder.Append(">");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (!isVoid && current->NodeType() == nsIDOMNode::ELEMENT_NODE) {
|
||||
builder.Append("</");
|
||||
nsIContent* elem = static_cast<nsIContent*>(current);
|
||||
if (elem->IsHTML() || elem->IsSVG() || elem->IsMathML()) {
|
||||
builder.Append(elem->Tag());
|
||||
} else {
|
||||
builder.Append(current->NodeName());
|
||||
}
|
||||
builder.Append(">");
|
||||
}
|
||||
isVoid = false;
|
||||
|
||||
if (current == aRoot) {
|
||||
return builder.ToString(aOut);
|
||||
}
|
||||
|
||||
if ((next = current->GetNextSibling())) {
|
||||
current = next;
|
||||
break;
|
||||
}
|
||||
|
||||
current = current->GetParentNode();
|
||||
|
||||
// Template case, if we are in a template's content, then the parent
|
||||
// should be the host template element.
|
||||
if (current->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
|
||||
DocumentFragment* frag = static_cast<DocumentFragment*>(current);
|
||||
nsIContent* fragHost = frag->GetHost();
|
||||
if (fragHost && nsNodeUtils::IsTemplateElement(fragHost)) {
|
||||
current = fragHost;
|
||||
}
|
||||
}
|
||||
|
||||
if (aDescendentsOnly && current == aRoot) {
|
||||
return builder.ToString(aOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FragmentOrElement::GetMarkup(bool aIncludeSelf, nsAString& aMarkup)
|
||||
{
|
||||
aMarkup.Truncate();
|
||||
|
||||
nsIDocument* doc = OwnerDoc();
|
||||
if (IsInHTMLDocument()) {
|
||||
Serialize(this, !aIncludeSelf, aMarkup);
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString contentType;
|
||||
doc->GetContentType(contentType);
|
||||
bool tryToCacheEncoder = !aIncludeSelf;
|
||||
|
||||
nsCOMPtr<nsIDocumentEncoder> docEncoder = doc->GetCachedEncoder();
|
||||
if (!docEncoder) {
|
||||
docEncoder =
|
||||
do_CreateInstance(PromiseFlatCString(
|
||||
nsDependentCString(NS_DOC_ENCODER_CONTRACTID_BASE) +
|
||||
NS_ConvertUTF16toUTF8(contentType)
|
||||
).get());
|
||||
}
|
||||
if (!docEncoder) {
|
||||
// This could be some type for which we create a synthetic document. Try
|
||||
// again as XML
|
||||
contentType.AssignLiteral("application/xml");
|
||||
docEncoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "application/xml");
|
||||
// Don't try to cache the encoder since it would point to a different
|
||||
// contentType once it has been reinitialized.
|
||||
tryToCacheEncoder = false;
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE_VOID(docEncoder);
|
||||
|
||||
uint32_t flags = nsIDocumentEncoder::OutputEncodeBasicEntities |
|
||||
// Output DOM-standard newlines
|
||||
nsIDocumentEncoder::OutputLFLineBreak |
|
||||
// Don't do linebreaking that's not present in
|
||||
// the source
|
||||
nsIDocumentEncoder::OutputRaw |
|
||||
// Only check for mozdirty when necessary (bug 599983)
|
||||
nsIDocumentEncoder::OutputIgnoreMozDirty;
|
||||
|
||||
if (IsEditable()) {
|
||||
nsCOMPtr<Element> elem = do_QueryInterface(this);
|
||||
nsIEditor* editor = elem ? elem->GetEditorInternal() : nullptr;
|
||||
if (editor && editor->OutputsMozDirty()) {
|
||||
flags &= ~nsIDocumentEncoder::OutputIgnoreMozDirty;
|
||||
}
|
||||
}
|
||||
|
||||
DebugOnly<nsresult> rv = docEncoder->NativeInit(doc, contentType, flags);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
if (aIncludeSelf) {
|
||||
docEncoder->SetNativeNode(this);
|
||||
} else {
|
||||
docEncoder->SetNativeContainerNode(this);
|
||||
}
|
||||
rv = docEncoder->EncodeToString(aMarkup);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
if (tryToCacheEncoder) {
|
||||
doc->SetCachedEncoder(docEncoder.forget());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FragmentOrElement::SetInnerHTMLInternal(const nsAString& aInnerHTML, ErrorResult& aError)
|
||||
{
|
||||
FragmentOrElement* target = this;
|
||||
// Handle template case.
|
||||
if (nsNodeUtils::IsTemplateElement(target)) {
|
||||
DocumentFragment* frag =
|
||||
static_cast<HTMLTemplateElement*>(target)->Content();
|
||||
MOZ_ASSERT(frag);
|
||||
target = frag;
|
||||
}
|
||||
|
||||
nsIDocument* doc = target->OwnerDoc();
|
||||
|
||||
// Batch possible DOMSubtreeModified events.
|
||||
mozAutoSubtreeModified subtree(doc, nullptr);
|
||||
|
||||
target->FireNodeRemovedForChildren();
|
||||
|
||||
// Needed when innerHTML is used in combination with contenteditable
|
||||
mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, true);
|
||||
|
||||
// Remove childnodes.
|
||||
uint32_t childCount = target->GetChildCount();
|
||||
nsAutoMutationBatch mb(target, true, false);
|
||||
for (uint32_t i = 0; i < childCount; ++i) {
|
||||
target->RemoveChildAt(0, true);
|
||||
}
|
||||
mb.RemovalDone();
|
||||
|
||||
nsAutoScriptLoaderDisabler sld(doc);
|
||||
|
||||
nsIAtom* contextLocalName = Tag();
|
||||
int32_t contextNameSpaceID = GetNameSpaceID();
|
||||
|
||||
ShadowRoot* shadowRoot = ShadowRoot::FromNode(this);
|
||||
if (shadowRoot) {
|
||||
// Fix up the context to be the host of the ShadowRoot.
|
||||
contextLocalName = shadowRoot->GetHost()->Tag();
|
||||
contextNameSpaceID = shadowRoot->GetHost()->GetNameSpaceID();
|
||||
}
|
||||
|
||||
if (doc->IsHTML()) {
|
||||
int32_t oldChildCount = target->GetChildCount();
|
||||
aError = nsContentUtils::ParseFragmentHTML(aInnerHTML,
|
||||
target,
|
||||
contextLocalName,
|
||||
contextNameSpaceID,
|
||||
doc->GetCompatibilityMode() ==
|
||||
eCompatibility_NavQuirks,
|
||||
true);
|
||||
mb.NodesAdded();
|
||||
// HTML5 parser has notified, but not fired mutation events.
|
||||
nsContentUtils::FireMutationEventsForDirectParsing(doc, target,
|
||||
oldChildCount);
|
||||
} else {
|
||||
nsCOMPtr<nsIDOMDocumentFragment> df;
|
||||
aError = nsContentUtils::CreateContextualFragment(target, aInnerHTML,
|
||||
true,
|
||||
getter_AddRefs(df));
|
||||
nsCOMPtr<nsINode> fragment = do_QueryInterface(df);
|
||||
if (!aError.Failed()) {
|
||||
// Suppress assertion about node removal mutation events that can't have
|
||||
// listeners anyway, because no one has had the chance to register mutation
|
||||
// listeners on the fragment that comes from the parser.
|
||||
nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
|
||||
|
||||
static_cast<nsINode*>(target)->AppendChild(*fragment, aError);
|
||||
mb.NodesAdded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsINode::nsSlots*
|
||||
FragmentOrElement::CreateSlots()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user