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

This commit is contained in:
Boris Zbarsky 2008-09-28 15:16:15 -04:00
Родитель 15a34c883c
Коммит c9e6929f52
4 изменённых файлов: 155 добавлений и 23 удалений

Просмотреть файл

@ -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<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;

Просмотреть файл

@ -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<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
@ -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<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);
}
@ -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<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;
}

Просмотреть файл

@ -300,7 +300,10 @@ nsSVGUseElement::CreateAnonymousContent()
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);
@ -416,7 +419,8 @@ nsSVGUseElement::LookupHref()
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);

Просмотреть файл

@ -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;