--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -2042,19 +2042,22 @@ nsINode::RemoveChildNode(nsIContent* aKi
}
aKid->UnbindFromTree();
}
// When replacing, aRefChild is the content being replaced; when
// inserting it's the content before which we're inserting. In the
// latter case it may be null.
+//
+// If aRv is a failure after this call, the insertion should not happen.
static
-bool IsAllowedAsChild(nsIContent* aNewChild, nsINode* aParent,
- bool aIsReplace, nsINode* aRefChild)
+void EnsureAllowedAsChild(nsIContent* aNewChild, nsINode* aParent,
+ bool aIsReplace, nsINode* aRefChild,
+ ErrorResult& aRv)
{
MOZ_ASSERT(aNewChild, "Must have new child");
MOZ_ASSERT_IF(aIsReplace, aRefChild);
MOZ_ASSERT(aParent);
MOZ_ASSERT(aParent->IsDocument() ||
aParent->IsDocumentFragment() ||
aParent->IsElement(),
"Nodes that are not documents, document fragments or elements "
@@ -2068,143 +2071,164 @@ bool IsAllowedAsChild(nsIContent* aNewCh
((aNewChild->GetFirstChild() ||
// HTML template elements and ShadowRoot hosts need
// to be checked to ensure that they are not inserted into
// the hosted content.
aNewChild->NodeInfo()->NameAtom() == nsGkAtoms::_template ||
aNewChild->GetShadowRoot()) &&
nsContentUtils::ContentIsHostIncludingDescendantOf(aParent,
aNewChild))) {
- return false;
+ aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
+ return;
}
// The allowed child nodes differ for documents and elements
switch (aNewChild->NodeType()) {
case nsINode::COMMENT_NODE :
case nsINode::PROCESSING_INSTRUCTION_NODE :
// OK in both cases
- return true;
+ return;
case nsINode::TEXT_NODE :
case nsINode::CDATA_SECTION_NODE :
case nsINode::ENTITY_REFERENCE_NODE :
// Allowed under Elements and DocumentFragments
- return aParent->NodeType() != nsINode::DOCUMENT_NODE;
+ if (aParent->NodeType() == nsINode::DOCUMENT_NODE) {
+ aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
+ }
+ return;
case nsINode::ELEMENT_NODE :
{
if (!aParent->IsDocument()) {
// Always ok to have elements under other elements or document fragments
- return true;
+ return;
}
nsIDocument* parentDocument = aParent->AsDocument();
Element* rootElement = parentDocument->GetRootElement();
if (rootElement) {
// Already have a documentElement, so this is only OK if we're
// replacing it.
- return aIsReplace && rootElement == aRefChild;
+ if (!aIsReplace || rootElement != aRefChild) {
+ aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
+ }
+ return;
}
// We don't have a documentElement yet. Our one remaining constraint is
// that the documentElement must come after the doctype.
if (!aRefChild) {
// Appending is just fine.
- return true;
+ return;
}
nsIContent* docTypeContent = parentDocument->GetDoctype();
if (!docTypeContent) {
// It's all good.
- return true;
+ return;
}
int32_t doctypeIndex = aParent->ComputeIndexOf(docTypeContent);
int32_t insertIndex = aParent->ComputeIndexOf(aRefChild);
// Now we're OK in the following two cases only:
// 1) We're replacing something that's not before the doctype
// 2) We're inserting before something that comes after the doctype
- return aIsReplace ? (insertIndex >= doctypeIndex) :
+ bool ok = aIsReplace ? (insertIndex >= doctypeIndex) :
insertIndex > doctypeIndex;
+ if (!ok) {
+ aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
+ }
+ return;
}
case nsINode::DOCUMENT_TYPE_NODE :
{
if (!aParent->IsDocument()) {
// doctypes only allowed under documents
- return false;
+ aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
+ return;
}
nsIDocument* parentDocument = aParent->AsDocument();
nsIContent* docTypeContent = parentDocument->GetDoctype();
if (docTypeContent) {
// Already have a doctype, so this is only OK if we're replacing it
- return aIsReplace && docTypeContent == aRefChild;
+ if (!aIsReplace || docTypeContent != aRefChild) {
+ aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
+ }
+ return;
}
// We don't have a doctype yet. Our one remaining constraint is
// that the doctype must come before the documentElement.
Element* rootElement = parentDocument->GetRootElement();
if (!rootElement) {
// It's all good
- return true;
+ return;
}
if (!aRefChild) {
// Trying to append a doctype, but have a documentElement
- return false;
+ aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
+ return;
}
int32_t rootIndex = aParent->ComputeIndexOf(rootElement);
int32_t insertIndex = aParent->ComputeIndexOf(aRefChild);
// Now we're OK if and only if insertIndex <= rootIndex. Indeed, either
// we end up replacing aRefChild or we end up before it. Either one is
// ok as long as aRefChild is not after rootElement.
- return insertIndex <= rootIndex;
+ if (insertIndex > rootIndex) {
+ aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
+ }
+ return;
}
case nsINode::DOCUMENT_FRAGMENT_NODE :
{
// Note that for now we only allow nodes inside document fragments if
// they're allowed inside elements. If we ever change this to allow
// doctype nodes in document fragments, we'll need to update this code.
// Also, there's a version of this code in ReplaceOrInsertBefore. If you
// change this code, change that too.
if (!aParent->IsDocument()) {
// All good here
- return true;
+ return;
}
bool sawElement = false;
for (nsIContent* child = aNewChild->GetFirstChild();
child;
child = child->GetNextSibling()) {
if (child->IsElement()) {
if (sawElement) {
// Can't put two elements into a document
- return false;
+ aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
+ return;
}
sawElement = true;
}
- // If we can put this content at the the right place, we might be ok;
+ // If we can put this content at the right place, we might be ok;
// if not, we bail out.
- if (!IsAllowedAsChild(child, aParent, aIsReplace, aRefChild)) {
- return false;
+ EnsureAllowedAsChild(child, aParent, aIsReplace, aRefChild, aRv);
+ if (aRv.Failed()) {
+ return;
}
}
// Everything in the fragment checked out ok, so we can stick it in here
- return true;
+ return;
}
default:
/*
* aNewChild is of invalid type.
*/
break;
}
- return false;
+ aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
}
void
nsINode::EnsurePreInsertionValidity(nsINode& aNewChild, nsINode* aRefChild,
ErrorResult& aError)
{
EnsurePreInsertionValidity1(aNewChild, aRefChild, aError);
if (aError.Failed()) {
@@ -2233,20 +2257,17 @@ nsINode::EnsurePreInsertionValidity2(boo
// This is anonymous content. Don't allow its insertion
// anywhere, since it might have UnbindFromTree calls coming
// its way.
aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return;
}
// Make sure that the inserted node is allowed as a child of its new parent.
- if (!IsAllowedAsChild(newContent, this, aReplace, aRefChild)) {
- aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
- return;
- }
+ EnsureAllowedAsChild(newContent, this, aReplace, aRefChild, aError);
}
nsINode*
nsINode::ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild,
nsINode* aRefChild, ErrorResult& aError)
{
// XXXbz I wish I could assert that nsContentUtils::IsSafeToRunScript() so we
// could rely on scriptblockers going out of scope to actually run XBL
@@ -2367,26 +2388,30 @@ nsINode::ReplaceOrInsertBefore(bool aRep
aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
return nullptr;
}
// And verify that newContent is still allowed as our child.
if (aNewChild == aRefChild) {
// We've already removed aRefChild. So even if we were doing a replace,
// now we're doing a simple insert before nodeToInsertBefore.
- if (!IsAllowedAsChild(newContent, this, false, nodeToInsertBefore)) {
- aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
+ EnsureAllowedAsChild(newContent, this, false, nodeToInsertBefore, aError);
+ if (aError.Failed()) {
return nullptr;
}
} else {
- if ((aRefChild && aRefChild->GetParent() != this) ||
- !IsAllowedAsChild(newContent, this, aReplace, aRefChild)) {
+ if (aRefChild && aRefChild->GetParent() != this) {
aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
return nullptr;
}
+ EnsureAllowedAsChild(newContent, this, aReplace, aRefChild, aError);
+ if (aError.Failed()) {
+ return nullptr;
+ }
+
// And recompute nodeToInsertBefore, just in case.
if (aReplace) {
nodeToInsertBefore = aRefChild->GetNextSibling();
} else {
nodeToInsertBefore = aRefChild ? aRefChild->AsContent() : nullptr;
}
}
}
@@ -2442,17 +2467,17 @@ nsINode::ReplaceOrInsertBefore(bool aRep
if (fragChildren->ElementAt(i)->GetParentNode()) {
aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
return nullptr;
}
}
// Note that unlike the single-element case above, none of our kids can
// be aRefChild, so we can always pass through aReplace in the
- // IsAllowedAsChild checks below and don't have to worry about whether
+ // EnsureAllowedAsChild checks below and don't have to worry about whether
// recomputing nodeToInsertBefore is OK.
// Verify that our aRefChild is still sensible
if (aRefChild && aRefChild->GetParent() != this) {
aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
return nullptr;
}
@@ -2460,33 +2485,33 @@ nsINode::ReplaceOrInsertBefore(bool aRep
if (aReplace) {
nodeToInsertBefore = aRefChild->GetNextSibling();
} else {
// If aRefChild has 'this' as a parent, it must be an nsIContent.
nodeToInsertBefore = aRefChild ? aRefChild->AsContent() : nullptr;
}
// And verify that newContent is still allowed as our child. Sadly, we
- // need to reimplement the relevant part of IsAllowedAsChild() because
+ // need to reimplement the relevant part of EnsureAllowedAsChild() because
// now our nodes are in an array and all. If you change this code,
// change the code there.
if (IsDocument()) {
bool sawElement = false;
for (uint32_t i = 0; i < count; ++i) {
nsIContent* child = fragChildren->ElementAt(i);
if (child->IsElement()) {
if (sawElement) {
// No good
aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
return nullptr;
}
sawElement = true;
}
- if (!IsAllowedAsChild(child, this, aReplace, aRefChild)) {
- aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
+ EnsureAllowedAsChild(child, this, aReplace, aRefChild, aError);
+ if (aError.Failed()) {
return nullptr;
}
}
}
}
}
mozAutoDocUpdate batch(GetComposedDoc(), true);