diff --git a/content/base/public/nsReferencedElement.h b/content/base/public/nsReferencedElement.h index d613d560abfb..5ad116cf1c93 100644 --- a/content/base/public/nsReferencedElement.h +++ b/content/base/public/nsReferencedElement.h @@ -70,9 +70,6 @@ public: nsReferencedElement() {} ~nsReferencedElement() { Unlink(); - if (mPendingNotification) { - mPendingNotification->Clear(); - } } /** @@ -114,15 +111,42 @@ protected: * a persistent notification. */ virtual PRBool IsPersistent() { return PR_FALSE; } + + /** + * Set ourselves up with our new document. Note that aDocument might be + * null. Either aWatch must be false or aRef must be empty. + */ + void HaveNewDocument(nsIDocument* aDocument, PRBool aWatch, + const nsString& aRef); private: static PRBool Observe(nsIContent* aOldContent, nsIContent* aNewContent, void* aData); - class Notification : public nsRunnable { + class Notification : public nsISupports { public: - Notification(nsReferencedElement* aTarget, nsIContent* aFrom, nsIContent* aTo) - : mTarget(aTarget), mFrom(aFrom), mTo(aTo) {} + virtual void SetTo(nsIContent* aTo) = 0; + virtual void Clear() { mTarget = nsnull; } + virtual ~Notification() {} + protected: + Notification(nsReferencedElement* aTarget) + : mTarget(aTarget) + { + NS_PRECONDITION(aTarget, "Must have a target"); + } + nsReferencedElement* mTarget; + }; + + class ChangeNotification : public nsRunnable, + public Notification + { + public: + ChangeNotification(nsReferencedElement* aTarget, nsIContent* aFrom, nsIContent* aTo) + : Notification(aTarget), mFrom(aFrom), mTo(aTo) + {} + virtual ~ChangeNotification() {} + + NS_DECL_ISUPPORTS_INHERITED NS_IMETHOD Run() { if (mTarget) { mTarget->mPendingNotification = nsnull; @@ -130,15 +154,40 @@ private: } return NS_OK; } - void SetTo(nsIContent* aTo) { mTo = aTo; } - void Clear() { mTarget = nsnull; mFrom = nsnull; mTo = nsnull; } - private: - nsReferencedElement* mTarget; + virtual void SetTo(nsIContent* aTo) { mTo = aTo; } + virtual void Clear() + { + Notification::Clear(); mFrom = nsnull; mTo = nsnull; + } + protected: nsCOMPtr mFrom; nsCOMPtr mTo; }; - friend class Notification; + friend class ChangeNotification; + class DocumentLoadNotification : public Notification, + public nsIObserver + { + public: + DocumentLoadNotification(nsReferencedElement* aTarget, + const nsString& aRef) : + Notification(aTarget) + { + if (!mTarget->IsPersistent()) { + mRef = aRef; + } + } + virtual ~DocumentLoadNotification() {} + + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + private: + virtual void SetTo(nsIContent* aTo) { } + + nsString mRef; + }; + friend class DocumentLoadNotification; + nsCOMPtr mWatchID; nsCOMPtr mWatchDocument; nsCOMPtr mContent; diff --git a/content/base/src/nsReferencedElement.cpp b/content/base/src/nsReferencedElement.cpp index eb0cd1addd93..a5458e52b2d7 100644 --- a/content/base/src/nsReferencedElement.cpp +++ b/content/base/src/nsReferencedElement.cpp @@ -128,8 +128,26 @@ nsReferencedElement::Reset(nsIContent* aFromContent, nsIURI* aURI, PRBool aWatch return; if (!EqualExceptRef(url, documentURL)) { - // Oops -- we don't support off-document references - return; + // Don't take the XBL codepath here, since we'll want to just + // normally set up our external resource document and then watch + // it as needed. + isXBL = PR_FALSE; + nsRefPtr load; + doc = doc->RequestExternalResource(url, aFromContent, getter_AddRefs(load)); + if (!doc) { + if (!load || !aWatch) { + // Nothing will ever happen here + return; + } + + DocumentLoadNotification* observer = + new DocumentLoadNotification(this, ref); + mPendingNotification = observer; + if (observer) { + load->AddObserver(observer); + } + // Keep going so we set up our watching stuff a bit + } } // Get the element @@ -150,6 +168,8 @@ nsReferencedElement::Reset(nsIContent* aFromContent, nsIURI* aURI, PRBool aWatch } } } + + // We don't have watching working yet for XBL, so bail out here. return; } @@ -158,16 +178,31 @@ nsReferencedElement::Reset(nsIContent* aFromContent, nsIURI* aURI, PRBool aWatch if (!atom) return; atom.swap(mWatchID); - mWatchDocument = doc; - mContent = mWatchDocument->AddIDTargetObserver(mWatchID, Observe, this); + } + + HaveNewDocument(doc, aWatch, ref); +} + +void +nsReferencedElement::HaveNewDocument(nsIDocument* aDocument, PRBool aWatch, + const nsString& aRef) +{ + if (aWatch) { + mWatchDocument = aDocument; + if (mWatchDocument) { + mContent = mWatchDocument->AddIDTargetObserver(mWatchID, Observe, this); + } return; } - nsCOMPtr domDoc = do_QueryInterface(doc); + if (!aDocument) { + return; + } + nsCOMPtr domDoc = do_QueryInterface(aDocument); NS_ASSERTION(domDoc, "Content doesn't reference a dom Document"); nsCOMPtr element; - rv = domDoc->GetElementById(ref, getter_AddRefs(element)); + domDoc->GetElementById(aRef, getter_AddRefs(element)); if (element) { mContent = do_QueryInterface(element); } @@ -186,6 +221,9 @@ nsReferencedElement::Unlink() if (mWatchDocument && mWatchID) { mWatchDocument->RemoveIDTargetObserver(mWatchID, Observe, this); } + if (mPendingNotification) { + mPendingNotification->Clear(); + } mWatchDocument = nsnull; mWatchID = nsnull; mContent = nsnull; @@ -200,8 +238,10 @@ nsReferencedElement::Observe(nsIContent* aOldContent, p->mPendingNotification->SetTo(aNewContent); } else { NS_ASSERTION(aOldContent == p->mContent, "Failed to track content!"); - p->mPendingNotification = new Notification(p, aOldContent, aNewContent); - nsContentUtils::AddScriptRunner(p->mPendingNotification); + ChangeNotification* watcher = + new ChangeNotification(p, aOldContent, aNewContent); + p->mPendingNotification = watcher; + nsContentUtils::AddScriptRunner(watcher); } PRBool keepTracking = p->IsPersistent(); if (!keepTracking) { @@ -210,3 +250,28 @@ nsReferencedElement::Observe(nsIContent* aOldContent, } return keepTracking; } + +NS_IMPL_ISUPPORTS_INHERITED0(nsReferencedElement::ChangeNotification, + nsRunnable) + +NS_IMPL_ISUPPORTS1(nsReferencedElement::DocumentLoadNotification, + nsIObserver) + +NS_IMETHODIMP +nsReferencedElement::DocumentLoadNotification::Observe(nsISupports* aSubject, + const char* aTopic, + const PRUnichar* aData) +{ + NS_ASSERTION(PL_strcmp(aTopic, "external-resource-document-created") == 0, + "Unexpected topic"); + if (mTarget) { + nsCOMPtr doc = do_QueryInterface(aSubject); + mTarget->mPendingNotification = nsnull; + NS_ASSERTION(!mTarget->mContent, "Why do we have content here?"); + // If we got here, that means we had Reset() called with aWatch == + // PR_TRUE. So keep watching if IsPersistent(). + mTarget->HaveNewDocument(doc, mTarget->IsPersistent(), mRef); + mTarget->ContentChanged(nsnull, mTarget->mContent); + } + return NS_OK; +} diff --git a/content/svg/content/src/nsSVGUseElement.cpp b/content/svg/content/src/nsSVGUseElement.cpp index e03a36490442..1e218f76bc7d 100644 --- a/content/svg/content/src/nsSVGUseElement.cpp +++ b/content/svg/content/src/nsSVGUseElement.cpp @@ -300,7 +300,10 @@ nsSVGUseElement::CreateAnonymousContent() nsCOMPtr newnode; nsCOMArray unused; - nsNodeUtils::Clone(targetContent, PR_TRUE, nsnull, unused, + nsNodeInfoManager* nodeInfoManager = + targetContent->GetOwnerDoc() == GetOwnerDoc() ? + nsnull : GetOwnerDoc()->NodeInfoManager(); + nsNodeUtils::Clone(targetContent, PR_TRUE, nodeInfoManager, unused, getter_AddRefs(newnode)); nsCOMPtr newcontent = do_QueryInterface(newnode); @@ -416,7 +419,8 @@ nsSVGUseElement::LookupHref() if (href.IsEmpty()) return; - nsCOMPtr targetURI, baseURI = GetBaseURI(); + nsCOMPtr targetURI; + nsCOMPtr baseURI = mOriginal ? mOriginal->GetBaseURI() : GetBaseURI(); nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href, GetCurrentDoc(), baseURI); diff --git a/layout/svg/base/src/nsSVGUtils.cpp b/layout/svg/base/src/nsSVGUtils.cpp index 78dfca4706d3..5a381a455ff2 100644 --- a/layout/svg/base/src/nsSVGUtils.cpp +++ b/layout/svg/base/src/nsSVGUtils.cpp @@ -408,8 +408,22 @@ nsresult nsSVGUtils::GetReferencedFrame(nsIFrame **aRefFrame, nsIURI* aURI, nsIC if (!content) return NS_ERROR_FAILURE; - // Get the Primary Frame - NS_ASSERTION(aPresShell, "Get referenced SVG frame -- no pres shell provided"); + nsIDocument* doc = content->GetCurrentDoc(); + if (!doc) + return NS_ERROR_FAILURE; + + if (aPresShell->GetDocument() != doc) { + // External reference; switch to the right presshell + aPresShell = doc->GetPrimaryShell(); + } +#ifdef DEBUG + else { + // Get the Primary Frame + NS_ASSERTION(aPresShell, + "Get referenced SVG frame -- no pres shell provided"); + } +#endif + if (!aPresShell) return NS_ERROR_FAILURE;