diff --git a/content/base/public/nsIDocument.h b/content/base/public/nsIDocument.h index 7cd740cc958..5cbe5e8caf4 100644 --- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -362,7 +362,10 @@ public: /** * Return the root content object for this document. */ - virtual nsIContent *GetRootContent() const = 0; + nsIContent *GetRootContent() const + { + return mRootContent; + } /** * Accessors to the collection of stylesheets owned by this document. @@ -910,6 +913,10 @@ protected: // This is just a weak pointer; the parent document owns its children. nsIDocument* mParentDocument; + // A weak reference to the only child element, or null if no + // such element exists. + nsIContent* mRootContent; + nsCOMPtr mBindingManager; nsNodeInfoManager* mNodeInfoManager; // [STRONG] diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 1a4248cd606..c204803f113 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -748,16 +748,27 @@ nsDocument::~nsDocument() mSubDocuments = nsnull; } - // Destroy link map now so we don't waste time removing - // links one by one - DestroyLinkMap(); + if (mRootContent) { + if (mRootContent->GetCurrentDoc()) { + NS_ASSERTION(mRootContent->GetCurrentDoc() == this, + "Unexpected current doc in root content"); + // The root content still has a pointer back to the document, + // clear the document pointer in all children. + + // Destroy link map now so we don't waste time removing + // links one by one + DestroyLinkMap(); - PRUint32 count = mChildren.ChildCount(); - for (indx = PRInt32(count) - 1; indx >= 0; --indx) { - mChildren.ChildAt(indx)->UnbindFromTree(); - mChildren.RemoveChildAt(indx); + PRUint32 count = mChildren.ChildCount(); + for (indx = PRInt32(count) - 1; indx >= 0; --indx) { + mChildren.ChildAt(indx)->UnbindFromTree(); + mChildren.RemoveChildAt(indx); + } + } } + mRootContent = nsnull; + // Let the stylesheets know we're going away indx = mStyleSheets.Count(); while (--indx >= 0) { @@ -972,6 +983,7 @@ nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup) // links one by one DestroyLinkMap(); + mRootContent = nsnull; PRUint32 count = mChildren.ChildCount(); for (PRInt32 i = PRInt32(count) - 1; i >= 0; i--) { nsCOMPtr content = mChildren.ChildAt(i); @@ -1679,22 +1691,6 @@ nsDocument::FindContentForSubDocument(nsIDocument *aDocument) const return data.mResult; } -nsIContent* -nsDocument::GetRootContent() const -{ - // Loop backwards because any non-elements, such as doctypes and PIs - // are likely to appear before the root element. - PRUint32 i; - for (i = mChildren.ChildCount(); i > 0; --i) { - nsIContent* child = mChildren.ChildAt(i - 1); - if (child->IsContentOfType(nsIContent::eELEMENT)) { - return child; - } - } - - return nsnull; -} - nsIContent * nsDocument::GetChildAt(PRUint32 aIndex) const { @@ -1717,13 +1713,37 @@ nsresult nsDocument::InsertChildAt(nsIContent* aKid, PRUint32 aIndex, PRBool aNotify) { - if (aKid->IsContentOfType(nsIContent::eELEMENT) && GetRootContent()) { - NS_ERROR("Inserting element child when we already have one"); - return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; + if (aKid->IsContentOfType(nsIContent::eELEMENT)) { + if (mRootContent) { + NS_ERROR("Inserting element child when we already have one"); + return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; + } + + mRootContent = aKid; } - return nsGenericElement::doInsertChildAt(aKid, aIndex, aNotify, - nsnull, this, mChildren); + nsresult rv = nsGenericElement::doInsertChildAt(aKid, aIndex, aNotify, + nsnull, this, mChildren); + + if (NS_FAILED(rv) && mRootContent == aKid) { + PRInt32 kidIndex = mChildren.IndexOfChild(aKid); + NS_ASSERTION(kidIndex == -1, + "Error result and still have same root content but it's in " + "our child list?"); + // Check to make sure that we're keeping mRootContent in sync with our + // child list... but really, if kidIndex != -1 we have major problems + // coming up; hence the assert above. This check is just a feeble attempt + // to not die due to mRootContent being bogus. + if (kidIndex == -1) { + mRootContent = nsnull; + } + } + +#ifdef DEBUG + VerifyRootContentState(); +#endif + + return rv; } nsresult @@ -1741,19 +1761,58 @@ nsresult nsDocument::RemoveChildAt(PRUint32 aIndex, PRBool aNotify) { nsCOMPtr oldKid = GetChildAt(aIndex); - if (!oldKid) { - return NS_OK; - } - - if (oldKid->IsContentOfType(nsIContent::eELEMENT)) { - // Destroy the link map up front before we mess with the child list. - DestroyLinkMap(); - } - - return nsGenericElement::doRemoveChildAt(aIndex, aNotify, oldKid, + nsresult rv = NS_OK; + if (oldKid) { + if (oldKid == mRootContent) { + NS_ASSERTION(oldKid->IsContentOfType(nsIContent::eELEMENT), + "Non-element root content?"); + // Destroy the link map up front and null out mRootContent before we mess + // with the child list. Hopefully no one in doRemoveChildAt will compare + // the content being removed to GetRootContent().... Need to do this + // before calling doRemoveChildAt because DOM events might fire while + // we're inside the doInsertChildAt call and want to set a new + // mRootContent; if they do that, setting mRootContent after the + // doRemoveChildAt call would clobber state. If we ever fix the issue of + // DOM events firing at inconvenient times, consider changing the order + // here. Just make sure we DestroyLinkMap() before unbinding the + // content. + DestroyLinkMap(); + mRootContent = nsnull; + } + + rv = nsGenericElement::doRemoveChildAt(aIndex, aNotify, oldKid, nsnull, this, mChildren); + if (NS_FAILED(rv) && mChildren.IndexOfChild(oldKid) != -1) { + mRootContent = oldKid; + } + } + +#ifdef DEBUG + VerifyRootContentState(); +#endif + + return rv; } +#ifdef DEBUG +void +nsDocument::VerifyRootContentState() +{ + nsIContent* elementChild = nsnull; + for (PRUint32 i = 0; i < GetChildCount(); ++i) { + nsIContent* kid = GetChildAt(i); + NS_ASSERTION(kid, "Must have kid here"); + + if (kid->IsContentOfType(nsIContent::eELEMENT)) { + NS_ASSERTION(!elementChild, "Multiple element kids?"); + elementChild = kid; + } + } + + NS_ASSERTION(mRootContent == elementChild, "Incorrect mRootContent"); +} +#endif // DEBUG + PRInt32 nsDocument::GetNumberOfStyleSheets() const { @@ -2489,11 +2548,28 @@ nsDocument::GetDoctype(nsIDOMDocumentType** aDoctype) *aDoctype = nsnull; PRInt32 i, count; count = mChildren.ChildCount(); - for (i = 0; i < count; i++) { - CallQueryInterface(mChildren.ChildAt(i), aDoctype); + nsCOMPtr rootContentNode(do_QueryInterface(mRootContent) ); + nsCOMPtr node; - if (*aDoctype) { + for (i = 0; i < count; i++) { + node = do_QueryInterface(mChildren.ChildAt(i)); + + NS_ASSERTION(node, "null element of mChildren"); + + // doctype can't be after the root + // XXX Do we really want to enforce this when we don't enforce + // anything else? + if (node == rootContentNode) return NS_OK; + + if (node) { + PRUint16 nodeType; + + node->GetNodeType(&nodeType); + + if (nodeType == nsIDOMNode::DOCUMENT_TYPE_NODE) { + return CallQueryInterface(node, aDoctype); + } } } @@ -2524,14 +2600,16 @@ nsDocument::GetDocumentElement(nsIDOMElement** aDocumentElement) { NS_ENSURE_ARG_POINTER(aDocumentElement); - nsIContent* root = GetRootContent(); - if (root) { - return CallQueryInterface(root, aDocumentElement); + nsresult rv = NS_OK; + + if (mRootContent) { + rv = CallQueryInterface(mRootContent, aDocumentElement); + NS_ASSERTION(NS_OK == rv, "Must be a DOM Element"); + } else { + *aDocumentElement = nsnull; } - *aDocumentElement = nsnull; - - return NS_OK; + return rv; } NS_IMETHODIMP @@ -3545,11 +3623,43 @@ nsDocument::GetLocalName(nsAString& aLocalName) return NS_OK; } +nsresult +nsDocument::IsAllowedAsChild(PRUint16 aNodeType, nsIContent* aRefContent) +{ + if (aNodeType != nsIDOMNode::COMMENT_NODE && + aNodeType != nsIDOMNode::ELEMENT_NODE && + aNodeType != nsIDOMNode::PROCESSING_INSTRUCTION_NODE && + aNodeType != nsIDOMNode::DOCUMENT_TYPE_NODE) { + return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; + } + + if (aNodeType == nsIDOMNode::ELEMENT_NODE && mRootContent && + mRootContent != aRefContent) { + // We already have a child Element, and we're not trying to + // replace it, so throw an error. + return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; + } + + if (aNodeType == nsIDOMNode::DOCUMENT_TYPE_NODE) { + nsCOMPtr docType; + GetDoctype(getter_AddRefs(docType)); + + nsCOMPtr docTypeContent = do_QueryInterface(docType); + if (docTypeContent && docTypeContent != aRefContent) { + // We already have a doctype, and we're not trying to + // replace it, so throw an error. + return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; + } + } + + return NS_OK; +} + NS_IMETHODIMP nsDocument::InsertBefore(nsIDOMNode* aNewChild, nsIDOMNode* aRefChild, nsIDOMNode** aReturn) { - return nsGenericElement::doReplaceOrInsertBefore(PR_FALSE, aNewChild, aRefChild, nsnull, this, + return nsGenericElement::doInsertBefore(aNewChild, aRefChild, nsnull, this, aReturn); } @@ -3557,7 +3667,7 @@ NS_IMETHODIMP nsDocument::ReplaceChild(nsIDOMNode* aNewChild, nsIDOMNode* aOldChild, nsIDOMNode** aReturn) { - return nsGenericElement::doReplaceOrInsertBefore(PR_TRUE, aNewChild, aOldChild, nsnull, this, + return nsGenericElement::doReplaceChild(aNewChild, aOldChild, nsnull, this, aReturn); } @@ -3945,7 +4055,7 @@ NS_IMETHODIMP nsDocument::LookupPrefix(const nsAString& aNamespaceURI, nsAString& aPrefix) { - nsCOMPtr root(do_QueryInterface(GetRootContent())); + nsCOMPtr root(do_QueryInterface(mRootContent)); if (root) { return root->LookupPrefix(aNamespaceURI, aPrefix); } @@ -3958,7 +4068,7 @@ NS_IMETHODIMP nsDocument::LookupNamespaceURI(const nsAString& aNamespacePrefix, nsAString& aNamespaceURI) { - if (NS_FAILED(nsContentUtils::LookupNamespaceURI(GetRootContent(), + if (NS_FAILED(nsContentUtils::LookupNamespaceURI(mRootContent, aNamespacePrefix, aNamespaceURI))) { SetDOMStringToNull(aNamespaceURI); @@ -5033,13 +5143,12 @@ nsDocument::Destroy() if (mIsGoingAway) return; + PRInt32 count = mChildren.ChildCount(); + mIsGoingAway = PR_TRUE; DestroyLinkMap(); - - PRInt32 count = mChildren.ChildCount(); - for (PRInt32 indx = count; indx > 0; --indx) { - mChildren.ChildAt(indx - 1)->UnbindFromTree(); - mChildren.RemoveChildAt(indx - 1); + for (PRInt32 indx = 0; indx < count; ++indx) { + mChildren.ChildAt(indx)->UnbindFromTree(); } // Propagate the out-of-band notification to each PresShell's anonymous @@ -5201,13 +5310,12 @@ nsDocument::OnPageShow(PRBool aPersisted) mVisible = PR_TRUE; UpdateLinkMap(); - nsIContent* root = GetRootContent(); - if (aPersisted && root) { + if (aPersisted) { // Send out notifications that our elements are attached. nsRefPtr links = NS_GetContentList(this, nsHTMLAtoms::link, kNameSpaceID_Unknown, - root); + mRootContent); if (links) { PRUint32 linkCount = links->Length(PR_TRUE); @@ -5229,12 +5337,11 @@ nsDocument::OnPageHide(PRBool aPersisted) { // Send out notifications that our elements are detached, // but only if this is not a full unload. - nsIContent* root = GetRootContent(); - if (aPersisted && root) { + if (aPersisted) { nsRefPtr links = NS_GetContentList(this, nsHTMLAtoms::link, kNameSpaceID_Unknown, - root); + mRootContent); if (links) { PRUint32 linkCount = links->Length(PR_TRUE); diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index 59923e41c87..a173e70919a 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -385,7 +385,6 @@ public: nsIDocument* aSubDoc); virtual nsIDocument* GetSubDocumentFor(nsIContent *aContent) const; virtual nsIContent* FindContentForSubDocument(nsIDocument *aDocument) const; - virtual nsIContent* GetRootContent() const; /** * Get the style sheets owned by this document. @@ -706,6 +705,10 @@ protected: // Dispatch an event to the ScriptGlobalObject for this document void DispatchEventToWindow(nsEvent *aEvent); +#ifdef DEBUG + void VerifyRootContentState(); +#endif + nsDocument(); virtual ~nsDocument(); @@ -769,6 +772,8 @@ protected: nsString mBaseTarget; private: + nsresult IsAllowedAsChild(PRUint16 aNodeType, nsIContent* aRefContent); + void PostUnblockOnloadEvent(); static EventHandlerFunc HandleOnloadBlockerEvent; static EventDestructorFunc DestroyOnloadBlockerEvent; diff --git a/content/base/src/nsDocumentFragment.cpp b/content/base/src/nsDocumentFragment.cpp index 558658888ec..79912482732 100644 --- a/content/base/src/nsDocumentFragment.cpp +++ b/content/base/src/nsDocumentFragment.cpp @@ -52,7 +52,7 @@ #include "nsIDOMUserDataHandler.h" class nsDocumentFragment : public nsGenericElement, - public nsIDOMDocumentFragment, + public nsIDocumentFragment, public nsIDOM3Node { public: @@ -62,6 +62,11 @@ public: // nsISupports NS_DECL_ISUPPORTS_INHERITED + // interface nsIDocumentFragment + NS_IMETHOD DisconnectChildren(); + NS_IMETHOD ReconnectChildren(); + NS_IMETHOD DropChildReferences(); + // interface nsIDOMDocumentFragment NS_IMETHOD GetNodeName(nsAString& aNodeName) { return nsGenericElement::GetNodeName(aNodeName); } @@ -189,6 +194,7 @@ nsDocumentFragment::~nsDocumentFragment() // QueryInterface implementation for nsDocumentFragment NS_INTERFACE_MAP_BEGIN(nsDocumentFragment) + NS_INTERFACE_MAP_ENTRY(nsIDocumentFragment) NS_INTERFACE_MAP_ENTRY(nsIDOMDocumentFragment) NS_INTERFACE_MAP_ENTRY(nsIDOMNode) NS_INTERFACE_MAP_ENTRY(nsIDOM3Node) @@ -202,6 +208,71 @@ NS_INTERFACE_MAP_END NS_IMPL_ADDREF(nsDocumentFragment) NS_IMPL_RELEASE(nsDocumentFragment) +NS_IMETHODIMP +nsDocumentFragment::DisconnectChildren() +{ + PRUint32 i, count = GetChildCount(); + + for (i = 0; i < count; i++) { + NS_ASSERTION(GetChildAt(i)->GetCurrentDoc() == nsnull, + "How did we get a child with a current doc?"); + // Safe to unbind PR_FALSE, since kids should never have a current document + // or a binding parent + GetChildAt(i)->UnbindFromTree(PR_FALSE); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsDocumentFragment::ReconnectChildren() +{ + PRUint32 i, count = GetChildCount(); + NS_PRECONDITION(GetCurrentDoc() == nsnull, + "We really shouldn't have a current doc!"); + + for (i = 0; i < count; i++) { + nsIContent *child = GetChildAt(i); + nsIContent *parent = child->GetParent(); + + if (parent) { + // This is potentially a O(n**2) operation, but it should only + // happen in error cases (such as out of memory or something + // similar) so we don't care for now. + // XXXbz I don't think this is O(n**2) with our IndexOf cache, is it? + + PRInt32 indx = parent->IndexOf(child); + + if (indx >= 0) { + parent->RemoveChildAt(indx, PR_TRUE); + } + } + + nsresult rv = child->BindToTree(nsnull, this, nsnull, PR_FALSE); + if (NS_FAILED(rv)) { + // It's all bad now... Just forget about this kid, I guess + child->UnbindFromTree(); + mAttrsAndChildren.RemoveChildAt(i); + // Adjust count and iterator accordingly + --count; + --i; + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsDocumentFragment::DropChildReferences() +{ + PRUint32 count = mAttrsAndChildren.ChildCount(); + while (count > 0) { + mAttrsAndChildren.RemoveChildAt(--count); + } + + return NS_OK; +} + NS_IMETHODIMP nsDocumentFragment::GetNodeType(PRUint16* aNodeType) { diff --git a/content/base/src/nsGenericElement.cpp b/content/base/src/nsGenericElement.cpp index ed3377f7680..90dd72d727a 100644 --- a/content/base/src/nsGenericElement.cpp +++ b/content/base/src/nsGenericElement.cpp @@ -772,8 +772,6 @@ nsDOMEventRTTearoff::AddEventListener(const nsAString& aType, //---------------------------------------------------------------------- -PRUint32 nsMutationGuard::sMutationCount = 0; - nsDOMSlots::nsDOMSlots(PtrBits aFlags) : mFlags(aFlags), mBindingParent(nsnull) @@ -2304,12 +2302,7 @@ nsGenericElement::doInsertChildAt(nsIContent* aKid, PRUint32 aIndex, NS_PRECONDITION(!aParent || aParent->GetCurrentDoc() == aDocument, "Incorrect aDocument"); - PRUint32 childCount = aChildArray.ChildCount(); - NS_ENSURE_TRUE(aIndex <= childCount, NS_ERROR_ILLEGAL_VALUE); - - nsMutationGuard::DidMutate(); - - PRBool isAppend = (aIndex == childCount); + PRBool isAppend = (aIndex == aChildArray.ChildCount()); mozAutoDocUpdate updateBatch(aDocument, UPDATE_CONTENT_MODEL, aNotify); @@ -2397,8 +2390,6 @@ nsGenericElement::doRemoveChildAt(PRUint32 aIndex, PRBool aNotify, NS_PRECONDITION(!aParent || aParent->GetCurrentDoc() == aDocument, "Incorrect aDocument"); - nsMutationGuard::DidMutate(); - nsINode* container = aParent; if (!container) { container = aDocument; @@ -2505,20 +2496,51 @@ nsGenericElement::DispatchClickEvent(nsPresContext* aPresContext, * aChild is one of aNode's ancestors. -- jst@citec.fi */ +/* static */ +PRBool +nsGenericElement::isSelfOrAncestor(nsIContent *aNode, + nsIContent *aPossibleAncestor) +{ + NS_PRECONDITION(aNode, "Must have a node"); + + if (aNode == aPossibleAncestor) + return PR_TRUE; + + /* + * If aPossibleAncestor doesn't have children it can't be our ancestor + */ + if (aPossibleAncestor->GetChildCount() == 0) { + return PR_FALSE; + } + + for (nsIContent* ancestor = aNode->GetParent(); + ancestor; + ancestor = ancestor->GetParent()) { + if (ancestor == aPossibleAncestor) { + /* + * We found aPossibleAncestor as one of our ancestors + */ + return PR_TRUE; + } + } + + return PR_FALSE; +} + NS_IMETHODIMP nsGenericElement::InsertBefore(nsIDOMNode *aNewChild, nsIDOMNode *aRefChild, nsIDOMNode **aReturn) { - return doReplaceOrInsertBefore(PR_FALSE, aNewChild, aRefChild, this, GetCurrentDoc(), - aReturn); + return doInsertBefore(aNewChild, aRefChild, this, GetCurrentDoc(), + aReturn); } NS_IMETHODIMP nsGenericElement::ReplaceChild(nsIDOMNode* aNewChild, nsIDOMNode* aOldChild, nsIDOMNode** aReturn) { - return doReplaceOrInsertBefore(PR_TRUE, aNewChild, aOldChild, this, GetCurrentDoc(), - aReturn); + return doReplaceChild(aNewChild, aOldChild, this, GetCurrentDoc(), + aReturn); } NS_IMETHODIMP @@ -2548,10 +2570,6 @@ PRBool IsAllowedAsChild(nsIContent* aNewChild, PRUint16 aNewNodeType, "Bogus node type passed"); #endif - if (aParent && nsContentUtils::ContentIsDescendantOf(aParent, aNewChild)) { - return PR_FALSE; - } - // The allowed child nodes differ for documents and elements switch (aNewNodeType) { case nsIDOMNode::COMMENT_NODE : @@ -2776,12 +2794,9 @@ NS_IMPL_ISUPPORTS1(nsFragmentObserver, nsIDocumentObserver) /* static */ nsresult -nsGenericElement::doReplaceOrInsertBefore(PRBool aReplace, - nsIDOMNode* aNewChild, - nsIDOMNode* aRefChild, - nsIContent* aParent, - nsIDocument* aDocument, - nsIDOMNode** aReturn) +nsGenericElement::doInsertBefore(nsIDOMNode* aNewChild, nsIDOMNode* aRefChild, + nsIContent* aParent, nsIDocument* aDocument, + nsIDOMNode** aReturn) { NS_PRECONDITION(aParent || aDocument, "Must have document if no parent!"); NS_PRECONDITION(!aParent || aParent->GetCurrentDoc() == aDocument, @@ -2789,51 +2804,54 @@ nsGenericElement::doReplaceOrInsertBefore(PRBool aReplace, *aReturn = nsnull; - if (!aNewChild || (aReplace && !aRefChild)) { + if (!aNewChild) { return NS_ERROR_NULL_POINTER; } - // Keep a strong reference to the node that we'll return to ensure it - // doesn't go away. - nsCOMPtr returnVal = aReplace ? aRefChild : aNewChild; - nsCOMPtr refContent; nsresult res = NS_OK; - PRInt32 insPos; + PRInt32 refPos = 0; nsINode* container = aParent; if (!container) { container = aDocument; } - // Figure out which index to insert at if (aRefChild) { - refContent = do_QueryInterface(aRefChild); - insPos = container->IndexOf(refContent); - if (insPos < 0) { + refContent = do_QueryInterface(aRefChild, &res); + + if (NS_FAILED(res)) { + /* + * If aRefChild doesn't support the nsIContent interface it can't be + * an existing child of this node. + */ return NS_ERROR_DOM_NOT_FOUND_ERR; } - if (aRefChild == aNewChild) { - NS_ADDREF(*aReturn = aNewChild); + refPos = container->IndexOf(refContent); - return NS_OK; + if (refPos < 0) { + return NS_ERROR_DOM_NOT_FOUND_ERR; } } else { - insPos = container->GetChildCount(); + refPos = container->GetChildCount(); } - nsCOMPtr newContent = do_QueryInterface(aNewChild); - if (!newContent) { + nsCOMPtr newContent(do_QueryInterface(aNewChild, &res)); + + if (NS_FAILED(res)) { return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; } PRUint16 nodeType = 0; - res = aNewChild->GetNodeType(&nodeType); - NS_ENSURE_SUCCESS(res, res); - // Make sure that the inserted node is allowed as a child of its new parent. - if (!IsAllowedAsChild(newContent, nodeType, aParent, aDocument, aReplace, + res = aNewChild->GetNodeType(&nodeType); + + if (NS_FAILED(res)) { + return res; + } + + if (!IsAllowedAsChild(newContent, nodeType, aParent, aDocument, PR_FALSE, refContent)) { return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; } @@ -2858,37 +2876,14 @@ nsGenericElement::doReplaceOrInsertBefore(PRBool aReplace, } } } - - // We want an update batch when we expect several mutations to be performed, - // which is when we're replacing a node, or when we're inserting a fragment. - mozAutoDocUpdate updateBatch(aDocument, UPDATE_CONTENT_MODEL, - aReplace || nodeType == nsIDOMNode::DOCUMENT_FRAGMENT_NODE); - - // If we're replacing - if (aReplace) { - // Getting (and addrefing) the following child here is sort of wasteful - // in the common case, but really, it's not that expensive. Get over it. - refContent = container->GetChildAt(insPos + 1); - - nsMutationGuard guard; - - res = container->RemoveChildAt(insPos, PR_TRUE); - NS_ENSURE_SUCCESS(res, res); - - if (guard.Mutated(1)) { - insPos = refContent ? container->IndexOf(refContent) : - container->GetChildCount(); - if (insPos < 0) { - return NS_ERROR_DOM_NOT_FOUND_ERR; - } - - // Passing PR_FALSE for aIsReplace since we now have removed the node - // to be replaced. - if (!IsAllowedAsChild(newContent, nodeType, aParent, aDocument, - PR_FALSE, refContent)) { - return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; - } - } + + /* + * Make sure the new child is not aParent or one of aParent's + * ancestors. Doing this check here should be safe even if newContent + * is a document fragment. + */ + if (aParent && isSelfOrAncestor(aParent, newContent)) { + return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; } /* @@ -2897,42 +2892,23 @@ nsGenericElement::doReplaceOrInsertBefore(PRBool aReplace, * individually (i.e. we don't add the actual document fragment). */ if (nodeType == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) { + nsCOMPtr doc_fragment(do_QueryInterface(newContent)); + NS_ENSURE_TRUE(doc_fragment, NS_ERROR_UNEXPECTED); + PRUint32 count = newContent->GetChildCount(); - PRBool do_notify = refContent || !aParent; + PRUint32 old_count = container->GetChildCount(); - // Copy the children into a separate array to avoid having to deal with - // mutations to the fragment while we're inserting. - nsCOMArray fragChildren; - if (!fragChildren.SetCapacity(count)) { - return NS_ERROR_OUT_OF_MEMORY; - } - PRUint32 i; - for (i = 0; i < count; i++) { - nsIContent* child = newContent->GetChildAt(i); - NS_ASSERTION(child->GetCurrentDoc() == nsnull, - "How did we get a child with a current doc?"); - fragChildren.AppendObject(child); - } + PRBool do_notify = !!aRefChild || !aParent; - // Remove the children from the fragment and flag for possible mutations. - PRBool mutated = PR_FALSE; - for (i = count; i > 0;) { - // We don't need to update i if someone mutates the DOM. The only thing - // that'd happen is that the resulting child list might be unexpected, - // but we should never crash since RemoveChildAt is out-of-bounds safe. - nsMutationGuard guard; - newContent->RemoveChildAt(--i, PR_TRUE); - mutated = mutated || guard.Mutated(1); - } - - // Set up observer that notifies if needed. nsRefPtr fragmentObs; if (count && !do_notify) { - fragmentObs = new nsFragmentObserver(container->GetChildCount(), aParent, aDocument); + fragmentObs = new nsFragmentObserver(old_count, aParent, aDocument); NS_ENSURE_TRUE(fragmentObs, NS_ERROR_OUT_OF_MEMORY); fragmentObs->Connect(); } + doc_fragment->DisconnectChildren(); + // If do_notify is true, then we don't have to handle the notifications // ourselves... Also, if count is 0 there will be no updates. So we only // want an update batch to happen if count is nonzero and do_notify is not @@ -2940,44 +2916,26 @@ nsGenericElement::doReplaceOrInsertBefore(PRBool aReplace, mozAutoDocUpdate updateBatch(aDocument, UPDATE_CONTENT_MODEL, count && !do_notify); - // Iterate through the fragment's children, and insert them in the new - // parent - for (i = 0; i < count; ++i) { - // Get the n:th child from the array. - nsIContent* childContent = fragChildren[i]; + /* + * Iterate through the fragment's children, removing each from + * the fragment and inserting it into the child list of its + * new parent. + */ - // If we've had any unexpeted mutations so far we need to recheck that - // the child can still be inserted. - if (mutated) { - // We really only need to update insPos if we *just* got an unexpected - // mutation as opposed to 3 insertions ago. But this is an edgecase so - // no need to over optimize. - insPos = refContent ? container->IndexOf(refContent) : - container->GetChildCount(); - if (insPos < 0) { - // Someone seriously messed up the childlist. We have no idea - // where to insert the remaining children, so just bail. - res = NS_ERROR_DOM_NOT_FOUND_ERR; - break; - } + nsCOMPtr childContent; - nsCOMPtr tmpNode = do_QueryInterface(childContent); - PRUint16 tmpType = 0; - tmpNode->GetNodeType(&tmpType); - - if (childContent->GetParent() || childContent->IsInDoc() || - !IsAllowedAsChild(childContent, tmpType, aParent, aDocument, PR_FALSE, - refContent)) { - res = NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; - break; - } - } - - nsMutationGuard guard; + for (PRUint32 i = 0; i < count; ++i) { + // Get the n:th child from the document fragment. Since we + // disconnected the children from the document fragment they + // won't be removed from the document fragment when inserted + // into the new parent. This lets us do this operation *much* + // faster. + childContent = newContent->GetChildAt(i); // XXXbz how come no reparenting here? That seems odd... - // Insert the child. - res = container->InsertChildAt(childContent, insPos, do_notify); + // Insert the child and increment the insertion position + res = container->InsertChildAt(childContent, refPos++, do_notify); + if (NS_FAILED(res)) { break; } @@ -2985,21 +2943,17 @@ nsGenericElement::doReplaceOrInsertBefore(PRBool aReplace, if (fragmentObs) { fragmentObs->ChildBound(); } - - // Check to see if any evil mutation events mucked around with the - // child list. - mutated = mutated || guard.Mutated(1); - - ++insPos; } if (NS_FAILED(res)) { + // This should put the children that were moved out of the + // document fragment back into the document fragment and remove + // them from the element or document they were inserted into. + + doc_fragment->ReconnectChildren(); if (fragmentObs) { fragmentObs->Disconnect(); } - - // We could try to put the nodes back into the fragment here if we - // really cared. return res; } @@ -3008,9 +2962,9 @@ nsGenericElement::doReplaceOrInsertBefore(PRBool aReplace, NS_ASSERTION(count && !do_notify, "Unexpected state"); fragmentObs->Finish(); } - } - else { - // Not inserting a fragment but rather a single node. + + doc_fragment->DropChildReferences(); + } else { nsIContent* bindingParent = newContent->GetBindingParent(); if (bindingParent == newContent || (bindingParent && bindingParent == newContent->GetParent())) { @@ -3018,65 +2972,245 @@ nsGenericElement::doReplaceOrInsertBefore(PRBool aReplace, return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } - PRBool newContentIsXUL = newContent->IsContentOfType(eXUL); - - // Remove the element from the old parent if one exists - nsINode* oldParent = newContent->GetParent(); - if (!oldParent) { - oldParent = newContent->GetCurrentDoc(); - - // See bug 53901. Crappy XUL sometimes lies about being in the document - if (oldParent && newContentIsXUL && oldParent->IndexOf(newContent) < 0) { - oldParent = nsnull; - } - } + /* + * Remove the element from the old parent if one exists, since oldParent + * is a nsIDOMNode this will do the right thing even if the parent of + * aNewChild is a document. This code also handles the case where the + * new child is alleady a child of this node-- jst@citec.fi + */ + nsCOMPtr oldParent; + res = aNewChild->GetParentNode(getter_AddRefs(oldParent)); + NS_ENSURE_SUCCESS(res, res); if (oldParent) { - PRInt32 removeIndex = oldParent->IndexOf(newContent); - NS_ASSERTION(removeIndex >= 0 && - !(oldParent == container && removeIndex == insPos), - "invalid removeIndex"); + nsCOMPtr tmpNode; - nsMutationGuard guard; + PRUint32 origChildCount = container->GetChildCount(); - res = oldParent->RemoveChildAt(removeIndex, PR_TRUE); - NS_ENSURE_SUCCESS(res, res); + /* + * We don't care here if the return fails or not. + */ + oldParent->RemoveChild(aNewChild, getter_AddRefs(tmpNode)); - // Adjust insert index if the node we ripped out was a sibling - // of the node we're inserting before - if (oldParent == container && removeIndex < insPos) { - --insPos; - } + PRUint32 newChildCount = container->GetChildCount(); - if (guard.Mutated(1)) { - insPos = refContent ? container->IndexOf(refContent) : - container->GetChildCount(); - if (insPos < 0) { - // Someone seriously messed up the childlist. We have no idea - // where to insert the new child, so just bail. - return NS_ERROR_DOM_NOT_FOUND_ERR; - } + /* + * Check if our child count changed during the RemoveChild call, if + * it did then oldParent is most likely this node. In this case we + * must check if refPos is still correct (unless it's zero). + */ + if (refPos && origChildCount != newChildCount) { + if (refContent) { + /* + * If we did get aRefChild we check if that is now at refPos - 1, + * this will happend if the new child was one of aRefChilds' + * previous siblings. + */ - if (newContent->GetParent() || newContent->IsInDoc() || - !IsAllowedAsChild(newContent, nodeType, aParent, aDocument, PR_FALSE, - refContent)) { - return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; + if (refContent == container->GetChildAt(refPos - 1)) { + refPos--; + } + } else { + /* + * If we didn't get aRefChild we simply decrement refPos. + */ + refPos--; } } } - if (!newContentIsXUL) { + if (!newContent->IsContentOfType(eXUL)) { nsContentUtils::ReparentContentWrapper(newContent, aParent, container->GetOwnerDoc(), old_doc); } - res = container->InsertChildAt(newContent, insPos, PR_TRUE); + res = container->InsertChildAt(newContent, refPos, PR_TRUE); + + if (NS_FAILED(res)) { + return res; + } + } + + *aReturn = aNewChild; + NS_ADDREF(*aReturn); + + return res; +} + +/* static */ +nsresult +nsGenericElement::doReplaceChild(nsIDOMNode* aNewChild, nsIDOMNode* aOldChild, + nsIContent* aParent, nsIDocument* aDocument, + nsIDOMNode** aReturn) +{ + NS_PRECONDITION(aParent || aDocument, "Must have document if no parent!"); + NS_PRECONDITION(!aParent || aParent->GetCurrentDoc() == aDocument, + "Incorrect aDocument"); + + *aReturn = nsnull; + + if (!aNewChild || !aOldChild) { + return NS_ERROR_NULL_POINTER; + } + + nsresult res = NS_OK; + PRInt32 oldPos = 0; + + nsCOMPtr oldContent = do_QueryInterface(aOldChild); + + nsINode* container = aParent; + if (!container) { + container = aDocument; + } + + // if oldContent is null IndexOf will return < 0, which is what we want + // since aOldChild couldn't be a child. + oldPos = container->IndexOf(oldContent); + if (oldPos < 0) { + return NS_ERROR_DOM_NOT_FOUND_ERR; + } + + nsCOMPtr replacedChild = container->GetChildAt(oldPos); + + PRUint16 nodeType = 0; + + res = aNewChild->GetNodeType(&nodeType); + + if (NS_FAILED(res)) { + return res; + } + + nsCOMPtr newContent(do_QueryInterface(aNewChild, &res)); + + if (NS_FAILED(res)) { + return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; + } + + if (!IsAllowedAsChild(newContent, nodeType, aParent, aDocument, PR_TRUE, + oldContent)) { + return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; + } + + nsIDocument* old_doc = newContent->GetOwnerDoc(); + + // XXXbz The document code and content code have two totally different + // security checks here. Why? Because I'm afraid to change such things this + // close to 1.8. But which should we do here, really? Or both? For example + // what should a caller with UniversalBrowserRead/Write/whatever be able to + // do, exactly? Do we need to be more careful with documents because random + // callers _can_ get access to them? That might be.... + if (old_doc && old_doc != container->GetOwnerDoc()) { + if (aParent) { + if (!nsContentUtils::CanCallerAccess(aNewChild)) { + return NS_ERROR_DOM_SECURITY_ERR; + } + } else { + nsCOMPtr doc(do_QueryInterface(aDocument)); + if (NS_FAILED(nsContentUtils::CheckSameOrigin(doc, aNewChild))) { + return NS_ERROR_DOM_SECURITY_ERR; + } + } + } + + /* + * Make sure the new child is not aParent or one of aParent's + * ancestors. Doing this check here should be safe even if newContent + * is a document fragment. + */ + if (aParent && isSelfOrAncestor(aParent, newContent)) { + return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; + } + + // We're ready to start inserting children, so let's start a batch + mozAutoDocUpdate updateBatch(aDocument, UPDATE_CONTENT_MODEL, PR_TRUE); + + /* + * Check if this is a document fragment. If it is, we need + * to remove the children of the document fragment and add them + * individually (i.e. we don't add the actual document fragment). + */ + if (nodeType == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) { + nsCOMPtr childContent; + PRUint32 i, count = newContent->GetChildCount(); + res = container->RemoveChildAt(oldPos, PR_TRUE); + NS_ENSURE_SUCCESS(res, res); + + /* + * Iterate through the fragments children, removing each from + * the fragment and inserting it into the child list of its + * new parent. + */ + for (i = 0; i < count; ++i) { + // Always get and remove the first child, since the child indexes + // change as we go along. + childContent = newContent->GetChildAt(0); + res = newContent->RemoveChildAt(0, PR_FALSE); + NS_ENSURE_SUCCESS(res, res); + + // XXXbz how come no reparenting here? + // Insert the child and increment the insertion position + res = container->InsertChildAt(childContent, oldPos++, PR_TRUE); + NS_ENSURE_SUCCESS(res, res); + } + } + else { + nsCOMPtr oldParent; + res = aNewChild->GetParentNode(getter_AddRefs(oldParent)); + NS_ENSURE_SUCCESS(res, res); + + /* + * Remove the element from the old parent if one exists, since oldParent + * is a nsIDOMNode this will do the right thing even if the parent of + * aNewChild is a document. This code also handles the case where the + * new child is alleady a child of this node-- jst@citec.fi + */ + if (oldParent) { + PRUint32 origChildCount = container->GetChildCount(); + + /* + * We don't care here if the return fails or not. + */ + nsCOMPtr tmpNode; + oldParent->RemoveChild(aNewChild, getter_AddRefs(tmpNode)); + + PRUint32 newChildCount = container->GetChildCount(); + + /* + * Check if our child count changed during the RemoveChild call, if + * it did then oldParent is most likely this node. In this case we + * must check if oldPos is still correct (unless it's zero). + */ + if (oldPos && origChildCount != newChildCount) { + /* + * Check if aOldChild is now at oldPos - 1, this will happend if + * the new child was one of aOldChilds' previous siblings. + */ + nsIContent *tmpContent = container->GetChildAt(oldPos - 1); + + if (oldContent == tmpContent) { + oldPos--; + } + } + } + + if (!newContent->IsContentOfType(eXUL)) { + nsContentUtils::ReparentContentWrapper(newContent, aParent, + container->GetOwnerDoc(), + old_doc); + } + + // If we're replacing a child with itself the child + // has already been removed from this element once we get here. + if (aNewChild != aOldChild) { + res = container->RemoveChildAt(oldPos, PR_TRUE); + NS_ENSURE_SUCCESS(res, res); + } + + res = container->InsertChildAt(newContent, oldPos, PR_TRUE); NS_ENSURE_SUCCESS(res, res); } - returnVal.swap(*aReturn); - - return res; + return CallQueryInterface(replacedChild, aReturn); } /* static */ diff --git a/content/base/src/nsGenericElement.h b/content/base/src/nsGenericElement.h index a2816a1fe5a..611e90d27c7 100644 --- a/content/base/src/nsGenericElement.h +++ b/content/base/src/nsGenericElement.h @@ -283,77 +283,6 @@ private: nsCOMPtr mContent; }; -/** - * Class used to detect unexpected mutations. To use the class create an - * nsMutationGuard on the stack before unexpected mutations could occur. - * You can then at any time call Mutated to check if any unexpected mutations - * have occured. - * - * When a guard is instantiated sMutationCount is set to 300. It is then - * decremented by every mutation (capped at 0). This means that we can only - * detect 300 mutations during the lifetime of a single guard, however that - * should be more then we ever care about as we usually only care if more then - * one mutation has occured. - * - * When the guard goes out of scope it will adjust sMutationCount so that over - * the lifetime of the guard the guard itself has not affected sMutationCount, - * while mutations that happened while the guard was alive still will. This - * allows a guard to be instantiated even if there is another guard higher up - * on the callstack watching for mutations. - * - * The only thing that has to be avoided is for an outer guard to be used - * while an inner guard is alive. This can be avoided by only ever - * instantiating a single guard per scope and only using the guard in the - * current scope. - */ -class nsMutationGuard { -public: - nsMutationGuard() - { - mDelta = eMaxMutations - sMutationCount; - sMutationCount = eMaxMutations; - } - ~nsMutationGuard() - { - sMutationCount = - mDelta > sMutationCount ? 0 : sMutationCount - mDelta; - } - - /** - * Returns true if any unexpected mutations have occured. You can pass in - * an 8-bit ignore count to ignore a number of expected mutations. - */ - PRBool Mutated(PRUint8 aIgnoreCount) - { - return sMutationCount < NS_STATIC_CAST(PRUint32, eMaxMutations - aIgnoreCount); - } - - // This function should be called whenever a mutation that we want to keep - // track of happen. For now this is only done when children are added or - // removed, but we might do it for attribute changes too in the future. - static void DidMutate() - { - if (sMutationCount) { - --sMutationCount; - } - } - -private: - // mDelta is the amount sMutationCount was adjusted when the guard was - // initialized. It is needed so that we can undo that adjustment once - // the guard dies. - PRUint32 mDelta; - - // The value 300 is not important, as long as it is bigger then anything - // ever passed to Mutated(). - enum { eMaxMutations = 300 }; - - - // sMutationCount is a global mutation counter which is decreased by one at - // every mutation. It is capped at 0 to avoid wrapping. - // It's value is always between 0 and 300, inclusive. - static PRUint32 sMutationCount; -}; /** * A generic base class for DOM elements, implementing many nsIContent, @@ -647,22 +576,43 @@ public: static PRBool ShouldFocus(nsIContent *aContent); /** - * Actual implementation of the DOM InsertBefore and ReplaceChild methods. - * Shared by nsDocument. When called from nsDocument, aParent will be null. + * Checks if a node is the ancestor of another. + */ + static PRBool isSelfOrAncestor(nsIContent *aNode, + nsIContent *aPossibleAncestor); + + /** + * Actual implementation of the DOM InsertBefore method. Shared by + * nsDocument. When called from nsDocument, aParent will be null. * - * @param aReplace True if aNewChild should replace aRefChild. False if - * aNewChild should be inserted before aRefChild. * @param aNewChild The child to insert - * @param aRefChild The child to insert before or replace + * @param aRefChild The child to insert before * @param aParent The parent to use for the new child * @param aDocument The document to use for the new child. * Must be non-null, if aParent is null and must match * aParent->GetCurrentDoc() if aParent is not null. * @param aReturn [out] the child we insert */ - static nsresult doReplaceOrInsertBefore(PRBool aReplace, nsIDOMNode* aNewChild, nsIDOMNode* aRefChild, - nsIContent* aParent, nsIDocument* aDocument, - nsIDOMNode** aReturn); + static nsresult doInsertBefore(nsIDOMNode* aNewChild, nsIDOMNode* aRefChild, + nsIContent* aParent, nsIDocument* aDocument, + nsIDOMNode** aReturn); + + /** + * Actual implementation of the DOM ReplaceChild method. Shared by + * nsDocument. When called from nsDocument, aParent will be null. + * + * @param aNewChild The child to replace with + * @param aOldChild The child to replace + * @param aParent The parent to use for the new child + * @param aDocument The document to use for the new child. + * Must be non-null if aParent is null and must match + * aParent->GetCurrentDoc() if aParent is not null. + * @param aChildArray The child array to work with + * @param aReturn [out] the child we insert + */ + static nsresult doReplaceChild(nsIDOMNode* aNewChild, nsIDOMNode* aOldChild, + nsIContent* aParent, nsIDocument* aDocument, + nsIDOMNode** aReturn); /** * Actual implementation of the DOM RemoveChild method. Shared by @@ -756,7 +706,7 @@ public: const nsAttrName* mName; const nsAttrValue* mValue; }; - + protected: /** * Set attribute and (if needed) notify documentobservers and fire off @@ -1061,6 +1011,41 @@ protected: nsAttrAndChildArray mAttrsAndChildren; }; +// Internal non-public interface + +// IID for the nsIDocumentFragment interface +#define NS_IDOCUMENTFRAGMENT_IID \ +{ 0xd8fb2853, 0xf6d6, 0x4499, \ + {0x9c, 0x60, 0x6c, 0xa2, 0x75, 0x35, 0x09, 0xeb} } + +// nsIDocumentFragment interface +/** + * These methods are supposed to be used when *all* children of a + * document fragment are moved at once into a new parent w/o + * changing the relationship between the children. If the moving + * operation fails and some children were moved to a new parent and + * some weren't, ReconnectChildren() should be called to remove the + * children from their possible new parent and re-insert the + * children into the document fragment. Once the operation is + * complete and all children are successfully moved into their new + * parent DropChildReferences() should be called so that the + * document fragment will loose its references to the children. + */ +class nsIDocumentFragment : public nsIDOMDocumentFragment +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IDOCUMENTFRAGMENT_IID) + + /** Tell the children their parent is gone */ + NS_IMETHOD DisconnectChildren() = 0; + /** Put all children back in the fragment */ + NS_IMETHOD ReconnectChildren() = 0; + /** Drop references to children */ + NS_IMETHOD DropChildReferences() = 0; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumentFragment, NS_IDOCUMENTFRAGMENT_IID) + #define NS_FORWARD_NSIDOMNODE_NO_CLONENODE(_to) \ NS_IMETHOD GetNodeName(nsAString& aNodeName) { \ return _to GetNodeName(aNodeName); \ diff --git a/content/html/document/src/nsHTMLDocument.cpp b/content/html/document/src/nsHTMLDocument.cpp index 1918a065698..1b2e1ea013e 100644 --- a/content/html/document/src/nsHTMLDocument.cpp +++ b/content/html/document/src/nsHTMLDocument.cpp @@ -417,6 +417,8 @@ nsHTMLDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup) mLinks = nsnull; mAnchors = nsnull; + mBodyContent = nsnull; + mImageMaps.Clear(); mForms = nsnull; @@ -1171,6 +1173,12 @@ nsHTMLDocument::ContentRemoved(nsIContent* aContainer, nsIContent* aContent, { NS_ABORT_IF_FALSE(aContent, "Null content!"); + if (aContainer == mRootContent) { + // Reset mBodyContent in case we got a new body. + + mBodyContent = nsnull; + } + nsresult rv = UnregisterNamedItems(aContent); if (NS_FAILED(rv)) { @@ -1623,62 +1631,66 @@ nsHTMLDocument::GetURL(nsAString& aURL) NS_IMETHODIMP nsHTMLDocument::GetBody(nsIDOMHTMLElement** aBody) { + NS_ENSURE_ARG_POINTER(aBody); *aBody = nsnull; - nsIContent* body = GetBodyContent(); - - if (body) { - // There is a body element, return that as the body. - return CallQueryInterface(body, aBody); - } - - // The document is most likely a frameset document so look for the - // outer most frameset element - nsCOMPtr nodeList; - - nsresult rv; - if (IsXHTML()) { - rv = GetElementsByTagNameNS(NS_LITERAL_STRING("http://www.w3.org/1999/xhtml"), - NS_LITERAL_STRING("frameset"), - getter_AddRefs(nodeList)); - } else { - rv = GetElementsByTagName(NS_LITERAL_STRING("frameset"), - getter_AddRefs(nodeList)); - } - NS_ENSURE_SUCCESS(rv, rv); - + nsISupports* element = nsnull; nsCOMPtr node; - nodeList->Item(0, getter_AddRefs(node)); - return node ? CallQueryInterface(node, aBody) : NS_OK; + if (mBodyContent || GetBodyContent()) { + // There is a body element, return that as the body. + element = mBodyContent; + } else { + // The document is most likely a frameset document so look for the + // outer most frameset element + + nsCOMPtr nodeList; + + nsresult rv; + if (IsXHTML()) { + rv = GetElementsByTagNameNS(NS_LITERAL_STRING("http://www.w3.org/1999/xhtml"), + NS_LITERAL_STRING("frameset"), + getter_AddRefs(nodeList)); + } else { + rv = GetElementsByTagName(NS_LITERAL_STRING("frameset"), + getter_AddRefs(nodeList)); + } + + if (nodeList) { + rv |= nodeList->Item(0, getter_AddRefs(node)); + + element = node; + } + + NS_ENSURE_SUCCESS(rv, rv); + } + + return element ? CallQueryInterface(element, aBody) : NS_OK; } NS_IMETHODIMP nsHTMLDocument::SetBody(nsIDOMHTMLElement* aBody) { - nsCOMPtr newBody = do_QueryInterface(aBody); - nsIContent* root = GetRootContent(); + nsCOMPtr body(do_QueryInterface(aBody)); + nsCOMPtr root(do_QueryInterface(mRootContent)); - // The body element must be either a body tag or a frameset tag. And we must - // have a html root tag, otherwise GetBody will not return the newly set - // body. - if (!newBody || !(newBody->Tag() == nsHTMLAtoms::body || - newBody->Tag() == nsHTMLAtoms::frameset) || - !root || !root->IsContentOfType(nsIContent::eHTML) || - root->Tag() != nsHTMLAtoms::html) { + // The body element must be either a body tag or a frameset tag. + if (!body || !root || !(body->Tag() == nsHTMLAtoms::body || + body->Tag() == nsHTMLAtoms::frameset)) { return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; } - nsCOMPtr rootElem = do_QueryInterface(root); nsCOMPtr tmp; - // Use DOM methods so that we pass through the appropriate security checks. - nsCOMPtr currentBody = do_QueryInterface(GetBodyContent()); - if (currentBody) { - return rootElem->ReplaceChild(aBody, currentBody, getter_AddRefs(tmp)); + if (mBodyContent || GetBodyContent()) { + root->ReplaceChild(aBody, mBodyContent, getter_AddRefs(tmp)); + } else { + root->AppendChild(aBody, getter_AddRefs(tmp)); } - return rootElem->AppendChild(aBody, getter_AddRefs(tmp)); + mBodyContent = aBody; + + return PR_FALSE; } NS_IMETHODIMP @@ -1972,10 +1984,10 @@ nsHTMLDocument::OpenCommon(const nsACString& aContentType, PRBool aReplace) // (http://bugzilla.mozilla.org/show_bug.cgi?id=55334). // Hold on to our root element - nsCOMPtr root = GetRootContent(); + nsCOMPtr root(mRootContent); if (root) { - PRInt32 rootIndex = mChildren.IndexOfChild(root); + PRInt32 rootIndex = mChildren.IndexOfChild(mRootContent); NS_ASSERTION(rootIndex >= 0, "Root must be in list!"); PRUint32 count = root->GetChildCount(); @@ -1985,7 +1997,7 @@ nsHTMLDocument::OpenCommon(const nsACString& aContentType, PRBool aReplace) root->RemoveChildAt(count, PR_TRUE); } - count = root->GetAttrCount(); + count = mRootContent->GetAttrCount(); // Remove all attributes from the root element while (count-- > 0) { @@ -1995,6 +2007,8 @@ nsHTMLDocument::OpenCommon(const nsACString& aContentType, PRBool aReplace) // Remove the root from the childlist mChildren.RemoveChildAt(rootIndex); + + mRootContent = nsnull; } // Call Reset(), this will now do the full reset, except removing @@ -2016,6 +2030,7 @@ nsHTMLDocument::OpenCommon(const nsACString& aContentType, PRBool aReplace) // element was never set to null) mChildren.AppendChild(root); + mRootContent = root; } if (mEditingIsOn) { @@ -2425,12 +2440,11 @@ nsHTMLDocument::GetElementById(const nsAString& aElementId, // If IdTableIsLive(), no need to look for the element in the document, // since we're fully maintaining our table's state as the DOM mutates. if (!IdTableIsLive()) { - nsIContent* root = GetRootContent(); if (IdTableShouldBecomeLive()) { // Just make sure our table is up to date and call this method again // to look up in the hashtable. - if (root) { - RegisterNamedItems(root); + if (mRootContent) { + RegisterNamedItems(mRootContent); } return GetElementById(aElementId, aReturn); } @@ -2438,8 +2452,8 @@ nsHTMLDocument::GetElementById(const nsAString& aElementId, NS_WARN_IF_FALSE(!aElementId.IsEmpty(), "getElementById(\"\") called, fix caller?"); - if (root && !aElementId.IsEmpty()) { - e = nsContentUtils::MatchElementId(root, idAtom); + if (mRootContent && !aElementId.IsEmpty()) { + e = nsContentUtils::MatchElementId(mRootContent, idAtom); } } @@ -2447,9 +2461,9 @@ nsHTMLDocument::GetElementById(const nsAString& aElementId, #ifdef DEBUG // No reason to call MatchElementId if !IdTableIsLive, since // we'd have done just that already - if (IdTableIsLive() && GetRootContent() && !aElementId.IsEmpty()) { + if (IdTableIsLive() && mRootContent && !aElementId.IsEmpty()) { nsIContent* eDebug = - nsContentUtils::MatchElementId(GetRootContent(), idAtom); + nsContentUtils::MatchElementId(mRootContent, idAtom); NS_ASSERTION(!eDebug, "We got null for |e| but MatchElementId found something?"); } @@ -2578,7 +2592,11 @@ nsHTMLDocument::GetPixelDimensions(nsIPresShell* aShell, // Find the element: this is what we'll want to use for the // document's width and height values. - nsIContent* body = GetBodyContent(); + if (!mBodyContent && !GetBodyContent()) { + return NS_OK; + } + + nsCOMPtr body = do_QueryInterface(mBodyContent); // Now grab its frame nsIFrame* frame = aShell->GetPrimaryFrameFor(body); @@ -2667,7 +2685,8 @@ nsHTMLDocument::GetAlinkColor(nsAString& aAlinkColor) { aAlinkColor.Truncate(); - nsCOMPtr body = do_QueryInterface(GetBodyContent()); + nsCOMPtr body; + GetBodyElement(getter_AddRefs(body)); if (body) { body->GetALink(aAlinkColor); @@ -2685,7 +2704,8 @@ nsHTMLDocument::GetAlinkColor(nsAString& aAlinkColor) NS_IMETHODIMP nsHTMLDocument::SetAlinkColor(const nsAString& aAlinkColor) { - nsCOMPtr body = do_QueryInterface(GetBodyContent()); + nsCOMPtr body; + GetBodyElement(getter_AddRefs(body)); if (body) { body->SetALink(aAlinkColor); @@ -2706,7 +2726,8 @@ nsHTMLDocument::GetLinkColor(nsAString& aLinkColor) { aLinkColor.Truncate(); - nsCOMPtr body = do_QueryInterface(GetBodyContent()); + nsCOMPtr body; + GetBodyElement(getter_AddRefs(body)); if (body) { body->GetLink(aLinkColor); @@ -2724,7 +2745,8 @@ nsHTMLDocument::GetLinkColor(nsAString& aLinkColor) NS_IMETHODIMP nsHTMLDocument::SetLinkColor(const nsAString& aLinkColor) { - nsCOMPtr body = do_QueryInterface(GetBodyContent()); + nsCOMPtr body; + GetBodyElement(getter_AddRefs(body)); if (body) { body->SetLink(aLinkColor); @@ -2745,7 +2767,8 @@ nsHTMLDocument::GetVlinkColor(nsAString& aVlinkColor) { aVlinkColor.Truncate(); - nsCOMPtr body = do_QueryInterface(GetBodyContent()); + nsCOMPtr body; + GetBodyElement(getter_AddRefs(body)); if (body) { body->GetVLink(aVlinkColor); @@ -2763,7 +2786,8 @@ nsHTMLDocument::GetVlinkColor(nsAString& aVlinkColor) NS_IMETHODIMP nsHTMLDocument::SetVlinkColor(const nsAString& aVlinkColor) { - nsCOMPtr body = do_QueryInterface(GetBodyContent()); + nsCOMPtr body; + GetBodyElement(getter_AddRefs(body)); if (body) { body->SetVLink(aVlinkColor); @@ -2784,7 +2808,8 @@ nsHTMLDocument::GetBgColor(nsAString& aBgColor) { aBgColor.Truncate(); - nsCOMPtr body = do_QueryInterface(GetBodyContent()); + nsCOMPtr body; + GetBodyElement(getter_AddRefs(body)); if (body) { body->GetBgColor(aBgColor); @@ -2796,7 +2821,8 @@ nsHTMLDocument::GetBgColor(nsAString& aBgColor) NS_IMETHODIMP nsHTMLDocument::SetBgColor(const nsAString& aBgColor) { - nsCOMPtr body = do_QueryInterface(GetBodyContent()); + nsCOMPtr body; + GetBodyElement(getter_AddRefs(body)); if (body) { body->SetBgColor(aBgColor); @@ -2811,7 +2837,8 @@ nsHTMLDocument::GetFgColor(nsAString& aFgColor) { aFgColor.Truncate(); - nsCOMPtr body = do_QueryInterface(GetBodyContent()); + nsCOMPtr body; + GetBodyElement(getter_AddRefs(body)); if (body) { body->GetText(aFgColor); @@ -2823,7 +2850,8 @@ nsHTMLDocument::GetFgColor(nsAString& aFgColor) NS_IMETHODIMP nsHTMLDocument::SetFgColor(const nsAString& aFgColor) { - nsCOMPtr body = do_QueryInterface(GetBodyContent()); + nsCOMPtr body; + GetBodyElement(getter_AddRefs(body)); if (body) { body->SetText(aFgColor); @@ -3307,11 +3335,10 @@ nsHTMLDocument::ResolveName(const nsAString& aName, entry->mNameContentList = list; NS_ADDREF(entry->mNameContentList); - nsIContent* root = GetRootContent(); - if (root && !aName.IsEmpty()) { + if (mRootContent && !aName.IsEmpty()) { // We'll never get here if !IsXHTML(), so we can just pass // PR_FALSE to FindNamedItems(). - FindNamedItems(name, root, *entry, PR_FALSE); + FindNamedItems(name, mRootContent, *entry, PR_FALSE); } } @@ -3400,27 +3427,42 @@ nsHTMLDocument::ResolveName(const nsAString& aName, //---------------------------- -nsIContent* +PRBool nsHTMLDocument::GetBodyContent() { - // Loop backwards because any non-elements, such as doctypes and PIs - // are likly to appear before the root element. - nsIContent* html = GetRootContent(); - if (html && html->Tag() == nsHTMLAtoms::html && - html->IsContentOfType(nsIContent::eHTML)) { + if (!mRootContent) { + return PR_FALSE; + } - // Look for body inside html - PRUint32 i, count = html->GetChildCount(); - for (i = 0; i < count; ++i) { - nsIContent* body = html->GetChildAt(i); - if (body->Tag() == nsHTMLAtoms::body && - body->IsContentOfType(nsIContent::eHTML)) { - return body; - } + PRUint32 i, child_count = mRootContent->GetChildCount(); + + for (i = 0; i < child_count; ++i) { + nsIContent *child = mRootContent->GetChildAt(i); + NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED); + + if (child->NodeInfo()->Equals(nsHTMLAtoms::body, mDefaultNamespaceID) && + child->IsContentOfType(nsIContent::eHTML)) { + mBodyContent = do_QueryInterface(child); + + return PR_TRUE; } } - return nsnull; + return PR_FALSE; +} + +void +nsHTMLDocument::GetBodyElement(nsIDOMHTMLBodyElement** aBody) +{ + *aBody = nsnull; + + if (!mBodyContent && !GetBodyContent()) { + // No body in this document. + + return; + } + + CallQueryInterface(mBodyContent, aBody); } // forms related stuff diff --git a/content/html/document/src/nsHTMLDocument.h b/content/html/document/src/nsHTMLDocument.h index 4fffd32fc71..213f9b28b20 100644 --- a/content/html/document/src/nsHTMLDocument.h +++ b/content/html/document/src/nsHTMLDocument.h @@ -234,7 +234,8 @@ protected: static void DocumentWriteTerminationFunc(nsISupports *aRef); - nsIContent* GetBodyContent(); + PRBool GetBodyContent(); + void GetBodyElement(nsIDOMHTMLBodyElement** aBody); void GetDomainURI(nsIURI **uri); @@ -320,6 +321,8 @@ protected: // Load flags of the document's channel PRUint32 mLoadFlags; + nsCOMPtr mBodyContent; + /* * Bug 13871: Frameset spoofing - find out if document.domain was set */ diff --git a/content/html/document/src/nsImageDocument.cpp b/content/html/document/src/nsImageDocument.cpp index 0771040027d..5c805ab10f3 100644 --- a/content/html/document/src/nsImageDocument.cpp +++ b/content/html/document/src/nsImageDocument.cpp @@ -347,7 +347,7 @@ nsImageDocument::SetScriptGlobalObject(nsIScriptGlobalObject* aScriptGlobalObjec nsHTMLDocument::SetScriptGlobalObject(aScriptGlobalObject); if (aScriptGlobalObject) { - if (!GetRootContent()) { + if (!mRootContent) { // Create synthetic document nsresult rv = CreateSyntheticDocument(); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create synthetic document"); @@ -606,7 +606,7 @@ nsImageDocument::CreateSyntheticDocument() nsresult rv = nsMediaDocument::CreateSyntheticDocument(); NS_ENSURE_SUCCESS(rv, rv); - nsIContent* body = GetBodyContent(); + nsCOMPtr body = do_QueryInterface(mBodyContent); if (!body) { NS_WARNING("no body on image document!"); return NS_ERROR_FAILURE; @@ -651,10 +651,7 @@ nsImageDocument::CheckOverflowing(PRBool changeState) nsPresContext *context = shell->GetPresContext(); nsRect visibleArea = context->GetVisibleArea(); - nsIContent* content = GetBodyContent(); - if (!content) { - return NS_OK; - } + nsCOMPtr content = do_QueryInterface(mBodyContent); nsRefPtr styleContext = context->StyleSet()->ResolveStyleFor(content, nsnull); diff --git a/content/html/document/src/nsMediaDocument.cpp b/content/html/document/src/nsMediaDocument.cpp index 1f280ff6eec..5668a6156fd 100644 --- a/content/html/document/src/nsMediaDocument.cpp +++ b/content/html/document/src/nsMediaDocument.cpp @@ -256,6 +256,7 @@ nsMediaDocument::CreateSyntheticDocument() if (!body) { return NS_ERROR_OUT_OF_MEMORY; } + mBodyContent = do_QueryInterface(body); root->AppendChildTo(body, PR_FALSE); diff --git a/content/html/document/src/nsPluginDocument.cpp b/content/html/document/src/nsPluginDocument.cpp index 5c23b27d0e5..1b6e6129040 100644 --- a/content/html/document/src/nsPluginDocument.cpp +++ b/content/html/document/src/nsPluginDocument.cpp @@ -222,7 +222,7 @@ nsPluginDocument::CreateSyntheticPluginDocument() NS_ENSURE_SUCCESS(rv, rv); // then attach our plugin - nsIContent* body = GetBodyContent(); + nsCOMPtr body = do_QueryInterface(mBodyContent); if (!body) { NS_WARNING("no body on plugin document!"); return NS_ERROR_FAILURE; diff --git a/content/svg/content/src/nsSVGSVGElement.cpp b/content/svg/content/src/nsSVGSVGElement.cpp index 53e9a6a3470..990d659e4b0 100644 --- a/content/svg/content/src/nsSVGSVGElement.cpp +++ b/content/svg/content/src/nsSVGSVGElement.cpp @@ -129,12 +129,6 @@ protected: // implementation helpers: void GetOffsetToAncestor(nsIContent* ancestor, float &x, float &y); - PRBool IsRoot() { - NS_ASSERTION((IsInDoc() && !GetParent()) == - (GetOwnerDoc() && (GetOwnerDoc()->GetRootContent() == this)), - "Can't determine if we're root"); - return IsInDoc() && !GetParent(); - } nsCOMPtr mWidth; nsCOMPtr mHeight; @@ -990,7 +984,8 @@ nsSVGSVGElement::GetCTM(nsIDOMSVGMatrix **_retval) if (!ancestorCTM) { // we didn't find an SVG ancestor float s=1, x=0, y=0; - if (IsRoot()) { + if (ownerDoc && + ownerDoc->GetRootContent() == NS_STATIC_CAST(nsIContent*, this)) { // we're the root element. get our currentScale and currentTranslate vals mCurrentScale->GetValue(&s); mCurrentTranslate->GetX(&x); @@ -1098,7 +1093,8 @@ nsSVGSVGElement::GetScreenCTM(nsIDOMSVGMatrix **_retval) if (!ancestorScreenCTM) { // we didn't find an SVG ancestor float s=1, x=0, y=0; - if (IsRoot()) { + if (ownerDoc && + ownerDoc->GetRootContent() == NS_STATIC_CAST(nsIContent*, this)) { // we're the root element. get our currentScale and currentTranslate vals mCurrentScale->GetValue(&s); mCurrentTranslate->GetX(&x); @@ -1276,7 +1272,8 @@ nsSVGSVGElement::SetCurrentScaleTranslate(float s, float x, float y) if (doc) { nsIPresShell* presShell = doc->GetShellAt(0); NS_ASSERTION(presShell, "no presShell"); - if (presShell && IsRoot()) { + if (presShell && + doc->GetRootContent() == NS_STATIC_CAST(nsIContent*, this)) { nsEventStatus status = nsEventStatus_eIgnore; nsGUIEvent event(PR_TRUE, NS_SVG_ZOOM, 0); event.eventStructType = NS_SVGZOOM_EVENT; @@ -1300,7 +1297,8 @@ nsSVGSVGElement::SetCurrentTranslate(float x, float y) if (doc) { nsIPresShell* presShell = doc->GetShellAt(0); NS_ASSERTION(presShell, "no presShell"); - if (presShell && IsRoot()) { + if (presShell && + doc->GetRootContent() == NS_STATIC_CAST(nsIContent*, this)) { nsEventStatus status = nsEventStatus_eIgnore; nsEvent event(PR_TRUE, NS_SVG_SCROLL); event.eventStructType = NS_SVG_EVENT; @@ -1402,7 +1400,8 @@ nsSVGSVGElement::DidModifySVGObservable (nsISVGValue* observable, // dispatch an SVGZoom or SVGScroll DOM event before repainting nsCOMPtr n = do_QueryInterface(observable); if (n && n==mCurrentScale) { - if (mDispatchEvent && IsRoot()) { + if (mDispatchEvent && + doc->GetRootContent() == NS_STATIC_CAST(nsIContent*, this)) { nsEventStatus status = nsEventStatus_eIgnore; nsGUIEvent event(PR_TRUE, NS_SVG_ZOOM, 0); event.eventStructType = NS_SVGZOOM_EVENT; @@ -1415,7 +1414,8 @@ nsSVGSVGElement::DidModifySVGObservable (nsISVGValue* observable, else { nsCOMPtr p = do_QueryInterface(observable); if (p && p==mCurrentTranslate) { - if (mDispatchEvent && IsRoot()) { + if (mDispatchEvent && + doc->GetRootContent() == NS_STATIC_CAST(nsIContent*, this)) { nsEventStatus status = nsEventStatus_eIgnore; nsEvent event(PR_TRUE, NS_SVG_SCROLL); event.eventStructType = NS_SVG_EVENT; diff --git a/content/svg/document/src/nsSVGDocument.cpp b/content/svg/document/src/nsSVGDocument.cpp index 25f63896296..f8b730509b6 100644 --- a/content/svg/document/src/nsSVGDocument.cpp +++ b/content/svg/document/src/nsSVGDocument.cpp @@ -121,10 +121,7 @@ nsSVGDocument::GetURL(nsAString& aURL) NS_IMETHODIMP nsSVGDocument::GetRootElement(nsIDOMSVGSVGElement** aRootElement) { - *aRootElement = nsnull; - nsIContent* root = GetRootContent(); - - return root ? CallQueryInterface(root, aRootElement) : NS_OK; + return CallQueryInterface(mRootContent, aRootElement); } //////////////////////////////////////////////////////////////////////// diff --git a/content/xml/document/src/nsXMLDocument.cpp b/content/xml/document/src/nsXMLDocument.cpp index fffad7848de..f002e112198 100644 --- a/content/xml/document/src/nsXMLDocument.cpp +++ b/content/xml/document/src/nsXMLDocument.cpp @@ -546,7 +546,7 @@ nsXMLDocument::Load(const nsAString& aUrl, PRBool *aReturn) mEventQService->PopThreadEventQueue(modalEventQueue); // We set return to true unless there was a parsing error - nsCOMPtr node = do_QueryInterface(GetRootContent()); + nsCOMPtr node = do_QueryInterface(mRootContent); if (node) { nsAutoString name, ns; if (NS_SUCCEEDED(node->GetLocalName(name)) && @@ -771,15 +771,14 @@ nsXMLDocument::GetElementById(const nsAString& aElementId, // If we tried to load a document and something went wrong, we might not have // root content. This can happen when you do document.load() and the document // to load is not XML, for example. - nsIContent* root = GetRootContent(); - if (!root) + if (!mRootContent) return NS_OK; // XXX For now, we do a brute force search of the content tree. // We should come up with a more efficient solution. // Note that content is *not* refcounted here, so do *not* release it! nsIContent *content = - nsContentUtils::MatchElementId(root, aElementId); + nsContentUtils::MatchElementId(mRootContent, aElementId); if (!content) { return NS_OK; diff --git a/content/xslt/src/xml/txXMLParser.cpp b/content/xslt/src/xml/txXMLParser.cpp index d718cad26cd..96d516ff287 100644 --- a/content/xslt/src/xml/txXMLParser.cpp +++ b/content/xslt/src/xml/txXMLParser.cpp @@ -85,8 +85,9 @@ class txXMLParser #endif nsresult -txParseDocumentFromURI(const nsAString& aHref, const txXPathNode& aLoader, - nsAString& aErrMsg, txXPathNode** aResult) +txParseDocumentFromURI(const nsAString& aHref, const nsAString& aReferrer, + const txXPathNode& aLoader, nsAString& aErrMsg, + txXPathNode** aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = nsnull; @@ -108,7 +109,11 @@ txParseDocumentFromURI(const nsAString& aHref, const txXPathNode& aLoader, nsCOMPtr http = do_QueryInterface(channel); if (http) { - http->SetReferrer(loaderUri); + nsCOMPtr refUri; + NS_NewURI(getter_AddRefs(refUri), aReferrer); + if (refUri) { + http->SetReferrer(refUri); + } http->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), NS_LITERAL_CSTRING("text/xml,application/xml,application/xhtml+xml,*/*;q=0.1"), PR_FALSE); diff --git a/content/xslt/src/xml/txXMLParser.h b/content/xslt/src/xml/txXMLParser.h index 7eb404b70c3..272d6111f3e 100644 --- a/content/xslt/src/xml/txXMLParser.h +++ b/content/xslt/src/xml/txXMLParser.h @@ -57,8 +57,9 @@ class txXPathNode; * of the document aLoader. */ extern "C" nsresult -txParseDocumentFromURI(const nsAString& aHref, const txXPathNode& aLoader, - nsAString& aErrMsg, txXPathNode** aResult); +txParseDocumentFromURI(const nsAString& aHref, const nsAString& aReferrer, + const txXPathNode& aLoader, nsAString& aErrMsg, + txXPathNode** aResult); #ifdef TX_EXE /** diff --git a/content/xslt/src/xslt/txExecutionState.cpp b/content/xslt/src/xslt/txExecutionState.cpp index 5ff8413bf6c..e20514cf24a 100644 --- a/content/xslt/src/xslt/txExecutionState.cpp +++ b/content/xslt/src/xslt/txExecutionState.cpp @@ -474,12 +474,13 @@ txExecutionState::retrieveDocument(const nsAString& aUri) if (!entry->mDocument) { // open URI - nsAutoString errMsg; - // XXX we should get the loader from the actual node + nsAutoString errMsg, refUri; + // XXX we should get the referrer from the actual node // triggering the load, but this will do for the time being + txXPathNodeUtils::getBaseURI(*mLoadedDocuments.mSourceDocument, refUri); nsresult rv; - rv = txParseDocumentFromURI(aUri, *mLoadedDocuments.mSourceDocument, - errMsg, + rv = txParseDocumentFromURI(aUri, refUri, + *mLoadedDocuments.mSourceDocument, errMsg, getter_Transfers(entry->mDocument)); if (NS_FAILED(rv) || !entry->mDocument) { diff --git a/content/xslt/src/xslt/txMozillaStylesheetCompiler.cpp b/content/xslt/src/xslt/txMozillaStylesheetCompiler.cpp index b9b89a82eea..31ab9e12a6e 100644 --- a/content/xslt/src/xslt/txMozillaStylesheetCompiler.cpp +++ b/content/xslt/src/xslt/txMozillaStylesheetCompiler.cpp @@ -799,44 +799,24 @@ void txSyncCompileObserver::onDoneCompiling(txStylesheetCompiler* aCompiler, nsresult TX_CompileStylesheet(nsIDOMNode* aNode, txStylesheet** aStylesheet) { - // If we move GetBaseURI to nsINode this can be simplified. - nsCOMPtr uri; - nsCOMPtr doc; - nsCOMPtr cont = do_QueryInterface(aNode); - if (cont) { - doc = cont->GetOwnerDoc(); - NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); - - uri = cont->GetBaseURI(); - } - else { - doc = do_QueryInterface(aNode); - NS_ASSERTION(doc, "aNode should be a doc or an element by now"); - - uri = doc->GetBaseURI(); + nsCOMPtr document; + aNode->GetOwnerDocument(getter_AddRefs(document)); + if (!document) { + document = do_QueryInterface(aNode); } - NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE); - - nsCAutoString spec; - uri->GetSpec(spec); - NS_ConvertUTF8toUTF16 baseURI(spec); - - uri = doc->GetDocumentURI(); - NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE); - - uri->GetSpec(spec); - NS_ConvertUTF8toUTF16 stylesheetURI(spec); + nsCOMPtr doc = do_QueryInterface(document); + nsIURI *uri = doc->GetBaseURI(); + nsCAutoString baseURI; + uri->GetSpec(baseURI); nsRefPtr obs = new txSyncCompileObserver(); NS_ENSURE_TRUE(obs, NS_ERROR_OUT_OF_MEMORY); - + NS_ConvertUTF8toUTF16 base(baseURI); nsRefPtr compiler = - new txStylesheetCompiler(stylesheetURI, obs); + new txStylesheetCompiler(base, obs); NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY); - compiler->setBaseURI(baseURI); - nsresult rv = handleNode(aNode, compiler); if (NS_FAILED(rv)) { compiler->cancel(rv); diff --git a/content/xslt/src/xslt/txStylesheetCompiler.cpp b/content/xslt/src/xslt/txStylesheetCompiler.cpp index ddb45806144..05637781d08 100644 --- a/content/xslt/src/xslt/txStylesheetCompiler.cpp +++ b/content/xslt/src/xslt/txStylesheetCompiler.cpp @@ -50,20 +50,20 @@ #include "txStringUtils.h" #include "txXSLTFunctions.h" -txStylesheetCompiler::txStylesheetCompiler(const nsAString& aStylesheetURI, +txStylesheetCompiler::txStylesheetCompiler(const nsAString& aBaseURI, txACompileObserver* aObserver) : txStylesheetCompilerState(aObserver) { - mStatus = init(aStylesheetURI, nsnull, nsnull); + mStatus = init(aBaseURI, nsnull, nsnull); } -txStylesheetCompiler::txStylesheetCompiler(const nsAString& aStylesheetURI, +txStylesheetCompiler::txStylesheetCompiler(const nsAString& aBaseURI, txStylesheet* aStylesheet, txListIterator* aInsertPosition, txACompileObserver* aObserver) : txStylesheetCompilerState(aObserver) { - mStatus = init(aStylesheetURI, aStylesheet, aInsertPosition); + mStatus = init(aBaseURI, aStylesheet, aInsertPosition); } nsrefcnt @@ -83,19 +83,6 @@ txStylesheetCompiler::Release() return mRefCnt; } -void -txStylesheetCompiler::setBaseURI(const nsString& aBaseURI) -{ - NS_ASSERTION(mObjectStack.size() == 1 && !mObjectStack.peek(), - "Execution already started"); - - if (NS_FAILED(mStatus)) { - return; - } - - mElementContext->mBaseURI = aBaseURI; -} - nsresult txStylesheetCompiler::startElement(PRInt32 aNamespaceID, nsIAtom* aLocalName, nsIAtom* aPrefix, @@ -407,7 +394,7 @@ txStylesheetCompiler::doneLoading() { PR_LOG(txLog::xslt, PR_LOG_ALWAYS, ("Compiler::doneLoading: %s\n", - NS_LossyConvertUTF16toASCII(mStylesheetURI).get())); + NS_LossyConvertUTF16toASCII(mURI).get())); if (NS_FAILED(mStatus)) { return mStatus; } @@ -423,7 +410,7 @@ txStylesheetCompiler::cancel(nsresult aError, const PRUnichar *aErrorText, { PR_LOG(txLog::xslt, PR_LOG_ALWAYS, ("Compiler::cancel: %s, module: %d, code %d\n", - NS_LossyConvertUTF16toASCII(mStylesheetURI).get(), + NS_LossyConvertUTF16toASCII(mURI).get(), NS_ERROR_GET_MODULE(aError), NS_ERROR_GET_CODE(aError))); if (NS_SUCCEEDED(mStatus)) { @@ -452,8 +439,8 @@ txStylesheetCompiler::loadURI(const nsAString& aUri, PR_LOG(txLog::xslt, PR_LOG_ALWAYS, ("Compiler::loadURI forwards %s thru %s\n", NS_LossyConvertUTF16toASCII(aUri).get(), - NS_LossyConvertUTF16toASCII(mStylesheetURI).get())); - if (mStylesheetURI.Equals(aUri)) { + NS_LossyConvertUTF16toASCII(mURI).get())); + if (mURI.Equals(aUri)) { return NS_ERROR_XSLT_LOAD_RECURSION; } return mObserver ? mObserver->loadURI(aUri, aReferrerUri, aCompiler) : @@ -567,21 +554,21 @@ txStylesheetCompilerState::txStylesheetCompilerState(txACompileObserver* aObserv } nsresult -txStylesheetCompilerState::init(const nsAString& aStylesheetURI, +txStylesheetCompilerState::init(const nsAString& aBaseURI, txStylesheet* aStylesheet, txListIterator* aInsertPosition) { NS_ASSERTION(!aStylesheet || aInsertPosition, "must provide insertposition if loading subsheet"); - mStylesheetURI = aStylesheetURI; + mURI = aBaseURI; // Check for fragment identifier of an embedded stylesheet. - PRInt32 fragment = aStylesheetURI.FindChar('#') + 1; + PRInt32 fragment = aBaseURI.FindChar('#') + 1; if (fragment > 0) { - PRInt32 fragmentLength = aStylesheetURI.Length() - fragment; + PRInt32 fragmentLength = aBaseURI.Length() - fragment; if (fragmentLength > 0) { // This is really an embedded stylesheet, not just a // "url#". We may want to unescape the fragment. - mTarget = Substring(aStylesheetURI, (PRUint32)fragment, + mTarget = Substring(aBaseURI, (PRUint32)fragment, fragmentLength); mEmbedStatus = eNeedEmbed; mHandlerTable = gTxEmbedHandler; @@ -606,7 +593,7 @@ txStylesheetCompilerState::init(const nsAString& aStylesheetURI, mIsTopCompiler = PR_TRUE; } - mElementContext = new txElementContext(aStylesheetURI); + mElementContext = new txElementContext(aBaseURI); NS_ENSURE_TRUE(mElementContext && mElementContext->mMappings, NS_ERROR_OUT_OF_MEMORY); @@ -763,7 +750,7 @@ txStylesheetCompilerState::loadIncludedStylesheet(const nsAString& aURI) PR_LOG(txLog::xslt, PR_LOG_ALWAYS, ("CompilerState::loadIncludedStylesheet: %s\n", NS_LossyConvertUTF16toASCII(aURI).get())); - if (mStylesheetURI.Equals(aURI)) { + if (mURI.Equals(aURI)) { return NS_ERROR_XSLT_LOAD_RECURSION; } NS_ENSURE_TRUE(mObserver, NS_ERROR_NOT_IMPLEMENTED); @@ -793,7 +780,7 @@ txStylesheetCompilerState::loadIncludedStylesheet(const nsAString& aURI) return NS_ERROR_OUT_OF_MEMORY; } - rv = mObserver->loadURI(aURI, mStylesheetURI, compiler); + rv = mObserver->loadURI(aURI, mURI, compiler); if (NS_FAILED(rv)) { mChildCompilerList.RemoveElement(compiler); } @@ -808,7 +795,7 @@ txStylesheetCompilerState::loadImportedStylesheet(const nsAString& aURI, PR_LOG(txLog::xslt, PR_LOG_ALWAYS, ("CompilerState::loadImportedStylesheet: %s\n", NS_LossyConvertUTF16toASCII(aURI).get())); - if (mStylesheetURI.Equals(aURI)) { + if (mURI.Equals(aURI)) { return NS_ERROR_XSLT_LOAD_RECURSION; } NS_ENSURE_TRUE(mObserver, NS_ERROR_NOT_IMPLEMENTED); @@ -826,7 +813,7 @@ txStylesheetCompilerState::loadImportedStylesheet(const nsAString& aURI, return NS_ERROR_OUT_OF_MEMORY; } - nsresult rv = mObserver->loadURI(aURI, mStylesheetURI, compiler); + nsresult rv = mObserver->loadURI(aURI, mURI, compiler); if (NS_FAILED(rv)) { mChildCompilerList.RemoveElement(compiler); } diff --git a/content/xslt/src/xslt/txStylesheetCompiler.h b/content/xslt/src/xslt/txStylesheetCompiler.h index bf724bb1b2b..ce69e31e6ce 100644 --- a/content/xslt/src/xslt/txStylesheetCompiler.h +++ b/content/xslt/src/xslt/txStylesheetCompiler.h @@ -99,7 +99,7 @@ public: txStylesheetCompilerState(txACompileObserver* aObserver); ~txStylesheetCompilerState(); - nsresult init(const nsAString& aStylesheetURI, txStylesheet* aStylesheet, + nsresult init(const nsAString& aBaseURI, txStylesheet* aStylesheet, txListIterator* aInsertPosition); // Embedded stylesheets state @@ -175,13 +175,13 @@ protected: eInEmbed, eHasEmbed } mEmbedStatus; - nsString mStylesheetURI; + nsString mURI; PRPackedBool mIsTopCompiler; PRPackedBool mDoneWithThisStylesheet; - txStack mObjectStack; - txStack mOtherStack; private: + txStack mObjectStack; + txStack mOtherStack; txInstruction** mNextInstrPtr; txListIterator mToplevelIterator; nsVoidArray mGotoTargetPointers; @@ -200,17 +200,15 @@ class txStylesheetCompiler : private txStylesheetCompilerState, { public: friend class txStylesheetCompilerState; - txStylesheetCompiler(const nsAString& aStylesheetURI, + txStylesheetCompiler(const nsAString& aBaseURI, txACompileObserver* aObserver); - txStylesheetCompiler(const nsAString& aStylesheetURI, + txStylesheetCompiler(const nsAString& aBaseURI, txStylesheet* aStylesheet, txListIterator* aInsertPosition, txACompileObserver* aObserver); virtual nsrefcnt AddRef(); virtual nsrefcnt Release(); - void setBaseURI(const nsString& aBaseURI); - nsresult startElement(PRInt32 aNamespaceID, nsIAtom* aLocalName, nsIAtom* aPrefix, txStylesheetAttr* aAttributes, PRInt32 aAttrCount); diff --git a/content/xul/content/src/nsXULElement.cpp b/content/xul/content/src/nsXULElement.cpp index c823ce53624..6694b665cc9 100644 --- a/content/xul/content/src/nsXULElement.cpp +++ b/content/xul/content/src/nsXULElement.cpp @@ -1019,8 +1019,6 @@ nsXULElement::RemoveChildAt(PRUint32 aIndex, PRBool aNotify) nsresult rv = EnsureContentsGenerated(); NS_ENSURE_SUCCESS(rv, rv); - nsMutationGuard::DidMutate(); - nsCOMPtr oldKid = mAttrsAndChildren.ChildAt(aIndex); NS_ENSURE_TRUE(oldKid, NS_ERROR_FAILURE); @@ -1080,7 +1078,7 @@ nsXULElement::RemoveChildAt(PRUint32 aIndex, PRBool aNotify) nsCOMPtr curItem; controlElement->GetCurrentItem(getter_AddRefs(curItem)); nsCOMPtr curNode = do_QueryInterface(curItem); - if (curNode && nsContentUtils::ContentIsDescendantOf(curNode, oldKid)) { + if (curNode && isSelfOrAncestor(curNode, oldKid)) { // Current item going away nsCOMPtr box; controlElement->GetBoxObject(getter_AddRefs(box)); diff --git a/content/xul/document/src/nsXULDocument.cpp b/content/xul/document/src/nsXULDocument.cpp index 85c9c748881..e2ebad8dc2c 100644 --- a/content/xul/document/src/nsXULDocument.cpp +++ b/content/xul/document/src/nsXULDocument.cpp @@ -1429,7 +1429,7 @@ nsXULDocument::GetPixelDimensions(nsIPresShell* aShell, PRInt32* aWidth, FlushPendingNotifications(Flush_Layout); - nsIFrame* frame = aShell->GetPrimaryFrameFor(GetRootContent()); + nsIFrame* frame = aShell->GetPrimaryFrameFor(mRootContent); if (frame) { nsIView* view = frame->GetView(); // If we have a view check if it's scrollable. If not, @@ -1963,7 +1963,7 @@ nsXULDocument::Init() nsresult nsXULDocument::StartLayout(void) { - if (!GetRootContent()) { + if (!mRootContent) { #ifdef PR_LOGGING if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING)) { nsCAutoString urlspec; @@ -2950,9 +2950,7 @@ nsXULDocument::ResumeWalk() mDocumentLoaded = PR_TRUE; nsAutoString title; - nsIContent *rootContent = GetRootContent(); - if (rootContent) - rootContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::title, title); + mRootContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::title, title); SetTitle(title); StartLayout(); @@ -3603,7 +3601,7 @@ nsXULDocument::OverlayForwardReference::Resolve() mOverlay->GetAttr(kNameSpaceID_None, nsXULAtoms::id, id); if (id.IsEmpty()) { // overlay had no id, use the root element - mDocument->InsertElement(mDocument->GetRootContent(), mOverlay, notify); + mDocument->InsertElement(mDocument->mRootContent, mOverlay, notify); mResolved = PR_TRUE; return eResolve_Succeeded; } diff --git a/content/xul/templates/src/nsXULContentUtils.cpp b/content/xul/templates/src/nsXULContentUtils.cpp index 5c3b7479da7..90cc48ccd79 100644 --- a/content/xul/templates/src/nsXULContentUtils.cpp +++ b/content/xul/templates/src/nsXULContentUtils.cpp @@ -333,7 +333,7 @@ nsXULContentUtils::MakeElementURI(nsIDocument* aDocument, const nsAString& aElem CopyUTF16toUTF8(aElementID, aURI); } else { - nsIURI *docURL = aDocument->GetDocumentURI(); + nsIURI *docURL = aDocument->GetBaseURI(); // XXX Urgh. This is so broken; I'd really just like to use // NS_MakeAbsolueURI(). Unfortunatly, doing that breaks @@ -389,7 +389,7 @@ nsXULContentUtils::MakeElementID(nsIDocument* aDocument, const nsAString& aURI, // Convert a URI into an element ID that can be accessed from the // DOM APIs. nsCAutoString spec; - aDocument->GetDocumentURI()->GetSpec(spec); + aDocument->GetBaseURI()->GetSpec(spec); // XXX FIX ME to not do a copy nsAutoString str(aURI); diff --git a/extensions/xforms/nsXFormsModelElement.cpp b/extensions/xforms/nsXFormsModelElement.cpp index 39604ee319b..79f2ef8fd1d 100644 --- a/extensions/xforms/nsXFormsModelElement.cpp +++ b/extensions/xforms/nsXFormsModelElement.cpp @@ -411,8 +411,6 @@ nsXFormsModelElement::InitializeInstances() // Parse the whitespace-separated list. nsCOMPtr content = do_QueryInterface(mElement); nsRefPtr baseURI = content->GetBaseURI(); - nsRefPtr docURI = content->GetOwnerDoc() ? - content->GetOwnerDoc()->GetDocumentURI() : nsnull; nsCStringArray schemas; schemas.ParseString(NS_ConvertUTF16toUTF8(schemaList).get(), " \t\r\n"); @@ -434,7 +432,7 @@ nsXFormsModelElement::InitializeInstances() newURL->GetRef(ref); newURL->SetRef(EmptyCString()); PRBool equals = PR_FALSE; - newURL->Equals(docURI, &equals); + newURL->Equals(baseURI, &equals); if (equals) { // We will not be able to locate the element using the // getElementById function defined on our document when diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index 718745d86c0..6f84c5b97dc 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -762,13 +762,13 @@ nsPresContext::SetShell(nsIPresShell* aShell) nsIDocument *doc = mShell->GetDocument(); NS_ASSERTION(doc, "expect document here"); if (doc) { - nsIURI *docURI = doc->GetDocumentURI(); + nsIURI *baseURI = doc->GetBaseURI(); - if (mMedium != nsLayoutAtoms::print && docURI) { + if (mMedium != nsLayoutAtoms::print && baseURI) { PRBool isChrome = PR_FALSE; PRBool isRes = PR_FALSE; - docURI->SchemeIs("chrome", &isChrome); - docURI->SchemeIs("resource", &isRes); + baseURI->SchemeIs("chrome", &isChrome); + baseURI->SchemeIs("resource", &isRes); if (!isChrome && !isRes) mImageAnimationMode = mImageAnimationModePref; diff --git a/layout/forms/nsIsIndexFrame.cpp b/layout/forms/nsIsIndexFrame.cpp index efea1d8fbb3..7fa85bf2340 100644 --- a/layout/forms/nsIsIndexFrame.cpp +++ b/layout/forms/nsIsIndexFrame.cpp @@ -371,8 +371,8 @@ nsIsIndexFrame::OnSubmit(nsPresContext* aPresContext) if (!document) return NS_OK; // No doc means don't submit, see Bug 28988 // Resolve url to an absolute url - nsIURI *baseURI = document->GetBaseURI(); - if (!baseURI) { + nsIURI *docURL = document->GetBaseURI(); + if (!docURL) { NS_ERROR("No Base URL found in Form Submit!\n"); return NS_OK; // No base URL -> exit early, see Bug 30721 } @@ -396,7 +396,7 @@ nsIsIndexFrame::OnSubmit(nsPresContext* aPresContext) // Necko's MakeAbsoluteURI doesn't reuse the baseURL's rel path if it is // passed a zero length rel path. nsCAutoString relPath; - baseURI->GetSpec(relPath); + docURL->GetSpec(relPath); if (!relPath.IsEmpty()) { CopyUTF8toUTF16(relPath, href); @@ -420,7 +420,7 @@ nsIsIndexFrame::OnSubmit(nsPresContext* aPresContext) if (NS_SUCCEEDED(result = NS_NewURI(getter_AddRefs(actionURL), href, flatDocCharset.get(), - baseURI))) { + docURL))) { result = actionURL->SchemeIs("javascript", &isJSURL); } // Append the URI encoded variable/value pairs for GET's @@ -436,7 +436,7 @@ nsIsIndexFrame::OnSubmit(nsPresContext* aPresContext) } nsCOMPtr uri; result = NS_NewURI(getter_AddRefs(uri), href, - flatDocCharset.get(), baseURI); + flatDocCharset.get(), docURL); if (NS_FAILED(result)) return result; // Now pass on absolute url to the click handler