Bug 1361268: Clear nsIDocument::mCachedRootElement *before* removing from doc's child list, rather than *after* removing.
Internally, doRemoveChildAt does quite a bit of cleanup, and that includes some
calls to nsIDocument::GetRootElement(). Before this patch, those calls will
misleadingly return the still-present mCachedRootElement member-var. We'd
really like those invocations to find an empty cached variable, and fall back
to checking the actual updated child list.
MozReview-Commit-ID: 8hhKcWyUVYQ
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -4172,18 +4172,28 @@ nsDocument::RemoveChildAt(uint32_t aInde
return;
}
if (oldKid->IsElement()) {
// Destroy the link map up front before we mess with the child list.
DestroyElementMaps();
}
+ // Preemptively clear mCachedRootElement, since we may be about to remove it
+ // from our child list, and we don't want to return this maybe-obsolete value
+ // from any GetRootElement() calls that happen inside of doRemoveChildAt().
+ // (NOTE: for this to be useful, doRemoveChildAt() must NOT trigger any
+ // GetRootElement() calls until after it's removed the child from mChildren.
+ // Any call before that point would restore this soon-to-be-obsolete cached
+ // answer, and our clearing here would be fruitless.)
+ mCachedRootElement = nullptr;
doRemoveChildAt(aIndex, aNotify, oldKid, mChildren);
- mCachedRootElement = nullptr;
+ MOZ_ASSERT(mCachedRootElement != oldKid,
+ "Stale pointer in mCachedRootElement, after we tried to clear it "
+ "(maybe somebody called GetRootElement() too early?)");
}
void
nsDocument::EnsureOnDemandBuiltInUASheet(StyleSheet* aSheet)
{
if (mOnDemandBuiltInUASheets.Contains(aSheet)) {
return;
}
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -1898,16 +1898,20 @@ nsINode::Append(const Sequence<OwningNod
AppendChild(*node, aRv);
}
void
nsINode::doRemoveChildAt(uint32_t aIndex, bool aNotify,
nsIContent* aKid, nsAttrAndChildArray& aChildArray)
{
+ // NOTE: This function must not trigger any calls to
+ // nsIDocument::GetRootElement() calls until *after* it has removed aKid from
+ // aChildArray. Any calls before then could potentially restore a stale
+ // value for our cached root element, per note in nsDocument::RemoveChildAt().
NS_PRECONDITION(aKid && aKid->GetParentNode() == this &&
aKid == GetChildAt(aIndex) &&
IndexOf(aKid) == (int32_t)aIndex, "Bogus aKid");
nsMutationGuard::DidMutate();
mozAutoDocUpdate updateBatch(GetComposedDoc(), UPDATE_CONTENT_MODEL, aNotify);
nsIContent* previousSibling = aKid->GetPreviousSibling();