Bug 433616 part 3. Integration of externa resource documents into nsReferencedElement, plus some SVG fixups needed to handle nsReferencedElement returning an element from a different document, r+sr=roc
--- a/content/base/public/nsReferencedElement.h
+++ b/content/base/public/nsReferencedElement.h
@@ -65,19 +65,16 @@ class nsCycleCollectionCallback;
* Override IsPersistent to return PR_TRUE if you want to keep tracking after
* the first change.
*/
class nsReferencedElement {
public:
nsReferencedElement() {}
~nsReferencedElement() {
Unlink();
- if (mPendingNotification) {
- mPendingNotification->Clear();
- }
}
/**
* Find which element, if any, is referenced.
*/
nsIContent* get() { return mContent; }
/**
@@ -109,40 +106,92 @@ protected:
mContent = aTo;
}
/**
* Override this to convert from a single-shot notification to
* 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;
mTarget->ContentChanged(mFrom, mTo);
}
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<nsIContent> mFrom;
nsCOMPtr<nsIContent> 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<nsIAtom> mWatchID;
nsCOMPtr<nsIDocument> mWatchDocument;
nsCOMPtr<nsIContent> mContent;
nsRefPtr<Notification> mPendingNotification;
};
#endif /*NSREFERENCEDELEMENT_H_*/
--- a/content/base/src/nsReferencedElement.cpp
+++ b/content/base/src/nsReferencedElement.cpp
@@ -123,18 +123,36 @@ nsReferencedElement::Reset(nsIContent* a
documentURL = do_QueryInterface(binding->PrototypeBinding()->DocURI());
isXBL = PR_TRUE;
}
}
if (!documentURL)
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<nsIDocument::ExternalResourceLoad> 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
if (isXBL) {
nsCOMPtr<nsIDOMNodeList> anonymousChildren;
doc->BindingManager()->
GetAnonymousNodesFor(bindingParent, getter_AddRefs(anonymousChildren));
@@ -145,34 +163,51 @@ nsReferencedElement::Reset(nsIContent* a
nsCOMPtr<nsIDOMNode> node;
anonymousChildren->Item(i, getter_AddRefs(node));
nsCOMPtr<nsIContent> c = do_QueryInterface(node);
if (c) {
mContent = nsContentUtils::MatchElementId(c, ref);
}
}
}
+
+ // We don't have watching working yet for XBL, so bail out here.
return;
}
if (aWatch) {
nsCOMPtr<nsIAtom> atom = do_GetAtom(ref);
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<nsIDOMDocument> domDoc = do_QueryInterface(doc);
+ if (!aDocument) {
+ return;
+ }
+ nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aDocument);
NS_ASSERTION(domDoc, "Content doesn't reference a dom Document");
nsCOMPtr<nsIDOMElement> element;
- rv = domDoc->GetElementById(ref, getter_AddRefs(element));
+ domDoc->GetElementById(aRef, getter_AddRefs(element));
if (element) {
mContent = do_QueryInterface(element);
}
}
void
nsReferencedElement::Traverse(nsCycleCollectionTraversalCallback* aCB)
{
@@ -181,32 +216,62 @@ nsReferencedElement::Traverse(nsCycleCol
}
void
nsReferencedElement::Unlink()
{
if (mWatchDocument && mWatchID) {
mWatchDocument->RemoveIDTargetObserver(mWatchID, Observe, this);
}
+ if (mPendingNotification) {
+ mPendingNotification->Clear();
+ }
mWatchDocument = nsnull;
mWatchID = nsnull;
mContent = nsnull;
}
PRBool
nsReferencedElement::Observe(nsIContent* aOldContent,
nsIContent* aNewContent, void* aData)
{
nsReferencedElement* p = static_cast<nsReferencedElement*>(aData);
if (p->mPendingNotification) {
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) {
p->mWatchDocument = nsnull;
p->mWatchID = nsnull;
}
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<nsIDocument> 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;
+}
--- a/content/svg/content/src/nsSVGUseElement.cpp
+++ b/content/svg/content/src/nsSVGUseElement.cpp
@@ -295,17 +295,20 @@ nsSVGUseElement::CreateAnonymousContent(
if (useImpl && useImpl->mOriginal == mOriginal)
return nsnull;
}
}
}
nsCOMPtr<nsIDOMNode> newnode;
nsCOMArray<nsINode> 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<nsIContent> newcontent = do_QueryInterface(newnode);
if (!newcontent)
return nsnull;
nsCOMPtr<nsIDOMSVGSymbolElement> symbol = do_QueryInterface(newcontent);
@@ -411,17 +414,18 @@ nsSVGUseElement::SyncWidthHeight(PRUint8
void
nsSVGUseElement::LookupHref()
{
const nsString &href = mStringAttributes[HREF].GetAnimValue();
if (href.IsEmpty())
return;
- nsCOMPtr<nsIURI> targetURI, baseURI = GetBaseURI();
+ nsCOMPtr<nsIURI> targetURI;
+ nsCOMPtr<nsIURI> baseURI = mOriginal ? mOriginal->GetBaseURI() : GetBaseURI();
nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
GetCurrentDoc(), baseURI);
mSource.Reset(this, targetURI);
}
void
nsSVGUseElement::TriggerReclone()
--- a/layout/svg/base/src/nsSVGUtils.cpp
+++ b/layout/svg/base/src/nsSVGUtils.cpp
@@ -403,18 +403,32 @@ nsresult nsSVGUtils::GetReferencedFrame(
nsIPresShell *aPresShell)
{
*aRefFrame = nsnull;
nsIContent* content = nsContentUtils::GetReferencedElement(aURI, aContent);
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;
*aRefFrame = aPresShell->GetPrimaryFrameFor(content);
if (!(*aRefFrame)) return NS_ERROR_FAILURE;
return NS_OK;
}